typebars 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cjs/analyzer.d.ts +20 -2
  2. package/dist/cjs/analyzer.js +1 -1
  3. package/dist/cjs/analyzer.js.map +1 -1
  4. package/dist/cjs/compiled-template.d.ts +5 -5
  5. package/dist/cjs/compiled-template.js +1 -1
  6. package/dist/cjs/compiled-template.js.map +1 -1
  7. package/dist/cjs/dispatch.d.ts +99 -0
  8. package/dist/cjs/dispatch.js +2 -0
  9. package/dist/cjs/dispatch.js.map +1 -0
  10. package/dist/cjs/errors.d.ts +7 -0
  11. package/dist/cjs/errors.js +2 -2
  12. package/dist/cjs/errors.js.map +1 -1
  13. package/dist/cjs/executor.d.ts +2 -2
  14. package/dist/cjs/executor.js +1 -1
  15. package/dist/cjs/executor.js.map +1 -1
  16. package/dist/cjs/index.d.ts +1 -1
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/parser.d.ts +62 -0
  19. package/dist/cjs/parser.js +1 -1
  20. package/dist/cjs/parser.js.map +1 -1
  21. package/dist/cjs/typebars.d.ts +5 -5
  22. package/dist/cjs/typebars.js +1 -1
  23. package/dist/cjs/typebars.js.map +1 -1
  24. package/dist/cjs/types.d.ts +14 -1
  25. package/dist/cjs/types.js.map +1 -1
  26. package/dist/esm/analyzer.d.ts +20 -2
  27. package/dist/esm/analyzer.js +1 -1
  28. package/dist/esm/analyzer.js.map +1 -1
  29. package/dist/esm/compiled-template.d.ts +5 -5
  30. package/dist/esm/compiled-template.js +1 -1
  31. package/dist/esm/compiled-template.js.map +1 -1
  32. package/dist/esm/dispatch.d.ts +99 -0
  33. package/dist/esm/dispatch.js +2 -0
  34. package/dist/esm/dispatch.js.map +1 -0
  35. package/dist/esm/errors.d.ts +7 -0
  36. package/dist/esm/errors.js +1 -1
  37. package/dist/esm/errors.js.map +1 -1
  38. package/dist/esm/executor.d.ts +2 -2
  39. package/dist/esm/executor.js +1 -1
  40. package/dist/esm/executor.js.map +1 -1
  41. package/dist/esm/index.d.ts +1 -1
  42. package/dist/esm/index.js.map +1 -1
  43. package/dist/esm/parser.d.ts +62 -0
  44. package/dist/esm/parser.js +1 -1
  45. package/dist/esm/parser.js.map +1 -1
  46. package/dist/esm/typebars.d.ts +5 -5
  47. package/dist/esm/typebars.js +1 -1
  48. package/dist/esm/typebars.js.map +1 -1
  49. package/dist/esm/types.d.ts +14 -1
  50. package/dist/esm/types.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,99 @@
1
+ import type { JSONSchema7 } from "json-schema";
2
+ import type { AnalysisResult, TemplateInput } from "./types.js";
3
+ /** Options controlling recursive dispatching behavior */
4
+ export interface DispatchAnalyzeOptions {
5
+ /** Schemas by template identifier */
6
+ identifierSchemas?: Record<number, JSONSchema7>;
7
+ /** Explicit coercion schema for static literal output type */
8
+ coerceSchema?: JSONSchema7;
9
+ /** When true, exclude entries containing Handlebars expressions */
10
+ excludeTemplateExpression?: boolean;
11
+ }
12
+ /** Options controlling recursive execution dispatching */
13
+ export interface DispatchExecuteOptions {
14
+ /** Explicit coercion schema for output type coercion */
15
+ coerceSchema?: JSONSchema7;
16
+ }
17
+ /**
18
+ * Dispatches a `TemplateInput` for analysis, handling the array/object/literal
19
+ * cases generically and delegating the string (template) case to a callback.
20
+ *
21
+ * @param template - The input to analyze
22
+ * @param options - Dispatching options (coerceSchema, excludeTemplateExpression)
23
+ * @param analyzeString - Callback for analyzing a string template.
24
+ * Receives `(template, coerceSchema?)` and must return an `AnalysisResult`.
25
+ * @param recurse - Callback for recursively analyzing a child `TemplateInput`.
26
+ * Receives `(child, options?)` and must return an `AnalysisResult`.
27
+ * This allows callers (like `Typebars`) to rebind `this` or inject
28
+ * additional context on each recursive call.
29
+ * @returns An `AnalysisResult`
30
+ */
31
+ export declare function dispatchAnalyze(template: TemplateInput, options: DispatchAnalyzeOptions | undefined, analyzeString: (template: string, coerceSchema?: JSONSchema7) => AnalysisResult, recurse: (child: TemplateInput, options?: DispatchAnalyzeOptions) => AnalysisResult): AnalysisResult;
32
+ /**
33
+ * Dispatches a `TemplateInput` for execution, handling the array/object/literal
34
+ * cases generically and delegating the string (template) case to a callback.
35
+ *
36
+ * @param template - The input to execute
37
+ * @param options - Dispatching options (coerceSchema)
38
+ * @param executeString - Callback for executing a string template.
39
+ * Receives `(template, coerceSchema?)` and must return the result.
40
+ * @param recurse - Callback for recursively executing a child `TemplateInput`.
41
+ * Receives `(child, options?)` and must return the result.
42
+ * @returns The execution result
43
+ */
44
+ export declare function dispatchExecute(template: TemplateInput, options: DispatchExecuteOptions | undefined, executeString: (template: string, coerceSchema?: JSONSchema7) => unknown, recurse: (child: TemplateInput, options?: DispatchExecuteOptions) => unknown): unknown;
45
+ /** Options for combined analyze-and-execute dispatching */
46
+ export interface DispatchAnalyzeAndExecuteOptions {
47
+ identifierSchemas?: Record<number, JSONSchema7>;
48
+ identifierData?: Record<number, Record<string, unknown>>;
49
+ coerceSchema?: JSONSchema7;
50
+ }
51
+ /**
52
+ * Dispatches a `TemplateInput` for combined analysis and execution,
53
+ * handling array/object/literal cases generically and delegating
54
+ * the string case to a callback.
55
+ *
56
+ * @param template - The input to process
57
+ * @param options - Options (identifierSchemas, identifierData, coerceSchema)
58
+ * @param processString - Callback for analyzing and executing a string template.
59
+ * Receives `(template, coerceSchema?)` and must return
60
+ * `{ analysis, value }`.
61
+ * @param recurse - Callback for recursively processing a child `TemplateInput`.
62
+ * @returns `{ analysis, value }` where `value` is `undefined` if analysis fails
63
+ */
64
+ export declare function dispatchAnalyzeAndExecute(template: TemplateInput, options: DispatchAnalyzeAndExecuteOptions | undefined, processString: (template: string, coerceSchema?: JSONSchema7) => {
65
+ analysis: AnalysisResult;
66
+ value: unknown;
67
+ }, recurse: (child: TemplateInput, options?: DispatchAnalyzeAndExecuteOptions) => {
68
+ analysis: AnalysisResult;
69
+ value: unknown;
70
+ }): {
71
+ analysis: AnalysisResult;
72
+ value: unknown;
73
+ };
74
+ /**
75
+ * Resolves the child `coerceSchema` for a given object key.
76
+ *
77
+ * When a `coerceSchema` is provided, navigates into its `properties`
78
+ * to find the schema for the given key. This allows deeply nested
79
+ * objects to propagate coercion at every level.
80
+ *
81
+ * @param coerceSchema - The parent coercion schema (may be `undefined`)
82
+ * @param key - The object property key
83
+ * @returns The child coercion schema, or `undefined`
84
+ */
85
+ export declare function resolveChildCoerceSchema(coerceSchema: JSONSchema7 | undefined, key: string): JSONSchema7 | undefined;
86
+ /**
87
+ * Determines whether a `TemplateInput` value should be excluded when
88
+ * `excludeTemplateExpression` is enabled.
89
+ *
90
+ * A value is excluded if it is a string containing at least one Handlebars
91
+ * expression (`{{…}}`). Literals (number, boolean, null), plain strings
92
+ * without expressions, objects, and arrays are never excluded at the
93
+ * entry level — objects and arrays are recursively filtered by the
94
+ * dispatching functions themselves.
95
+ *
96
+ * @param input - The template input to check
97
+ * @returns `true` if the input should be excluded
98
+ */
99
+ export declare function shouldExcludeEntry(input: TemplateInput): boolean;
@@ -0,0 +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 dispatchAnalyze(){return dispatchAnalyze},get dispatchAnalyzeAndExecute(){return dispatchAnalyzeAndExecute},get dispatchExecute(){return dispatchExecute},get resolveChildCoerceSchema(){return resolveChildCoerceSchema},get shouldExcludeEntry(){return shouldExcludeEntry}});const _parserts=require("./parser.js");const _schemaresolverts=require("./schema-resolver.js");const _typests=require("./types.js");const _utilsts=require("./utils.js");function dispatchAnalyze(template,options,analyzeString,recurse){if((0,_typests.isArrayInput)(template)){const exclude=options?.excludeTemplateExpression===true;if(exclude){const kept=template.filter(item=>!shouldExcludeEntry(item));return(0,_utilsts.aggregateArrayAnalysis)(kept.length,index=>recurse(kept[index],options))}return(0,_utilsts.aggregateArrayAnalysis)(template.length,index=>recurse(template[index],options))}if((0,_typests.isObjectInput)(template)){return dispatchObjectAnalysis(template,options,recurse)}if((0,_typests.isLiteralInput)(template)){return{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)}}return analyzeString(template,options?.coerceSchema)}function dispatchObjectAnalysis(template,options,recurse){const coerceSchema=options?.coerceSchema;const exclude=options?.excludeTemplateExpression===true;const keys=exclude?Object.keys(template).filter(key=>!shouldExcludeEntry(template[key])):Object.keys(template);return(0,_utilsts.aggregateObjectAnalysis)(keys,key=>{const childCoerceSchema=resolveChildCoerceSchema(coerceSchema,key);return recurse(template[key],{identifierSchemas:options?.identifierSchemas,coerceSchema:childCoerceSchema,excludeTemplateExpression:options?.excludeTemplateExpression})})}function dispatchExecute(template,options,executeString,recurse){if((0,_typests.isArrayInput)(template)){const result=[];for(const element of template){result.push(recurse(element,options))}return result}if((0,_typests.isObjectInput)(template)){const coerceSchema=options?.coerceSchema;const result={};for(const[key,value]of Object.entries(template)){const childCoerceSchema=resolveChildCoerceSchema(coerceSchema,key);result[key]=recurse(value,{...options,coerceSchema:childCoerceSchema})}return result}if((0,_typests.isLiteralInput)(template))return template;return executeString(template,options?.coerceSchema)}function dispatchAnalyzeAndExecute(template,options,processString,recurse){if((0,_typests.isArrayInput)(template)){return(0,_utilsts.aggregateArrayAnalysisAndExecution)(template.length,index=>recurse(template[index],options))}if((0,_typests.isObjectInput)(template)){const coerceSchema=options?.coerceSchema;return(0,_utilsts.aggregateObjectAnalysisAndExecution)(Object.keys(template),key=>{const childCoerceSchema=resolveChildCoerceSchema(coerceSchema,key);return recurse(template[key],{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}}return processString(template,options?.coerceSchema)}function resolveChildCoerceSchema(coerceSchema,key){return coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined}function shouldExcludeEntry(input){return typeof input==="string"&&(0,_parserts.hasHandlebarsExpression)(input)}
2
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/dispatch.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport { hasHandlebarsExpression } from \"./parser.ts\";\nimport { resolveSchemaPath } from \"./schema-resolver.ts\";\nimport type { AnalysisResult, TemplateInput } 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} from \"./utils.ts\";\n\n// ─── Template Input Dispatching ──────────────────────────────────────────────\n// Factorized dispatching for recursive processing of `TemplateInput` values.\n//\n// Every method in the engine (`analyze`, `execute`, `analyzeAndExecute`,\n// `compile`) follows the same recursive pattern:\n//\n// 1. If the input is an **array** → process each element recursively\n// 2. If the input is an **object** → process each property recursively\n// 3. If the input is a **literal** (number, boolean, null) → passthrough\n// 4. If the input is a **string** → delegate to a template-specific handler\n//\n// This module extracts the common dispatching logic into generic functions\n// that accept a callback for the string (template) case. This eliminates\n// the duplication across `Typebars`, `CompiledTemplate`, and `analyzer.ts`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Options controlling recursive dispatching behavior */\nexport interface DispatchAnalyzeOptions {\n\t/** Schemas by template identifier */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Explicit coercion schema for static literal output type */\n\tcoerceSchema?: JSONSchema7;\n\t/** When true, exclude entries containing Handlebars expressions */\n\texcludeTemplateExpression?: boolean;\n}\n\n/** Options controlling recursive execution dispatching */\nexport interface DispatchExecuteOptions {\n\t/** Explicit coercion schema for output type coercion */\n\tcoerceSchema?: JSONSchema7;\n}\n\n// ─── Analysis Dispatching ────────────────────────────────────────────────────\n\n/**\n * Dispatches a `TemplateInput` for analysis, handling the array/object/literal\n * cases generically and delegating the string (template) case to a callback.\n *\n * @param template - The input to analyze\n * @param options - Dispatching options (coerceSchema, excludeTemplateExpression)\n * @param analyzeString - Callback for analyzing a string template.\n * Receives `(template, coerceSchema?)` and must return an `AnalysisResult`.\n * @param recurse - Callback for recursively analyzing a child `TemplateInput`.\n * Receives `(child, options?)` and must return an `AnalysisResult`.\n * This allows callers (like `Typebars`) to rebind `this` or inject\n * additional context on each recursive call.\n * @returns An `AnalysisResult`\n */\nexport function dispatchAnalyze(\n\ttemplate: TemplateInput,\n\toptions: DispatchAnalyzeOptions | undefined,\n\tanalyzeString: (\n\t\ttemplate: string,\n\t\tcoerceSchema?: JSONSchema7,\n\t) => AnalysisResult,\n\trecurse: (\n\t\tchild: TemplateInput,\n\t\toptions?: DispatchAnalyzeOptions,\n\t) => AnalysisResult,\n): AnalysisResult {\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\tconst exclude = options?.excludeTemplateExpression === true;\n\t\tif (exclude) {\n\t\t\tconst kept = template.filter(\n\t\t\t\t(item) => !shouldExcludeEntry(item as TemplateInput),\n\t\t\t);\n\t\t\treturn aggregateArrayAnalysis(kept.length, (index) =>\n\t\t\t\trecurse(kept[index] as TemplateInput, options),\n\t\t\t);\n\t\t}\n\t\treturn aggregateArrayAnalysis(template.length, (index) =>\n\t\t\trecurse(template[index] as TemplateInput, options),\n\t\t);\n\t}\n\n\t// ── Object ────────────────────────────────────────────────────────────\n\tif (isObjectInput(template)) {\n\t\treturn dispatchObjectAnalysis(template, options, recurse);\n\t}\n\n\t// ── Literal (number, boolean, null) ───────────────────────────────────\n\tif (isLiteralInput(template)) {\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tdiagnostics: [],\n\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t};\n\t}\n\n\t// ── String template ──────────────────────────────────────────────────\n\treturn analyzeString(template, options?.coerceSchema);\n}\n\n/**\n * Dispatches object analysis with `coerceSchema` propagation and\n * `excludeTemplateExpression` filtering.\n *\n * Extracted as a separate function because the object case is the most\n * complex (key filtering + per-key coerceSchema resolution).\n */\nfunction dispatchObjectAnalysis(\n\ttemplate: Record<string, TemplateInput>,\n\toptions: DispatchAnalyzeOptions | undefined,\n\trecurse: (\n\t\tchild: TemplateInput,\n\t\toptions?: DispatchAnalyzeOptions,\n\t) => AnalysisResult,\n): AnalysisResult {\n\tconst coerceSchema = options?.coerceSchema;\n\tconst exclude = options?.excludeTemplateExpression === true;\n\n\tconst keys = exclude\n\t\t? Object.keys(template).filter(\n\t\t\t\t(key) => !shouldExcludeEntry(template[key] as TemplateInput),\n\t\t\t)\n\t\t: Object.keys(template);\n\n\treturn aggregateObjectAnalysis(keys, (key) => {\n\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\treturn recurse(template[key] as TemplateInput, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\texcludeTemplateExpression: options?.excludeTemplateExpression,\n\t\t});\n\t});\n}\n\n// ─── Execution Dispatching ───────────────────────────────────────────────────\n\n/**\n * Dispatches a `TemplateInput` for execution, handling the array/object/literal\n * cases generically and delegating the string (template) case to a callback.\n *\n * @param template - The input to execute\n * @param options - Dispatching options (coerceSchema)\n * @param executeString - Callback for executing a string template.\n * Receives `(template, coerceSchema?)` and must return the result.\n * @param recurse - Callback for recursively executing a child `TemplateInput`.\n * Receives `(child, options?)` and must return the result.\n * @returns The execution result\n */\nexport function dispatchExecute(\n\ttemplate: TemplateInput,\n\toptions: DispatchExecuteOptions | undefined,\n\texecuteString: (template: string, coerceSchema?: JSONSchema7) => unknown,\n\trecurse: (child: TemplateInput, options?: DispatchExecuteOptions) => unknown,\n): unknown {\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\tconst result: unknown[] = [];\n\t\tfor (const element of template) {\n\t\t\tresult.push(recurse(element, options));\n\t\t}\n\t\treturn result;\n\t}\n\n\t// ── Object ────────────────────────────────────────────────────────────\n\tif (isObjectInput(template)) {\n\t\tconst coerceSchema = options?.coerceSchema;\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\tresult[key] = recurse(value, {\n\t\t\t\t...options,\n\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t}\n\n\t// ── Literal (number, boolean, null) ───────────────────────────────────\n\tif (isLiteralInput(template)) return template;\n\n\t// ── String template ──────────────────────────────────────────────────\n\treturn executeString(template, options?.coerceSchema);\n}\n\n// ─── Analyze-and-Execute Dispatching ─────────────────────────────────────────\n\n/** Options for combined analyze-and-execute dispatching */\nexport interface DispatchAnalyzeAndExecuteOptions {\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\tcoerceSchema?: JSONSchema7;\n}\n\n/**\n * Dispatches a `TemplateInput` for combined analysis and execution,\n * handling array/object/literal cases generically and delegating\n * the string case to a callback.\n *\n * @param template - The input to process\n * @param options - Options (identifierSchemas, identifierData, coerceSchema)\n * @param processString - Callback for analyzing and executing a string template.\n * Receives `(template, coerceSchema?)` and must return\n * `{ analysis, value }`.\n * @param recurse - Callback for recursively processing a child `TemplateInput`.\n * @returns `{ analysis, value }` where `value` is `undefined` if analysis fails\n */\nexport function dispatchAnalyzeAndExecute(\n\ttemplate: TemplateInput,\n\toptions: DispatchAnalyzeAndExecuteOptions | undefined,\n\tprocessString: (\n\t\ttemplate: string,\n\t\tcoerceSchema?: JSONSchema7,\n\t) => { analysis: AnalysisResult; value: unknown },\n\trecurse: (\n\t\tchild: TemplateInput,\n\t\toptions?: DispatchAnalyzeAndExecuteOptions,\n\t) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\treturn aggregateArrayAnalysisAndExecution(template.length, (index) =>\n\t\t\trecurse(template[index] as TemplateInput, options),\n\t\t);\n\t}\n\n\t// ── Object ────────────────────────────────────────────────────────────\n\tif (isObjectInput(template)) {\n\t\tconst coerceSchema = options?.coerceSchema;\n\t\treturn aggregateObjectAnalysisAndExecution(Object.keys(template), (key) => {\n\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\treturn recurse(template[key] as TemplateInput, {\n\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t});\n\t\t});\n\t}\n\n\t// ── Literal (number, boolean, null) ───────────────────────────────────\n\tif (isLiteralInput(template)) {\n\t\treturn {\n\t\t\tanalysis: {\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\tvalue: template,\n\t\t};\n\t}\n\n\t// ── String template ──────────────────────────────────────────────────\n\treturn processString(template, options?.coerceSchema);\n}\n\n// ─── Internal Utilities ──────────────────────────────────────────────────────\n\n/**\n * Resolves the child `coerceSchema` for a given object key.\n *\n * When a `coerceSchema` is provided, navigates into its `properties`\n * to find the schema for the given key. This allows deeply nested\n * objects to propagate coercion at every level.\n *\n * @param coerceSchema - The parent coercion schema (may be `undefined`)\n * @param key - The object property key\n * @returns The child coercion schema, or `undefined`\n */\nexport function resolveChildCoerceSchema(\n\tcoerceSchema: JSONSchema7 | undefined,\n\tkey: string,\n): JSONSchema7 | undefined {\n\treturn coerceSchema ? resolveSchemaPath(coerceSchema, [key]) : undefined;\n}\n\n/**\n * Determines whether a `TemplateInput` value should be excluded when\n * `excludeTemplateExpression` is enabled.\n *\n * A value is excluded if it is a string containing at least one Handlebars\n * expression (`{{…}}`). Literals (number, boolean, null), plain strings\n * without expressions, objects, and arrays are never excluded at the\n * entry level — objects and arrays are recursively filtered by the\n * dispatching functions themselves.\n *\n * @param input - The template input to check\n * @returns `true` if the input should be excluded\n */\nexport function shouldExcludeEntry(input: TemplateInput): boolean {\n\treturn typeof input === \"string\" && hasHandlebarsExpression(input);\n}\n"],"names":["dispatchAnalyze","dispatchAnalyzeAndExecute","dispatchExecute","resolveChildCoerceSchema","shouldExcludeEntry","template","options","analyzeString","recurse","isArrayInput","exclude","excludeTemplateExpression","kept","filter","item","aggregateArrayAnalysis","length","index","isObjectInput","dispatchObjectAnalysis","isLiteralInput","valid","diagnostics","outputSchema","inferPrimitiveSchema","coerceSchema","keys","Object","key","aggregateObjectAnalysis","childCoerceSchema","identifierSchemas","executeString","result","element","push","value","entries","processString","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","identifierData","analysis","resolveSchemaPath","undefined","input","hasHandlebarsExpression"],"mappings":"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"}
@@ -103,3 +103,10 @@ export declare function createUnknownHelperMessage(helperName: string): string;
103
103
  * Creates a message for an expression that cannot be statically analyzed.
104
104
  */
105
105
  export declare function createUnanalyzableMessage(nodeType: string): string;
106
+ /**
107
+ * Creates a message for an invalid `$root` path traversal attempt.
108
+ *
109
+ * `$root` references the entire input schema/data and cannot be followed
110
+ * by property access (e.g. `$root.name` is invalid — use `{{name}}` instead).
111
+ */
112
+ export declare function createRootPathTraversalMessage(fullPath: string): string;
@@ -1,3 +1,3 @@
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 TemplateAnalysisError(){return TemplateAnalysisError},get TemplateError(){return TemplateError},get TemplateParseError(){return TemplateParseError},get TemplateRuntimeError(){return TemplateRuntimeError},get UnsupportedSchemaError(){return UnsupportedSchemaError},get createMissingArgumentMessage(){return createMissingArgumentMessage},get createPropertyNotFoundMessage(){return createPropertyNotFoundMessage},get createTypeMismatchMessage(){return createTypeMismatchMessage},get createUnanalyzableMessage(){return createUnanalyzableMessage},get createUnknownHelperMessage(){return createUnknownHelperMessage}});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 TemplateError extends Error{toJSON(){return{name:this.name,message:this.message}}constructor(message){super(message);this.name="TemplateError"}}class TemplateParseError extends TemplateError{toJSON(){return{name:this.name,message:this.message,loc:this.loc,source:this.source}}constructor(message,loc,source){super(`Parse error: ${message}`),_define_property(this,"loc",void 0),_define_property(this,"source",void 0),this.loc=loc,this.source=source;this.name="TemplateParseError"}}class TemplateAnalysisError extends TemplateError{toJSON(){return{name:this.name,message:this.message,errorCount:this.errorCount,warningCount:this.warningCount,diagnostics:this.diagnostics}}constructor(diagnostics){const errors=diagnostics.filter(d=>d.severity==="error");const warnings=diagnostics.filter(d=>d.severity==="warning");const summary=errors.map(d=>formatDiagnosticLine(d)).join("\n");super(`Static analysis failed with ${errors.length} error(s):
2
- ${summary}`),_define_property(this,"diagnostics",void 0),_define_property(this,"errors",void 0),_define_property(this,"warnings",void 0),_define_property(this,"errorCount",void 0),_define_property(this,"warningCount",void 0);this.name="TemplateAnalysisError";this.diagnostics=diagnostics;this.errors=errors;this.warnings=warnings;this.errorCount=errors.length;this.warningCount=warnings.length}}class TemplateRuntimeError extends TemplateError{constructor(message){super(`Runtime error: ${message}`);this.name="TemplateRuntimeError"}}class UnsupportedSchemaError extends TemplateError{toJSON(){return{name:this.name,message:this.message,keyword:this.keyword,schemaPath:this.schemaPath}}constructor(keyword,schemaPath){super(`Unsupported JSON Schema feature: "${keyword}" at "${schemaPath}". `+"Conditional schemas (if/then/else) cannot be resolved during static analysis "+"because they depend on runtime data. Consider using oneOf/anyOf combinators instead."),_define_property(this,"keyword",void 0),_define_property(this,"schemaPath",void 0),this.keyword=keyword,this.schemaPath=schemaPath;this.name="UnsupportedSchemaError"}}function formatDiagnosticLine(diag){const parts=[` • [${diag.code}] ${diag.message}`];if(diag.loc){parts.push(`(at ${diag.loc.start.line}:${diag.loc.start.column})`)}return parts.join(" ")}function createPropertyNotFoundMessage(path,availableProperties){const base=`Property "${path}" does not exist in the context schema`;if(availableProperties.length===0)return base;return`${base}. Available properties: ${availableProperties.join(", ")}`}function createTypeMismatchMessage(helperName,expected,actual){return`"{{#${helperName}}}" expects ${expected}, but resolved schema has type "${actual}"`}function createMissingArgumentMessage(helperName){return`"{{#${helperName}}}" requires an argument`}function createUnknownHelperMessage(helperName){return`Unknown block helper "{{#${helperName}}}" — cannot analyze statically`}function createUnanalyzableMessage(nodeType){return`Expression of type "${nodeType}" cannot be statically analyzed`}
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 TemplateAnalysisError(){return TemplateAnalysisError},get TemplateError(){return TemplateError},get TemplateParseError(){return TemplateParseError},get TemplateRuntimeError(){return TemplateRuntimeError},get UnsupportedSchemaError(){return UnsupportedSchemaError},get createMissingArgumentMessage(){return createMissingArgumentMessage},get createPropertyNotFoundMessage(){return createPropertyNotFoundMessage},get createRootPathTraversalMessage(){return createRootPathTraversalMessage},get createTypeMismatchMessage(){return createTypeMismatchMessage},get createUnanalyzableMessage(){return createUnanalyzableMessage},get createUnknownHelperMessage(){return createUnknownHelperMessage}});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 TemplateError extends Error{toJSON(){return{name:this.name,message:this.message}}constructor(message){super(message);this.name="TemplateError"}}class TemplateParseError extends TemplateError{toJSON(){return{name:this.name,message:this.message,loc:this.loc,source:this.source}}constructor(message,loc,source){super(`Parse error: ${message}`),_define_property(this,"loc",void 0),_define_property(this,"source",void 0),this.loc=loc,this.source=source;this.name="TemplateParseError"}}class TemplateAnalysisError extends TemplateError{toJSON(){return{name:this.name,message:this.message,errorCount:this.errorCount,warningCount:this.warningCount,diagnostics:this.diagnostics}}constructor(diagnostics){const errors=diagnostics.filter(d=>d.severity==="error");const warnings=diagnostics.filter(d=>d.severity==="warning");const summary=errors.map(d=>formatDiagnosticLine(d)).join("\n");super(`Static analysis failed with ${errors.length} error(s):
2
+ ${summary}`),_define_property(this,"diagnostics",void 0),_define_property(this,"errors",void 0),_define_property(this,"warnings",void 0),_define_property(this,"errorCount",void 0),_define_property(this,"warningCount",void 0);this.name="TemplateAnalysisError";this.diagnostics=diagnostics;this.errors=errors;this.warnings=warnings;this.errorCount=errors.length;this.warningCount=warnings.length}}class TemplateRuntimeError extends TemplateError{constructor(message){super(`Runtime error: ${message}`);this.name="TemplateRuntimeError"}}class UnsupportedSchemaError extends TemplateError{toJSON(){return{name:this.name,message:this.message,keyword:this.keyword,schemaPath:this.schemaPath}}constructor(keyword,schemaPath){super(`Unsupported JSON Schema feature: "${keyword}" at "${schemaPath}". `+"Conditional schemas (if/then/else) cannot be resolved during static analysis "+"because they depend on runtime data. Consider using oneOf/anyOf combinators instead."),_define_property(this,"keyword",void 0),_define_property(this,"schemaPath",void 0),this.keyword=keyword,this.schemaPath=schemaPath;this.name="UnsupportedSchemaError"}}function formatDiagnosticLine(diag){const parts=[` • [${diag.code}] ${diag.message}`];if(diag.loc){parts.push(`(at ${diag.loc.start.line}:${diag.loc.start.column})`)}return parts.join(" ")}function createPropertyNotFoundMessage(path,availableProperties){const base=`Property "${path}" does not exist in the context schema`;if(availableProperties.length===0)return base;return`${base}. Available properties: ${availableProperties.join(", ")}`}function createTypeMismatchMessage(helperName,expected,actual){return`"{{#${helperName}}}" expects ${expected}, but resolved schema has type "${actual}"`}function createMissingArgumentMessage(helperName){return`"{{#${helperName}}}" requires an argument`}function createUnknownHelperMessage(helperName){return`Unknown block helper "{{#${helperName}}}" — cannot analyze statically`}function createUnanalyzableMessage(nodeType){return`Expression of type "${nodeType}" cannot be statically analyzed`}function createRootPathTraversalMessage(fullPath){return`"$root" references the entire input and cannot be used with property access ("${fullPath}"). Use "{{${fullPath.replace("$root.","")}}}" instead`}
3
3
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors.ts"],"sourcesContent":["import type { TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Base Class ──────────────────────────────────────────────────────────────\n// All template engine errors extend this class, enabling targeted catch blocks:\n// `catch (e) { if (e instanceof TemplateError) … }`\n// Subclasses:\n// - `TemplateParseError` — invalid template syntax\n// - `TemplateAnalysisError` — static analysis failures (diagnostics)\n// - `TemplateRuntimeError` — execution failures\n// - `UnsupportedSchemaError` — schema uses unsupported JSON Schema features\n\nexport class TemplateError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"TemplateError\";\n\t}\n\n\t/**\n\t * Serializes the error into a JSON-compatible object, suitable for sending\n\t * to a frontend or a structured logging system.\n\t */\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t};\n\t}\n}\n\n// ─── Parse Error ─────────────────────────────────────────────────────────────\n// Thrown when Handlebars fails to parse the template (invalid syntax).\n\nexport class TemplateParseError extends TemplateError {\n\tconstructor(\n\t\tmessage: string,\n\t\t/** Approximate position of the error in the source */\n\t\tpublic readonly loc?: { line: number; column: number },\n\t\t/** Fragment of the template source around the error */\n\t\tpublic readonly source?: string,\n\t) {\n\t\tsuper(`Parse error: ${message}`);\n\t\tthis.name = \"TemplateParseError\";\n\t}\n\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tloc: this.loc,\n\t\t\tsource: this.source,\n\t\t};\n\t}\n}\n\n// ─── Static Analysis Error ───────────────────────────────────────────────────\n// Thrown in strict mode when the analysis produces at least one error.\n// Contains the full list of diagnostics for detailed inspection.\n\nexport class TemplateAnalysisError extends TemplateError {\n\t/** Full list of diagnostics (errors + warnings) */\n\tpublic readonly diagnostics: TemplateDiagnostic[];\n\n\t/** Only diagnostics with \"error\" severity */\n\tpublic readonly errors: TemplateDiagnostic[];\n\n\t/** Only diagnostics with \"warning\" severity */\n\tpublic readonly warnings: TemplateDiagnostic[];\n\n\t/** Total number of errors */\n\tpublic readonly errorCount: number;\n\n\t/** Total number of warnings */\n\tpublic readonly warningCount: number;\n\n\tconstructor(diagnostics: TemplateDiagnostic[]) {\n\t\tconst errors = diagnostics.filter((d) => d.severity === \"error\");\n\t\tconst warnings = diagnostics.filter((d) => d.severity === \"warning\");\n\n\t\tconst summary = errors.map((d) => formatDiagnosticLine(d)).join(\"\\n\");\n\t\tsuper(`Static analysis failed with ${errors.length} error(s):\\n${summary}`);\n\n\t\tthis.name = \"TemplateAnalysisError\";\n\t\tthis.diagnostics = diagnostics;\n\t\tthis.errors = errors;\n\t\tthis.warnings = warnings;\n\t\tthis.errorCount = errors.length;\n\t\tthis.warningCount = warnings.length;\n\t}\n\n\t/**\n\t * Serializes the analysis error into a JSON-compatible object.\n\t *\n\t * Designed for direct use in API responses:\n\t * ```\n\t * res.status(400).json(error.toJSON());\n\t * ```\n\t *\n\t * Returned structure:\n\t * ```\n\t * {\n\t * name: \"TemplateAnalysisError\",\n\t * message: \"Static analysis failed with 2 error(s): ...\",\n\t * errorCount: 2,\n\t * warningCount: 0,\n\t * diagnostics: [\n\t * {\n\t * severity: \"error\",\n\t * code: \"UNKNOWN_PROPERTY\",\n\t * message: \"Property \\\"foo\\\" does not exist...\",\n\t * loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 7 } },\n\t * source: \"{{foo}}\",\n\t * details: { path: \"foo\", availableProperties: [\"name\", \"age\"] }\n\t * }\n\t * ]\n\t * }\n\t * ```\n\t */\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\terrorCount: this.errorCount,\n\t\t\twarningCount: this.warningCount,\n\t\t\tdiagnostics: this.diagnostics,\n\t\t};\n\t}\n}\n\n// ─── Runtime Error ───────────────────────────────────────────────────────────\n// Thrown when template execution fails (accessing a non-existent property\n// in strict mode, unexpected type, etc.).\n\nexport class TemplateRuntimeError extends TemplateError {\n\tconstructor(message: string) {\n\t\tsuper(`Runtime error: ${message}`);\n\t\tthis.name = \"TemplateRuntimeError\";\n\t}\n}\n\n// ─── Unsupported Schema Error ────────────────────────────────────────────────\n// Thrown when the provided JSON Schema uses features that cannot be handled\n// by static analysis (e.g. `if/then/else` conditional schemas).\n//\n// These features are data-dependent and fundamentally non-resolvable without\n// runtime values. Rather than silently ignoring them (which would produce\n// incorrect analysis results), we fail fast with a clear error message.\n\nexport class UnsupportedSchemaError extends TemplateError {\n\tconstructor(\n\t\t/** The unsupported keyword(s) detected (e.g. `\"if/then/else\"`) */\n\t\tpublic readonly keyword: string,\n\t\t/** JSON pointer path to the location in the schema (e.g. `\"/properties/user\"`) */\n\t\tpublic readonly schemaPath: string,\n\t) {\n\t\tsuper(\n\t\t\t`Unsupported JSON Schema feature: \"${keyword}\" at \"${schemaPath}\". ` +\n\t\t\t\t\"Conditional schemas (if/then/else) cannot be resolved during static analysis \" +\n\t\t\t\t\"because they depend on runtime data. Consider using oneOf/anyOf combinators instead.\",\n\t\t);\n\t\tthis.name = \"UnsupportedSchemaError\";\n\t}\n\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tkeyword: this.keyword,\n\t\t\tschemaPath: this.schemaPath,\n\t\t};\n\t}\n}\n\n// ─── Internal Utilities ──────────────────────────────────────────────────────\n\n/**\n * Formats a single diagnostic line for the summary message\n * of a `TemplateAnalysisError`.\n *\n * Produces a human-readable format:\n * ` • [UNKNOWN_PROPERTY] Property \"foo\" does not exist (at 1:0)`\n */\nfunction formatDiagnosticLine(diag: TemplateDiagnostic): string {\n\tconst parts: string[] = [` • [${diag.code}] ${diag.message}`];\n\n\tif (diag.loc) {\n\t\tparts.push(`(at ${diag.loc.start.line}:${diag.loc.start.column})`);\n\t}\n\n\treturn parts.join(\" \");\n}\n\n// ─── Common Error Factories ──────────────────────────────────────────────────\n// These functions simplify the creation of typed errors across the codebase.\n\n/**\n * Creates a structured diagnostic message for a missing property.\n * Used by the analyzer to produce enriched error messages with suggestions.\n */\nexport function createPropertyNotFoundMessage(\n\tpath: string,\n\tavailableProperties: string[],\n): string {\n\tconst base = `Property \"${path}\" does not exist in the context schema`;\n\tif (availableProperties.length === 0) return base;\n\treturn `${base}. Available properties: ${availableProperties.join(\", \")}`;\n}\n\n/**\n * Creates a message for a type mismatch on a block helper.\n */\nexport function createTypeMismatchMessage(\n\thelperName: string,\n\texpected: string,\n\tactual: string,\n): string {\n\treturn `\"{{#${helperName}}}\" expects ${expected}, but resolved schema has type \"${actual}\"`;\n}\n\n/**\n * Creates a message for a missing argument on a block helper.\n */\nexport function createMissingArgumentMessage(helperName: string): string {\n\treturn `\"{{#${helperName}}}\" requires an argument`;\n}\n\n/**\n * Creates a message for an unknown block helper.\n */\nexport function createUnknownHelperMessage(helperName: string): string {\n\treturn `Unknown block helper \"{{#${helperName}}}\" — cannot analyze statically`;\n}\n\n/**\n * Creates a message for an expression that cannot be statically analyzed.\n */\nexport function createUnanalyzableMessage(nodeType: string): string {\n\treturn `Expression of type \"${nodeType}\" cannot be statically analyzed`;\n}\n"],"names":["TemplateAnalysisError","TemplateError","TemplateParseError","TemplateRuntimeError","UnsupportedSchemaError","createMissingArgumentMessage","createPropertyNotFoundMessage","createTypeMismatchMessage","createUnanalyzableMessage","createUnknownHelperMessage","Error","toJSON","name","message","loc","source","errorCount","warningCount","diagnostics","errors","filter","d","severity","warnings","summary","map","formatDiagnosticLine","join","length","keyword","schemaPath","diag","parts","code","push","start","line","column","path","availableProperties","base","helperName","expected","actual","nodeType"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/errors.ts"],"sourcesContent":["import type { TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Base Class ──────────────────────────────────────────────────────────────\n// All template engine errors extend this class, enabling targeted catch blocks:\n// `catch (e) { if (e instanceof TemplateError) … }`\n// Subclasses:\n// - `TemplateParseError` — invalid template syntax\n// - `TemplateAnalysisError` — static analysis failures (diagnostics)\n// - `TemplateRuntimeError` — execution failures\n// - `UnsupportedSchemaError` — schema uses unsupported JSON Schema features\n\nexport class TemplateError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"TemplateError\";\n\t}\n\n\t/**\n\t * Serializes the error into a JSON-compatible object, suitable for sending\n\t * to a frontend or a structured logging system.\n\t */\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t};\n\t}\n}\n\n// ─── Parse Error ─────────────────────────────────────────────────────────────\n// Thrown when Handlebars fails to parse the template (invalid syntax).\n\nexport class TemplateParseError extends TemplateError {\n\tconstructor(\n\t\tmessage: string,\n\t\t/** Approximate position of the error in the source */\n\t\tpublic readonly loc?: { line: number; column: number },\n\t\t/** Fragment of the template source around the error */\n\t\tpublic readonly source?: string,\n\t) {\n\t\tsuper(`Parse error: ${message}`);\n\t\tthis.name = \"TemplateParseError\";\n\t}\n\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tloc: this.loc,\n\t\t\tsource: this.source,\n\t\t};\n\t}\n}\n\n// ─── Static Analysis Error ───────────────────────────────────────────────────\n// Thrown in strict mode when the analysis produces at least one error.\n// Contains the full list of diagnostics for detailed inspection.\n\nexport class TemplateAnalysisError extends TemplateError {\n\t/** Full list of diagnostics (errors + warnings) */\n\tpublic readonly diagnostics: TemplateDiagnostic[];\n\n\t/** Only diagnostics with \"error\" severity */\n\tpublic readonly errors: TemplateDiagnostic[];\n\n\t/** Only diagnostics with \"warning\" severity */\n\tpublic readonly warnings: TemplateDiagnostic[];\n\n\t/** Total number of errors */\n\tpublic readonly errorCount: number;\n\n\t/** Total number of warnings */\n\tpublic readonly warningCount: number;\n\n\tconstructor(diagnostics: TemplateDiagnostic[]) {\n\t\tconst errors = diagnostics.filter((d) => d.severity === \"error\");\n\t\tconst warnings = diagnostics.filter((d) => d.severity === \"warning\");\n\n\t\tconst summary = errors.map((d) => formatDiagnosticLine(d)).join(\"\\n\");\n\t\tsuper(`Static analysis failed with ${errors.length} error(s):\\n${summary}`);\n\n\t\tthis.name = \"TemplateAnalysisError\";\n\t\tthis.diagnostics = diagnostics;\n\t\tthis.errors = errors;\n\t\tthis.warnings = warnings;\n\t\tthis.errorCount = errors.length;\n\t\tthis.warningCount = warnings.length;\n\t}\n\n\t/**\n\t * Serializes the analysis error into a JSON-compatible object.\n\t *\n\t * Designed for direct use in API responses:\n\t * ```\n\t * res.status(400).json(error.toJSON());\n\t * ```\n\t *\n\t * Returned structure:\n\t * ```\n\t * {\n\t * name: \"TemplateAnalysisError\",\n\t * message: \"Static analysis failed with 2 error(s): ...\",\n\t * errorCount: 2,\n\t * warningCount: 0,\n\t * diagnostics: [\n\t * {\n\t * severity: \"error\",\n\t * code: \"UNKNOWN_PROPERTY\",\n\t * message: \"Property \\\"foo\\\" does not exist...\",\n\t * loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 7 } },\n\t * source: \"{{foo}}\",\n\t * details: { path: \"foo\", availableProperties: [\"name\", \"age\"] }\n\t * }\n\t * ]\n\t * }\n\t * ```\n\t */\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\terrorCount: this.errorCount,\n\t\t\twarningCount: this.warningCount,\n\t\t\tdiagnostics: this.diagnostics,\n\t\t};\n\t}\n}\n\n// ─── Runtime Error ───────────────────────────────────────────────────────────\n// Thrown when template execution fails (accessing a non-existent property\n// in strict mode, unexpected type, etc.).\n\nexport class TemplateRuntimeError extends TemplateError {\n\tconstructor(message: string) {\n\t\tsuper(`Runtime error: ${message}`);\n\t\tthis.name = \"TemplateRuntimeError\";\n\t}\n}\n\n// ─── Unsupported Schema Error ────────────────────────────────────────────────\n// Thrown when the provided JSON Schema uses features that cannot be handled\n// by static analysis (e.g. `if/then/else` conditional schemas).\n//\n// These features are data-dependent and fundamentally non-resolvable without\n// runtime values. Rather than silently ignoring them (which would produce\n// incorrect analysis results), we fail fast with a clear error message.\n\nexport class UnsupportedSchemaError extends TemplateError {\n\tconstructor(\n\t\t/** The unsupported keyword(s) detected (e.g. `\"if/then/else\"`) */\n\t\tpublic readonly keyword: string,\n\t\t/** JSON pointer path to the location in the schema (e.g. `\"/properties/user\"`) */\n\t\tpublic readonly schemaPath: string,\n\t) {\n\t\tsuper(\n\t\t\t`Unsupported JSON Schema feature: \"${keyword}\" at \"${schemaPath}\". ` +\n\t\t\t\t\"Conditional schemas (if/then/else) cannot be resolved during static analysis \" +\n\t\t\t\t\"because they depend on runtime data. Consider using oneOf/anyOf combinators instead.\",\n\t\t);\n\t\tthis.name = \"UnsupportedSchemaError\";\n\t}\n\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tkeyword: this.keyword,\n\t\t\tschemaPath: this.schemaPath,\n\t\t};\n\t}\n}\n\n// ─── Internal Utilities ──────────────────────────────────────────────────────\n\n/**\n * Formats a single diagnostic line for the summary message\n * of a `TemplateAnalysisError`.\n *\n * Produces a human-readable format:\n * ` • [UNKNOWN_PROPERTY] Property \"foo\" does not exist (at 1:0)`\n */\nfunction formatDiagnosticLine(diag: TemplateDiagnostic): string {\n\tconst parts: string[] = [` • [${diag.code}] ${diag.message}`];\n\n\tif (diag.loc) {\n\t\tparts.push(`(at ${diag.loc.start.line}:${diag.loc.start.column})`);\n\t}\n\n\treturn parts.join(\" \");\n}\n\n// ─── Common Error Factories ──────────────────────────────────────────────────\n// These functions simplify the creation of typed errors across the codebase.\n\n/**\n * Creates a structured diagnostic message for a missing property.\n * Used by the analyzer to produce enriched error messages with suggestions.\n */\nexport function createPropertyNotFoundMessage(\n\tpath: string,\n\tavailableProperties: string[],\n): string {\n\tconst base = `Property \"${path}\" does not exist in the context schema`;\n\tif (availableProperties.length === 0) return base;\n\treturn `${base}. Available properties: ${availableProperties.join(\", \")}`;\n}\n\n/**\n * Creates a message for a type mismatch on a block helper.\n */\nexport function createTypeMismatchMessage(\n\thelperName: string,\n\texpected: string,\n\tactual: string,\n): string {\n\treturn `\"{{#${helperName}}}\" expects ${expected}, but resolved schema has type \"${actual}\"`;\n}\n\n/**\n * Creates a message for a missing argument on a block helper.\n */\nexport function createMissingArgumentMessage(helperName: string): string {\n\treturn `\"{{#${helperName}}}\" requires an argument`;\n}\n\n/**\n * Creates a message for an unknown block helper.\n */\nexport function createUnknownHelperMessage(helperName: string): string {\n\treturn `Unknown block helper \"{{#${helperName}}}\" — cannot analyze statically`;\n}\n\n/**\n * Creates a message for an expression that cannot be statically analyzed.\n */\nexport function createUnanalyzableMessage(nodeType: string): string {\n\treturn `Expression of type \"${nodeType}\" cannot be statically analyzed`;\n}\n\n/**\n * Creates a message for an invalid `$root` path traversal attempt.\n *\n * `$root` references the entire input schema/data and cannot be followed\n * by property access (e.g. `$root.name` is invalid — use `{{name}}` instead).\n */\nexport function createRootPathTraversalMessage(fullPath: string): string {\n\treturn `\"$root\" references the entire input and cannot be used with property access (\"${fullPath}\"). Use \"{{${fullPath.replace(\"$root.\", \"\")}}}\" instead`;\n}\n"],"names":["TemplateAnalysisError","TemplateError","TemplateParseError","TemplateRuntimeError","UnsupportedSchemaError","createMissingArgumentMessage","createPropertyNotFoundMessage","createRootPathTraversalMessage","createTypeMismatchMessage","createUnanalyzableMessage","createUnknownHelperMessage","Error","toJSON","name","message","loc","source","errorCount","warningCount","diagnostics","errors","filter","d","severity","warnings","summary","map","formatDiagnosticLine","join","length","keyword","schemaPath","diag","parts","code","push","start","line","column","path","availableProperties","base","helperName","expected","actual","nodeType","fullPath","replace"],"mappings":"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"}
@@ -31,7 +31,7 @@ export interface ExecutorContext {
31
31
  * @param data - The main context data
32
32
  * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`
33
33
  */
34
- export declare function execute(template: TemplateInput, data: Record<string, unknown>, identifierData?: Record<number, Record<string, unknown>>): unknown;
34
+ export declare function execute(template: TemplateInput, data: unknown, identifierData?: Record<number, Record<string, unknown>>): unknown;
35
35
  /**
36
36
  * Executes a template from an already-parsed AST.
37
37
  *
@@ -45,7 +45,7 @@ export declare function execute(template: TemplateInput, data: Record<string, un
45
45
  * @param data - The main context data
46
46
  * @param ctx - Optional execution context
47
47
  */
48
- export declare function executeFromAst(ast: hbs.AST.Program, template: string, data: Record<string, unknown>, ctx?: ExecutorContext): unknown;
48
+ export declare function executeFromAst(ast: hbs.AST.Program, template: string, data: unknown, ctx?: ExecutorContext): unknown;
49
49
  /**
50
50
  * Navigates through a data object by following a path of segments.
51
51
  *
@@ -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 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()}
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 _dispatchts=require("./dispatch.js");const _errorsts=require("./errors.js");const _parserts=require("./parser.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){return(0,_dispatchts.dispatchExecute)(template,undefined,tpl=>{const ast=(0,_parserts.parse)(tpl);return executeFromAst(ast,tpl,data,{identifierData})},child=>execute(child,data,identifierData))}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((0,_parserts.isRootPathTraversal)(cleanSegments)){return undefined}if((0,_parserts.isRootSegments)(cleanSegments)){if(identifier!==null&&identifierData){const source=identifierData[identifier];return source??undefined}if(identifier!==null){return undefined}return data}if(identifier!==null&&identifierData){const source=identifierData[identifier];if(source){return resolveDataPath(source,cleanSegments)}return undefined}if(identifier!==null&&!identifierData){return undefined}return resolveDataPath(data,cleanSegments)}function resolveDataPath(data,segments){let current=data;for(const segment of segments){if(current===null||current===undefined){return undefined}if(typeof current!=="object"){return undefined}current=current[segment]}return current}function mergeDataWithIdentifiers(data,identifierData){const base=data!==null&&typeof data==="object"&&!Array.isArray(data)?data:{};const merged={...base,[_parserts.ROOT_TOKEN]:data};if(!identifierData)return merged;for(const[id,idData]of Object.entries(identifierData)){merged[`${_parserts.ROOT_TOKEN}:${id}`]=idData;for(const[key,value]of Object.entries(idData)){merged[`${key}:${id}`]=value}}return merged}function renderWithHandlebars(template,data,ctx){try{if(ctx?.compiledTemplate){return ctx.compiledTemplate(data)}const cache=ctx?.compilationCache??globalCompilationCache;const hbs=ctx?.hbs??_handlebars.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
@@ -1 +1 @@
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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"}
1
+ {"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { dispatchExecute } from \"./dispatch.ts\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n\tROOT_TOKEN,\n} from \"./parser.ts\";\nimport type { TemplateInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n\t/**\n\t * Explicit coercion schema for the output value.\n\t * When set with a primitive type, the execution result will be coerced\n\t * to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\treturn dispatchExecute(\n\t\ttemplate,\n\t\tundefined,\n\t\t// String handler — parse and execute the AST\n\t\t(tpl) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn executeFromAst(ast, tpl, data, { identifierData });\n\t\t},\n\t\t// Recursive handler — re-enter execute() for child elements\n\t\t(child) => execute(child, data, identifierData),\n\t);\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): 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 BEFORE\n\t// checking for $root, so that both {{$root}} and {{$root:N}} are\n\t// handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// $root path traversal ($root.name) — not supported, return undefined\n\t// (the analyzer already rejects it with a diagnostic).\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\treturn undefined;\n\t}\n\n\t// $root → return the entire data context (or identifier data)\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null && identifierData) {\n\t\t\tconst source = identifierData[identifier];\n\t\t\treturn source ?? undefined;\n\t\t}\n\t\tif (identifier !== null) {\n\t\t\t// Template uses an identifier but no identifierData was provided\n\t\t\treturn undefined;\n\t\t}\n\t\treturn data;\n\t}\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\t// Always include $root so that Handlebars can resolve {{$root}} in\n\t// mixed templates and block helpers (where we delegate to Handlebars\n\t// instead of resolving expressions ourselves).\n\t// When data is a primitive (e.g. number passed with {{$root}}), we\n\t// wrap it into an object so Handlebars can still function.\n\tconst base: Record<string, unknown> =\n\t\tdata !== null && typeof data === \"object\" && !Array.isArray(data)\n\t\t\t? (data as Record<string, unknown>)\n\t\t\t: {};\n\tconst merged: Record<string, unknown> = { ...base, [ROOT_TOKEN]: data };\n\n\tif (!identifierData) return merged;\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\t// Add `$root:N` so Handlebars can resolve {{$root:N}} in mixed/block\n\t\t// templates (where we delegate to Handlebars instead of resolving\n\t\t// expressions ourselves). The value is the entire identifier data object.\n\t\tmerged[`${ROOT_TOKEN}:${id}`] = idData;\n\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"],"names":["clearCompilationCache","execute","executeFromAst","resolveDataPath","globalCompilationCache","LRUCache","template","data","identifierData","dispatchExecute","undefined","tpl","ast","parse","child","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","result","value","String","expr","isThisExpression","segments","extractPathSegments","TemplateRuntimeError","cleanSegments","identifier","extractExpressionIdentifier","isRootPathTraversal","isRootSegments","source","current","segment","base","Array","isArray","ROOT_TOKEN","id","idData","Object","entries","key","compiledTemplate","cache","compilationCache","hbs","Handlebars","compiled","get","compile","noEscape","strict","set","error","message","Error","clear"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  export type { AnalyzeOptions } from "./analyzer.js";
2
2
  export * from "./errors.js";
3
3
  export { Typebars } from "./typebars.js";
4
- export { defineHelper, isArrayInput, type TemplateInput, type TemplateInputArray, } from "./types.js";
4
+ export { defineHelper, isArrayInput, type TemplateData, type TemplateInput, type TemplateInputArray, } from "./types.js";
@@ -1 +1 @@
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"}
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 TemplateData,\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,mCAOlB"}
@@ -1,3 +1,4 @@
1
+ export declare const ROOT_TOKEN = "$root";
1
2
  /**
2
3
  * Parses a template string and returns the Handlebars AST.
3
4
  *
@@ -37,6 +38,44 @@ export declare function extractPathSegments(expr: hbs.AST.Expression): string[];
37
38
  * (used inside `{{#each}}` blocks).
38
39
  */
39
40
  export declare function isThisExpression(expr: hbs.AST.Expression): boolean;
41
+ /**
42
+ * Checks whether an AST expression is a `PathExpression` whose first
43
+ * segment is the `$root` token.
44
+ *
45
+ * This covers both the valid `{{$root}}` case (single segment) and the
46
+ * invalid `{{$root.name}}` case (multiple segments). The caller must
47
+ * check `parts.length` to distinguish valid from invalid usage.
48
+ *
49
+ * **Note:** This does NOT match `{{$root:2}}` because Handlebars parses
50
+ * it as `parts: ["$root:2"]` (identifier still attached). Use
51
+ * `isRootSegments()` on cleaned segments instead when identifier
52
+ * extraction has already been performed.
53
+ */
54
+ export declare function isRootExpression(expr: hbs.AST.Expression): boolean;
55
+ /**
56
+ * Checks whether an array of **cleaned** path segments represents a
57
+ * `$root` reference (i.e. exactly one segment equal to `"$root"`).
58
+ *
59
+ * This is the segment-level counterpart of `isRootExpression()` and is
60
+ * meant to be called **after** `extractExpressionIdentifier()` has
61
+ * stripped the `:N` suffix. It correctly handles both `{{$root}}` and
62
+ * `{{$root:2}}`.
63
+ *
64
+ * @param cleanSegments - Path segments with identifiers already removed
65
+ * @returns `true` if the segments represent a `$root` reference
66
+ */
67
+ export declare function isRootSegments(cleanSegments: string[]): boolean;
68
+ /**
69
+ * Checks whether cleaned segments represent a **path traversal** on
70
+ * `$root` (e.g. `$root.name`, `$root.address.city`).
71
+ *
72
+ * Path traversal on `$root` is forbidden — users should write `{{name}}`
73
+ * instead of `{{$root.name}}`.
74
+ *
75
+ * @param cleanSegments - Path segments with identifiers already removed
76
+ * @returns `true` if the first segment is `$root` and there are additional segments
77
+ */
78
+ export declare function isRootPathTraversal(cleanSegments: string[]): boolean;
40
79
  /**
41
80
  * Returns the semantically significant statements of a Program by
42
81
  * filtering out `ContentStatement` nodes that contain only whitespace.
@@ -64,6 +103,29 @@ export declare function getEffectivelySingleBlock(program: hbs.AST.Program): hbs
64
103
  * Example: ` {{age}} ` → true
65
104
  */
66
105
  export declare function getEffectivelySingleExpression(program: hbs.AST.Program): hbs.AST.MustacheStatement | null;
106
+ /**
107
+ * Determines whether a string contains Handlebars expressions.
108
+ *
109
+ * A string contains template expressions if it includes `{{` (opening
110
+ * delimiter for Handlebars mustache/block statements). This is a fast
111
+ * substring check — **no parsing is performed**.
112
+ *
113
+ * Used by `excludeTemplateExpression` to filter out properties whose
114
+ * values are dynamic templates.
115
+ *
116
+ * **Limitation:** This is a simple `includes("{{")` check, not a full
117
+ * parser. It will produce false positives for strings that contain `{{`
118
+ * as literal text (e.g. `"Use {{name}} syntax"` in documentation strings)
119
+ * or in escaped contexts. For the current use case (filtering object/array
120
+ * entries that are likely templates), this trade-off is acceptable because:
121
+ * - It avoids the cost of parsing every string value
122
+ * - False positives only cause an entry to be excluded from the output
123
+ * schema (conservative behavior)
124
+ *
125
+ * @param value - The string to check
126
+ * @returns `true` if the string contains at least one `{{` sequence
127
+ */
128
+ export declare function hasHandlebarsExpression(value: string): boolean;
67
129
  /**
68
130
  * Determines whether an AST can be executed via the fast-path (direct
69
131
  * concatenation without going through `Handlebars.compile()`).
@@ -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 canUseFastPath(){return canUseFastPath},get coerceLiteral(){return coerceLiteral},get detectLiteralType(){return detectLiteralType},get extractExpressionIdentifier(){return extractExpressionIdentifier},get extractPathSegments(){return extractPathSegments},get getEffectiveBody(){return getEffectiveBody},get getEffectivelySingleBlock(){return getEffectivelySingleBlock},get getEffectivelySingleExpression(){return getEffectivelySingleExpression},get isSingleExpression(){return isSingleExpression},get isThisExpression(){return isThisExpression},get parse(){return parse},get parseIdentifier(){return parseIdentifier}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _errorsts=require("./errors.js");function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}const IDENTIFIER_RE=/^(.+):(\d+)$/;const NUMERIC_LITERAL_RE=/^-?\d+(\.\d+)?$/;function parse(template){try{return _handlebars.default.parse(template)}catch(error){const message=error instanceof Error?error.message:String(error);const locMatch=message.match(/line\s+(\d+).*?column\s+(\d+)/i);const loc=locMatch?{line:parseInt(locMatch[1]??"0",10),column:parseInt(locMatch[2]??"0",10)}:undefined;throw new _errorsts.TemplateParseError(message,loc)}}function isSingleExpression(ast){const{body}=ast;return body.length===1&&body[0]?.type==="MustacheStatement"}function extractPathSegments(expr){if(expr.type==="PathExpression"){return expr.parts}return[]}function isThisExpression(expr){if(expr.type!=="PathExpression")return false;const path=expr;return path.original==="this"||path.original==="."}function getEffectiveBody(program){return program.body.filter(s=>!(s.type==="ContentStatement"&&s.value.trim()===""))}function getEffectivelySingleBlock(program){const effective=getEffectiveBody(program);if(effective.length===1&&effective[0]?.type==="BlockStatement"){return effective[0]}return null}function getEffectivelySingleExpression(program){const effective=getEffectiveBody(program);if(effective.length===1&&effective[0]?.type==="MustacheStatement"){return effective[0]}return null}function canUseFastPath(ast){return ast.body.every(s=>s.type==="ContentStatement"||s.type==="MustacheStatement"&&s.params.length===0&&!s.hash)}function detectLiteralType(text){if(NUMERIC_LITERAL_RE.test(text))return"number";if(text==="true"||text==="false")return"boolean";if(text==="null")return"null";return null}function coerceLiteral(raw){const trimmed=raw.trim();const type=detectLiteralType(trimmed);if(type==="number")return Number(trimmed);if(type==="boolean")return trimmed==="true";if(type==="null")return null;return raw}function parseIdentifier(segment){const match=segment.match(IDENTIFIER_RE);if(match){return{key:match[1]??segment,identifier:parseInt(match[2]??"0",10)}}return{key:segment,identifier:null}}function extractExpressionIdentifier(segments){if(segments.length===0){return{cleanSegments:[],identifier:null}}const lastSegment=segments[segments.length-1];const parsed=parseIdentifier(lastSegment);if(parsed.identifier!==null){const cleanSegments=[...segments.slice(0,-1),parsed.key];return{cleanSegments,identifier:parsed.identifier}}return{cleanSegments:segments,identifier:null}}
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 ROOT_TOKEN(){return ROOT_TOKEN},get canUseFastPath(){return canUseFastPath},get coerceLiteral(){return coerceLiteral},get detectLiteralType(){return detectLiteralType},get extractExpressionIdentifier(){return extractExpressionIdentifier},get extractPathSegments(){return extractPathSegments},get getEffectiveBody(){return getEffectiveBody},get getEffectivelySingleBlock(){return getEffectivelySingleBlock},get getEffectivelySingleExpression(){return getEffectivelySingleExpression},get hasHandlebarsExpression(){return hasHandlebarsExpression},get isRootExpression(){return isRootExpression},get isRootPathTraversal(){return isRootPathTraversal},get isRootSegments(){return isRootSegments},get isSingleExpression(){return isSingleExpression},get isThisExpression(){return isThisExpression},get parse(){return parse},get parseIdentifier(){return parseIdentifier}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _errorsts=require("./errors.js");function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}const ROOT_TOKEN="$root";const IDENTIFIER_RE=/^(.+):(-?\d+)$/;const NUMERIC_LITERAL_RE=/^-?\d+(\.\d+)?$/;function parse(template){try{return _handlebars.default.parse(template)}catch(error){const message=error instanceof Error?error.message:String(error);const locMatch=message.match(/line\s+(\d+).*?column\s+(\d+)/i);const loc=locMatch?{line:parseInt(locMatch[1]??"0",10),column:parseInt(locMatch[2]??"0",10)}:undefined;throw new _errorsts.TemplateParseError(message,loc)}}function isSingleExpression(ast){const{body}=ast;return body.length===1&&body[0]?.type==="MustacheStatement"}function extractPathSegments(expr){if(expr.type==="PathExpression"){return expr.parts}return[]}function isThisExpression(expr){if(expr.type!=="PathExpression")return false;const path=expr;return path.original==="this"||path.original==="."}function isRootExpression(expr){if(expr.type!=="PathExpression")return false;const path=expr;return path.parts.length>0&&path.parts[0]===ROOT_TOKEN}function isRootSegments(cleanSegments){return cleanSegments.length===1&&cleanSegments[0]===ROOT_TOKEN}function isRootPathTraversal(cleanSegments){return cleanSegments.length>1&&cleanSegments[0]===ROOT_TOKEN}function getEffectiveBody(program){return program.body.filter(s=>!(s.type==="ContentStatement"&&s.value.trim()===""))}function getEffectivelySingleBlock(program){const effective=getEffectiveBody(program);if(effective.length===1&&effective[0]?.type==="BlockStatement"){return effective[0]}return null}function getEffectivelySingleExpression(program){const effective=getEffectiveBody(program);if(effective.length===1&&effective[0]?.type==="MustacheStatement"){return effective[0]}return null}function hasHandlebarsExpression(value){return value.includes("{{")}function canUseFastPath(ast){return ast.body.every(s=>s.type==="ContentStatement"||s.type==="MustacheStatement"&&s.params.length===0&&!s.hash)}function detectLiteralType(text){if(NUMERIC_LITERAL_RE.test(text))return"number";if(text==="true"||text==="false")return"boolean";if(text==="null")return"null";return null}function coerceLiteral(raw){const trimmed=raw.trim();const type=detectLiteralType(trimmed);if(type==="number")return Number(trimmed);if(type==="boolean")return trimmed==="true";if(type==="null")return null;return raw}function parseIdentifier(segment){const match=segment.match(IDENTIFIER_RE);if(match){return{key:match[1]??segment,identifier:parseInt(match[2]??"0",10)}}return{key:segment,identifier:null}}function extractExpressionIdentifier(segments){if(segments.length===0){return{cleanSegments:[],identifier:null}}const lastSegment=segments[segments.length-1];const parsed=parseIdentifier(lastSegment);if(parsed.identifier!==null){const cleanSegments=[...segments.slice(0,-1),parsed.key];return{cleanSegments,identifier:parsed.identifier}}return{cleanSegments:segments,identifier:null}}
2
2
  //# sourceMappingURL=parser.js.map