typebars 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/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\t/** When true, exclude entries containing Handlebars expressions */\n\texcludeTemplateExpression?: boolean;\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\tconst childOptions = resolveArrayChildOptions(options);\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, childOptions),\n\t\t\t);\n\t\t}\n\t\treturn aggregateArrayAnalysis(template.length, (index) =>\n\t\t\trecurse(template[index] as TemplateInput, childOptions),\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\tconst exclude = options?.excludeTemplateExpression === true;\n\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\tconst childOptions = resolveArrayChildOptions(options);\n\t\tconst elements = exclude\n\t\t\t? template.filter((item) => !shouldExcludeEntry(item as TemplateInput))\n\t\t\t: template;\n\t\tconst result: unknown[] = [];\n\t\tfor (const element of elements) {\n\t\t\tresult.push(recurse(element as TemplateInput, childOptions));\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\tconst keys = exclude\n\t\t\t? Object.keys(template).filter(\n\t\t\t\t\t(key) => !shouldExcludeEntry(template[key] as TemplateInput),\n\t\t\t\t)\n\t\t\t: Object.keys(template);\n\t\tfor (const key of keys) {\n\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\tresult[key] = recurse(template[key] as TemplateInput, {\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\t// At root level, if the string contains expressions and exclude is on,\n\t// return null (there is no parent to remove it from).\n\tif (exclude && shouldExcludeEntry(template)) {\n\t\treturn null;\n\t}\n\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\t/** When true, exclude entries containing Handlebars expressions */\n\texcludeTemplateExpression?: boolean;\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\tconst exclude = options?.excludeTemplateExpression === true;\n\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\tconst childOptions = resolveArrayChildOptions(options);\n\t\tconst elements = exclude\n\t\t\t? template.filter((item) => !shouldExcludeEntry(item as TemplateInput))\n\t\t\t: template;\n\t\treturn aggregateArrayAnalysisAndExecution(elements.length, (index) =>\n\t\t\trecurse(elements[index] as TemplateInput, childOptions),\n\t\t);\n\t}\n\n\t// ── Object ────────────────────────────────────────────────────────────\n\tif (isObjectInput(template)) {\n\t\tconst coerceSchema = options?.coerceSchema;\n\t\tconst keys = exclude\n\t\t\t? Object.keys(template).filter(\n\t\t\t\t\t(key) => !shouldExcludeEntry(template[key] as TemplateInput),\n\t\t\t\t)\n\t\t\t: Object.keys(template);\n\t\treturn aggregateObjectAnalysisAndExecution(keys, (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\texcludeTemplateExpression: options?.excludeTemplateExpression,\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\t// At root level, if the string contains expressions and exclude is on,\n\t// return null with a valid analysis (no parent to remove from).\n\tif (exclude && shouldExcludeEntry(template)) {\n\t\treturn {\n\t\t\tanalysis: {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: { type: \"null\" },\n\t\t\t},\n\t\t\tvalue: null,\n\t\t};\n\t}\n\n\treturn processString(template, options?.coerceSchema);\n}\n\n// ─── Internal Utilities ──────────────────────────────────────────────────────\n\n/**\n * Resolves the child options for array element recursion.\n *\n * When a `coerceSchema` with `items` is provided, the child options\n * will use `coerceSchema.items` as the element-level coercion schema.\n * All other options are passed through unchanged.\n *\n * @param options - The parent dispatching options (may be `undefined`)\n * @returns New options with `coerceSchema` resolved to the items schema, or\n * the original options if no items schema is available.\n */\nfunction resolveArrayChildOptions<\n\tT extends { coerceSchema?: JSONSchema7 } | undefined,\n>(options: T): T {\n\tif (!options?.coerceSchema) return options;\n\n\tconst itemsSchema = resolveItemsCoerceSchema(options.coerceSchema);\n\tif (itemsSchema === options.coerceSchema) return options;\n\n\treturn { ...options, coerceSchema: itemsSchema };\n}\n\n/**\n * Extracts the `items` schema from an array-typed `coerceSchema`.\n *\n * If the schema declares `items` as a single schema (not a tuple),\n * returns that schema. Otherwise returns `undefined`.\n *\n * @param coerceSchema - The parent coercion schema\n * @returns The items coercion schema, or `undefined`\n */\nfunction resolveItemsCoerceSchema(\n\tcoerceSchema: JSONSchema7,\n): JSONSchema7 | undefined {\n\tif (typeof coerceSchema === \"boolean\") return undefined;\n\n\tconst items = coerceSchema.items;\n\tif (items != null && typeof items === \"object\" && !Array.isArray(items)) {\n\t\treturn items as JSONSchema7;\n\t}\n\n\treturn undefined;\n}\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":["hasHandlebarsExpression","resolveSchemaPath","inferPrimitiveSchema","isArrayInput","isLiteralInput","isObjectInput","aggregateArrayAnalysis","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysis","aggregateObjectAnalysisAndExecution","dispatchAnalyze","template","options","analyzeString","recurse","exclude","excludeTemplateExpression","childOptions","resolveArrayChildOptions","kept","filter","item","shouldExcludeEntry","length","index","dispatchObjectAnalysis","valid","diagnostics","outputSchema","coerceSchema","keys","Object","key","childCoerceSchema","resolveChildCoerceSchema","identifierSchemas","dispatchExecute","executeString","elements","result","element","push","dispatchAnalyzeAndExecute","processString","identifierData","analysis","value","type","itemsSchema","resolveItemsCoerceSchema","undefined","items","Array","isArray","input"],"mappings":"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"}
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, IdentifierData, 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\t/** When true, exclude entries containing Handlebars expressions */\n\texcludeTemplateExpression?: boolean;\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\tconst childOptions = resolveArrayChildOptions(options);\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, childOptions),\n\t\t\t);\n\t\t}\n\t\treturn aggregateArrayAnalysis(template.length, (index) =>\n\t\t\trecurse(template[index] as TemplateInput, childOptions),\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\tconst exclude = options?.excludeTemplateExpression === true;\n\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\tconst childOptions = resolveArrayChildOptions(options);\n\t\tconst elements = exclude\n\t\t\t? template.filter((item) => !shouldExcludeEntry(item as TemplateInput))\n\t\t\t: template;\n\t\tconst result: unknown[] = [];\n\t\tfor (const element of elements) {\n\t\t\tresult.push(recurse(element as TemplateInput, childOptions));\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\tconst keys = exclude\n\t\t\t? Object.keys(template).filter(\n\t\t\t\t\t(key) => !shouldExcludeEntry(template[key] as TemplateInput),\n\t\t\t\t)\n\t\t\t: Object.keys(template);\n\t\tfor (const key of keys) {\n\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\tresult[key] = recurse(template[key] as TemplateInput, {\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\t// At root level, if the string contains expressions and exclude is on,\n\t// return null (there is no parent to remove it from).\n\tif (exclude && shouldExcludeEntry(template)) {\n\t\treturn null;\n\t}\n\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?: IdentifierData;\n\tcoerceSchema?: JSONSchema7;\n\t/** When true, exclude entries containing Handlebars expressions */\n\texcludeTemplateExpression?: boolean;\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\tconst exclude = options?.excludeTemplateExpression === true;\n\n\t// ── Array ─────────────────────────────────────────────────────────────\n\tif (isArrayInput(template)) {\n\t\tconst childOptions = resolveArrayChildOptions(options);\n\t\tconst elements = exclude\n\t\t\t? template.filter((item) => !shouldExcludeEntry(item as TemplateInput))\n\t\t\t: template;\n\t\treturn aggregateArrayAnalysisAndExecution(elements.length, (index) =>\n\t\t\trecurse(elements[index] as TemplateInput, childOptions),\n\t\t);\n\t}\n\n\t// ── Object ────────────────────────────────────────────────────────────\n\tif (isObjectInput(template)) {\n\t\tconst coerceSchema = options?.coerceSchema;\n\t\tconst keys = exclude\n\t\t\t? Object.keys(template).filter(\n\t\t\t\t\t(key) => !shouldExcludeEntry(template[key] as TemplateInput),\n\t\t\t\t)\n\t\t\t: Object.keys(template);\n\t\treturn aggregateObjectAnalysisAndExecution(keys, (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\texcludeTemplateExpression: options?.excludeTemplateExpression,\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\t// At root level, if the string contains expressions and exclude is on,\n\t// return null with a valid analysis (no parent to remove from).\n\tif (exclude && shouldExcludeEntry(template)) {\n\t\treturn {\n\t\t\tanalysis: {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: { type: \"null\" },\n\t\t\t},\n\t\t\tvalue: null,\n\t\t};\n\t}\n\n\treturn processString(template, options?.coerceSchema);\n}\n\n// ─── Internal Utilities ──────────────────────────────────────────────────────\n\n/**\n * Resolves the child options for array element recursion.\n *\n * When a `coerceSchema` with `items` is provided, the child options\n * will use `coerceSchema.items` as the element-level coercion schema.\n * All other options are passed through unchanged.\n *\n * @param options - The parent dispatching options (may be `undefined`)\n * @returns New options with `coerceSchema` resolved to the items schema, or\n * the original options if no items schema is available.\n */\nfunction resolveArrayChildOptions<\n\tT extends { coerceSchema?: JSONSchema7 } | undefined,\n>(options: T): T {\n\tif (!options?.coerceSchema) return options;\n\n\tconst itemsSchema = resolveItemsCoerceSchema(options.coerceSchema);\n\tif (itemsSchema === options.coerceSchema) return options;\n\n\treturn { ...options, coerceSchema: itemsSchema };\n}\n\n/**\n * Extracts the `items` schema from an array-typed `coerceSchema`.\n *\n * If the schema declares `items` as a single schema (not a tuple),\n * returns that schema. Otherwise returns `undefined`.\n *\n * @param coerceSchema - The parent coercion schema\n * @returns The items coercion schema, or `undefined`\n */\nfunction resolveItemsCoerceSchema(\n\tcoerceSchema: JSONSchema7,\n): JSONSchema7 | undefined {\n\tif (typeof coerceSchema === \"boolean\") return undefined;\n\n\tconst items = coerceSchema.items;\n\tif (items != null && typeof items === \"object\" && !Array.isArray(items)) {\n\t\treturn items as JSONSchema7;\n\t}\n\n\treturn undefined;\n}\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":["hasHandlebarsExpression","resolveSchemaPath","inferPrimitiveSchema","isArrayInput","isLiteralInput","isObjectInput","aggregateArrayAnalysis","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysis","aggregateObjectAnalysisAndExecution","dispatchAnalyze","template","options","analyzeString","recurse","exclude","excludeTemplateExpression","childOptions","resolveArrayChildOptions","kept","filter","item","shouldExcludeEntry","length","index","dispatchObjectAnalysis","valid","diagnostics","outputSchema","coerceSchema","keys","Object","key","childCoerceSchema","resolveChildCoerceSchema","identifierSchemas","dispatchExecute","executeString","elements","result","element","push","dispatchAnalyzeAndExecute","processString","identifierData","analysis","value","type","itemsSchema","resolveItemsCoerceSchema","undefined","items","Array","isArray","input"],"mappings":"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"}
@@ -1,11 +1,17 @@
1
1
  import Handlebars from "handlebars";
2
2
  import type { JSONSchema7 } from "json-schema";
3
- import type { HelperDefinition, TemplateInput } from "./types.js";
3
+ import type { HelperDefinition, IdentifierData, TemplateInput } from "./types.js";
4
4
  import { LRUCache } from "./utils.js";
5
5
  /** Optional context for execution (used by Typebars/CompiledTemplate) */
6
6
  export interface ExecutorContext {
7
- /** Data by identifier `{ [id]: { key: value } }` */
8
- identifierData?: Record<number, Record<string, unknown>>;
7
+ /**
8
+ * Data by identifier `{ [id]: { key: value } }`.
9
+ *
10
+ * Each identifier can map to a single object (standard) or an array
11
+ * of objects (aggregated multi-version data). When the value is an
12
+ * array, `{{key:N}}` extracts the property from each element.
13
+ */
14
+ identifierData?: IdentifierData;
9
15
  /** Pre-compiled Handlebars template (for CompiledTemplate) */
10
16
  compiledTemplate?: HandlebarsTemplateDelegate;
11
17
  /** Isolated Handlebars environment (for custom helpers) */
@@ -33,7 +39,7 @@ export interface ExecutorContext {
33
39
  * @param data - The main context data
34
40
  * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`
35
41
  */
36
- export declare function execute(template: TemplateInput, data: unknown, identifierData?: Record<number, Record<string, unknown>>): unknown;
42
+ export declare function execute(template: TemplateInput, data: unknown, identifierData?: IdentifierData): unknown;
37
43
  /**
38
44
  * Executes a template from an already-parsed AST.
39
45
  *
@@ -1,2 +1,2 @@
1
- import Handlebars from"handlebars";import{dispatchExecute}from"./dispatch.js";import{TemplateRuntimeError}from"./errors.js";import{MapHelpers}from"./helpers/map-helpers.js";import{canUseFastPath,coerceLiteral,extractExpressionIdentifier,extractPathSegments,getEffectiveBody,getEffectivelySingleBlock,getEffectivelySingleExpression,isRootPathTraversal,isRootSegments,isSingleExpression,isThisExpression,parse,ROOT_TOKEN}from"./parser.js";import{LRUCache}from"./utils.js";const globalCompilationCache=new LRUCache(128);export function execute(template,data,identifierData){return dispatchExecute(template,undefined,tpl=>{const ast=parse(tpl);return executeFromAst(ast,tpl,data,{identifierData})},child=>execute(child,data,identifierData))}export function executeFromAst(ast,template,data,ctx){const identifierData=ctx?.identifierData;if(isSingleExpression(ast)){const stmt=ast.body[0];if(stmt.params.length===0&&!stmt.hash){return resolveExpression(stmt.path,data,identifierData,ctx?.helpers)}}const singleExpr=getEffectivelySingleExpression(ast);if(singleExpr&&singleExpr.params.length===0&&!singleExpr.hash){return resolveExpression(singleExpr.path,data,identifierData,ctx?.helpers)}if(singleExpr&&(singleExpr.params.length>0||singleExpr.hash)){const directResult=tryDirectHelperExecution(singleExpr,data,ctx);if(directResult!==undefined){return directResult.value}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}if(canUseFastPath(ast)&&ast.body.length>1){return executeFastPath(ast,data,identifierData)}const singleBlock=getEffectivelySingleBlock(ast);if(singleBlock){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);const effective=getEffectiveBody(ast);const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){return coerceValue(raw,ctx?.coerceSchema)}return raw}function coerceValue(raw,coerceSchema){if(coerceSchema){const targetType=coerceSchema.type;if(typeof targetType==="string"){if(targetType==="string")return raw;if(targetType==="number"||targetType==="integer"){const trimmed=raw.trim();if(trimmed==="")return undefined;const num=Number(trimmed);if(Number.isNaN(num))return undefined;if(targetType==="integer"&&!Number.isInteger(num))return undefined;return num}if(targetType==="boolean"){const lower=raw.trim().toLowerCase();if(lower==="true")return true;if(lower==="false")return false;return undefined}if(targetType==="null")return null}}return coerceLiteral(raw)}function executeFastPath(ast,data,identifierData){let result="";for(const stmt of ast.body){if(stmt.type==="ContentStatement"){result+=stmt.value}else if(stmt.type==="MustacheStatement"){const value=resolveExpression(stmt.path,data,identifierData);if(value!=null){result+=String(value)}}}return result}function resolveExpression(expr,data,identifierData,helpers){if(isThisExpression(expr)){return data}if(expr.type==="StringLiteral")return expr.value;if(expr.type==="NumberLiteral")return expr.value;if(expr.type==="BooleanLiteral")return expr.value;if(expr.type==="NullLiteral")return null;if(expr.type==="UndefinedLiteral")return undefined;if(expr.type==="SubExpression"){const subExpr=expr;if(subExpr.path.type==="PathExpression"){const helperName=subExpr.path.original;const helper=helpers?.get(helperName);if(helper){const isMap=helperName===MapHelpers.MAP_HELPER_NAME;const resolvedArgs=[];for(let i=0;i<subExpr.params.length;i++){const param=subExpr.params[i];if(isMap&&i===1&&param.type==="StringLiteral"){resolvedArgs.push(param.value)}else{resolvedArgs.push(resolveExpression(param,data,identifierData,helpers))}}return helper.fn(...resolvedArgs)}}return undefined}const segments=extractPathSegments(expr);if(segments.length===0){throw new TemplateRuntimeError(`Cannot resolve expression of type "${expr.type}"`)}const{cleanSegments,identifier}=extractExpressionIdentifier(segments);if(isRootPathTraversal(cleanSegments)){return undefined}if(isRootSegments(cleanSegments)){if(identifier!==null&&identifierData){const source=identifierData[identifier];return source??undefined}if(identifier!==null){return undefined}return data}if(identifier!==null&&identifierData){const source=identifierData[identifier];if(source){return resolveDataPath(source,cleanSegments)}return undefined}if(identifier!==null&&!identifierData){return undefined}return resolveDataPath(data,cleanSegments)}export function resolveDataPath(data,segments){let current=data;for(const segment of segments){if(current===null||current===undefined){return undefined}if(typeof current!=="object"){return undefined}current=current[segment]}return current}function mergeDataWithIdentifiers(data,identifierData){const base=data!==null&&typeof data==="object"&&!Array.isArray(data)?data:{};const merged={...base,[ROOT_TOKEN]:data};if(!identifierData)return merged;for(const[id,idData]of Object.entries(identifierData)){merged[`${ROOT_TOKEN}:${id}`]=idData;for(const[key,value]of Object.entries(idData)){merged[`${key}:${id}`]=value}}return merged}function renderWithHandlebars(template,data,ctx){try{if(ctx?.compiledTemplate){return ctx.compiledTemplate(data)}const cache=ctx?.compilationCache??globalCompilationCache;const hbs=ctx?.hbs??Handlebars;let compiled=cache.get(template);if(!compiled){compiled=hbs.compile(template,{noEscape:true,strict:false});cache.set(template,compiled)}return compiled(data)}catch(error){const message=error instanceof Error?error.message:String(error);throw new TemplateRuntimeError(message)}}export function clearCompilationCache(){globalCompilationCache.clear()}const DIRECT_EXECUTION_HELPERS=new Set([MapHelpers.MAP_HELPER_NAME]);function tryDirectHelperExecution(stmt,data,ctx){if(stmt.path.type!=="PathExpression")return undefined;const helperName=stmt.path.original;if(!DIRECT_EXECUTION_HELPERS.has(helperName))return undefined;const helper=ctx?.helpers?.get(helperName);if(!helper)return undefined;const isMap=helperName===MapHelpers.MAP_HELPER_NAME;const resolvedArgs=[];for(let i=0;i<stmt.params.length;i++){const param=stmt.params[i];if(isMap&&i===1){if(param.type==="StringLiteral"){resolvedArgs.push(param.value)}else{resolvedArgs.push(resolveExpression(param,data,ctx?.identifierData,ctx?.helpers))}}else{resolvedArgs.push(resolveExpression(param,data,ctx?.identifierData,ctx?.helpers))}}const value=helper.fn(...resolvedArgs);return{value}}
1
+ import Handlebars from"handlebars";import{dispatchExecute}from"./dispatch.js";import{TemplateRuntimeError}from"./errors.js";import{MapHelpers}from"./helpers/map-helpers.js";import{canUseFastPath,coerceLiteral,extractExpressionIdentifier,extractPathSegments,getEffectiveBody,getEffectivelySingleBlock,getEffectivelySingleExpression,isRootPathTraversal,isRootSegments,isSingleExpression,isThisExpression,parse,ROOT_TOKEN}from"./parser.js";import{LRUCache}from"./utils.js";const globalCompilationCache=new LRUCache(128);export function execute(template,data,identifierData){return dispatchExecute(template,undefined,tpl=>{const ast=parse(tpl);return executeFromAst(ast,tpl,data,{identifierData})},child=>execute(child,data,identifierData))}export function executeFromAst(ast,template,data,ctx){const identifierData=ctx?.identifierData;if(isSingleExpression(ast)){const stmt=ast.body[0];if(stmt.params.length===0&&!stmt.hash){return resolveExpression(stmt.path,data,identifierData,ctx?.helpers)}}const singleExpr=getEffectivelySingleExpression(ast);if(singleExpr&&singleExpr.params.length===0&&!singleExpr.hash){return resolveExpression(singleExpr.path,data,identifierData,ctx?.helpers)}if(singleExpr&&(singleExpr.params.length>0||singleExpr.hash)){const directResult=tryDirectHelperExecution(singleExpr,data,ctx);if(directResult!==undefined){return directResult.value}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}if(canUseFastPath(ast)&&ast.body.length>1){return executeFastPath(ast,data,identifierData)}const singleBlock=getEffectivelySingleBlock(ast);if(singleBlock){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);const effective=getEffectiveBody(ast);const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){return coerceValue(raw,ctx?.coerceSchema)}return raw}function coerceValue(raw,coerceSchema){if(coerceSchema){const targetType=coerceSchema.type;if(typeof targetType==="string"){if(targetType==="string")return raw;if(targetType==="number"||targetType==="integer"){const trimmed=raw.trim();if(trimmed==="")return undefined;const num=Number(trimmed);if(Number.isNaN(num))return undefined;if(targetType==="integer"&&!Number.isInteger(num))return undefined;return num}if(targetType==="boolean"){const lower=raw.trim().toLowerCase();if(lower==="true")return true;if(lower==="false")return false;return undefined}if(targetType==="null")return null}}return coerceLiteral(raw)}function executeFastPath(ast,data,identifierData){let result="";for(const stmt of ast.body){if(stmt.type==="ContentStatement"){result+=stmt.value}else if(stmt.type==="MustacheStatement"){const value=resolveExpression(stmt.path,data,identifierData);if(value!=null){result+=String(value)}}}return result}function resolveExpression(expr,data,identifierData,helpers){if(isThisExpression(expr)){return data}if(expr.type==="StringLiteral")return expr.value;if(expr.type==="NumberLiteral")return expr.value;if(expr.type==="BooleanLiteral")return expr.value;if(expr.type==="NullLiteral")return null;if(expr.type==="UndefinedLiteral")return undefined;if(expr.type==="SubExpression"){const subExpr=expr;if(subExpr.path.type==="PathExpression"){const helperName=subExpr.path.original;const helper=helpers?.get(helperName);if(helper){const isMap=helperName===MapHelpers.MAP_HELPER_NAME;const resolvedArgs=[];for(let i=0;i<subExpr.params.length;i++){const param=subExpr.params[i];if(isMap&&i===1&&param.type==="StringLiteral"){resolvedArgs.push(param.value)}else{resolvedArgs.push(resolveExpression(param,data,identifierData,helpers))}}return helper.fn(...resolvedArgs)}}return undefined}const segments=extractPathSegments(expr);if(segments.length===0){throw new TemplateRuntimeError(`Cannot resolve expression of type "${expr.type}"`)}const{cleanSegments,identifier}=extractExpressionIdentifier(segments);if(isRootPathTraversal(cleanSegments)){return undefined}if(isRootSegments(cleanSegments)){if(identifier!==null&&identifierData){const source=identifierData[identifier];return source??undefined}if(identifier!==null){return undefined}return data}if(identifier!==null&&identifierData){const source=identifierData[identifier];if(source){if(Array.isArray(source)){return source.map(item=>resolveDataPath(item,cleanSegments)).filter(v=>v!==undefined)}return resolveDataPath(source,cleanSegments)}return undefined}if(identifier!==null&&!identifierData){return undefined}return resolveDataPath(data,cleanSegments)}export function resolveDataPath(data,segments){let current=data;for(const segment of segments){if(current===null||current===undefined){return undefined}if(typeof current!=="object"){return undefined}current=current[segment]}return current}function mergeDataWithIdentifiers(data,identifierData){const base=data!==null&&typeof data==="object"&&!Array.isArray(data)?data:{};const merged={...base,[ROOT_TOKEN]:data};if(!identifierData)return merged;for(const[id,idData]of Object.entries(identifierData)){merged[`${ROOT_TOKEN}:${id}`]=idData;if(Array.isArray(idData)){continue}for(const[key,value]of Object.entries(idData)){merged[`${key}:${id}`]=value}}return merged}function renderWithHandlebars(template,data,ctx){try{if(ctx?.compiledTemplate){return ctx.compiledTemplate(data)}const cache=ctx?.compilationCache??globalCompilationCache;const hbs=ctx?.hbs??Handlebars;let compiled=cache.get(template);if(!compiled){compiled=hbs.compile(template,{noEscape:true,strict:false});cache.set(template,compiled)}return compiled(data)}catch(error){const message=error instanceof Error?error.message:String(error);throw new TemplateRuntimeError(message)}}export function clearCompilationCache(){globalCompilationCache.clear()}const DIRECT_EXECUTION_HELPERS=new Set([MapHelpers.MAP_HELPER_NAME]);function tryDirectHelperExecution(stmt,data,ctx){if(stmt.path.type!=="PathExpression")return undefined;const helperName=stmt.path.original;if(!DIRECT_EXECUTION_HELPERS.has(helperName))return undefined;const helper=ctx?.helpers?.get(helperName);if(!helper)return undefined;const isMap=helperName===MapHelpers.MAP_HELPER_NAME;const resolvedArgs=[];for(let i=0;i<stmt.params.length;i++){const param=stmt.params[i];if(isMap&&i===1){if(param.type==="StringLiteral"){resolvedArgs.push(param.value)}else{resolvedArgs.push(resolveExpression(param,data,ctx?.identifierData,ctx?.helpers))}}else{resolvedArgs.push(resolveExpression(param,data,ctx?.identifierData,ctx?.helpers))}}const value=helper.fn(...resolvedArgs);return{value}}
2
2
  //# sourceMappingURL=executor.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { dispatchExecute } from \"./dispatch.ts\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport { MapHelpers } from \"./helpers/map-helpers.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n\tROOT_TOKEN,\n} from \"./parser.ts\";\nimport type { HelperDefinition, TemplateInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n\t/**\n\t * Explicit coercion schema for the output value.\n\t * When set with a primitive type, the execution result will be coerced\n\t * to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/** Registered helpers (for direct execution of special helpers like `map`) */\n\thelpers?: Map<string, HelperDefinition>;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\treturn dispatchExecute(\n\t\ttemplate,\n\t\tundefined,\n\t\t// String handler — parse and execute the AST\n\t\t(tpl) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn executeFromAst(ast, tpl, data, { identifierData });\n\t\t},\n\t\t// Recursive handler — re-enter execute() for child elements\n\t\t(child) => execute(child, data, identifierData),\n\t);\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData, ctx?.helpers);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(\n\t\t\tsingleExpr.path,\n\t\t\tdata,\n\t\t\tidentifierData,\n\t\t\tctx?.helpers,\n\t\t);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\t// ── Special case: helpers that return non-primitive values ────────\n\t\t// Some helpers (e.g. `map`) return arrays or objects. Handlebars\n\t\t// would stringify these, so we resolve their arguments directly and\n\t\t// call the helper's fn to preserve the raw return value.\n\t\tconst directResult = tryDirectHelperExecution(singleExpr, data, ctx);\n\t\tif (directResult !== undefined) {\n\t\t\treturn directResult.value;\n\t\t}\n\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 4: mixed template ───────────────────────────────────────────\n\t// For purely static templates (only ContentStatements), coerce the\n\t// result to match the coerceSchema type or auto-detect the literal type.\n\t// For truly mixed templates (text + blocks + expressions), return string.\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\tconst raw = renderWithHandlebars(template, merged, ctx);\n\n\tconst effective = getEffectiveBody(ast);\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\treturn raw;\n}\n\n// ─── Value Coercion ──────────────────────────────────────────────────────────\n// Coerces a raw string from Handlebars rendering based on an optional\n// coerceSchema. When no schema is provided, falls back to auto-detection\n// via `coerceLiteral`.\n\n/**\n * Coerces a raw string value based on an optional coercion schema.\n *\n * - If `coerceSchema` declares a primitive type (`string`, `number`,\n * `integer`, `boolean`, `null`), the value is cast to that type.\n * - Otherwise, falls back to `coerceLiteral` (auto-detection).\n *\n * @param raw - The raw string from Handlebars rendering\n * @param coerceSchema - Optional schema declaring the desired output type\n * @returns The coerced value\n */\nfunction coerceValue(raw: string, coerceSchema?: JSONSchema7): unknown {\n\tif (coerceSchema) {\n\t\tconst targetType = coerceSchema.type;\n\t\tif (typeof targetType === \"string\") {\n\t\t\tif (targetType === \"string\") return raw;\n\t\t\tif (targetType === \"number\" || targetType === \"integer\") {\n\t\t\t\tconst trimmed = raw.trim();\n\t\t\t\tif (trimmed === \"\") return undefined;\n\t\t\t\tconst num = Number(trimmed);\n\t\t\t\tif (Number.isNaN(num)) return undefined;\n\t\t\t\tif (targetType === \"integer\" && !Number.isInteger(num))\n\t\t\t\t\treturn undefined;\n\t\t\t\treturn num;\n\t\t\t}\n\t\t\tif (targetType === \"boolean\") {\n\t\t\t\tconst lower = raw.trim().toLowerCase();\n\t\t\t\tif (lower === \"true\") return true;\n\t\t\t\tif (lower === \"false\") return false;\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (targetType === \"null\") return null;\n\t\t}\n\t}\n\t// No coerceSchema or non-primitive type → auto-detect\n\treturn coerceLiteral(raw);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n\thelpers?: Map<string, HelperDefinition>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// ── SubExpression (nested helper call) ────────────────────────────────\n\t// E.g. `(map users 'cartItems')` used as an argument to another helper.\n\t// Resolve all arguments recursively and call the helper's fn directly.\n\tif (expr.type === \"SubExpression\") {\n\t\tconst subExpr = expr as hbs.AST.SubExpression;\n\t\tif (subExpr.path.type === \"PathExpression\") {\n\t\t\tconst helperName = (subExpr.path as hbs.AST.PathExpression).original;\n\t\t\tconst helper = helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\t\t\t\tconst resolvedArgs: unknown[] = [];\n\t\t\t\tfor (let i = 0; i < subExpr.params.length; i++) {\n\t\t\t\t\tconst param = subExpr.params[i] as hbs.AST.Expression;\n\t\t\t\t\t// For `map`, the second argument is a property name literal\n\t\t\t\t\tif (isMap && i === 1 && param.type === \"StringLiteral\") {\n\t\t\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolvedArgs.push(\n\t\t\t\t\t\t\tresolveExpression(param, data, identifierData, helpers),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn helper.fn(...resolvedArgs);\n\t\t\t}\n\t\t}\n\t\t// Unknown sub-expression helper — return undefined\n\t\treturn undefined;\n\t}\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment BEFORE\n\t// checking for $root, so that both {{$root}} and {{$root:N}} are\n\t// handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// $root path traversal ($root.name) — not supported, return undefined\n\t// (the analyzer already rejects it with a diagnostic).\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\treturn undefined;\n\t}\n\n\t// $root → return the entire data context (or identifier data)\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null && identifierData) {\n\t\t\tconst source = identifierData[identifier];\n\t\t\treturn source ?? undefined;\n\t\t}\n\t\tif (identifier !== null) {\n\t\t\t// Template uses an identifier but no identifierData was provided\n\t\t\treturn undefined;\n\t\t}\n\t\treturn data;\n\t}\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\t// Always include $root so that Handlebars can resolve {{$root}} in\n\t// mixed templates and block helpers (where we delegate to Handlebars\n\t// instead of resolving expressions ourselves).\n\t// When data is a primitive (e.g. number passed with {{$root}}), we\n\t// wrap it into an object so Handlebars can still function.\n\tconst base: Record<string, unknown> =\n\t\tdata !== null && typeof data === \"object\" && !Array.isArray(data)\n\t\t\t? (data as Record<string, unknown>)\n\t\t\t: {};\n\tconst merged: Record<string, unknown> = { ...base, [ROOT_TOKEN]: data };\n\n\tif (!identifierData) return merged;\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\t// Add `$root:N` so Handlebars can resolve {{$root:N}} in mixed/block\n\t\t// templates (where we delegate to Handlebars instead of resolving\n\t\t// expressions ourselves). The value is the entire identifier data object.\n\t\tmerged[`${ROOT_TOKEN}:${id}`] = idData;\n\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n\n// ─── Direct Helper Execution ─────────────────────────────────────────────────\n// Some helpers (e.g. `map`) return non-primitive values (arrays, objects)\n// that Handlebars would stringify. For these helpers, we resolve their\n// arguments directly and call the helper's `fn` to preserve the raw value.\n\n/** Set of helper names that must be executed directly (bypass Handlebars) */\nconst DIRECT_EXECUTION_HELPERS = new Set<string>([MapHelpers.MAP_HELPER_NAME]);\n\n/**\n * Attempts to execute a helper directly (without Handlebars rendering).\n *\n * Returns `{ value }` if the helper was executed directly, or `undefined`\n * if the helper should go through the normal Handlebars rendering path.\n *\n * @param stmt - The MustacheStatement containing the helper call\n * @param data - The context data\n * @param ctx - Optional execution context (with helpers and identifierData)\n */\nfunction tryDirectHelperExecution(\n\tstmt: hbs.AST.MustacheStatement,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): { value: unknown } | undefined {\n\t// Get the helper name from the path\n\tif (stmt.path.type !== \"PathExpression\") return undefined;\n\tconst helperName = (stmt.path as hbs.AST.PathExpression).original;\n\n\t// Only intercept known direct-execution helpers\n\tif (!DIRECT_EXECUTION_HELPERS.has(helperName)) return undefined;\n\n\t// Look up the helper definition\n\tconst helper = ctx?.helpers?.get(helperName);\n\tif (!helper) return undefined;\n\n\t// Resolve each argument from the data context.\n\t// For the `map` helper, the resolution strategy is:\n\t// - Arg 0 (array): resolve as a data path (e.g. `users` → array)\n\t// - Arg 1 (property): must be a StringLiteral (e.g. `\"name\"`)\n\t// The analyzer enforces this — bare identifiers like `name` are\n\t// rejected at analysis time because Handlebars would resolve them\n\t// as a data path instead of a literal property name.\n\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\n\tconst resolvedArgs: unknown[] = [];\n\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\tconst param = stmt.params[i] as hbs.AST.Expression;\n\n\t\t// For `map`, the second argument (index 1) is a property name —\n\t\t// it must be a StringLiteral (enforced by the analyzer).\n\t\tif (isMap && i === 1) {\n\t\t\tif (param.type === \"StringLiteral\") {\n\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t} else {\n\t\t\t\t// Fallback: resolve normally (will likely be undefined at runtime)\n\t\t\t\tresolvedArgs.push(\n\t\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tresolvedArgs.push(\n\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t);\n\t\t}\n\t}\n\n\t// Call the helper's fn directly with the resolved arguments\n\tconst value = helper.fn(...resolvedArgs);\n\treturn { value };\n}\n"],"names":["Handlebars","dispatchExecute","TemplateRuntimeError","MapHelpers","canUseFastPath","coerceLiteral","extractExpressionIdentifier","extractPathSegments","getEffectiveBody","getEffectivelySingleBlock","getEffectivelySingleExpression","isRootPathTraversal","isRootSegments","isSingleExpression","isThisExpression","parse","ROOT_TOKEN","LRUCache","globalCompilationCache","execute","template","data","identifierData","undefined","tpl","ast","executeFromAst","child","ctx","stmt","body","params","length","hash","resolveExpression","path","helpers","singleExpr","directResult","tryDirectHelperExecution","value","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceValue","coerceSchema","executeFastPath","singleBlock","effective","allContent","every","s","type","targetType","trimmed","trim","num","Number","isNaN","isInteger","lower","toLowerCase","result","String","expr","subExpr","helperName","original","helper","get","isMap","MAP_HELPER_NAME","resolvedArgs","i","param","push","fn","segments","cleanSegments","identifier","source","resolveDataPath","current","segment","base","Array","isArray","id","idData","Object","entries","key","compiledTemplate","cache","compilationCache","hbs","compiled","compile","noEscape","strict","set","error","message","Error","clearCompilationCache","clear","DIRECT_EXECUTION_HELPERS","Set","has"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { dispatchExecute } from \"./dispatch.ts\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport { MapHelpers } from \"./helpers/map-helpers.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n\tROOT_TOKEN,\n} from \"./parser.ts\";\nimport type {\n\tHelperDefinition,\n\tIdentifierData,\n\tTemplateInput,\n} 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/**\n\t * Data by identifier `{ [id]: { key: value } }`.\n\t *\n\t * Each identifier can map to a single object (standard) or an array\n\t * of objects (aggregated multi-version data). When the value is an\n\t * array, `{{key:N}}` extracts the property from each element.\n\t */\n\tidentifierData?: IdentifierData;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n\t/**\n\t * Explicit coercion schema for the output value.\n\t * When set with a primitive type, the execution result will be coerced\n\t * to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/** Registered helpers (for direct execution of special helpers like `map`) */\n\thelpers?: Map<string, HelperDefinition>;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: unknown,\n\tidentifierData?: IdentifierData,\n): unknown {\n\treturn dispatchExecute(\n\t\ttemplate,\n\t\tundefined,\n\t\t// String handler — parse and execute the AST\n\t\t(tpl) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn executeFromAst(ast, tpl, data, { identifierData });\n\t\t},\n\t\t// Recursive handler — re-enter execute() for child elements\n\t\t(child) => execute(child, data, identifierData),\n\t);\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData, ctx?.helpers);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(\n\t\t\tsingleExpr.path,\n\t\t\tdata,\n\t\t\tidentifierData,\n\t\t\tctx?.helpers,\n\t\t);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\t// ── Special case: helpers that return non-primitive values ────────\n\t\t// Some helpers (e.g. `map`) return arrays or objects. Handlebars\n\t\t// would stringify these, so we resolve their arguments directly and\n\t\t// call the helper's fn to preserve the raw return value.\n\t\tconst directResult = tryDirectHelperExecution(singleExpr, data, ctx);\n\t\tif (directResult !== undefined) {\n\t\t\treturn directResult.value;\n\t\t}\n\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 4: mixed template ───────────────────────────────────────────\n\t// For purely static templates (only ContentStatements), coerce the\n\t// result to match the coerceSchema type or auto-detect the literal type.\n\t// For truly mixed templates (text + blocks + expressions), return string.\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\tconst raw = renderWithHandlebars(template, merged, ctx);\n\n\tconst effective = getEffectiveBody(ast);\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\treturn raw;\n}\n\n// ─── Value Coercion ──────────────────────────────────────────────────────────\n// Coerces a raw string from Handlebars rendering based on an optional\n// coerceSchema. When no schema is provided, falls back to auto-detection\n// via `coerceLiteral`.\n\n/**\n * Coerces a raw string value based on an optional coercion schema.\n *\n * - If `coerceSchema` declares a primitive type (`string`, `number`,\n * `integer`, `boolean`, `null`), the value is cast to that type.\n * - Otherwise, falls back to `coerceLiteral` (auto-detection).\n *\n * @param raw - The raw string from Handlebars rendering\n * @param coerceSchema - Optional schema declaring the desired output type\n * @returns The coerced value\n */\nfunction coerceValue(raw: string, coerceSchema?: JSONSchema7): unknown {\n\tif (coerceSchema) {\n\t\tconst targetType = coerceSchema.type;\n\t\tif (typeof targetType === \"string\") {\n\t\t\tif (targetType === \"string\") return raw;\n\t\t\tif (targetType === \"number\" || targetType === \"integer\") {\n\t\t\t\tconst trimmed = raw.trim();\n\t\t\t\tif (trimmed === \"\") return undefined;\n\t\t\t\tconst num = Number(trimmed);\n\t\t\t\tif (Number.isNaN(num)) return undefined;\n\t\t\t\tif (targetType === \"integer\" && !Number.isInteger(num))\n\t\t\t\t\treturn undefined;\n\t\t\t\treturn num;\n\t\t\t}\n\t\t\tif (targetType === \"boolean\") {\n\t\t\t\tconst lower = raw.trim().toLowerCase();\n\t\t\t\tif (lower === \"true\") return true;\n\t\t\t\tif (lower === \"false\") return false;\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (targetType === \"null\") return null;\n\t\t}\n\t}\n\t// No coerceSchema or non-primitive type → auto-detect\n\treturn coerceLiteral(raw);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: unknown,\n\tidentifierData?: IdentifierData,\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?: IdentifierData,\n\thelpers?: Map<string, HelperDefinition>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// ── SubExpression (nested helper call) ────────────────────────────────\n\t// E.g. `(map users 'cartItems')` used as an argument to another helper.\n\t// Resolve all arguments recursively and call the helper's fn directly.\n\tif (expr.type === \"SubExpression\") {\n\t\tconst subExpr = expr as hbs.AST.SubExpression;\n\t\tif (subExpr.path.type === \"PathExpression\") {\n\t\t\tconst helperName = (subExpr.path as hbs.AST.PathExpression).original;\n\t\t\tconst helper = helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\t\t\t\tconst resolvedArgs: unknown[] = [];\n\t\t\t\tfor (let i = 0; i < subExpr.params.length; i++) {\n\t\t\t\t\tconst param = subExpr.params[i] as hbs.AST.Expression;\n\t\t\t\t\t// For `map`, the second argument is a property name literal\n\t\t\t\t\tif (isMap && i === 1 && param.type === \"StringLiteral\") {\n\t\t\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolvedArgs.push(\n\t\t\t\t\t\t\tresolveExpression(param, data, identifierData, helpers),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn helper.fn(...resolvedArgs);\n\t\t\t}\n\t\t}\n\t\t// Unknown sub-expression helper — return undefined\n\t\treturn undefined;\n\t}\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment BEFORE\n\t// checking for $root, so that both {{$root}} and {{$root:N}} are\n\t// handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// $root path traversal ($root.name) — not supported, return undefined\n\t// (the analyzer already rejects it with a diagnostic).\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\treturn undefined;\n\t}\n\n\t// $root → return the entire data context (or identifier data)\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null && identifierData) {\n\t\t\tconst source = identifierData[identifier];\n\t\t\treturn source ?? undefined;\n\t\t}\n\t\tif (identifier !== null) {\n\t\t\t// Template uses an identifier but no identifierData was provided\n\t\t\treturn undefined;\n\t\t}\n\t\treturn data;\n\t}\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\t// ── Aggregated identifier (array of objects) ──────────────────\n\t\t\t// When the identifier maps to an array of objects (multi-version\n\t\t\t// data), extract the property from each element to produce a\n\t\t\t// result array. E.g. {{accountId:4}} on [{accountId:\"A\"},{accountId:\"B\"}]\n\t\t\t// → [\"A\", \"B\"]\n\t\t\tif (Array.isArray(source)) {\n\t\t\t\treturn source\n\t\t\t\t\t.map((item) => resolveDataPath(item, cleanSegments))\n\t\t\t\t\t.filter((v) => v !== undefined);\n\t\t\t}\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?: IdentifierData,\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\n\t\t// object (or array for aggregated identifiers).\n\t\tmerged[`${ROOT_TOKEN}:${id}`] = idData;\n\n\t\t// ── Aggregated identifier (array of objects) ─────────────────\n\t\t// When the identifier data is an array (multi-version), we cannot\n\t\t// flatten individual properties into `\"key:N\"` keys because there\n\t\t// are multiple values per key. The array is only accessible via\n\t\t// `$root:N` (already set above). Handlebars helpers like `map`\n\t\t// can then consume it: `{{ map ($root:4) \"accountId\" }}`.\n\t\tif (Array.isArray(idData)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n\n// ─── Direct Helper Execution ─────────────────────────────────────────────────\n// Some helpers (e.g. `map`) return non-primitive values (arrays, objects)\n// that Handlebars would stringify. For these helpers, we resolve their\n// arguments directly and call the helper's `fn` to preserve the raw value.\n\n/** Set of helper names that must be executed directly (bypass Handlebars) */\nconst DIRECT_EXECUTION_HELPERS = new Set<string>([MapHelpers.MAP_HELPER_NAME]);\n\n/**\n * Attempts to execute a helper directly (without Handlebars rendering).\n *\n * Returns `{ value }` if the helper was executed directly, or `undefined`\n * if the helper should go through the normal Handlebars rendering path.\n *\n * @param stmt - The MustacheStatement containing the helper call\n * @param data - The context data\n * @param ctx - Optional execution context (with helpers and identifierData)\n */\nfunction tryDirectHelperExecution(\n\tstmt: hbs.AST.MustacheStatement,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): { value: unknown } | undefined {\n\t// Get the helper name from the path\n\tif (stmt.path.type !== \"PathExpression\") return undefined;\n\tconst helperName = (stmt.path as hbs.AST.PathExpression).original;\n\n\t// Only intercept known direct-execution helpers\n\tif (!DIRECT_EXECUTION_HELPERS.has(helperName)) return undefined;\n\n\t// Look up the helper definition\n\tconst helper = ctx?.helpers?.get(helperName);\n\tif (!helper) return undefined;\n\n\t// Resolve each argument from the data context.\n\t// For the `map` helper, the resolution strategy is:\n\t// - Arg 0 (array): resolve as a data path (e.g. `users` → array)\n\t// - Arg 1 (property): must be a StringLiteral (e.g. `\"name\"`)\n\t// The analyzer enforces this — bare identifiers like `name` are\n\t// rejected at analysis time because Handlebars would resolve them\n\t// as a data path instead of a literal property name.\n\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\n\tconst resolvedArgs: unknown[] = [];\n\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\tconst param = stmt.params[i] as hbs.AST.Expression;\n\n\t\t// For `map`, the second argument (index 1) is a property name —\n\t\t// it must be a StringLiteral (enforced by the analyzer).\n\t\tif (isMap && i === 1) {\n\t\t\tif (param.type === \"StringLiteral\") {\n\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t} else {\n\t\t\t\t// Fallback: resolve normally (will likely be undefined at runtime)\n\t\t\t\tresolvedArgs.push(\n\t\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tresolvedArgs.push(\n\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t);\n\t\t}\n\t}\n\n\t// Call the helper's fn directly with the resolved arguments\n\tconst value = helper.fn(...resolvedArgs);\n\treturn { value };\n}\n"],"names":["Handlebars","dispatchExecute","TemplateRuntimeError","MapHelpers","canUseFastPath","coerceLiteral","extractExpressionIdentifier","extractPathSegments","getEffectiveBody","getEffectivelySingleBlock","getEffectivelySingleExpression","isRootPathTraversal","isRootSegments","isSingleExpression","isThisExpression","parse","ROOT_TOKEN","LRUCache","globalCompilationCache","execute","template","data","identifierData","undefined","tpl","ast","executeFromAst","child","ctx","stmt","body","params","length","hash","resolveExpression","path","helpers","singleExpr","directResult","tryDirectHelperExecution","value","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceValue","coerceSchema","executeFastPath","singleBlock","effective","allContent","every","s","type","targetType","trimmed","trim","num","Number","isNaN","isInteger","lower","toLowerCase","result","String","expr","subExpr","helperName","original","helper","get","isMap","MAP_HELPER_NAME","resolvedArgs","i","param","push","fn","segments","cleanSegments","identifier","source","Array","isArray","map","item","resolveDataPath","filter","v","current","segment","base","id","idData","Object","entries","key","compiledTemplate","cache","compilationCache","hbs","compiled","compile","noEscape","strict","set","error","message","Error","clearCompilationCache","clear","DIRECT_EXECUTION_HELPERS","Set","has"],"mappings":"AAAA,OAAOA,eAAgB,YAAa,AAEpC,QAASC,eAAe,KAAQ,eAAgB,AAChD,QAASC,oBAAoB,KAAQ,aAAc,AACnD,QAASC,UAAU,KAAQ,0BAA2B,AACtD,QACCC,cAAc,CACdC,aAAa,CACbC,2BAA2B,CAC3BC,mBAAmB,CACnBC,gBAAgB,CAChBC,yBAAyB,CACzBC,8BAA8B,CAC9BC,mBAAmB,CACnBC,cAAc,CACdC,kBAAkB,CAClBC,gBAAgB,CAChBC,KAAK,CACLC,UAAU,KACJ,aAAc,AAMrB,QAASC,QAAQ,KAAQ,YAAa,CAkEtC,MAAMC,uBAAyB,IAAID,SAClC,IAiBD,QAAO,SAASE,QACfC,QAAuB,CACvBC,IAAa,CACbC,cAA+B,EAE/B,OAAOrB,gBACNmB,SACAG,UAEA,AAACC,MACA,MAAMC,IAAMV,MAAMS,KAClB,OAAOE,eAAeD,IAAKD,IAAKH,KAAM,CAAEC,cAAe,EACxD,EAEA,AAACK,OAAUR,QAAQQ,MAAON,KAAMC,gBAElC,CAiBA,OAAO,SAASI,eACfD,GAAoB,CACpBL,QAAgB,CAChBC,IAAa,CACbO,GAAqB,EAErB,MAAMN,eAAiBM,KAAKN,eAK5B,GAAIT,mBAAmBY,KAAM,CAC5B,MAAMI,KAAOJ,IAAIK,IAAI,CAAC,EAAE,CACxB,GAAID,KAAKE,MAAM,CAACC,MAAM,GAAK,GAAK,CAACH,KAAKI,IAAI,CAAE,CAC3C,OAAOC,kBAAkBL,KAAKM,IAAI,CAAEd,KAAMC,eAAgBM,KAAKQ,QAChE,CACD,CAGA,MAAMC,WAAa3B,+BAA+Be,KAClD,GAAIY,YAAcA,WAAWN,MAAM,CAACC,MAAM,GAAK,GAAK,CAACK,WAAWJ,IAAI,CAAE,CACrE,OAAOC,kBACNG,WAAWF,IAAI,CACfd,KACAC,eACAM,KAAKQ,QAEP,CAOA,GAAIC,YAAeA,CAAAA,WAAWN,MAAM,CAACC,MAAM,CAAG,GAAKK,WAAWJ,IAAI,AAAD,EAAI,CAKpE,MAAMK,aAAeC,yBAAyBF,WAAYhB,KAAMO,KAChE,GAAIU,eAAiBf,UAAW,CAC/B,OAAOe,aAAaE,KAAK,AAC1B,CAEA,MAAMC,OAASC,yBAAyBrB,KAAMC,gBAC9C,MAAMqB,IAAMC,qBAAqBxB,SAAUqB,OAAQb,KACnD,OAAOiB,YAAYF,IAAKf,KAAKkB,aAC9B,CAMA,GAAI1C,eAAeqB,MAAQA,IAAIK,IAAI,CAACE,MAAM,CAAG,EAAG,CAC/C,OAAOe,gBAAgBtB,IAAKJ,KAAMC,eACnC,CAKA,MAAM0B,YAAcvC,0BAA0BgB,KAC9C,GAAIuB,YAAa,CAChB,MAAMP,OAASC,yBAAyBrB,KAAMC,gBAC9C,MAAMqB,IAAMC,qBAAqBxB,SAAUqB,OAAQb,KACnD,OAAOiB,YAAYF,IAAKf,KAAKkB,aAC9B,CAMA,MAAML,OAASC,yBAAyBrB,KAAMC,gBAC9C,MAAMqB,IAAMC,qBAAqBxB,SAAUqB,OAAQb,KAEnD,MAAMqB,UAAYzC,iBAAiBiB,KACnC,MAAMyB,WAAaD,UAAUE,KAAK,CAAC,AAACC,GAAMA,EAAEC,IAAI,GAAK,oBACrD,GAAIH,WAAY,CACf,OAAOL,YAAYF,IAAKf,KAAKkB,aAC9B,CAEA,OAAOH,GACR,CAkBA,SAASE,YAAYF,GAAW,CAAEG,YAA0B,EAC3D,GAAIA,aAAc,CACjB,MAAMQ,WAAaR,aAAaO,IAAI,CACpC,GAAI,OAAOC,aAAe,SAAU,CACnC,GAAIA,aAAe,SAAU,OAAOX,IACpC,GAAIW,aAAe,UAAYA,aAAe,UAAW,CACxD,MAAMC,QAAUZ,IAAIa,IAAI,GACxB,GAAID,UAAY,GAAI,OAAOhC,UAC3B,MAAMkC,IAAMC,OAAOH,SACnB,GAAIG,OAAOC,KAAK,CAACF,KAAM,OAAOlC,UAC9B,GAAI+B,aAAe,WAAa,CAACI,OAAOE,SAAS,CAACH,KACjD,OAAOlC,UACR,OAAOkC,GACR,CACA,GAAIH,aAAe,UAAW,CAC7B,MAAMO,MAAQlB,IAAIa,IAAI,GAAGM,WAAW,GACpC,GAAID,QAAU,OAAQ,OAAO,KAC7B,GAAIA,QAAU,QAAS,OAAO,MAC9B,OAAOtC,SACR,CACA,GAAI+B,aAAe,OAAQ,OAAO,IACnC,CACD,CAEA,OAAOjD,cAAcsC,IACtB,CAiBA,SAASI,gBACRtB,GAAoB,CACpBJ,IAAa,CACbC,cAA+B,EAE/B,IAAIyC,OAAS,GAEb,IAAK,MAAMlC,QAAQJ,IAAIK,IAAI,CAAE,CAC5B,GAAID,KAAKwB,IAAI,GAAK,mBAAoB,CACrCU,QAAU,AAAClC,KAAkCW,KAAK,AACnD,MAAO,GAAIX,KAAKwB,IAAI,GAAK,oBAAqB,CAC7C,MAAMb,MAAQN,kBACb,AAACL,KAAmCM,IAAI,CACxCd,KACAC,gBAID,GAAIkB,OAAS,KAAM,CAClBuB,QAAUC,OAAOxB,MAClB,CACD,CACD,CAEA,OAAOuB,MACR,CAiBA,SAAS7B,kBACR+B,IAAwB,CACxB5C,IAAa,CACbC,cAA+B,CAC/Bc,OAAuC,EAGvC,GAAItB,iBAAiBmD,MAAO,CAC3B,OAAO5C,IACR,CAGA,GAAI4C,KAAKZ,IAAI,GAAK,gBACjB,OAAO,AAACY,KAA+BzB,KAAK,CAC7C,GAAIyB,KAAKZ,IAAI,GAAK,gBACjB,OAAO,AAACY,KAA+BzB,KAAK,CAC7C,GAAIyB,KAAKZ,IAAI,GAAK,iBACjB,OAAO,AAACY,KAAgCzB,KAAK,CAC9C,GAAIyB,KAAKZ,IAAI,GAAK,cAAe,OAAO,KACxC,GAAIY,KAAKZ,IAAI,GAAK,mBAAoB,OAAO9B,UAK7C,GAAI0C,KAAKZ,IAAI,GAAK,gBAAiB,CAClC,MAAMa,QAAUD,KAChB,GAAIC,QAAQ/B,IAAI,CAACkB,IAAI,GAAK,iBAAkB,CAC3C,MAAMc,WAAa,AAACD,QAAQ/B,IAAI,CAA4BiC,QAAQ,CACpE,MAAMC,OAASjC,SAASkC,IAAIH,YAC5B,GAAIE,OAAQ,CACX,MAAME,MAAQJ,aAAehE,WAAWqE,eAAe,CACvD,MAAMC,aAA0B,EAAE,CAClC,IAAK,IAAIC,EAAI,EAAGA,EAAIR,QAAQnC,MAAM,CAACC,MAAM,CAAE0C,IAAK,CAC/C,MAAMC,MAAQT,QAAQnC,MAAM,CAAC2C,EAAE,CAE/B,GAAIH,OAASG,IAAM,GAAKC,MAAMtB,IAAI,GAAK,gBAAiB,CACvDoB,aAAaG,IAAI,CAAC,AAACD,MAAgCnC,KAAK,CACzD,KAAO,CACNiC,aAAaG,IAAI,CAChB1C,kBAAkByC,MAAOtD,KAAMC,eAAgBc,SAEjD,CACD,CACA,OAAOiC,OAAOQ,EAAE,IAAIJ,aACrB,CACD,CAEA,OAAOlD,SACR,CAGA,MAAMuD,SAAWvE,oBAAoB0D,MACrC,GAAIa,SAAS9C,MAAM,GAAK,EAAG,CAC1B,MAAM,IAAI9B,qBACT,CAAC,mCAAmC,EAAE+D,KAAKZ,IAAI,CAAC,CAAC,CAAC,CAEpD,CAKA,KAAM,CAAE0B,aAAa,CAAEC,UAAU,CAAE,CAAG1E,4BAA4BwE,UAIlE,GAAInE,oBAAoBoE,eAAgB,CACvC,OAAOxD,SACR,CAGA,GAAIX,eAAemE,eAAgB,CAClC,GAAIC,aAAe,MAAQ1D,eAAgB,CAC1C,MAAM2D,OAAS3D,cAAc,CAAC0D,WAAW,CACzC,OAAOC,QAAU1D,SAClB,CACA,GAAIyD,aAAe,KAAM,CAExB,OAAOzD,SACR,CACA,OAAOF,IACR,CAEA,GAAI2D,aAAe,MAAQ1D,eAAgB,CAC1C,MAAM2D,OAAS3D,cAAc,CAAC0D,WAAW,CACzC,GAAIC,OAAQ,CAMX,GAAIC,MAAMC,OAAO,CAACF,QAAS,CAC1B,OAAOA,OACLG,GAAG,CAAC,AAACC,MAASC,gBAAgBD,KAAMN,gBACpCQ,MAAM,CAAC,AAACC,GAAMA,IAAMjE,UACvB,CACA,OAAO+D,gBAAgBL,OAAQF,cAChC,CAEA,OAAOxD,SACR,CAEA,GAAIyD,aAAe,MAAQ,CAAC1D,eAAgB,CAE3C,OAAOC,SACR,CAEA,OAAO+D,gBAAgBjE,KAAM0D,cAC9B,CAUA,OAAO,SAASO,gBAAgBjE,IAAa,CAAEyD,QAAkB,EAChE,IAAIW,QAAmBpE,KAEvB,IAAK,MAAMqE,WAAWZ,SAAU,CAC/B,GAAIW,UAAY,MAAQA,UAAYlE,UAAW,CAC9C,OAAOA,SACR,CAEA,GAAI,OAAOkE,UAAY,SAAU,CAChC,OAAOlE,SACR,CAEAkE,QAAU,AAACA,OAAmC,CAACC,QAAQ,AACxD,CAEA,OAAOD,OACR,CA2BA,SAAS/C,yBACRrB,IAAa,CACbC,cAA+B,EAO/B,MAAMqE,KACLtE,OAAS,MAAQ,OAAOA,OAAS,UAAY,CAAC6D,MAAMC,OAAO,CAAC9D,MACxDA,KACD,CAAC,EACL,MAAMoB,OAAkC,CAAE,GAAGkD,IAAI,CAAE,CAAC3E,WAAW,CAAEK,IAAK,EAEtE,GAAI,CAACC,eAAgB,OAAOmB,OAE5B,IAAK,KAAM,CAACmD,GAAIC,OAAO,GAAIC,OAAOC,OAAO,CAACzE,gBAAiB,CAK1DmB,MAAM,CAAC,CAAC,EAAEzB,WAAW,CAAC,EAAE4E,GAAG,CAAC,CAAC,CAAGC,OAQhC,GAAIX,MAAMC,OAAO,CAACU,QAAS,CAC1B,QACD,CAEA,IAAK,KAAM,CAACG,IAAKxD,MAAM,GAAIsD,OAAOC,OAAO,CAACF,QAAS,CAClDpD,MAAM,CAAC,CAAC,EAAEuD,IAAI,CAAC,EAAEJ,GAAG,CAAC,CAAC,CAAGpD,KAC1B,CACD,CAEA,OAAOC,MACR,CAmBA,SAASG,qBACRxB,QAAgB,CAChBC,IAA6B,CAC7BO,GAAqB,EAErB,GAAI,CAEH,GAAIA,KAAKqE,iBAAkB,CAC1B,OAAOrE,IAAIqE,gBAAgB,CAAC5E,KAC7B,CAGA,MAAM6E,MAAQtE,KAAKuE,kBAAoBjF,uBACvC,MAAMkF,IAAMxE,KAAKwE,KAAOpG,WAExB,IAAIqG,SAAWH,MAAM5B,GAAG,CAAClD,UACzB,GAAI,CAACiF,SAAU,CACdA,SAAWD,IAAIE,OAAO,CAAClF,SAAU,CAGhCmF,SAAU,KAEVC,OAAQ,KACT,GACAN,MAAMO,GAAG,CAACrF,SAAUiF,SACrB,CAEA,OAAOA,SAAShF,KACjB,CAAE,MAAOqF,MAAgB,CACxB,MAAMC,QAAUD,iBAAiBE,MAAQF,MAAMC,OAAO,CAAG3C,OAAO0C,MAChE,OAAM,IAAIxG,qBAAqByG,QAChC,CACD,CAMA,OAAO,SAASE,wBACf3F,uBAAuB4F,KAAK,EAC7B,CAQA,MAAMC,yBAA2B,IAAIC,IAAY,CAAC7G,WAAWqE,eAAe,CAAC,EAY7E,SAASjC,yBACRV,IAA+B,CAC/BR,IAAa,CACbO,GAAqB,EAGrB,GAAIC,KAAKM,IAAI,CAACkB,IAAI,GAAK,iBAAkB,OAAO9B,UAChD,MAAM4C,WAAa,AAACtC,KAAKM,IAAI,CAA4BiC,QAAQ,CAGjE,GAAI,CAAC2C,yBAAyBE,GAAG,CAAC9C,YAAa,OAAO5C,UAGtD,MAAM8C,OAASzC,KAAKQ,SAASkC,IAAIH,YACjC,GAAI,CAACE,OAAQ,OAAO9C,UASpB,MAAMgD,MAAQJ,aAAehE,WAAWqE,eAAe,CAEvD,MAAMC,aAA0B,EAAE,CAClC,IAAK,IAAIC,EAAI,EAAGA,EAAI7C,KAAKE,MAAM,CAACC,MAAM,CAAE0C,IAAK,CAC5C,MAAMC,MAAQ9C,KAAKE,MAAM,CAAC2C,EAAE,CAI5B,GAAIH,OAASG,IAAM,EAAG,CACrB,GAAIC,MAAMtB,IAAI,GAAK,gBAAiB,CACnCoB,aAAaG,IAAI,CAAC,AAACD,MAAgCnC,KAAK,CACzD,KAAO,CAENiC,aAAaG,IAAI,CAChB1C,kBAAkByC,MAAOtD,KAAMO,KAAKN,eAAgBM,KAAKQ,SAE3D,CACD,KAAO,CACNqC,aAAaG,IAAI,CAChB1C,kBAAkByC,MAAOtD,KAAMO,KAAKN,eAAgBM,KAAKQ,SAE3D,CACD,CAGA,MAAMI,MAAQ6B,OAAOQ,EAAE,IAAIJ,cAC3B,MAAO,CAAEjC,KAAM,CAChB"}
@@ -1,2 +1,2 @@
1
- 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}import{HelperFactory}from"./helper-factory.js";function mapProperty(array,property){if(!Array.isArray(array)){return[]}const prop=String(property);const flattened=array.flat(1);return flattened.map(item=>{if(item!==null&&item!==undefined&&typeof item==="object"){return item[prop]}return undefined})}export class MapHelpers extends HelperFactory{buildDefinitions(defs){this.registerMap(defs)}registerMap(defs){defs.set(MapHelpers.MAP_HELPER_NAME,{fn:(array,property)=>mapProperty(array,property),params:[{name:"array",type:{type:"array"},description:"The array of objects to extract values from"},{name:"property",type:{type:"string"},description:"The property name to extract from each element"}],returnType:{type:"array"},description:'Extracts a property from each element of an array: {{ map users "name" }} → ["Alice", "Bob", "Charlie"]'})}}_define_property(MapHelpers,"MAP_HELPER_NAME","map");
1
+ 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}import{HelperFactory}from"./helper-factory.js";function mapProperty(array,property){if(!Array.isArray(array)){return[]}const prop=String(property);const flattened=array.flat(1);return flattened.map(item=>{if(item!==null&&item!==undefined&&typeof item==="object"&&!Array.isArray(item)){return item[prop]}return undefined})}export class MapHelpers extends HelperFactory{buildDefinitions(defs){this.registerMap(defs)}registerMap(defs){defs.set(MapHelpers.MAP_HELPER_NAME,{fn:(array,property)=>mapProperty(array,property),params:[{name:"array",type:{type:"array"},description:"The array of objects to extract values from"},{name:"property",type:{type:"string"},description:"The property name to extract from each element"}],returnType:{type:"array"},description:'Extracts a property from each element of an array: {{ map users "name" }} → ["Alice", "Bob", "Charlie"]'})}}_define_property(MapHelpers,"MAP_HELPER_NAME","map");
2
2
  //# sourceMappingURL=map-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/helpers/map-helpers.ts"],"sourcesContent":["import type { HelperDefinition } from \"../types.ts\";\nimport { HelperFactory } from \"./helper-factory.ts\";\n\n// ─── MapHelpers ──────────────────────────────────────────────────────────────\n// Aggregates all map-related helpers for the template engine.\n//\n// Provides helpers for working with arrays of objects:\n//\n// - **`map`** — Extracts a specific property from each element of an\n// array, returning a new array of those values.\n// Usage: `{{ map users \"name\" }}` → `[\"Alice\", \"Bob\", \"Charlie\"]`\n//\n// ─── Registration ────────────────────────────────────────────────────────────\n// MapHelpers are automatically pre-registered by the `Typebars`\n// constructor. They can also be registered manually on any object\n// implementing `HelperRegistry`:\n//\n// const factory = new MapHelpers();\n// factory.register(engine); // registers all helpers\n// factory.unregister(engine); // removes all helpers\n//\n// ─── Static Analysis ─────────────────────────────────────────────────────────\n// The `map` helper has special static analysis handling in the analyzer:\n// - The first argument must resolve to an array of objects\n// - The second argument must be a quoted string literal (e.g. `\"name\"`, not `name`)\n// - The property must exist in the item schema of the array\n// - The inferred return type is `{ type: \"array\", items: <property schema> }`\n\n// ─── Internal utilities ─────────────────────────────────────────────────────\n\n/**\n * Extracts a property from each element of an array.\n *\n * @param array - The array of objects\n * @param property - The property name to extract from each element\n * @returns A new array containing the extracted property values\n */\nfunction mapProperty(array: unknown, property: unknown): unknown[] {\n\tif (!Array.isArray(array)) {\n\t\treturn [];\n\t}\n\tconst prop = String(property);\n\t// Use flatMap semantics: if the array contains nested arrays\n\t// (e.g. from a previous map), flatten one level before extracting.\n\t// This enables chaining like `{{ map (map users 'cartItems') 'productId' }}`\n\t// where the inner map returns an array of arrays.\n\tconst flattened = array.flat(1);\n\treturn flattened.map((item: unknown) => {\n\t\tif (item !== null && item !== undefined && typeof item === \"object\") {\n\t\t\treturn (item as Record<string, unknown>)[prop];\n\t\t}\n\t\treturn undefined;\n\t});\n}\n\n// ─── Main class ─────────────────────────────────────────────────────────────\n\nexport class MapHelpers extends HelperFactory {\n\t/** The name used for special-case detection in the analyzer/executor */\n\tstatic readonly MAP_HELPER_NAME = \"map\";\n\n\t// ─── buildDefinitions (required by HelperFactory) ──────────────────\n\n\tprotected buildDefinitions(defs: Map<string, HelperDefinition>): void {\n\t\tthis.registerMap(defs);\n\t}\n\n\t// ── map ──────────────────────────────────────────────────────────────\n\n\t/** Registers the `map` helper */\n\tprivate registerMap(defs: Map<string, HelperDefinition>): void {\n\t\tdefs.set(MapHelpers.MAP_HELPER_NAME, {\n\t\t\tfn: (array: unknown, property: unknown) => mapProperty(array, property),\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: \"array\",\n\t\t\t\t\ttype: { type: \"array\" },\n\t\t\t\t\tdescription: \"The array of objects to extract values from\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"property\",\n\t\t\t\t\ttype: { type: \"string\" },\n\t\t\t\t\tdescription: \"The property name to extract from each element\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"array\" },\n\t\t\tdescription:\n\t\t\t\t'Extracts a property from each element of an array: {{ map users \"name\" }} → [\"Alice\", \"Bob\", \"Charlie\"]',\n\t\t});\n\t}\n}\n"],"names":["HelperFactory","mapProperty","array","property","Array","isArray","prop","String","flattened","flat","map","item","undefined","MapHelpers","buildDefinitions","defs","registerMap","set","MAP_HELPER_NAME","fn","params","name","type","description","returnType"],"mappings":"oLACA,OAASA,aAAa,KAAQ,qBAAsB,CAoCpD,SAASC,YAAYC,KAAc,CAAEC,QAAiB,EACrD,GAAI,CAACC,MAAMC,OAAO,CAACH,OAAQ,CAC1B,MAAO,EAAE,AACV,CACA,MAAMI,KAAOC,OAAOJ,UAKpB,MAAMK,UAAYN,MAAMO,IAAI,CAAC,GAC7B,OAAOD,UAAUE,GAAG,CAAC,AAACC,OACrB,GAAIA,OAAS,MAAQA,OAASC,WAAa,OAAOD,OAAS,SAAU,CACpE,OAAO,AAACA,IAAgC,CAACL,KAAK,AAC/C,CACA,OAAOM,SACR,EACD,CAIA,OAAO,MAAMC,mBAAmBb,cAM/B,AAAUc,iBAAiBC,IAAmC,CAAQ,CACrE,IAAI,CAACC,WAAW,CAACD,KAClB,CAKA,AAAQC,YAAYD,IAAmC,CAAQ,CAC9DA,KAAKE,GAAG,CAACJ,WAAWK,eAAe,CAAE,CACpCC,GAAI,CAACjB,MAAgBC,WAAsBF,YAAYC,MAAOC,UAC9DiB,OAAQ,CACP,CACCC,KAAM,QACNC,KAAM,CAAEA,KAAM,OAAQ,EACtBC,YAAa,6CACd,EACA,CACCF,KAAM,WACNC,KAAM,CAAEA,KAAM,QAAS,EACvBC,YAAa,gDACd,EACA,CACDC,WAAY,CAAEF,KAAM,OAAQ,EAC5BC,YACC,yGACF,EACD,CACD,CA/BC,iBAFYV,WAEIK,kBAAkB"}
1
+ {"version":3,"sources":["../../../src/helpers/map-helpers.ts"],"sourcesContent":["import type { HelperDefinition } from \"../types.ts\";\nimport { HelperFactory } from \"./helper-factory.ts\";\n\n// ─── MapHelpers ──────────────────────────────────────────────────────────────\n// Aggregates all map-related helpers for the template engine.\n//\n// Provides helpers for working with arrays of objects:\n//\n// - **`map`** — Extracts a specific property from each element of an\n// array, returning a new array of those values.\n// Usage: `{{ map users \"name\" }}` → `[\"Alice\", \"Bob\", \"Charlie\"]`\n//\n// ─── Registration ────────────────────────────────────────────────────────────\n// MapHelpers are automatically pre-registered by the `Typebars`\n// constructor. They can also be registered manually on any object\n// implementing `HelperRegistry`:\n//\n// const factory = new MapHelpers();\n// factory.register(engine); // registers all helpers\n// factory.unregister(engine); // removes all helpers\n//\n// ─── Static Analysis ─────────────────────────────────────────────────────────\n// The `map` helper has special static analysis handling in the analyzer:\n// - The first argument must resolve to an array of objects\n// - The second argument must be a quoted string literal (e.g. `\"name\"`, not `name`)\n// - The property must exist in the item schema of the array\n// - The inferred return type is `{ type: \"array\", items: <property schema> }`\n\n// ─── Internal utilities ─────────────────────────────────────────────────────\n\n/**\n * Extracts a property from each element of an array.\n *\n * **Note:** The input array is automatically flattened one level (`flat(1)`)\n * before mapping. This is designed to support multi-node outputs and chained\n * map calls which produce arrays of arrays. If your data is already flat,\n * this has no effect.\n *\n * @example\n * // Input: [[{name:\"a\"},{name:\"b\"}],[{name:\"c\"}]]\n * // After flat(1): [{name:\"a\"},{name:\"b\"},{name:\"c\"}]\n * // Result: [\"a\",\"b\",\"c\"]\n *\n * @param array - The array of objects (will be flattened one level)\n * @param property - The property name to extract from each element\n * @returns A new array containing the extracted property values\n */\nfunction mapProperty(array: unknown, property: unknown): unknown[] {\n\tif (!Array.isArray(array)) {\n\t\treturn [];\n\t}\n\tconst prop = String(property);\n\t// Use flatMap semantics: if the array contains nested arrays\n\t// (e.g. from a previous map), flatten one level before extracting.\n\t// This enables chaining like `{{ map (map users 'cartItems') 'productId' }}`\n\t// where the inner map returns an array of arrays.\n\tconst flattened = array.flat(1);\n\treturn flattened.map((item: unknown) => {\n\t\tif (\n\t\t\titem !== null &&\n\t\t\titem !== undefined &&\n\t\t\ttypeof item === \"object\" &&\n\t\t\t!Array.isArray(item)\n\t\t) {\n\t\t\treturn (item as Record<string, unknown>)[prop];\n\t\t}\n\t\treturn undefined;\n\t});\n}\n\n// ─── Main class ─────────────────────────────────────────────────────────────\n\nexport class MapHelpers extends HelperFactory {\n\t/** The name used for special-case detection in the analyzer/executor */\n\tstatic readonly MAP_HELPER_NAME = \"map\";\n\n\t// ─── buildDefinitions (required by HelperFactory) ──────────────────\n\n\tprotected buildDefinitions(defs: Map<string, HelperDefinition>): void {\n\t\tthis.registerMap(defs);\n\t}\n\n\t// ── map ──────────────────────────────────────────────────────────────\n\n\t/** Registers the `map` helper */\n\tprivate registerMap(defs: Map<string, HelperDefinition>): void {\n\t\tdefs.set(MapHelpers.MAP_HELPER_NAME, {\n\t\t\tfn: (array: unknown, property: unknown) => mapProperty(array, property),\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: \"array\",\n\t\t\t\t\ttype: { type: \"array\" },\n\t\t\t\t\tdescription: \"The array of objects to extract values from\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"property\",\n\t\t\t\t\ttype: { type: \"string\" },\n\t\t\t\t\tdescription: \"The property name to extract from each element\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"array\" },\n\t\t\tdescription:\n\t\t\t\t'Extracts a property from each element of an array: {{ map users \"name\" }} → [\"Alice\", \"Bob\", \"Charlie\"]',\n\t\t});\n\t}\n}\n"],"names":["HelperFactory","mapProperty","array","property","Array","isArray","prop","String","flattened","flat","map","item","undefined","MapHelpers","buildDefinitions","defs","registerMap","set","MAP_HELPER_NAME","fn","params","name","type","description","returnType"],"mappings":"oLACA,OAASA,aAAa,KAAQ,qBAAsB,CA8CpD,SAASC,YAAYC,KAAc,CAAEC,QAAiB,EACrD,GAAI,CAACC,MAAMC,OAAO,CAACH,OAAQ,CAC1B,MAAO,EAAE,AACV,CACA,MAAMI,KAAOC,OAAOJ,UAKpB,MAAMK,UAAYN,MAAMO,IAAI,CAAC,GAC7B,OAAOD,UAAUE,GAAG,CAAC,AAACC,OACrB,GACCA,OAAS,MACTA,OAASC,WACT,OAAOD,OAAS,UAChB,CAACP,MAAMC,OAAO,CAACM,MACd,CACD,OAAO,AAACA,IAAgC,CAACL,KAAK,AAC/C,CACA,OAAOM,SACR,EACD,CAIA,OAAO,MAAMC,mBAAmBb,cAM/B,AAAUc,iBAAiBC,IAAmC,CAAQ,CACrE,IAAI,CAACC,WAAW,CAACD,KAClB,CAKA,AAAQC,YAAYD,IAAmC,CAAQ,CAC9DA,KAAKE,GAAG,CAACJ,WAAWK,eAAe,CAAE,CACpCC,GAAI,CAACjB,MAAgBC,WAAsBF,YAAYC,MAAOC,UAC9DiB,OAAQ,CACP,CACCC,KAAM,QACNC,KAAM,CAAEA,KAAM,OAAQ,EACtBC,YAAa,6CACd,EACA,CACCF,KAAM,WACNC,KAAM,CAAEA,KAAM,QAAS,EACvBC,YAAa,gDACd,EACA,CACDC,WAAY,CAAEF,KAAM,OAAQ,EAC5BC,YACC,yGACF,EACD,CACD,CA/BC,iBAFYV,WAEIK,kBAAkB"}
@@ -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 TemplateData, type TemplateInput, type TemplateInputArray, } from "./types.js";
4
+ export { defineHelper, type IdentifierData, type IdentifierDataEntry, 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 TemplateData,\n\ttype TemplateInput,\n\ttype TemplateInputArray,\n} from \"./types\";\n"],"names":["Typebars","defineHelper","isArrayInput"],"mappings":"AACA,WAAc,UAAW,AACzB,QAASA,QAAQ,KAAQ,YAAa,AACtC,QACCC,YAAY,CACZC,YAAY,KAIN,SAAU"}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export type { AnalyzeOptions } from \"./analyzer\";\nexport * from \"./errors\";\nexport { Typebars } from \"./typebars\";\nexport {\n\tdefineHelper,\n\ttype IdentifierData,\n\ttype IdentifierDataEntry,\n\tisArrayInput,\n\ttype TemplateData,\n\ttype TemplateInput,\n\ttype TemplateInputArray,\n} from \"./types\";\n"],"names":["Typebars","defineHelper","isArrayInput"],"mappings":"AACA,WAAc,UAAW,AACzB,QAASA,QAAQ,KAAQ,YAAa,AACtC,QACCC,YAAY,CAGZC,YAAY,KAIN,SAAU"}
@@ -1,2 +1,2 @@
1
- import{UnsupportedSchemaError}from"./errors.js";import{deepEqual}from"./utils.js";export function findConditionalSchemaLocations(schema,path="",visited=new Set){const locations=[];if(visited.has(schema))return locations;visited.add(schema);for(const kw of["if","then","else"]){if(schema[kw]!==undefined){locations.push({keyword:"if/then/else",schemaPath:path||"/"});break}}if(schema.properties){for(const[key,prop]of Object.entries(schema.properties)){if(prop&&typeof prop!=="boolean"){locations.push(...findConditionalSchemaLocations(prop,`${path}/properties/${key}`,visited))}}}if(schema.additionalProperties&&typeof schema.additionalProperties==="object"){locations.push(...findConditionalSchemaLocations(schema.additionalProperties,`${path}/additionalProperties`,visited))}if(schema.items){if(Array.isArray(schema.items)){for(let i=0;i<schema.items.length;i++){const item=schema.items[i];if(item&&typeof item!=="boolean"){locations.push(...findConditionalSchemaLocations(item,`${path}/items/${i}`,visited))}}}else if(typeof schema.items!=="boolean"){locations.push(...findConditionalSchemaLocations(schema.items,`${path}/items`,visited))}}for(const keyword of["allOf","anyOf","oneOf"]){const branches=schema[keyword];if(branches){for(let i=0;i<branches.length;i++){const branch=branches[i];if(branch&&typeof branch!=="boolean"){locations.push(...findConditionalSchemaLocations(branch,`${path}/${keyword}/${i}`,visited))}}}}if(schema.not&&typeof schema.not!=="boolean"){locations.push(...findConditionalSchemaLocations(schema.not,`${path}/not`,visited))}for(const defsKey of["definitions","$defs"]){const defs=schema[defsKey];if(defs){for(const[name,def]of Object.entries(defs)){if(def&&typeof def!=="boolean"){locations.push(...findConditionalSchemaLocations(def,`${path}/${defsKey}/${name}`,visited))}}}}return locations}export function assertNoConditionalSchema(schema,path="",visited=new Set){const locations=findConditionalSchemaLocations(schema,path,visited);if(locations.length>0){const first=locations[0];throw new UnsupportedSchemaError(first.keyword,first.schemaPath)}}export function resolveRef(schema,root){if(!schema.$ref)return schema;const ref=schema.$ref;const match=ref.match(/^#\/(definitions|\$defs)\/(.+)$/);if(!match){throw new Error(`Unsupported $ref format: "${ref}". Only internal #/definitions/ references are supported.`)}const defsKey=match[1];const name=match[2]??"";const defs=defsKey==="definitions"?root.definitions:root.$defs;if(!defs||!(name in defs)){throw new Error(`Cannot resolve $ref "${ref}": definition "${name}" not found.`)}const def=defs[name];if(!def||typeof def==="boolean"){throw new Error(`Cannot resolve $ref "${ref}": definition "${name}" not found.`)}return resolveRef(def,root)}function resolveSegment(schema,segment,root){const resolved=resolveRef(schema,root);if(resolved.properties&&segment in resolved.properties){const prop=resolved.properties[segment];if(prop&&typeof prop!=="boolean")return resolveRef(prop,root);if(prop===true)return{}}if(resolved.additionalProperties!==undefined&&resolved.additionalProperties!==false){if(resolved.additionalProperties===true){return{}}return resolveRef(resolved.additionalProperties,root)}const schemaType=resolved.type;const isArray=schemaType==="array"||Array.isArray(schemaType)&&schemaType.includes("array");if(isArray&&segment==="length"){return{type:"integer"}}if(isArray&&/^\d+$/.test(segment)){if(resolved.items===undefined){return{}}if(typeof resolved.items==="boolean"){return{}}if(Array.isArray(resolved.items)){const idx=Number.parseInt(segment,10);const item=resolved.items[idx];if(item!==undefined&&typeof item!=="boolean"){return resolveRef(item,root)}if(item!==undefined&&typeof item==="boolean"){return{}}if(resolved.additionalItems===false){return undefined}if(resolved.additionalItems!==undefined&&resolved.additionalItems!==true&&typeof resolved.additionalItems==="object"){return resolveRef(resolved.additionalItems,root)}return{}}return resolveRef(resolved.items,root)}const combinatorResult=resolveInCombinators(resolved,segment,root);if(combinatorResult)return combinatorResult;return undefined}function resolveInCombinators(schema,segment,root){if(schema.allOf){const matches=schema.allOf.filter(b=>typeof b!=="boolean").map(branch=>resolveSegment(branch,segment,root)).filter(s=>s!==undefined);if(matches.length===1)return matches[0];if(matches.length>1)return{allOf:matches}}for(const key of["anyOf","oneOf"]){if(!schema[key])continue;const matches=schema[key].filter(b=>typeof b!=="boolean").map(branch=>resolveSegment(branch,segment,root)).filter(s=>s!==undefined);if(matches.length===1)return matches[0];if(matches.length>1)return{[key]:matches}}return undefined}export function resolveSchemaPath(schema,path){if(path.length===0)return resolveRef(schema,schema);let current=resolveRef(schema,schema);const root=schema;for(const segment of path){const next=resolveSegment(current,segment,root);if(next===undefined)return undefined;current=next}return current}export function resolveArrayItems(schema,root){const resolved=resolveRef(schema,root);const schemaType=resolved.type;const isArray=schemaType==="array"||Array.isArray(schemaType)&&schemaType.includes("array");if(!isArray&&resolved.items===undefined){return undefined}if(resolved.items===undefined){return{}}if(typeof resolved.items==="boolean"){return{}}if(Array.isArray(resolved.items)){const schemas=resolved.items.filter(item=>typeof item!=="boolean").map(item=>resolveRef(item,root));if(schemas.length===0)return{};return{oneOf:schemas}}return resolveRef(resolved.items,root)}export function simplifySchema(schema){for(const key of["oneOf","anyOf"]){const arr=schema[key];if(arr&&arr.length===1){const first=arr[0];if(first!==undefined&&typeof first!=="boolean")return simplifySchema(first)}}if(schema.allOf&&schema.allOf.length===1){const first=schema.allOf[0];if(first!==undefined&&typeof first!=="boolean")return simplifySchema(first)}for(const key of["oneOf","anyOf"]){const arr=schema[key];if(arr&&arr.length>1){const unique=[];for(const entry of arr){if(typeof entry==="boolean")continue;const isDuplicate=unique.some(existing=>deepEqual(existing,entry));if(!isDuplicate){unique.push(simplifySchema(entry))}}if(unique.length===1)return unique[0];return{...schema,[key]:unique}}}return schema}
1
+ import{UnsupportedSchemaError}from"./errors.js";import{deepEqual}from"./utils.js";export function findConditionalSchemaLocations(schema,path="",visited=new Set){const locations=[];if(visited.has(schema))return locations;visited.add(schema);for(const kw of["if","then","else"]){if(schema[kw]!==undefined){locations.push({keyword:"if/then/else",schemaPath:path||"/"});break}}if(schema.properties){for(const[key,prop]of Object.entries(schema.properties)){if(prop&&typeof prop!=="boolean"){locations.push(...findConditionalSchemaLocations(prop,`${path}/properties/${key}`,visited))}}}if(schema.additionalProperties&&typeof schema.additionalProperties==="object"){locations.push(...findConditionalSchemaLocations(schema.additionalProperties,`${path}/additionalProperties`,visited))}if(schema.items){if(Array.isArray(schema.items)){for(let i=0;i<schema.items.length;i++){const item=schema.items[i];if(item&&typeof item!=="boolean"){locations.push(...findConditionalSchemaLocations(item,`${path}/items/${i}`,visited))}}}else if(typeof schema.items!=="boolean"){locations.push(...findConditionalSchemaLocations(schema.items,`${path}/items`,visited))}}for(const keyword of["allOf","anyOf","oneOf"]){const branches=schema[keyword];if(branches){for(let i=0;i<branches.length;i++){const branch=branches[i];if(branch&&typeof branch!=="boolean"){locations.push(...findConditionalSchemaLocations(branch,`${path}/${keyword}/${i}`,visited))}}}}if(schema.not&&typeof schema.not!=="boolean"){locations.push(...findConditionalSchemaLocations(schema.not,`${path}/not`,visited))}for(const defsKey of["definitions","$defs"]){const defs=schema[defsKey];if(defs){for(const[name,def]of Object.entries(defs)){if(def&&typeof def!=="boolean"){locations.push(...findConditionalSchemaLocations(def,`${path}/${defsKey}/${name}`,visited))}}}}return locations}export function assertNoConditionalSchema(schema,path="",visited=new Set){const locations=findConditionalSchemaLocations(schema,path,visited);if(locations.length>0){const first=locations[0];throw new UnsupportedSchemaError(first.keyword,first.schemaPath)}}export function resolveRef(schema,root){if(!schema.$ref)return schema;const ref=schema.$ref;const match=ref.match(/^#\/(definitions|\$defs)\/(.+)$/);if(!match){throw new Error(`Unsupported $ref format: "${ref}". Only internal #/definitions/ references are supported.`)}const defsKey=match[1];const name=match[2]??"";const defs=defsKey==="definitions"?root.definitions:root.$defs;if(!defs||!(name in defs)){throw new Error(`Cannot resolve $ref "${ref}": definition "${name}" not found.`)}const def=defs[name];if(!def||typeof def==="boolean"){throw new Error(`Cannot resolve $ref "${ref}": definition "${name}" not found.`)}return resolveRef(def,root)}function resolveSegment(schema,segment,root){const resolved=resolveRef(schema,root);if(resolved.properties&&segment in resolved.properties){const prop=resolved.properties[segment];if(prop&&typeof prop!=="boolean")return resolveRef(prop,root);if(prop===true)return{}}if(resolved.additionalProperties!==undefined&&resolved.additionalProperties!==false){if(resolved.additionalProperties===true){return{}}return resolveRef(resolved.additionalProperties,root)}const schemaType=resolved.type;const isArray=schemaType==="array"||Array.isArray(schemaType)&&schemaType.includes("array");if(isArray&&segment==="length"){return{type:"integer"}}if(isArray&&/^\d+$/.test(segment)){if(resolved.items===undefined){return{}}if(typeof resolved.items==="boolean"){return{}}if(Array.isArray(resolved.items)){const idx=Number.parseInt(segment,10);const item=resolved.items[idx];if(item!==undefined&&typeof item!=="boolean"){return resolveRef(item,root)}if(item!==undefined&&typeof item==="boolean"){return{}}if(resolved.additionalItems===false){return undefined}if(resolved.additionalItems!==undefined&&resolved.additionalItems!==true&&typeof resolved.additionalItems==="object"){return resolveRef(resolved.additionalItems,root)}return{}}return resolveRef(resolved.items,root)}const combinatorResult=resolveInCombinators(resolved,segment,root);if(combinatorResult)return combinatorResult;return undefined}function resolveInCombinators(schema,segment,root){if(schema.allOf){const matches=schema.allOf.filter(b=>typeof b!=="boolean").map(branch=>resolveSegment(branch,segment,root)).filter(s=>s!==undefined);if(matches.length===1)return matches[0];if(matches.length>1)return{allOf:matches}}for(const key of["anyOf","oneOf"]){if(!schema[key])continue;const matches=schema[key].filter(b=>typeof b!=="boolean").map(branch=>resolveSegment(branch,segment,root)).filter(s=>s!==undefined);if(matches.length===1)return matches[0];if(matches.length>1)return{[key]:matches}}return undefined}export function resolveSchemaPath(schema,path){if(path.length===0)return resolveRef(schema,schema);let current=resolveRef(schema,schema);const root=schema;for(const segment of path){const next=resolveSegment(current,segment,root);if(next===undefined)return undefined;current=next}return current}export function resolveArrayItems(schema,root){const resolved=resolveRef(schema,root);const schemaType=resolved.type;const isArray=schemaType==="array"||Array.isArray(schemaType)&&schemaType.includes("array");if(!isArray&&resolved.items===undefined){return undefined}if(resolved.items===undefined){return{}}if(typeof resolved.items==="boolean"){return{}}if(Array.isArray(resolved.items)){const schemas=resolved.items.filter(item=>typeof item!=="boolean").map(item=>resolveRef(item,root));if(schemas.length===0)return{};return{oneOf:schemas}}return resolveRef(resolved.items,root)}export function simplifySchema(schema){for(const key of["oneOf","anyOf"]){const arr=schema[key];if(arr&&arr.length===1){const first=arr[0];if(first!==undefined&&typeof first!=="boolean")return simplifySchema(first)}}if(schema.allOf&&schema.allOf.length===1){const first=schema.allOf[0];if(first!==undefined&&typeof first!=="boolean")return simplifySchema(first)}let result=schema;for(const key of["oneOf","anyOf"]){const arr=result[key];if(arr&&arr.length>1){const unique=[];for(const entry of arr){if(typeof entry==="boolean")continue;const simplified=simplifySchema(entry);const isDuplicate=unique.some(existing=>deepEqual(existing,simplified));if(!isDuplicate){unique.push(simplified)}}if(unique.length===1)return unique[0];result={...result,[key]:unique}}}if(result.allOf&&result.allOf.length>1){const simplifiedBranches=result.allOf.map(branch=>{if(typeof branch==="boolean"||branch===undefined)return branch;return simplifySchema(branch)});result={...result,allOf:simplifiedBranches}}if(result.properties){const simplifiedProps={};let changed=false;for(const[key,prop]of Object.entries(result.properties)){if(prop&&typeof prop!=="boolean"){const simplified=simplifySchema(prop);simplifiedProps[key]=simplified;if(simplified!==prop)changed=true}else{simplifiedProps[key]=prop}}if(changed){result={...result,properties:simplifiedProps}}}if(result.items){if(Array.isArray(result.items)){let itemsChanged=false;const simplifiedItems=result.items.map(item=>{if(item&&typeof item!=="boolean"){const simplified=simplifySchema(item);if(simplified!==item)itemsChanged=true;return simplified}return item});if(itemsChanged){result={...result,items:simplifiedItems}}}else if(typeof result.items!=="boolean"){const simplified=simplifySchema(result.items);if(simplified!==result.items){result={...result,items:simplified}}}}if(result.additionalProperties&&typeof result.additionalProperties==="object"){const simplified=simplifySchema(result.additionalProperties);if(simplified!==result.additionalProperties){result={...result,additionalProperties:simplified}}}return result}
2
2
  //# sourceMappingURL=schema-resolver.js.map