typebars 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1582 -0
- package/dist/analyzer.d.ts +59 -0
- package/dist/analyzer.js +4 -0
- package/dist/analyzer.js.map +9 -0
- package/dist/chunk-1gm6cf0e.js +5 -0
- package/dist/chunk-1gm6cf0e.js.map +10 -0
- package/dist/chunk-1qwj7pjc.js +4 -0
- package/dist/chunk-1qwj7pjc.js.map +10 -0
- package/dist/chunk-4zv02svp.js +7 -0
- package/dist/chunk-4zv02svp.js.map +10 -0
- package/dist/chunk-60gk3q7z.js +5 -0
- package/dist/chunk-60gk3q7z.js.map +10 -0
- package/dist/chunk-6955jpr7.js +5 -0
- package/dist/chunk-6955jpr7.js.map +10 -0
- package/dist/chunk-6c0pw73w.js +7 -0
- package/dist/chunk-6c0pw73w.js.map +10 -0
- package/dist/chunk-7j6q1e3z.js +5 -0
- package/dist/chunk-7j6q1e3z.js.map +14 -0
- package/dist/chunk-8g0d6h85.js +5 -0
- package/dist/chunk-8g0d6h85.js.map +10 -0
- package/dist/chunk-9mg6qfrs.js +5 -0
- package/dist/chunk-9mg6qfrs.js.map +10 -0
- package/dist/chunk-a37yzqra.js +5 -0
- package/dist/chunk-a37yzqra.js.map +11 -0
- package/dist/chunk-awgj10qg.js +4 -0
- package/dist/chunk-awgj10qg.js.map +10 -0
- package/dist/chunk-fhvf5y4x.js +7 -0
- package/dist/chunk-fhvf5y4x.js.map +10 -0
- package/dist/chunk-ggdfaqhe.js +5 -0
- package/dist/chunk-ggdfaqhe.js.map +10 -0
- package/dist/chunk-hc1jnqaw.js +5 -0
- package/dist/chunk-hc1jnqaw.js.map +10 -0
- package/dist/chunk-kznb0bev.js +5 -0
- package/dist/chunk-kznb0bev.js.map +10 -0
- package/dist/chunk-mx8neh7q.js +5 -0
- package/dist/chunk-mx8neh7q.js.map +10 -0
- package/dist/chunk-p3xzf1ew.js +5 -0
- package/dist/chunk-p3xzf1ew.js.map +10 -0
- package/dist/chunk-qh2r1pa1.js +5 -0
- package/dist/chunk-qh2r1pa1.js.map +10 -0
- package/dist/chunk-qpzzr2rd.js +5 -0
- package/dist/chunk-qpzzr2rd.js.map +10 -0
- package/dist/chunk-vka4e61h.js +7 -0
- package/dist/chunk-vka4e61h.js.map +10 -0
- package/dist/chunk-xbvk4ygq.js +5 -0
- package/dist/chunk-xbvk4ygq.js.map +11 -0
- package/dist/chunk-ybh51hbe.js +7 -0
- package/dist/chunk-ybh51hbe.js.map +10 -0
- package/dist/chunk-yczpjh73.js +4 -0
- package/dist/chunk-yczpjh73.js.map +10 -0
- package/dist/chunk-yraqh2tz.js +5 -0
- package/dist/chunk-yraqh2tz.js.map +10 -0
- package/dist/chunk-z6yh5qvc.js +5 -0
- package/dist/chunk-z6yh5qvc.js.map +10 -0
- package/dist/compiled-template.d.ts +130 -0
- package/dist/compiled-template.js +4 -0
- package/dist/compiled-template.js.map +9 -0
- package/dist/errors.d.ts +93 -0
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +9 -0
- package/dist/executor.d.ts +55 -0
- package/dist/executor.js +4 -0
- package/dist/executor.js.map +9 -0
- package/dist/helpers/helper-factory.d.ts +56 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/logical-helpers.d.ts +15 -0
- package/dist/helpers/math-helpers.d.ts +13 -0
- package/dist/helpers/utils.d.ts +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +9 -0
- package/dist/parser.d.ts +146 -0
- package/dist/parser.js +4 -0
- package/dist/parser.js.map +9 -0
- package/dist/schema-resolver.d.ts +50 -0
- package/dist/schema-resolver.js +4 -0
- package/dist/schema-resolver.js.map +9 -0
- package/dist/typebars.d.ts +130 -0
- package/dist/typebars.js +4 -0
- package/dist/typebars.js.map +9 -0
- package/dist/types.d.ts +334 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +9 -0
- package/dist/utils.d.ts +113 -0
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +9 -0
- package/package.json +42 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/typebars.ts", "../src/helpers/math-helpers.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport {\n\tCompiledTemplate,\n\ttype CompiledTemplateOptions,\n} from \"./compiled-template.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { executeFromAst } from \"./executor.ts\";\nimport { MathHelpers } from \"./helpers/index.ts\";\nimport { parse } from \"./parser.ts\";\nimport type {\n\tAnalysisResult,\n\tAnalyzeAndExecuteOptions,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateEngineOptions,\n\tTemplateInput,\n\tValidationResult,\n} from \"./types.ts\";\nimport {\n\tinferPrimitiveSchema,\n\tisLiteralInput,\n\tisObjectInput,\n} from \"./types.ts\";\nimport {\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\tLRUCache,\n} from \"./utils.ts\";\n\n// ─── Typebars ────────────────────────────────────────────────────────────────\n// Public entry point of the template engine. Orchestrates three phases:\n//\n// 1. **Parsing** — transforms the template string into an AST (via Handlebars)\n// 2. **Analysis** — static validation + return type inference\n// 3. **Execution** — renders the template with real data\n//\n// ─── Architecture v2 ─────────────────────────────────────────────────────────\n// - **LRU cache** for parsed ASTs and compiled Handlebars templates\n// - **Isolated Handlebars environment** per instance (custom helpers)\n// - **`compile()` pattern**: parse-once / execute-many\n// - **`validate()` method**: API shortcut without `outputSchema`\n// - **`registerHelper()`**: custom helpers with static typing\n// - **`ExecuteOptions`**: options object for `execute()`\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing variables from specific data\n// sources, identified by an integer N.\n//\n// - `identifierSchemas`: mapping `{ [id]: JSONSchema7 }` for static analysis\n// - `identifierData`: mapping `{ [id]: Record<string, unknown> }` for execution\n//\n// Usage:\n// engine.execute(\"{{meetingId:1}}\", data, { identifierData: { 1: node1Data } });\n// engine.analyze(\"{{meetingId:1}}\", schema, { 1: node1Schema });\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n\nexport class Typebars {\n\t/** Isolated Handlebars environment — each engine has its own helpers */\n\tprivate readonly hbs: typeof Handlebars;\n\n\t/** LRU cache of parsed ASTs (avoids re-parsing) */\n\tprivate readonly astCache: LRUCache<string, hbs.AST.Program>;\n\n\t/** LRU cache of compiled Handlebars templates (avoids recompilation) */\n\tprivate readonly compilationCache: LRUCache<\n\t\tstring,\n\t\tHandlebarsTemplateDelegate\n\t>;\n\n\t/** Custom helpers registered on this instance */\n\tprivate readonly helpers = new Map<string, HelperDefinition>();\n\n\tconstructor(options: TemplateEngineOptions = {}) {\n\t\tthis.hbs = Handlebars.create();\n\t\tthis.astCache = new LRUCache(options.astCacheSize ?? 256);\n\t\tthis.compilationCache = new LRUCache(options.compilationCacheSize ?? 256);\n\n\t\t// ── Built-in helpers (math) ──────────────────────────────────────\n\t\tMathHelpers.register(this);\n\n\t\t// ── Custom helpers via options ───────────────────────────────────\n\t\tif (options.helpers) {\n\t\t\tfor (const helper of options.helpers) {\n\t\t\t\tconst { name, ...definition } = helper;\n\t\t\t\tthis.registerHelper(name, definition);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Compilation ───────────────────────────────────────────────────────\n\n\t/**\n\t * Compiles a template and returns a `CompiledTemplate` ready to be\n\t * executed or analyzed without re-parsing.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is compiled recursively.\n\t *\n\t * @param template - The template to compile\n\t * @returns A reusable `CompiledTemplate`\n\t */\n\tcompile(template: TemplateInput): CompiledTemplate {\n\t\tif (isObjectInput(template)) {\n\t\t\tconst children: Record<string, CompiledTemplate> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tchildren[key] = this.compile(value);\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromObject(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn CompiledTemplate.fromLiteral(template, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst options: CompiledTemplateOptions = {\n\t\t\thelpers: this.helpers,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t};\n\t\treturn CompiledTemplate.fromTemplate(ast, template, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes a template against a JSON Schema v7 describing\n\t * the available context.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is analyzed recursively and the\n\t * `outputSchema` reflects the object structure with resolved types.\n\t *\n\t * @param template - The template to analyze\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysis(Object.keys(template), (key) =>\n\t\t\t\tthis.analyze(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t};\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\treturn analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────────\n\n\t/**\n\t * Validates a template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param template - The template to validate\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(template, inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Syntax Validation ───────────────────────────────────────────────────\n\n\t/**\n\t * Checks only that the template syntax is valid (parsing).\n\t * Does not require a schema — useful for quick feedback in an editor.\n\t *\n\t * For objects, recursively checks each property.\n\t *\n\t * @param template - The template to validate\n\t * @returns `true` if the template is syntactically correct\n\t */\n\tisValidSyntax(template: TemplateInput): boolean {\n\t\tif (isObjectInput(template)) {\n\t\t\treturn Object.values(template).every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isLiteralInput(template)) return true;\n\t\ttry {\n\t\t\tthis.getCachedAst(template);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Executes a template with the provided data.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is executed recursively and an object with\n\t * resolved values is returned.\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param template - The template to execute\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, identifierSchemas)\n\t * @returns The execution result\n\t */\n\texecute(\n\t\ttemplate: TemplateInput,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: ExecuteOptions,\n\t): unknown {\n\t\t// ── Object template → recursive execution ────────────────────────────\n\t\tif (isObjectInput(template)) {\n\t\t\tconst result: Record<string, unknown> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tresult[key] = this.execute(value, data, options);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Passthrough for literal values ────────────────────────────────────\n\t\tif (isLiteralInput(template)) return template;\n\n\t\t// ── Parse once ───────────────────────────────────────────────────────\n\t\tconst ast = this.getCachedAst(template);\n\n\t\t// ── Pre-execution static validation ──────────────────────────────────\n\t\tif (options?.schema) {\n\t\t\tconst analysis = analyzeFromAst(ast, template, options.schema, {\n\t\t\t\tidentifierSchemas: options.identifierSchemas,\n\t\t\t\thelpers: this.helpers,\n\t\t\t});\n\t\t\tif (!analysis.valid) {\n\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t}\n\t\t}\n\n\t\t// ── Execution ────────────────────────────────────────────────────────\n\t\treturn executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────────\n\n\t/**\n\t * Analyzes a template and, if valid, executes it with the provided data.\n\t * Returns both the analysis result and the executed value.\n\t *\n\t * For objects, each property is analyzed and executed recursively.\n\t * The entire object is considered invalid if at least one property is.\n\t *\n\t * @param template - The template\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - (optional) Options for template identifiers\n\t * @returns An object `{ analysis, value }` where `value` is `undefined`\n\t * if analysis failed.\n\t */\n\tanalyzeAndExecute(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: AnalyzeAndExecuteOptions,\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysisAndExecution(Object.keys(template), (key) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tanalysis: {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t\t},\n\t\t\t\tvalue: template,\n\t\t\t};\n\t\t}\n\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst analysis = analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\n\t\tif (!analysis.valid) {\n\t\t\treturn { analysis, value: undefined };\n\t\t}\n\n\t\tconst value = executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t\treturn { analysis, value };\n\t}\n\n\t// ─── Custom Helper Management ──────────────────────────────────────────\n\n\t/**\n\t * Registers a custom helper on this engine instance.\n\t *\n\t * The helper is available for both execution (via Handlebars) and\n\t * static analysis (via its declared `returnType`).\n\t *\n\t * @param name - Helper name (e.g. `\"uppercase\"`)\n\t * @param definition - Helper definition (implementation + return type)\n\t * @returns `this` to allow chaining\n\t */\n\tregisterHelper(name: string, definition: HelperDefinition): this {\n\t\tthis.helpers.set(name, definition);\n\t\tthis.hbs.registerHelper(name, definition.fn);\n\n\t\t// Invalidate the compilation cache because helpers have changed\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a custom helper from this engine instance.\n\t *\n\t * @param name - Name of the helper to remove\n\t * @returns `this` to allow chaining\n\t */\n\tunregisterHelper(name: string): this {\n\t\tthis.helpers.delete(name);\n\t\tthis.hbs.unregisterHelper(name);\n\n\t\t// Invalidate the compilation cache\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Checks whether a helper is registered on this instance.\n\t *\n\t * @param name - Helper name\n\t * @returns `true` if the helper is registered\n\t */\n\thasHelper(name: string): boolean {\n\t\treturn this.helpers.has(name);\n\t}\n\n\t// ─── Cache Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Clears all internal caches (AST + compilation).\n\t *\n\t * Useful after a configuration change or to free memory.\n\t */\n\tclearCaches(): void {\n\t\tthis.astCache.clear();\n\t\tthis.compilationCache.clear();\n\t}\n\n\t// ─── Internals ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Retrieves the AST of a template from the cache, or parses and caches it.\n\t */\n\tprivate getCachedAst(template: string): hbs.AST.Program {\n\t\tlet ast = this.astCache.get(template);\n\t\tif (!ast) {\n\t\t\tast = parse(template);\n\t\t\tthis.astCache.set(template, ast);\n\t\t}\n\t\treturn ast;\n\t}\n}\n",
|
|
6
|
+
"import type { HelperDefinition } from \"../types.ts\";\n\n// ─── MathHelpers ─────────────────────────────────────────────────────────────\n// Aggregates all math-related helpers for the template engine.\n//\n// Provides two kinds of helpers:\n//\n// 1. **Named helpers** — one helper per operation (`add`, `subtract`, `divide`, …)\n// Usage: `{{ add a b }}`, `{{ abs value }}`, `{{ round value 2 }}`\n//\n// 2. **Generic `math` helper** — single helper with the operator as a parameter\n// Usage: `{{ math a \"+\" b }}`, `{{ math a \"/\" b }}`, `{{ math a \"**\" b }}`\n//\n// ─── Registration ────────────────────────────────────────────────────────────\n// MathHelpers are automatically pre-registered by the `Typebars` constructor.\n// They can also be registered manually on any object implementing\n// `HelperRegistry`:\n//\n// MathHelpers.register(engine); // registers all helpers\n// MathHelpers.unregister(engine); // removes all helpers\n//\n// ─── Supported operators (generic `math` helper) ─────────────────────────────\n// + Addition\n// - Subtraction\n// * Multiplication\n// / Division\n// % Modulo\n// ** Exponentiation\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Minimal registration interface — avoids tight coupling with Typebars */\ninterface HelperRegistry {\n\tregisterHelper(name: string, definition: HelperDefinition): unknown;\n\tunregisterHelper(name: string): unknown;\n}\n\n/** Operators supported by the generic `math` helper */\ntype MathOperator = \"+\" | \"-\" | \"*\" | \"/\" | \"%\" | \"**\";\n\nconst SUPPORTED_OPERATORS = new Set<string>([\"+\", \"-\", \"*\", \"/\", \"%\", \"**\"]);\n\n// ─── Internal utilities ─────────────────────────────────────────────────────\n\n/**\n * Converts an unknown value to a number.\n * Returns `0` when conversion fails (non-numeric string, object, etc.).\n */\nfunction toNumber(value: unknown): number {\n\tif (typeof value === \"number\") return value;\n\tif (typeof value === \"string\") {\n\t\tconst n = Number(value);\n\t\treturn Number.isNaN(n) ? 0 : n;\n\t}\n\treturn 0;\n}\n\n/**\n * Applies a binary operator to two operands.\n */\nfunction applyOperator(a: number, op: MathOperator, b: number): number {\n\tswitch (op) {\n\t\tcase \"+\":\n\t\t\treturn a + b;\n\t\tcase \"-\":\n\t\t\treturn a - b;\n\t\tcase \"*\":\n\t\t\treturn a * b;\n\t\tcase \"/\":\n\t\t\treturn b === 0 ? Infinity : a / b;\n\t\tcase \"%\":\n\t\t\treturn b === 0 ? NaN : a % b;\n\t\tcase \"**\":\n\t\t\treturn a ** b;\n\t}\n}\n\n// ─── Main class ─────────────────────────────────────────────────────────────\n\nexport class MathHelpers {\n\t// ─── All registered helper names ─────────────────────────────────────\n\t// Used by `register()` and `unregister()` to iterate.\n\tprivate static readonly HELPER_NAMES: readonly string[] = [\n\t\t// Binary operators\n\t\t\"add\",\n\t\t\"subtract\",\n\t\t\"sub\",\n\t\t\"multiply\",\n\t\t\"mul\",\n\t\t\"divide\",\n\t\t\"div\",\n\t\t\"modulo\",\n\t\t\"mod\",\n\t\t\"pow\",\n\n\t\t// Unary functions\n\t\t\"abs\",\n\t\t\"ceil\",\n\t\t\"floor\",\n\t\t\"round\",\n\t\t\"sqrt\",\n\n\t\t// Min / Max (binary)\n\t\t\"min\",\n\t\t\"max\",\n\n\t\t// Generic helper\n\t\t\"math\",\n\t];\n\n\t/** Set derived from `HELPER_NAMES` for O(1) lookup in `isMathHelper()` */\n\tprivate static readonly HELPER_NAMES_SET: ReadonlySet<string> = new Set(\n\t\tMathHelpers.HELPER_NAMES,\n\t);\n\n\t// ─── Helper definitions ─────────────────────────────────────────────\n\n\t/** Returns all definitions as a `Map<name, HelperDefinition>` */\n\tstatic getDefinitions(): Map<string, HelperDefinition> {\n\t\tconst defs = new Map<string, HelperDefinition>();\n\n\t\tMathHelpers.registerBinaryOperators(defs);\n\t\tMathHelpers.registerUnaryFunctions(defs);\n\t\tMathHelpers.registerMinMax(defs);\n\t\tMathHelpers.registerGenericMath(defs);\n\n\t\treturn defs;\n\t}\n\n\t// ── Binary operators ─────────────────────────────────────────────\n\n\t/** Registers add, subtract/sub, multiply/mul, divide/div, modulo/mod, pow */\n\tprivate static registerBinaryOperators(\n\t\tdefs: Map<string, HelperDefinition>,\n\t): void {\n\t\t// add — Addition : {{ add a b }}\n\t\tconst addDef: HelperDefinition = {\n\t\t\tfn: (a: unknown, b: unknown) => toNumber(a) + toNumber(b),\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"First operand\" },\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Second operand\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Adds two numbers: {{ add a b }}\",\n\t\t};\n\t\tdefs.set(\"add\", addDef);\n\n\t\t// subtract / sub — Subtraction: {{ subtract a b }} or {{ sub a b }}\n\t\tconst subtractDef: HelperDefinition = {\n\t\t\tfn: (a: unknown, b: unknown) => toNumber(a) - toNumber(b),\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: \"a\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"Value to subtract from\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"b\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"Value to subtract\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Subtracts b from a: {{ subtract a b }}\",\n\t\t};\n\t\tdefs.set(\"subtract\", subtractDef);\n\t\tdefs.set(\"sub\", subtractDef);\n\n\t\t// multiply / mul — Multiplication: {{ multiply a b }} or {{ mul a b }}\n\t\tconst multiplyDef: HelperDefinition = {\n\t\t\tfn: (a: unknown, b: unknown) => toNumber(a) * toNumber(b),\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"First factor\" },\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Second factor\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Multiplies two numbers: {{ multiply a b }}\",\n\t\t};\n\t\tdefs.set(\"multiply\", multiplyDef);\n\t\tdefs.set(\"mul\", multiplyDef);\n\n\t\t// divide / div — Division: {{ divide a b }} or {{ div a b }}\n\t\tconst divideDef: HelperDefinition = {\n\t\t\tfn: (a: unknown, b: unknown) => {\n\t\t\t\tconst divisor = toNumber(b);\n\t\t\t\treturn divisor === 0 ? Infinity : toNumber(a) / divisor;\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"Dividend\" },\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Divisor\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription:\n\t\t\t\t\"Divides a by b: {{ divide a b }}. Returns Infinity if b is 0.\",\n\t\t};\n\t\tdefs.set(\"divide\", divideDef);\n\t\tdefs.set(\"div\", divideDef);\n\n\t\t// modulo / mod — Modulo: {{ modulo a b }} or {{ mod a b }}\n\t\tconst moduloDef: HelperDefinition = {\n\t\t\tfn: (a: unknown, b: unknown) => {\n\t\t\t\tconst divisor = toNumber(b);\n\t\t\t\treturn divisor === 0 ? NaN : toNumber(a) % divisor;\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"Dividend\" },\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Divisor\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Returns the remainder of a divided by b: {{ modulo a b }}\",\n\t\t};\n\t\tdefs.set(\"modulo\", moduloDef);\n\t\tdefs.set(\"mod\", moduloDef);\n\n\t\t// pow — Exponentiation : {{ pow base exponent }}\n\t\tdefs.set(\"pow\", {\n\t\t\tfn: (base: unknown, exponent: unknown) =>\n\t\t\t\ttoNumber(base) ** toNumber(exponent),\n\t\t\tparams: [\n\t\t\t\t{ name: \"base\", type: { type: \"number\" }, description: \"The base\" },\n\t\t\t\t{\n\t\t\t\t\tname: \"exponent\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"The exponent\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription:\n\t\t\t\t\"Raises base to the power of exponent: {{ pow base exponent }}\",\n\t\t});\n\t}\n\n\t// ── Unary functions ──────────────────────────────────────────────\n\n\t/** Registers abs, ceil, floor, round, sqrt */\n\tprivate static registerUnaryFunctions(\n\t\tdefs: Map<string, HelperDefinition>,\n\t): void {\n\t\t// abs — Absolute value: {{ abs value }}\n\t\tdefs.set(\"abs\", {\n\t\t\tfn: (value: unknown) => Math.abs(toNumber(value)),\n\t\t\tparams: [\n\t\t\t\t{ name: \"value\", type: { type: \"number\" }, description: \"The number\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Returns the absolute value: {{ abs value }}\",\n\t\t});\n\n\t\t// ceil — Round up: {{ ceil value }}\n\t\tdefs.set(\"ceil\", {\n\t\t\tfn: (value: unknown) => Math.ceil(toNumber(value)),\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: \"value\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"The number to round up\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Rounds up to the nearest integer: {{ ceil value }}\",\n\t\t});\n\n\t\t// floor — Round down: {{ floor value }}\n\t\tdefs.set(\"floor\", {\n\t\t\tfn: (value: unknown) => Math.floor(toNumber(value)),\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: \"value\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"The number to round down\",\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Rounds down to the nearest integer: {{ floor value }}\",\n\t\t});\n\n\t\t// round — Rounding: {{ round value }} or {{ round value precision }}\n\t\t// With precision: {{ round 3.14159 2 }} → 3.14\n\t\tdefs.set(\"round\", {\n\t\t\tfn: (value: unknown, precision: unknown) => {\n\t\t\t\tconst n = toNumber(value);\n\t\t\t\t// If precision is a Handlebars options object (not a number),\n\t\t\t\t// it means the second parameter was not provided.\n\t\t\t\tif (\n\t\t\t\t\tprecision === undefined ||\n\t\t\t\t\tprecision === null ||\n\t\t\t\t\ttypeof precision === \"object\"\n\t\t\t\t) {\n\t\t\t\t\treturn Math.round(n);\n\t\t\t\t}\n\t\t\t\tconst p = toNumber(precision);\n\t\t\t\tconst factor = 10 ** p;\n\t\t\t\treturn Math.round(n * factor) / factor;\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{\n\t\t\t\t\tname: \"value\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"The number to round\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: \"precision\",\n\t\t\t\t\ttype: { type: \"number\" },\n\t\t\t\t\tdescription: \"Number of decimal places (default: 0)\",\n\t\t\t\t\toptional: true,\n\t\t\t\t},\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription:\n\t\t\t\t\"Rounds to the nearest integer or to a given precision: {{ round value }} or {{ round value 2 }}\",\n\t\t});\n\n\t\t// sqrt — Square root: {{ sqrt value }}\n\t\tdefs.set(\"sqrt\", {\n\t\t\tfn: (value: unknown) => Math.sqrt(toNumber(value)),\n\t\t\tparams: [\n\t\t\t\t{ name: \"value\", type: { type: \"number\" }, description: \"The number\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Returns the square root: {{ sqrt value }}\",\n\t\t});\n\t}\n\n\t// ── Min / Max ────────────────────────────────────────────────────\n\n\t/** Registers min and max */\n\tprivate static registerMinMax(defs: Map<string, HelperDefinition>): void {\n\t\t// min — Minimum : {{ min a b }}\n\t\tdefs.set(\"min\", {\n\t\t\tfn: (a: unknown, b: unknown) => Math.min(toNumber(a), toNumber(b)),\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"First number\" },\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Second number\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Returns the smaller of two numbers: {{ min a b }}\",\n\t\t});\n\n\t\t// max — Maximum : {{ max a b }}\n\t\tdefs.set(\"max\", {\n\t\t\tfn: (a: unknown, b: unknown) => Math.max(toNumber(a), toNumber(b)),\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"First number\" },\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Second number\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription: \"Returns the larger of two numbers: {{ max a b }}\",\n\t\t});\n\t}\n\n\t// ── Generic helper ───────────────────────────────────────────────\n\n\t/** Registers the generic `math` helper with operator as a parameter */\n\tprivate static registerGenericMath(\n\t\tdefs: Map<string, HelperDefinition>,\n\t): void {\n\t\t// Usage : {{ math a \"+\" b }}, {{ math a \"/\" b }}, {{ math a \"**\" b }}\n\t\tdefs.set(\"math\", {\n\t\t\tfn: (a: unknown, operator: unknown, b: unknown) => {\n\t\t\t\tconst op = String(operator);\n\t\t\t\tif (!SUPPORTED_OPERATORS.has(op)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`[math helper] Unknown operator \"${op}\". ` +\n\t\t\t\t\t\t\t`Supported: ${[...SUPPORTED_OPERATORS].join(\", \")} `,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn applyOperator(toNumber(a), op as MathOperator, toNumber(b));\n\t\t\t},\n\t\t\tparams: [\n\t\t\t\t{ name: \"a\", type: { type: \"number\" }, description: \"Left operand\" },\n\t\t\t\t{\n\t\t\t\t\tname: \"operator\",\n\t\t\t\t\ttype: { type: \"string\", enum: [\"+\", \"-\", \"*\", \"/\", \"%\", \"**\"] },\n\t\t\t\t\tdescription: 'Arithmetic operator: \"+\", \"-\", \"*\", \"/\", \"%\", \"**\"',\n\t\t\t\t},\n\t\t\t\t{ name: \"b\", type: { type: \"number\" }, description: \"Right operand\" },\n\t\t\t],\n\t\t\treturnType: { type: \"number\" },\n\t\t\tdescription:\n\t\t\t\t'Generic math helper with operator as parameter: {{ math a \"+\" b }}, {{ math a \"/\" b }}. ' +\n\t\t\t\t\"Supported operators: +, -, *, /, %, **\",\n\t\t});\n\t}\n\n\t// ─── Registration / Unregistration ───────────────────────────────────\n\n\t/**\n\t * Registers all math helpers on a `Typebars` instance\n\t * (or any object implementing `HelperRegistry`).\n\t *\n\t * **Note:** MathHelpers are automatically pre-registered by the\n\t * `Typebars` constructor. This method is only useful if you have\n\t * called `unregister()` and want to re-enable them, or if you are\n\t * registering on a custom registry.\n\t *\n\t * @param registry - The engine or target registry\n\t *\n\t * @example\n\t * ```\n\t * const engine = new Typebars();\n\t * // Math helpers are already available!\n\t * engine.analyzeAndExecute(\"{{ divide total count }}\", schema, data);\n\t * engine.analyzeAndExecute(\"{{ math price '*' quantity }}\", schema, data);\n\t * ```\n\t */\n\tstatic register(registry: HelperRegistry): void {\n\t\tconst defs = MathHelpers.getDefinitions();\n\t\tfor (const [name, def] of defs) {\n\t\t\tregistry.registerHelper(name, def);\n\t\t}\n\t}\n\n\t/**\n\t * Removes all math helpers from the registry.\n\t *\n\t * @param registry - The engine or target registry\n\t */\n\tstatic unregister(registry: HelperRegistry): void {\n\t\tfor (const name of MathHelpers.HELPER_NAMES) {\n\t\t\tregistry.unregisterHelper(name);\n\t\t}\n\t}\n\n\t/**\n\t * Returns the list of all math helper names.\n\t * Useful for checking whether a given helper belongs to the math pack.\n\t */\n\tstatic getHelperNames(): readonly string[] {\n\t\treturn MathHelpers.HELPER_NAMES;\n\t}\n\n\t/**\n\t * Checks whether a helper name belongs to the math pack.\n\t *\n\t * @param name - The helper name to check\n\t */\n\tstatic isMathHelper(name: string): boolean {\n\t\treturn MathHelpers.HELPER_NAMES_SET.has(name);\n\t}\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "073ABADC78560F0964756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class B extends Error{constructor(j){super(j);this.name="TemplateError"}toJSON(){return{name:this.name,message:this.message}}}class G extends B{loc;source;constructor(j,k,q){super(`Parse error: ${j}`);this.loc=k;this.source=q;this.name="TemplateParseError"}toJSON(){return{name:this.name,message:this.message,loc:this.loc,source:this.source}}}class H extends B{diagnostics;errors;warnings;errorCount;warningCount;constructor(j){let k=j.filter((A)=>A.severity==="error"),q=j.filter((A)=>A.severity==="warning"),F=k.map((A)=>J(A)).join(`
|
|
2
|
+
`);super(`Static analysis failed with ${k.length} error(s):
|
|
3
|
+
${F}`);this.name="TemplateAnalysisError",this.diagnostics=j,this.errors=k,this.warnings=q,this.errorCount=k.length,this.warningCount=q.length}toJSON(){return{name:this.name,message:this.message,errorCount:this.errorCount,warningCount:this.warningCount,diagnostics:this.diagnostics}}}class I extends B{constructor(j){super(`Runtime error: ${j}`);this.name="TemplateRuntimeError"}}function J(j){let k=[` • [${j.code}] ${j.message}`];if(j.loc)k.push(`(at ${j.loc.start.line}:${j.loc.start.column})`);return k.join(" ")}function K(j,k){let q=`Property "${j}" does not exist in the context schema`;if(k.length===0)return q;return`${q}. Available properties: ${k.join(", ")}`}function M(j,k,q){return`"{{#${j}}}" expects ${k}, but resolved schema has type "${q}"`}function O(j){return`"{{#${j}}}" requires an argument`}function Q(j){return`Unknown block helper "{{#${j}}}" — cannot analyze statically`}function R(j){return`Expression of type "${j}" cannot be statically analyzed`}
|
|
4
|
+
export{B as z,G as A,H as B,I as C,K as D,M as E,O as F,Q as G,R as H};
|
|
5
|
+
|
|
6
|
+
//# debugId=C1C5104B96E761E764756E2164756E21
|
|
7
|
+
//# sourceMappingURL=chunk-ybh51hbe.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Base Class ──────────────────────────────────────────────────────────────\n// All template engine errors extend this class, enabling targeted catch blocks:\n// `catch (e) { if (e instanceof TemplateError) … }`\n\nexport class TemplateError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"TemplateError\";\n\t}\n\n\t/**\n\t * Serializes the error into a JSON-compatible object, suitable for sending\n\t * to a frontend or a structured logging system.\n\t */\n\ttoJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t};\n\t}\n}\n\n// ─── Parse Error ─────────────────────────────────────────────────────────────\n// Thrown when Handlebars fails to parse the template (invalid syntax).\n\nexport class TemplateParseError extends TemplateError {\n\tconstructor(\n\t\tmessage: string,\n\t\t/** Approximate position of the error in the source */\n\t\tpublic readonly loc?: { line: number; column: number },\n\t\t/** Fragment of the template source around the error */\n\t\tpublic readonly source?: string,\n\t) {\n\t\tsuper(`Parse error: ${message}`);\n\t\tthis.name = \"TemplateParseError\";\n\t}\n\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\tloc: this.loc,\n\t\t\tsource: this.source,\n\t\t};\n\t}\n}\n\n// ─── Static Analysis Error ───────────────────────────────────────────────────\n// Thrown in strict mode when the analysis produces at least one error.\n// Contains the full list of diagnostics for detailed inspection.\n\nexport class TemplateAnalysisError extends TemplateError {\n\t/** Full list of diagnostics (errors + warnings) */\n\tpublic readonly diagnostics: TemplateDiagnostic[];\n\n\t/** Only diagnostics with \"error\" severity */\n\tpublic readonly errors: TemplateDiagnostic[];\n\n\t/** Only diagnostics with \"warning\" severity */\n\tpublic readonly warnings: TemplateDiagnostic[];\n\n\t/** Total number of errors */\n\tpublic readonly errorCount: number;\n\n\t/** Total number of warnings */\n\tpublic readonly warningCount: number;\n\n\tconstructor(diagnostics: TemplateDiagnostic[]) {\n\t\tconst errors = diagnostics.filter((d) => d.severity === \"error\");\n\t\tconst warnings = diagnostics.filter((d) => d.severity === \"warning\");\n\n\t\tconst summary = errors.map((d) => formatDiagnosticLine(d)).join(\"\\n\");\n\t\tsuper(`Static analysis failed with ${errors.length} error(s):\\n${summary}`);\n\n\t\tthis.name = \"TemplateAnalysisError\";\n\t\tthis.diagnostics = diagnostics;\n\t\tthis.errors = errors;\n\t\tthis.warnings = warnings;\n\t\tthis.errorCount = errors.length;\n\t\tthis.warningCount = warnings.length;\n\t}\n\n\t/**\n\t * Serializes the analysis error into a JSON-compatible object.\n\t *\n\t * Designed for direct use in API responses:\n\t * ```\n\t * res.status(400).json(error.toJSON());\n\t * ```\n\t *\n\t * Returned structure:\n\t * ```\n\t * {\n\t * name: \"TemplateAnalysisError\",\n\t * message: \"Static analysis failed with 2 error(s): ...\",\n\t * errorCount: 2,\n\t * warningCount: 0,\n\t * diagnostics: [\n\t * {\n\t * severity: \"error\",\n\t * code: \"UNKNOWN_PROPERTY\",\n\t * message: \"Property \\\"foo\\\" does not exist...\",\n\t * loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 7 } },\n\t * source: \"{{foo}}\",\n\t * details: { path: \"foo\", availableProperties: [\"name\", \"age\"] }\n\t * }\n\t * ]\n\t * }\n\t * ```\n\t */\n\toverride toJSON(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tmessage: this.message,\n\t\t\terrorCount: this.errorCount,\n\t\t\twarningCount: this.warningCount,\n\t\t\tdiagnostics: this.diagnostics,\n\t\t};\n\t}\n}\n\n// ─── Runtime Error ───────────────────────────────────────────────────────────\n// Thrown when template execution fails (accessing a non-existent property\n// in strict mode, unexpected type, etc.).\n\nexport class TemplateRuntimeError extends TemplateError {\n\tconstructor(message: string) {\n\t\tsuper(`Runtime error: ${message}`);\n\t\tthis.name = \"TemplateRuntimeError\";\n\t}\n}\n\n// ─── Internal Utilities ──────────────────────────────────────────────────────\n\n/**\n * Formats a single diagnostic line for the summary message\n * of a `TemplateAnalysisError`.\n *\n * Produces a human-readable format:\n * ` • [UNKNOWN_PROPERTY] Property \"foo\" does not exist (at 1:0)`\n */\nfunction formatDiagnosticLine(diag: TemplateDiagnostic): string {\n\tconst parts: string[] = [` • [${diag.code}] ${diag.message}`];\n\n\tif (diag.loc) {\n\t\tparts.push(`(at ${diag.loc.start.line}:${diag.loc.start.column})`);\n\t}\n\n\treturn parts.join(\" \");\n}\n\n// ─── Common Error Factories ──────────────────────────────────────────────────\n// These functions simplify the creation of typed errors across the codebase.\n\n/**\n * Creates a structured diagnostic message for a missing property.\n * Used by the analyzer to produce enriched error messages with suggestions.\n */\nexport function createPropertyNotFoundMessage(\n\tpath: string,\n\tavailableProperties: string[],\n): string {\n\tconst base = `Property \"${path}\" does not exist in the context schema`;\n\tif (availableProperties.length === 0) return base;\n\treturn `${base}. Available properties: ${availableProperties.join(\", \")}`;\n}\n\n/**\n * Creates a message for a type mismatch on a block helper.\n */\nexport function createTypeMismatchMessage(\n\thelperName: string,\n\texpected: string,\n\tactual: string,\n): string {\n\treturn `\"{{#${helperName}}}\" expects ${expected}, but resolved schema has type \"${actual}\"`;\n}\n\n/**\n * Creates a message for a missing argument on a block helper.\n */\nexport function createMissingArgumentMessage(helperName: string): string {\n\treturn `\"{{#${helperName}}}\" requires an argument`;\n}\n\n/**\n * Creates a message for an unknown block helper.\n */\nexport function createUnknownHelperMessage(helperName: string): string {\n\treturn `Unknown block helper \"{{#${helperName}}}\" — cannot analyze statically`;\n}\n\n/**\n * Creates a message for an expression that cannot be statically analyzed.\n */\nexport function createUnanalyzableMessage(nodeType: string): string {\n\treturn `Expression of type \"${nodeType}\" cannot be statically analyzed`;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "AAMO,MAAM,UAAsB,KAAM,CACxC,WAAW,CAAC,EAAiB,CAC5B,MAAM,CAAO,EACb,KAAK,KAAO,gBAOb,MAAM,EAA4B,CACjC,MAAO,CACN,KAAM,KAAK,KACX,QAAS,KAAK,OACf,EAEF,CAKO,MAAM,UAA2B,CAAc,CAIpC,IAEA,OALjB,WAAW,CACV,EAEgB,EAEA,EACf,CACD,MAAM,gBAAgB,GAAS,EAJf,WAEA,cAGhB,KAAK,KAAO,qBAGJ,MAAM,EAA4B,CAC1C,MAAO,CACN,KAAM,KAAK,KACX,QAAS,KAAK,QACd,IAAK,KAAK,IACV,OAAQ,KAAK,MACd,EAEF,CAMO,MAAM,UAA8B,CAAc,CAExC,YAGA,OAGA,SAGA,WAGA,aAEhB,WAAW,CAAC,EAAmC,CAC9C,IAAM,EAAS,EAAY,OAAO,CAAC,IAAM,EAAE,WAAa,OAAO,EACzD,EAAW,EAAY,OAAO,CAAC,IAAM,EAAE,WAAa,SAAS,EAE7D,EAAU,EAAO,IAAI,CAAC,IAAM,EAAqB,CAAC,CAAC,EAAE,KAAK;AAAA,CAAI,EACpE,MAAM,+BAA+B,EAAO;AAAA,EAAqB,GAAS,EAE1E,KAAK,KAAO,wBACZ,KAAK,YAAc,EACnB,KAAK,OAAS,EACd,KAAK,SAAW,EAChB,KAAK,WAAa,EAAO,OACzB,KAAK,aAAe,EAAS,OA+BrB,MAAM,EAA4B,CAC1C,MAAO,CACN,KAAM,KAAK,KACX,QAAS,KAAK,QACd,WAAY,KAAK,WACjB,aAAc,KAAK,aACnB,YAAa,KAAK,WACnB,EAEF,CAMO,MAAM,UAA6B,CAAc,CACvD,WAAW,CAAC,EAAiB,CAC5B,MAAM,kBAAkB,GAAS,EACjC,KAAK,KAAO,uBAEd,CAWA,SAAS,CAAoB,CAAC,EAAkC,CAC/D,IAAM,EAAkB,CAAC,QAAO,EAAK,SAAS,EAAK,SAAS,EAE5D,GAAI,EAAK,IACR,EAAM,KAAK,OAAO,EAAK,IAAI,MAAM,QAAQ,EAAK,IAAI,MAAM,SAAS,EAGlE,OAAO,EAAM,KAAK,GAAG,EAUf,SAAS,CAA6B,CAC5C,EACA,EACS,CACT,IAAM,EAAO,aAAa,0CAC1B,GAAI,EAAoB,SAAW,EAAG,OAAO,EAC7C,MAAO,GAAG,4BAA+B,EAAoB,KAAK,IAAI,IAMhE,SAAS,CAAyB,CACxC,EACA,EACA,EACS,CACT,MAAO,OAAO,gBAAyB,oCAA2C,KAM5E,SAAS,CAA4B,CAAC,EAA4B,CACxE,MAAO,OAAO,4BAMR,SAAS,CAA0B,CAAC,EAA4B,CACtE,MAAO,4BAA4B,mCAM7B,SAAS,CAAyB,CAAC,EAA0B,CACnE,MAAO,uBAAuB",
|
|
8
|
+
"debugId": "C1C5104B96E761E764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{j as u,k as v,l as S}from"./chunk-6955jpr7.js";import{A as y,n as C,r as k,s as D,t as W,u as E,v as T,x as P}from"./chunk-ggdfaqhe.js";import{F as Z,G as _,H as R,I as $,J as B}from"./chunk-vka4e61h.js";import{M as X,N,O as Y}from"./chunk-mx8neh7q.js";import{P as M,R as b,S as V,T as q}from"./chunk-fhvf5y4x.js";function o(j,w,A){if(v(j))return d(j,w,A);if(u(j))return{valid:!0,diagnostics:[],outputSchema:S(j)};let F=C(j);return n(F,j,w,{identifierSchemas:A})}function d(j,w,A){return q(Object.keys(j),(F)=>o(j[F],w,A))}function n(j,w,A,F){let z={root:A,current:A,diagnostics:[],template:w,identifierSchemas:F?.identifierSchemas,helpers:F?.helpers},G=J(j,z);return{valid:!z.diagnostics.some((I)=>I.severity==="error"),diagnostics:z.diagnostics,outputSchema:Y(G)}}function l(j,w){switch(j.type){case"ContentStatement":case"CommentStatement":return;case"MustacheStatement":return g(j,w);case"BlockStatement":return f(j,w);default:K(w,"UNANALYZABLE","warning",`Unsupported AST node type: "${j.type}"`,j);return}}function g(j,w){if(j.path.type==="SubExpression")return K(w,"UNANALYZABLE","warning","Sub-expressions are not statically analyzable",j),{};if(j.params.length>0||j.hash){let A=i(j.path),F=w.helpers?.get(A);if(F){let z=F.params;if(z){let G=z.filter((H)=>!H.optional).length;if(j.params.length<G)K(w,"MISSING_ARGUMENT","error",`Helper "${A}" expects at least ${G} argument(s), but got ${j.params.length}`,j,{helperName:A,expected:`${G} argument(s)`,actual:`${j.params.length} argument(s)`})}for(let G=0;G<j.params.length;G++){let H=O(j.params[G],w,j),I=z?.[G];if(H&&I?.type){let L=I.type;if(!p(H,L)){let h=I.name;K(w,"TYPE_MISMATCH","error",`Helper "${A}" parameter "${h}" expects ${Q(L)}, but got ${Q(H)}`,j,{helperName:A,expected:Q(L),actual:Q(H)})}}}return F.returnType??{type:"string"}}return K(w,"UNKNOWN_HELPER","warning",`Unknown inline helper "${A}" — cannot analyze statically`,j,{helperName:A}),{type:"string"}}return O(j.path,w,j)??{}}function p(j,w){if(!w.type||!j.type)return!0;let A=Array.isArray(w.type)?w.type:[w.type];return(Array.isArray(j.type)?j.type:[j.type]).some((z)=>A.some((G)=>z===G||G==="number"&&z==="integer"||G==="integer"&&z==="number"))}function J(j,w){let A=W(j);if(A.length===0)return{type:"string"};let F=T(j);if(F)return g(F,w);let z=E(j);if(z)return f(z,w);if(A.every((H)=>H.type==="ContentStatement")){let H=A.map((L)=>L.value).join("").trim();if(H==="")return{type:"string"};let I=P(H);if(I)return{type:I}}for(let H of j.body)l(H,w);return{type:"string"}}function f(j,w){let A=r(j);switch(A){case"if":case"unless":{let F=U(j);if(F)O(F,w,j);else K(w,"MISSING_ARGUMENT","error",R(A),j,{helperName:A});let z=J(j.program,w);if(j.inverse){let G=J(j.inverse,w);if(M(z,G))return z;return Y({oneOf:[z,G]})}return z}case"each":{let F=U(j);if(!F){K(w,"MISSING_ARGUMENT","error",R("each"),j,{helperName:"each"});let I=w.current;if(w.current={},J(j.program,w),w.current=I,j.inverse)J(j.inverse,w);return{type:"string"}}let z=O(F,w,j);if(!z){let I=w.current;if(w.current={},J(j.program,w),w.current=I,j.inverse)J(j.inverse,w);return{type:"string"}}let G=N(z,w.root);if(!G){K(w,"TYPE_MISMATCH","error",_("each","an array",Q(z)),j,{helperName:"each",expected:"array",actual:Q(z)});let I=w.current;if(w.current={},J(j.program,w),w.current=I,j.inverse)J(j.inverse,w);return{type:"string"}}let H=w.current;if(w.current=G,J(j.program,w),w.current=H,j.inverse)J(j.inverse,w);return{type:"string"}}case"with":{let F=U(j);if(!F){K(w,"MISSING_ARGUMENT","error",R("with"),j,{helperName:"with"});let I=w.current;w.current={};let L=J(j.program,w);if(w.current=I,j.inverse)J(j.inverse,w);return L}let z=O(F,w,j),G=w.current;w.current=z??{};let H=J(j.program,w);if(w.current=G,j.inverse)J(j.inverse,w);return H}default:{let F=w.helpers?.get(A);if(F){for(let z of j.params)O(z,w,j);if(J(j.program,w),j.inverse)J(j.inverse,w);return F.returnType??{type:"string"}}if(K(w,"UNKNOWN_HELPER","warning",$(A),j,{helperName:A}),J(j.program,w),j.inverse)J(j.inverse,w);return{type:"string"}}}}function O(j,w,A){if(D(j))return w.current;let F=k(j);if(F.length===0){if(j.type==="StringLiteral")return{type:"string"};if(j.type==="NumberLiteral")return{type:"number"};if(j.type==="BooleanLiteral")return{type:"boolean"};if(j.type==="NullLiteral")return{type:"null"};if(j.type==="UndefinedLiteral")return{};K(w,"UNANALYZABLE","warning",B(j.type),A??j);return}let{cleanSegments:z,identifier:G}=y(F);if(G!==null)return a(z,G,w,A??j);let H=X(w.current,z);if(H===void 0){let I=z.join("."),L=V(w.current);K(w,"UNKNOWN_PROPERTY","error",Z(I,L),A??j,{path:I,availableProperties:L});return}return H}function a(j,w,A,F){let z=j.join(".");if(!A.identifierSchemas){K(A,"MISSING_IDENTIFIER_SCHEMAS","error",`Property "${z}:${w}" uses an identifier but no identifier schemas were provided`,F,{path:`${z}:${w}`,identifier:w});return}let G=A.identifierSchemas[w];if(!G){K(A,"UNKNOWN_IDENTIFIER","error",`Property "${z}:${w}" references identifier ${w} but no schema exists for this identifier`,F,{path:`${z}:${w}`,identifier:w});return}let H=X(G,j);if(H===void 0){let I=V(G);K(A,"IDENTIFIER_PROPERTY_NOT_FOUND","error",`Property "${z}" does not exist in the schema for identifier ${w}`,F,{path:z,identifier:w,availableProperties:I});return}return H}function U(j){return j.params[0]}function r(j){if(j.path.type==="PathExpression")return j.path.original;return""}function i(j){if(j.type==="PathExpression")return j.original;return""}function K(j,w,A,F,z,G){let H={severity:A,code:w,message:F};if(z&&"loc"in z&&z.loc)H.loc={start:{line:z.loc.start.line,column:z.loc.start.column},end:{line:z.loc.end.line,column:z.loc.end.column}},H.source=b(j.template,H.loc);if(G)H.details=G;j.diagnostics.push(H)}function Q(j){if(j.type)return Array.isArray(j.type)?j.type.join(" | "):j.type;if(j.oneOf)return"oneOf(...)";if(j.anyOf)return"anyOf(...)";if(j.allOf)return"allOf(...)";if(j.enum)return"enum";return"unknown"}export{o as c,n as d,f as e};
|
|
2
|
+
|
|
3
|
+
//# debugId=4E3D7DCBEE379AF464756E2164756E21
|
|
4
|
+
//# sourceMappingURL=chunk-yczpjh73.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/analyzer.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { JSONSchema7 } from \"json-schema\";\nimport {\n\tcreateMissingArgumentMessage,\n\tcreatePropertyNotFoundMessage,\n\tcreateTypeMismatchMessage,\n\tcreateUnanalyzableMessage,\n\tcreateUnknownHelperMessage,\n} from \"./errors.ts\";\nimport {\n\tdetectLiteralType,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport {\n\tresolveArrayItems,\n\tresolveSchemaPath,\n\tsimplifySchema,\n} from \"./schema-resolver.ts\";\nimport type {\n\tAnalysisResult,\n\tDiagnosticCode,\n\tDiagnosticDetails,\n\tHelperDefinition,\n\tTemplateDiagnostic,\n\tTemplateInput,\n\tTemplateInputObject,\n} from \"./types.ts\";\nimport {\n\tinferPrimitiveSchema,\n\tisLiteralInput,\n\tisObjectInput,\n} from \"./types.ts\";\nimport {\n\taggregateObjectAnalysis,\n\tdeepEqual,\n\textractSourceSnippet,\n\tgetSchemaPropertyNames,\n} from \"./utils.ts\";\n\n// ─── Static Analyzer ─────────────────────────────────────────────────────────\n// Analyse statique d'un template Handlebars par rapport à un JSON Schema v7\n// décrivant le contexte disponible.\n//\n// Architecture fusionnée (v2) :\n// Un seul parcours de l'AST effectue simultanément la **validation** et\n// l'**inférence du type de retour**. Cela élimine la duplication entre les\n// anciennes fonctions `validate*` et `infer*`, et améliore la performance\n// en évitant un double parcours.\n//\n// Contexte :\n// Le contexte d'analyse utilise un pattern **save/restore** au lieu de\n// créer de nouveaux objets à chaque récursion (`{ ...ctx, current: X }`).\n// Cela réduit la pression sur le GC pour les templates profondément imbriqués.\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// La syntaxe `{{key:N}}` permet de référencer une variable depuis un schema\n// spécifique, identifié par un entier N. Le paramètre optionnel\n// `identifierSchemas` fournit un mapping `{ [id]: JSONSchema7 }`.\n//\n// Règles de résolution :\n// - `{{meetingId}}` → validé contre `inputSchema` (comportement standard)\n// - `{{meetingId:1}}` → validé contre `identifierSchemas[1]`\n// - `{{meetingId:1}}` sans `identifierSchemas[1]` → erreur\n\n// ─── Types internes ──────────────────────────────────────────────────────────\n\n/** Contexte transmis récursivement pendant le parcours de l'AST */\ninterface AnalysisContext {\n\t/** Schema racine (pour résoudre les $ref) */\n\troot: JSONSchema7;\n\t/** Schema du contexte courant (change avec #each, #with) — muté via save/restore */\n\tcurrent: JSONSchema7;\n\t/** Accumulateur de diagnostics */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** Template source complet (pour extraire les snippets d'erreur) */\n\ttemplate: string;\n\t/** Schemas par identifiant de template (pour la syntaxe {{key:N}}) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Helpers custom enregistrés (pour l'analyse statique) */\n\thelpers?: Map<string, HelperDefinition>;\n}\n\n// ─── API publique ────────────────────────────────────────────────────────────\n\n/**\n * Analyse statiquement un template par rapport à un JSON Schema v7 décrivant\n * le contexte disponible.\n *\n * Version backward-compatible — parse le template en interne.\n *\n * @param template - La chaîne de template (ex: `\"Hello {{user.name}}\"`)\n * @param inputSchema - JSON Schema v7 décrivant les variables disponibles\n * @param identifierSchemas - (optionnel) Schemas par identifiant `{ [id]: JSONSchema7 }`\n * @returns Un `AnalysisResult` contenant la validité, les diagnostics et le\n * schema de sortie inféré.\n */\nexport function analyze(\n\ttemplate: TemplateInput,\n\tinputSchema: JSONSchema7,\n\tidentifierSchemas?: Record<number, JSONSchema7>,\n): AnalysisResult {\n\tif (isObjectInput(template)) {\n\t\treturn analyzeObjectTemplate(template, inputSchema, identifierSchemas);\n\t}\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\tconst ast = parse(template);\n\treturn analyzeFromAst(ast, template, inputSchema, { identifierSchemas });\n}\n\n/**\n * Analyse un objet template récursivement (version standalone).\n * Chaque propriété est analysée individuellement, les diagnostics sont\n * fusionnés, et le `outputSchema` reflète la structure de l'objet.\n */\nfunction analyzeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tinputSchema: JSONSchema7,\n\tidentifierSchemas?: Record<number, JSONSchema7>,\n): AnalysisResult {\n\treturn aggregateObjectAnalysis(Object.keys(template), (key) =>\n\t\tanalyze(template[key] as TemplateInput, inputSchema, identifierSchemas),\n\t);\n}\n\n/**\n * Analyse statiquement un template à partir d'un AST déjà parsé.\n *\n * C'est la fonction interne utilisée par `Typebars.compile()` et\n * `CompiledTemplate.analyze()` pour éviter un re-parsing coûteux.\n *\n * @param ast - L'AST Handlebars déjà parsé\n * @param template - Le template source (pour les snippets d'erreur)\n * @param inputSchema - JSON Schema v7 décrivant les variables disponibles\n * @param options - Options supplémentaires\n * @returns Un `AnalysisResult`\n */\nexport function analyzeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tinputSchema: JSONSchema7,\n\toptions?: {\n\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\thelpers?: Map<string, HelperDefinition>;\n\t},\n): AnalysisResult {\n\tconst ctx: AnalysisContext = {\n\t\troot: inputSchema,\n\t\tcurrent: inputSchema,\n\t\tdiagnostics: [],\n\t\ttemplate,\n\t\tidentifierSchemas: options?.identifierSchemas,\n\t\thelpers: options?.helpers,\n\t};\n\n\t// Parcours unique : inférence du type + validation en un seul pass.\n\tconst outputSchema = inferProgramType(ast, ctx);\n\n\tconst hasErrors = ctx.diagnostics.some((d) => d.severity === \"error\");\n\n\treturn {\n\t\tvalid: !hasErrors,\n\t\tdiagnostics: ctx.diagnostics,\n\t\toutputSchema: simplifySchema(outputSchema),\n\t};\n}\n\n// ─── Parcours unifié de l'AST ────────────────────────────────────────────────\n// Un seul ensemble de fonctions gère à la fois la validation (émission de\n// diagnostics) et l'inférence de type (retour d'un JSONSchema7).\n//\n// Fonctions principales :\n// - `inferProgramType` — point d'entrée pour un Program (corps de template ou bloc)\n// - `processStatement` — dispatche un statement (validation side-effect)\n// - `processMustache` — gère un MustacheStatement (expression ou helper inline)\n// - `inferBlockType` — gère un BlockStatement (if, each, with, custom…)\n\n/**\n * Dispatche le traitement d'un statement individuel.\n *\n * Appelé par `inferProgramType` dans le cas \"template mixte\" pour valider\n * chaque statement tout en ignorant le type retourné (le résultat est\n * toujours `string` pour un template mixte).\n *\n * @returns Le schema inféré pour ce statement, ou `undefined` pour les\n * statements sans sémantique (ContentStatement, CommentStatement).\n */\nfunction processStatement(\n\tstmt: hbs.AST.Statement,\n\tctx: AnalysisContext,\n): JSONSchema7 | undefined {\n\tswitch (stmt.type) {\n\t\tcase \"ContentStatement\":\n\t\tcase \"CommentStatement\":\n\t\t\t// Texte statique ou commentaire — rien à valider, pas de type à inférer\n\t\t\treturn undefined;\n\n\t\tcase \"MustacheStatement\":\n\t\t\treturn processMustache(stmt as hbs.AST.MustacheStatement, ctx);\n\n\t\tcase \"BlockStatement\":\n\t\t\treturn inferBlockType(stmt as hbs.AST.BlockStatement, ctx);\n\n\t\tdefault:\n\t\t\t// Nœud AST non reconnu — on émet un warning plutôt qu'une erreur\n\t\t\t// pour ne pas bloquer sur des extensions futures de Handlebars.\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"UNANALYZABLE\",\n\t\t\t\t\"warning\",\n\t\t\t\t`Unsupported AST node type: \"${stmt.type}\"`,\n\t\t\t\tstmt,\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Traite un MustacheStatement `{{expression}}` ou `{{helper arg}}`.\n *\n * Distingue deux cas :\n * 1. **Expression simple** (`{{name}}`, `{{user.age}}`) — résolution dans le schema\n * 2. **Helper inline** (`{{uppercase name}}`) — params > 0 ou hash présent\n *\n * @returns Le schema inféré pour cette expression\n */\nfunction processMustache(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\t// Sub-expressions (helpers imbriqués) ne sont pas supportées pour\n\t// l'analyse statique — on émet un warning.\n\tif (stmt.path.type === \"SubExpression\") {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNANALYZABLE\",\n\t\t\t\"warning\",\n\t\t\t\"Sub-expressions are not statically analyzable\",\n\t\t\tstmt,\n\t\t);\n\t\treturn {};\n\t}\n\n\t// ── Détection des helpers inline ─────────────────────────────────────────\n\t// Si le MustacheStatement a des paramètres ou un hash, c'est un appel\n\t// de helper (ex: `{{uppercase name}}`), pas une simple expression.\n\tif (stmt.params.length > 0 || stmt.hash) {\n\t\tconst helperName = getExpressionName(stmt.path);\n\n\t\t// Vérifier si le helper est enregistré\n\t\tconst helper = ctx.helpers?.get(helperName);\n\t\tif (helper) {\n\t\t\tconst helperParams = helper.params;\n\n\t\t\t// ── Vérifier le nombre de paramètres requis ──────────────────\n\t\t\tif (helperParams) {\n\t\t\t\tconst requiredCount = helperParams.filter((p) => !p.optional).length;\n\t\t\t\tif (stmt.params.length < requiredCount) {\n\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t`Helper \"${helperName}\" expects at least ${requiredCount} argument(s), but got ${stmt.params.length}`,\n\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thelperName,\n\t\t\t\t\t\t\texpected: `${requiredCount} argument(s)`,\n\t\t\t\t\t\t\tactual: `${stmt.params.length} argument(s)`,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ── Valider chaque paramètre (existence + type) ─────────────\n\t\t\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\t\t\tconst resolvedSchema = resolveExpressionWithDiagnostics(\n\t\t\t\t\tstmt.params[i] as hbs.AST.Expression,\n\t\t\t\t\tctx,\n\t\t\t\t\tstmt,\n\t\t\t\t);\n\n\t\t\t\t// Vérifier la compatibilité de type si le helper déclare\n\t\t\t\t// le type attendu pour ce paramètre\n\t\t\t\tconst helperParam = helperParams?.[i];\n\t\t\t\tif (resolvedSchema && helperParam?.type) {\n\t\t\t\t\tconst expectedType = helperParam.type;\n\t\t\t\t\tif (!isParamTypeCompatible(resolvedSchema, expectedType)) {\n\t\t\t\t\t\tconst paramName = helperParam.name;\n\t\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t`Helper \"${helperName}\" parameter \"${paramName}\" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,\n\t\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\thelperName,\n\t\t\t\t\t\t\t\texpected: schemaTypeLabel(expectedType),\n\t\t\t\t\t\t\t\tactual: schemaTypeLabel(resolvedSchema),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn helper.returnType ?? { type: \"string\" };\n\t\t}\n\n\t\t// Helper inline inconnu — warning\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\"warning\",\n\t\t\t`Unknown inline helper \"${helperName}\" — cannot analyze statically`,\n\t\t\tstmt,\n\t\t\t{ helperName },\n\t\t);\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Expression simple ────────────────────────────────────────────────────\n\treturn resolveExpressionWithDiagnostics(stmt.path, ctx, stmt) ?? {};\n}\n\n/**\n * Vérifie si un type résolu est compatible avec le type attendu par un\n * paramètre de helper.\n *\n * Règles de compatibilité :\n * - Si l'un des deux schemas n'a pas de `type`, on ne peut pas valider → compatible\n * - `integer` est compatible avec `number` (integer ⊂ number)\n * - Pour les types multiples (ex: `[\"string\", \"number\"]`), il suffit qu'un\n * type résolu corresponde à un type attendu\n */\nfunction isParamTypeCompatible(\n\tresolved: JSONSchema7,\n\texpected: JSONSchema7,\n): boolean {\n\t// Si l'un des deux n'a pas d'info de type, on ne peut pas valider\n\tif (!expected.type || !resolved.type) return true;\n\n\tconst expectedTypes = Array.isArray(expected.type)\n\t\t? expected.type\n\t\t: [expected.type];\n\tconst resolvedTypes = Array.isArray(resolved.type)\n\t\t? resolved.type\n\t\t: [resolved.type];\n\n\t// Au moins un type résolu doit être compatible avec un type attendu\n\treturn resolvedTypes.some((rt) =>\n\t\texpectedTypes.some(\n\t\t\t(et) =>\n\t\t\t\trt === et ||\n\t\t\t\t// integer est un sous-type de number\n\t\t\t\t(et === \"number\" && rt === \"integer\") ||\n\t\t\t\t(et === \"integer\" && rt === \"number\"),\n\t\t),\n\t);\n}\n\n/**\n * Infère le type de sortie d'un `Program` (corps d'un template ou d'un bloc).\n *\n * Gère 4 cas, du plus spécifique au plus général :\n *\n * 1. **Expression unique** `{{expr}}` → type de l'expression\n * 2. **Bloc unique** `{{#if}}…{{/if}}` → type du bloc\n * 3. **Contenu textuel pur** → détection de littéral (number, boolean, null)\n * 4. **Template mixte** → toujours `string` (concaténation)\n *\n * La validation est effectuée en même temps que l'inférence : chaque\n * expression et bloc est validé lors de son traitement.\n */\nfunction inferProgramType(\n\tprogram: hbs.AST.Program,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst effective = getEffectiveBody(program);\n\n\t// Aucun statement significatif → string vide\n\tif (effective.length === 0) {\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Cas 1 : une seule expression {{expr}} ──────────────────────────────\n\tconst singleExpr = getEffectivelySingleExpression(program);\n\tif (singleExpr) {\n\t\treturn processMustache(singleExpr, ctx);\n\t}\n\n\t// ── Cas 2 : un seul bloc {{#if}}, {{#each}}, {{#with}}, … ─────────────\n\tconst singleBlock = getEffectivelySingleBlock(program);\n\tif (singleBlock) {\n\t\treturn inferBlockType(singleBlock, ctx);\n\t}\n\n\t// ── Cas 3 : uniquement des ContentStatements (pas d'expressions) ───────\n\t// Si le texte concaténé (trimé) est un littéral typé (nombre, booléen,\n\t// null), on infère le type correspondant.\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\tconst text = effective\n\t\t\t.map((s) => (s as hbs.AST.ContentStatement).value)\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\n\t\tif (text === \"\") return { type: \"string\" };\n\n\t\tconst literalType = detectLiteralType(text);\n\t\tif (literalType) return { type: literalType };\n\t}\n\n\t// ── Cas 4 : template mixte (texte + expressions, blocs multiples…) ────\n\t// On parcourt tous les statements pour valider (side-effects : diagnostics).\n\t// Le résultat est toujours string (concaténation).\n\tfor (const stmt of program.body) {\n\t\tprocessStatement(stmt, ctx);\n\t}\n\treturn { type: \"string\" };\n}\n\n/**\n * Infère le type de sortie d'un BlockStatement et valide son contenu.\n *\n * Supporte les helpers built-in (`if`, `unless`, `each`, `with`) et les\n * helpers custom enregistrés via `Typebars.registerHelper()`.\n *\n * Utilise le pattern **save/restore** pour le contexte : au lieu de créer\n * un nouvel objet `{ ...ctx, current: X }` à chaque récursion, on sauvegarde\n * `ctx.current`, on le mute, on traite le corps, puis on restaure. Cela\n * réduit la pression sur le GC pour les templates profondément imbriqués.\n */\nfunction inferBlockType(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = getBlockHelperName(stmt);\n\n\tswitch (helperName) {\n\t\t// ── if / unless ──────────────────────────────────────────────────────\n\t\tcase \"if\":\n\t\tcase \"unless\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (arg) {\n\t\t\t\tresolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\t} else {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(helperName),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Inférer le type de la branche \"then\"\n\t\t\tconst thenType = inferProgramType(stmt.program, ctx);\n\n\t\t\tif (stmt.inverse) {\n\t\t\t\tconst elseType = inferProgramType(stmt.inverse, ctx);\n\t\t\t\t// Si les deux branches ont le même type → type unique\n\t\t\t\tif (deepEqual(thenType, elseType)) return thenType;\n\t\t\t\t// Sinon → union des deux types\n\t\t\t\treturn simplifySchema({ oneOf: [thenType, elseType] });\n\t\t\t}\n\n\t\t\t// Pas de branche else → le résultat est le type de la branche then\n\t\t\t// (conceptuellement optionnel, mais Handlebars retourne \"\" pour falsy)\n\t\t\treturn thenType;\n\t\t}\n\n\t\t// ── each ─────────────────────────────────────────────────────────────\n\t\tcase \"each\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (!arg) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(\"each\"),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName: \"each\" },\n\t\t\t\t);\n\t\t\t\t// Valider le corps avec un contexte vide (best-effort)\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\tconst collectionSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\tif (!collectionSchema) {\n\t\t\t\t// Le chemin n'a pas pu être résolu — diagnostic déjà émis.\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Résoudre le schema des éléments du tableau\n\t\t\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\t\t\tif (!itemSchema) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateTypeMismatchMessage(\n\t\t\t\t\t\t\"each\",\n\t\t\t\t\t\t\"an array\",\n\t\t\t\t\t\tschemaTypeLabel(collectionSchema),\n\t\t\t\t\t),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{\n\t\t\t\t\t\thelperName: \"each\",\n\t\t\t\t\t\texpected: \"array\",\n\t\t\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\t// Valider le corps avec un contexte vide (best-effort)\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Valider le corps avec le schema des éléments comme contexte\n\t\t\tconst saved = ctx.current;\n\t\t\tctx.current = itemSchema;\n\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\tctx.current = saved;\n\n\t\t\t// La branche inverse ({{else}}) garde le contexte parent\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\n\t\t\t// Un each concatène les rendus → toujours string\n\t\t\treturn { type: \"string\" };\n\t\t}\n\n\t\t// ── with ─────────────────────────────────────────────────────────────\n\t\tcase \"with\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (!arg) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(\"with\"),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName: \"with\" },\n\t\t\t\t);\n\t\t\t\t// Valider le corps avec un contexte vide\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tconst result = inferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst innerSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\n\t\t\tconst saved = ctx.current;\n\t\t\tctx.current = innerSchema ?? {};\n\t\t\tconst result = inferProgramType(stmt.program, ctx);\n\t\t\tctx.current = saved;\n\n\t\t\t// La branche inverse garde le contexte parent\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Helper custom ou inconnu ─────────────────────────────────────────\n\t\tdefault: {\n\t\t\tconst helper = ctx.helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\t// Helper custom enregistré — valider les paramètres\n\t\t\t\tfor (const param of stmt.params) {\n\t\t\t\t\tresolveExpressionWithDiagnostics(\n\t\t\t\t\t\tparam as hbs.AST.Expression,\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tstmt,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Valider le corps avec le contexte courant\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn helper.returnType ?? { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Helper inconnu — warning\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\t\"warning\",\n\t\t\t\tcreateUnknownHelperMessage(helperName),\n\t\t\t\tstmt,\n\t\t\t\t{ helperName },\n\t\t\t);\n\t\t\t// Valider quand même le corps avec le contexte courant (best-effort)\n\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\treturn { type: \"string\" };\n\t\t}\n\t}\n}\n\n// ─── Résolution d'expressions ────────────────────────────────────────────────\n\n/**\n * Résout une expression AST en un sous-schema, en émettant un diagnostic\n * si le chemin n'est pas résolvable.\n *\n * Gère la syntaxe `{{key:N}}` :\n * - Si l'expression a un identifiant N → résolution dans `identifierSchemas[N]`\n * - Si l'identifiant N n'a pas de schema associé → erreur\n * - Si pas d'identifiant → résolution dans `ctx.current` (comportement standard)\n *\n * @returns Le sous-schema résolu, ou `undefined` si le chemin est invalide.\n */\nfunction resolveExpressionWithDiagnostics(\n\texpr: hbs.AST.Expression,\n\tctx: AnalysisContext,\n\t/** Nœud AST parent (pour la localisation du diagnostic) */\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\t// Gestion de `this` / `.` → retourne le contexte courant\n\tif (isThisExpression(expr)) {\n\t\treturn ctx.current;\n\t}\n\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\t// Expression qui n'est pas un PathExpression (ex: literal, SubExpression)\n\t\tif (expr.type === \"StringLiteral\") return { type: \"string\" };\n\t\tif (expr.type === \"NumberLiteral\") return { type: \"number\" };\n\t\tif (expr.type === \"BooleanLiteral\") return { type: \"boolean\" };\n\t\tif (expr.type === \"NullLiteral\") return { type: \"null\" };\n\t\tif (expr.type === \"UndefinedLiteral\") return {};\n\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNANALYZABLE\",\n\t\t\t\"warning\",\n\t\t\tcreateUnanalyzableMessage(expr.type),\n\t\t\tparentNode ?? expr,\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// ── Extraction de l'identifiant ────────────────────────────────────────\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null) {\n\t\t// L'expression utilise la syntaxe {{key:N}} — résoudre depuis\n\t\t// le schema de l'identifiant N.\n\t\treturn resolveWithIdentifier(\n\t\t\tcleanSegments,\n\t\t\tidentifier,\n\t\t\tctx,\n\t\t\tparentNode ?? expr,\n\t\t);\n\t}\n\n\t// ── Résolution standard (pas d'identifiant) ────────────────────────────\n\tconst resolved = resolveSchemaPath(ctx.current, cleanSegments);\n\tif (resolved === undefined) {\n\t\tconst fullPath = cleanSegments.join(\".\");\n\t\tconst availableProperties = getSchemaPropertyNames(ctx.current);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(fullPath, availableProperties),\n\t\t\tparentNode ?? expr,\n\t\t\t{ path: fullPath, availableProperties },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Résout une expression avec identifiant `{{key:N}}` en cherchant dans\n * le schema associé à l'identifiant N.\n *\n * Émet un diagnostic d'erreur si :\n * - Aucun `identifierSchemas` n'a été fourni\n * - L'identifiant N n'a pas de schema associé\n * - La propriété n'existe pas dans le schema de l'identifiant\n */\nfunction resolveWithIdentifier(\n\tcleanSegments: string[],\n\tidentifier: number,\n\tctx: AnalysisContext,\n\tnode: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\tconst fullPath = cleanSegments.join(\".\");\n\n\t// Pas d'identifierSchemas fourni du tout\n\tif (!ctx.identifierSchemas) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_IDENTIFIER_SCHEMAS\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" uses an identifier but no identifier schemas were provided`,\n\t\t\tnode,\n\t\t\t{ path: `${fullPath}:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// L'identifiant n'existe pas dans les schemas fournis\n\tconst idSchema = ctx.identifierSchemas[identifier];\n\tif (!idSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_IDENTIFIER\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" references identifier ${identifier} but no schema exists for this identifier`,\n\t\t\tnode,\n\t\t\t{ path: `${fullPath}:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// Résoudre le chemin dans le schema de l'identifiant\n\tconst resolved = resolveSchemaPath(idSchema, cleanSegments);\n\tif (resolved === undefined) {\n\t\tconst availableProperties = getSchemaPropertyNames(idSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"IDENTIFIER_PROPERTY_NOT_FOUND\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}\" does not exist in the schema for identifier ${identifier}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\tpath: fullPath,\n\t\t\t\tidentifier,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n// ─── Utilitaires ─────────────────────────────────────────────────────────────\n\n/**\n * Extrait le premier argument d'un BlockStatement.\n *\n * Dans l'AST Handlebars, pour `{{#if active}}` :\n * - `stmt.path` → PathExpression(\"if\") ← le nom du helper\n * - `stmt.params[0]` → PathExpression(\"active\") ← l'argument réel\n *\n * @returns L'expression argument, ou `undefined` si le bloc n'a pas d'argument.\n */\nfunction getBlockArgument(\n\tstmt: hbs.AST.BlockStatement,\n): hbs.AST.Expression | undefined {\n\treturn stmt.params[0] as hbs.AST.Expression | undefined;\n}\n\n/**\n * Récupère le nom du helper d'un BlockStatement (ex: \"if\", \"each\", \"with\").\n */\nfunction getBlockHelperName(stmt: hbs.AST.BlockStatement): string {\n\tif (stmt.path.type === \"PathExpression\") {\n\t\treturn (stmt.path as hbs.AST.PathExpression).original;\n\t}\n\treturn \"\";\n}\n\n/**\n * Récupère le nom d'une expression (premier segment du PathExpression).\n * Utilisé pour identifier les helpers inline.\n */\nfunction getExpressionName(expr: hbs.AST.Expression): string {\n\tif (expr.type === \"PathExpression\") {\n\t\treturn (expr as hbs.AST.PathExpression).original;\n\t}\n\treturn \"\";\n}\n\n/**\n * Ajoute un diagnostic enrichi au contexte d'analyse.\n *\n * Chaque diagnostic inclut :\n * - Un `code` machine-readable pour le frontend\n * - Un `message` humain décrivant le problème\n * - Un `source` snippet du template (si la position est disponible)\n * - Des `details` structurés pour le debugging\n */\nfunction addDiagnostic(\n\tctx: AnalysisContext,\n\tcode: DiagnosticCode,\n\tseverity: \"error\" | \"warning\",\n\tmessage: string,\n\tnode?: hbs.AST.Node,\n\tdetails?: DiagnosticDetails,\n): void {\n\tconst diagnostic: TemplateDiagnostic = { severity, code, message };\n\n\t// Extraire la position et le snippet source si disponible\n\tif (node && \"loc\" in node && node.loc) {\n\t\tdiagnostic.loc = {\n\t\t\tstart: { line: node.loc.start.line, column: node.loc.start.column },\n\t\t\tend: { line: node.loc.end.line, column: node.loc.end.column },\n\t\t};\n\t\t// Extraire le fragment de template autour de l'erreur\n\t\tdiagnostic.source = extractSourceSnippet(ctx.template, diagnostic.loc);\n\t}\n\n\tif (details) {\n\t\tdiagnostic.details = details;\n\t}\n\n\tctx.diagnostics.push(diagnostic);\n}\n\n/**\n * Retourne un label lisible du type d'un schema (pour les messages d'erreur).\n */\nfunction schemaTypeLabel(schema: JSONSchema7): string {\n\tif (schema.type) {\n\t\treturn Array.isArray(schema.type) ? schema.type.join(\" | \") : schema.type;\n\t}\n\tif (schema.oneOf) return \"oneOf(...)\";\n\tif (schema.anyOf) return \"anyOf(...)\";\n\tif (schema.allOf) return \"allOf(...)\";\n\tif (schema.enum) return \"enum\";\n\treturn \"unknown\";\n}\n\n// ─── Export pour usage interne ────────────────────────────────────────────────\n// `inferBlockType` est exporté pour permettre des tests unitaires ciblés\n// sur l'inférence de type des blocs.\nexport { inferBlockType };\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "4E3D7DCBEE379AF464756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{d as N}from"./chunk-yczpjh73.js";import{g as J}from"./chunk-p3xzf1ew.js";import{l as I}from"./chunk-6955jpr7.js";import{D as K}from"./chunk-vka4e61h.js";import{T as L,U as M}from"./chunk-fhvf5y4x.js";class H{state;options;hbsCompiled=null;get ast(){return this.state.kind==="template"?this.state.ast:null}get template(){return this.state.kind==="template"?this.state.source:""}constructor(w,q){this.state=w,this.options=q}static fromTemplate(w,q,B){return new H({kind:"template",ast:w,source:q},B)}static fromLiteral(w,q){return new H({kind:"literal",value:w},q)}static fromObject(w,q){return new H({kind:"object",children:w},q)}analyze(w,q){switch(this.state.kind){case"object":{let{children:B}=this.state;return L(Object.keys(B),(D)=>{let G=B[D];if(!G)throw Error(`unreachable: missing child "${D}"`);return G.analyze(w,q)})}case"literal":return{valid:!0,diagnostics:[],outputSchema:I(this.state.value)};case"template":return N(this.state.ast,this.state.source,w,{identifierSchemas:q,helpers:this.options.helpers})}}validate(w,q){let B=this.analyze(w,q);return{valid:B.valid,diagnostics:B.diagnostics}}execute(w,q){switch(this.state.kind){case"object":{let{children:B}=this.state,D={};for(let[G,Q]of Object.entries(B))D[G]=Q.execute(w,q);return D}case"literal":return this.state.value;case"template":{if(q?.schema){let B=this.analyze(q.schema,q.identifierSchemas);if(!B.valid)throw new K(B.diagnostics)}return J(this.state.ast,this.state.source,w,this.buildExecutorContext(q))}}}analyzeAndExecute(w,q,B){switch(this.state.kind){case"object":{let{children:D}=this.state;return M(Object.keys(D),(G)=>D[G].analyzeAndExecute(w,q,B))}case"literal":return{analysis:{valid:!0,diagnostics:[],outputSchema:I(this.state.value)},value:this.state.value};case"template":{let D=this.analyze(w,B?.identifierSchemas);if(!D.valid)return{analysis:D,value:void 0};let G=J(this.state.ast,this.state.source,q,this.buildExecutorContext({identifierData:B?.identifierData}));return{analysis:D,value:G}}}}buildExecutorContext(w){return{identifierData:w?.identifierData,compiledTemplate:this.getOrCompileHbs(),hbs:this.options.hbs,compilationCache:this.options.compilationCache}}getOrCompileHbs(){if(!this.hbsCompiled)this.hbsCompiled=this.options.hbs.compile(this.template,{noEscape:!0,strict:!1});return this.hbsCompiled}}
|
|
2
|
+
export{H as b};
|
|
3
|
+
|
|
4
|
+
//# debugId=BFAFD14D960B2FAD64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-yraqh2tz.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/compiled-template.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { type ExecutorContext, executeFromAst } from \"./executor.ts\";\nimport type {\n\tAnalysisResult,\n\tExecuteOptions,\n\tHelperDefinition,\n\tValidationResult,\n} from \"./types.ts\";\nimport { inferPrimitiveSchema } from \"./types.ts\";\nimport {\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\ttype LRUCache,\n} from \"./utils.ts\";\n\n// ─── CompiledTemplate ────────────────────────────────────────────────────────\n// Template pré-parsé et prêt à être exécuté ou analysé sans re-parsing.\n//\n// Le pattern compile-once / execute-many évite le coût du parsing Handlebars\n// à chaque appel. L'AST est parsé une seule fois lors de la compilation,\n// et le template Handlebars est compilé paresseusement au premier `execute()`.\n//\n// Usage :\n// const tpl = engine.compile(\"Hello {{name}}\");\n// tpl.execute({ name: \"Alice\" }); // pas de re-parsing\n// tpl.execute({ name: \"Bob\" }); // pas de re-parsing ni recompilation\n// tpl.analyze(schema); // pas de re-parsing\n//\n// ─── État interne (TemplateState) ────────────────────────────────────────────\n// Le CompiledTemplate fonctionne en 3 modes exclusifs, modélisés par un\n// discriminated union `TemplateState` :\n//\n// - `\"template\"` — template Handlebars parsé (AST + source string)\n// - `\"literal\"` — valeur primitive passthrough (number, boolean, null)\n// - `\"object\"` — objet dont chaque propriété est un CompiledTemplate enfant\n//\n// Ce design élimine les champs optionnels et les `!` assertions en faveur\n// d'un narrowing TypeScript naturel via `switch (this.state.kind)`.\n//\n// ─── Avantages par rapport à l'API directe ───────────────────────────────────\n// - **Performance** : parsing et compilation ne sont faits qu'une seule fois\n// - **API simplifiée** : pas besoin de repasser le template string à chaque appel\n// - **Cohérence** : le même AST est utilisé pour l'analyse et l'exécution\n\n// ─── Types internes ──────────────────────────────────────────────────────────\n\n/** Options internes passées par le Typebars lors de la compilation */\nexport interface CompiledTemplateOptions {\n\t/** Helpers custom enregistrés sur l'engine */\n\thelpers: Map<string, HelperDefinition>;\n\t/** Environnement Handlebars isolé (avec les helpers enregistrés) */\n\thbs: typeof Handlebars;\n\t/** Cache de compilation partagé par l'engine */\n\tcompilationCache: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n/** État interne discriminé du CompiledTemplate */\ntype TemplateState =\n\t| {\n\t\t\treadonly kind: \"template\";\n\t\t\treadonly ast: hbs.AST.Program;\n\t\t\treadonly source: string;\n\t }\n\t| { readonly kind: \"literal\"; readonly value: number | boolean | null }\n\t| {\n\t\t\treadonly kind: \"object\";\n\t\t\treadonly children: Record<string, CompiledTemplate>;\n\t };\n\n// ─── Classe publique ─────────────────────────────────────────────────────────\n\nexport class CompiledTemplate {\n\t/** État interne discriminé */\n\tprivate readonly state: TemplateState;\n\n\t/** Options héritées du Typebars parent */\n\tprivate readonly options: CompiledTemplateOptions;\n\n\t/** Template Handlebars compilé (lazy — créé au premier `execute()` qui en a besoin) */\n\tprivate hbsCompiled: HandlebarsTemplateDelegate | null = null;\n\n\t// ─── Accesseurs publics (backward-compatible) ────────────────────────\n\n\t/** L'AST Handlebars pré-parsé — `null` en mode littéral ou objet */\n\tget ast(): hbs.AST.Program | null {\n\t\treturn this.state.kind === \"template\" ? this.state.ast : null;\n\t}\n\n\t/** Le template source original — string vide en mode littéral ou objet */\n\tget template(): string {\n\t\treturn this.state.kind === \"template\" ? this.state.source : \"\";\n\t}\n\n\t// ─── Construction ────────────────────────────────────────────────────\n\n\tprivate constructor(state: TemplateState, options: CompiledTemplateOptions) {\n\t\tthis.state = state;\n\t\tthis.options = options;\n\t}\n\n\t/**\n\t * Crée un CompiledTemplate pour un template Handlebars parsé.\n\t *\n\t * @param ast - L'AST Handlebars pré-parsé\n\t * @param source - Le template source original\n\t * @param options - Options héritées du Typebars\n\t */\n\tstatic fromTemplate(\n\t\tast: hbs.AST.Program,\n\t\tsource: string,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"template\", ast, source }, options);\n\t}\n\n\t/**\n\t * Crée un CompiledTemplate en mode passthrough pour une valeur littérale\n\t * (number, boolean, null). Aucun parsing ni compilation n'est effectué.\n\t *\n\t * @param value - La valeur primitive\n\t * @param options - Options héritées du Typebars\n\t * @returns Un CompiledTemplate qui retourne toujours `value`\n\t */\n\tstatic fromLiteral(\n\t\tvalue: number | boolean | null,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"literal\", value }, options);\n\t}\n\n\t/**\n\t * Crée un CompiledTemplate en mode objet, où chaque propriété est un\n\t * CompiledTemplate enfant. Toutes les opérations sont déléguées\n\t * récursivement aux enfants.\n\t *\n\t * @param children - Les templates enfants compilés `{ [key]: CompiledTemplate }`\n\t * @param options - Options héritées du Typebars\n\t * @returns Un CompiledTemplate qui délègue aux enfants\n\t */\n\tstatic fromObject(\n\t\tchildren: Record<string, CompiledTemplate>,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"object\", children }, options);\n\t}\n\n\t// ─── Analyse statique ────────────────────────────────────────────────\n\n\t/**\n\t * Analyse statiquement ce template par rapport à un JSON Schema v7.\n\t *\n\t * Retourne un `AnalysisResult` contenant :\n\t * - `valid` — `true` si aucune erreur\n\t * - `diagnostics` — liste de diagnostics (erreurs + warnings)\n\t * - `outputSchema` — JSON Schema décrivant le type de retour\n\t *\n\t * L'AST étant pré-parsé, cette méthode ne re-parse jamais le template.\n\t *\n\t * @param inputSchema - JSON Schema décrivant les variables disponibles\n\t * @param identifierSchemas - (optionnel) Schemas par identifiant `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\treturn aggregateObjectAnalysis(Object.keys(children), (key) => {\n\t\t\t\t\tconst child = children[key];\n\t\t\t\t\tif (!child) throw new Error(`unreachable: missing child \"${key}\"`);\n\t\t\t\t\treturn child.analyze(inputSchema, identifierSchemas);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t};\n\n\t\t\tcase \"template\":\n\t\t\t\treturn analyzeFromAst(this.state.ast, this.state.source, inputSchema, {\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t\thelpers: this.options.helpers,\n\t\t\t\t});\n\t\t}\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────\n\n\t/**\n\t * Valide le template contre un schema sans retourner le type de sortie.\n\t *\n\t * C'est un raccourci d'API pour `analyze()` qui ne retourne que `valid`\n\t * et `diagnostics`, sans `outputSchema`. L'analyse complète (y compris\n\t * l'inférence de type) est exécutée en interne — cette méthode ne\n\t * fournit pas de gain de performance, uniquement une API simplifiée.\n\t *\n\t * @param inputSchema - JSON Schema décrivant les variables disponibles\n\t * @param identifierSchemas - (optionnel) Schemas par identifiant\n\t */\n\tvalidate(\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Exécution ───────────────────────────────────────────────────────\n\n\t/**\n\t * Exécute ce template avec les données fournies.\n\t *\n\t * Le type de retour dépend de la structure du template :\n\t * - Expression unique `{{expr}}` → valeur brute (number, boolean, object…)\n\t * - Template mixte ou avec blocs → `string`\n\t * - Littéral primitif → la valeur telle quelle\n\t * - Objet template → objet avec les valeurs résolues\n\t *\n\t * Si un `schema` est fourni dans les options, l'analyse statique est\n\t * lancée avant l'exécution. Une `TemplateAnalysisError` est levée en\n\t * cas d'erreur.\n\t *\n\t * @param data - Les données de contexte pour le rendu\n\t * @param options - Options d'exécution (schema, identifierData, etc.)\n\t * @returns Le résultat de l'exécution\n\t */\n\texecute(data: Record<string, unknown>, options?: ExecuteOptions): unknown {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst result: Record<string, unknown> = {};\n\t\t\t\tfor (const [key, child] of Object.entries(children)) {\n\t\t\t\t\tresult[key] = child.execute(data, options);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn this.state.value;\n\n\t\t\tcase \"template\": {\n\t\t\t\t// Validation statique préalable si un schema est fourni\n\t\t\t\tif (options?.schema) {\n\t\t\t\t\tconst analysis = this.analyze(\n\t\t\t\t\t\toptions.schema,\n\t\t\t\t\t\toptions.identifierSchemas,\n\t\t\t\t\t);\n\t\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext(options),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Raccourcis combinés ─────────────────────────────────────────────\n\n\t/**\n\t * Analyse et exécute le template en un seul appel.\n\t *\n\t * Retourne à la fois le résultat d'analyse et la valeur exécutée.\n\t * Si l'analyse échoue, `value` est `undefined`.\n\t *\n\t * @param inputSchema - JSON Schema décrivant les variables disponibles\n\t * @param data - Les données de contexte pour le rendu\n\t * @param options - Options supplémentaires\n\t * @returns `{ analysis, value }`\n\t */\n\tanalyzeAndExecute(\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: {\n\t\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\t\tidentifierData?: Record<number, Record<string, unknown>>;\n\t\t},\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\treturn aggregateObjectAnalysisAndExecution(\n\t\t\t\t\tObject.keys(children),\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: key comes from Object.keys(children), access is guaranteed\n\t\t\t\t\t(key) => children[key]!.analyzeAndExecute(inputSchema, data, options),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tanalysis: {\n\t\t\t\t\t\tvalid: true,\n\t\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t\t},\n\t\t\t\t\tvalue: this.state.value,\n\t\t\t\t};\n\n\t\t\tcase \"template\": {\n\t\t\t\tconst analysis = this.analyze(inputSchema, options?.identifierSchemas);\n\n\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\treturn { analysis, value: undefined };\n\t\t\t\t}\n\n\t\t\t\tconst value = executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext({\n\t\t\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn { analysis, value };\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Internals ───────────────────────────────────────────────────────\n\n\t/**\n\t * Construit le contexte d'exécution pour `executeFromAst`.\n\t *\n\t * Utilise la compilation Handlebars lazy : le template n'est compilé\n\t * qu'au premier appel qui en a besoin (pas les expressions uniques).\n\t */\n\tprivate buildExecutorContext(options?: ExecuteOptions): ExecutorContext {\n\t\treturn {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\tcompiledTemplate: this.getOrCompileHbs(),\n\t\t\thbs: this.options.hbs,\n\t\t\tcompilationCache: this.options.compilationCache,\n\t\t};\n\t}\n\n\t/**\n\t * Compile le template Handlebars de manière lazy et le met en cache.\n\t *\n\t * La compilation n'est faite qu'une seule fois — les appels suivants\n\t * retournent le template compilé en mémoire.\n\t *\n\t * Pré-condition : cette méthode n'est appelée que depuis le mode \"template\".\n\t */\n\tprivate getOrCompileHbs(): HandlebarsTemplateDelegate {\n\t\tif (!this.hbsCompiled) {\n\t\t\t// En mode \"template\", `this.template` retourne la source string\n\t\t\tthis.hbsCompiled = this.options.hbs.compile(this.template, {\n\t\t\t\tnoEscape: true,\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t}\n\t\treturn this.hbsCompiled;\n\t}\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "BFAFD14D960B2FAD64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{d as N}from"./chunk-awgj10qg.js";import{g as J}from"./chunk-qpzzr2rd.js";import{l as I}from"./chunk-6955jpr7.js";import{B as K}from"./chunk-ybh51hbe.js";import{Q as L,R as M}from"./chunk-4zv02svp.js";class H{state;options;hbsCompiled=null;get ast(){return this.state.kind==="template"?this.state.ast:null}get template(){return this.state.kind==="template"?this.state.source:""}constructor(w,q){this.state=w,this.options=q}static fromTemplate(w,q,B){return new H({kind:"template",ast:w,source:q},B)}static fromLiteral(w,q){return new H({kind:"literal",value:w},q)}static fromObject(w,q){return new H({kind:"object",children:w},q)}analyze(w,q){switch(this.state.kind){case"object":{let{children:B}=this.state;return L(Object.keys(B),(D)=>{let G=B[D];if(!G)throw Error(`unreachable: missing child "${D}"`);return G.analyze(w,q)})}case"literal":return{valid:!0,diagnostics:[],outputSchema:I(this.state.value)};case"template":return N(this.state.ast,this.state.source,w,{identifierSchemas:q,helpers:this.options.helpers})}}validate(w,q){let B=this.analyze(w,q);return{valid:B.valid,diagnostics:B.diagnostics}}execute(w,q){switch(this.state.kind){case"object":{let{children:B}=this.state,D={};for(let[G,Q]of Object.entries(B))D[G]=Q.execute(w,q);return D}case"literal":return this.state.value;case"template":{if(q?.schema){let B=this.analyze(q.schema,q.identifierSchemas);if(!B.valid)throw new K(B.diagnostics)}return J(this.state.ast,this.state.source,w,this.buildExecutorContext(q))}}}analyzeAndExecute(w,q,B){switch(this.state.kind){case"object":{let{children:D}=this.state;return M(Object.keys(D),(G)=>D[G].analyzeAndExecute(w,q,B))}case"literal":return{analysis:{valid:!0,diagnostics:[],outputSchema:I(this.state.value)},value:this.state.value};case"template":{let D=this.analyze(w,B?.identifierSchemas);if(!D.valid)return{analysis:D,value:void 0};let G=J(this.state.ast,this.state.source,q,this.buildExecutorContext({identifierData:B?.identifierData}));return{analysis:D,value:G}}}}buildExecutorContext(w){return{identifierData:w?.identifierData,compiledTemplate:this.getOrCompileHbs(),hbs:this.options.hbs,compilationCache:this.options.compilationCache}}getOrCompileHbs(){if(!this.hbsCompiled)this.hbsCompiled=this.options.hbs.compile(this.template,{noEscape:!0,strict:!1});return this.hbsCompiled}}
|
|
2
|
+
export{H as b};
|
|
3
|
+
|
|
4
|
+
//# debugId=7E9B6C0E0074084D64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-z6yh5qvc.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/compiled-template.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { type ExecutorContext, executeFromAst } from \"./executor.ts\";\nimport type {\n\tAnalysisResult,\n\tExecuteOptions,\n\tHelperDefinition,\n\tValidationResult,\n} from \"./types.ts\";\nimport { inferPrimitiveSchema } from \"./types.ts\";\nimport {\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\ttype LRUCache,\n} from \"./utils.ts\";\n\n// ─── CompiledTemplate ────────────────────────────────────────────────────────\n// Pre-parsed template ready to be executed or analyzed without re-parsing.\n//\n// The compile-once / execute-many pattern avoids the cost of Handlebars\n// parsing on every call. The AST is parsed once at compile time, and the\n// Handlebars template is lazily compiled on the first `execute()`.\n//\n// Usage:\n// const tpl = engine.compile(\"Hello {{name}}\");\n// tpl.execute({ name: \"Alice\" }); // no re-parsing\n// tpl.execute({ name: \"Bob\" }); // no re-parsing or recompilation\n// tpl.analyze(schema); // no re-parsing\n//\n// ─── Internal State (TemplateState) ──────────────────────────────────────────\n// CompiledTemplate operates in 3 exclusive modes, modeled by a discriminated\n// union `TemplateState`:\n//\n// - `\"template\"` — parsed Handlebars template (AST + source string)\n// - `\"literal\"` — primitive passthrough value (number, boolean, null)\n// - `\"object\"` — object where each property is a child CompiledTemplate\n//\n// This design eliminates optional fields and `!` assertions in favor of\n// natural TypeScript narrowing via `switch (this.state.kind)`.\n//\n// ─── Advantages Over the Direct API ──────────────────────────────────────────\n// - **Performance**: parsing and compilation happen only once\n// - **Simplified API**: no need to re-pass the template string on each call\n// - **Consistency**: the same AST is used for both analysis and execution\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\n/** Internal options passed by Typebars during compilation */\nexport interface CompiledTemplateOptions {\n\t/** Custom helpers registered on the engine */\n\thelpers: Map<string, HelperDefinition>;\n\t/** Isolated Handlebars environment (with registered helpers) */\n\thbs: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n/** Discriminated internal state of the CompiledTemplate */\ntype TemplateState =\n\t| {\n\t\t\treadonly kind: \"template\";\n\t\t\treadonly ast: hbs.AST.Program;\n\t\t\treadonly source: string;\n\t }\n\t| { readonly kind: \"literal\"; readonly value: number | boolean | null }\n\t| {\n\t\t\treadonly kind: \"object\";\n\t\t\treadonly children: Record<string, CompiledTemplate>;\n\t };\n\n// ─── Public Class ────────────────────────────────────────────────────────────\n\nexport class CompiledTemplate {\n\t/** Discriminated internal state */\n\tprivate readonly state: TemplateState;\n\n\t/** Options inherited from the parent Typebars instance */\n\tprivate readonly options: CompiledTemplateOptions;\n\n\t/** Compiled Handlebars template (lazy — created on the first `execute()` that needs it) */\n\tprivate hbsCompiled: HandlebarsTemplateDelegate | null = null;\n\n\t// ─── Public Accessors (backward-compatible) ──────────────────────────\n\n\t/** The pre-parsed Handlebars AST — `null` in literal or object mode */\n\tget ast(): hbs.AST.Program | null {\n\t\treturn this.state.kind === \"template\" ? this.state.ast : null;\n\t}\n\n\t/** The original template source — empty string in literal or object mode */\n\tget template(): string {\n\t\treturn this.state.kind === \"template\" ? this.state.source : \"\";\n\t}\n\n\t// ─── Construction ────────────────────────────────────────────────────\n\n\tprivate constructor(state: TemplateState, options: CompiledTemplateOptions) {\n\t\tthis.state = state;\n\t\tthis.options = options;\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate for a parsed Handlebars template.\n\t *\n\t * @param ast - The pre-parsed Handlebars AST\n\t * @param source - The original template source\n\t * @param options - Options inherited from Typebars\n\t */\n\tstatic fromTemplate(\n\t\tast: hbs.AST.Program,\n\t\tsource: string,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"template\", ast, source }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in passthrough mode for a literal value\n\t * (number, boolean, null). No parsing or compilation is performed.\n\t *\n\t * @param value - The primitive value\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that always returns `value`\n\t */\n\tstatic fromLiteral(\n\t\tvalue: number | boolean | null,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"literal\", value }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in object mode, where each property is a\n\t * child CompiledTemplate. All operations are recursively delegated\n\t * to the children.\n\t *\n\t * @param children - The compiled child templates `{ [key]: CompiledTemplate }`\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that delegates to children\n\t */\n\tstatic fromObject(\n\t\tchildren: Record<string, CompiledTemplate>,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"object\", children }, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes this template against a JSON Schema v7.\n\t *\n\t * Returns an `AnalysisResult` containing:\n\t * - `valid` — `true` if no errors\n\t * - `diagnostics` — list of diagnostics (errors + warnings)\n\t * - `outputSchema` — JSON Schema describing the return type\n\t *\n\t * Since the AST is pre-parsed, this method never re-parses the template.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\treturn aggregateObjectAnalysis(Object.keys(children), (key) => {\n\t\t\t\t\tconst child = children[key];\n\t\t\t\t\tif (!child) throw new Error(`unreachable: missing child \"${key}\"`);\n\t\t\t\t\treturn child.analyze(inputSchema, identifierSchemas);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t};\n\n\t\t\tcase \"template\":\n\t\t\t\treturn analyzeFromAst(this.state.ast, this.state.source, inputSchema, {\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t\thelpers: this.options.helpers,\n\t\t\t\t});\n\t\t}\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────\n\n\t/**\n\t * Validates the template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────\n\n\t/**\n\t * Executes this template with the provided data.\n\t *\n\t * The return type depends on the template structure:\n\t * - Single expression `{{expr}}` → raw value (number, boolean, object…)\n\t * - Mixed template or with blocks → `string`\n\t * - Primitive literal → the value as-is\n\t * - Object template → object with resolved values\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, etc.)\n\t * @returns The execution result\n\t */\n\texecute(data: Record<string, unknown>, options?: ExecuteOptions): unknown {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst result: Record<string, unknown> = {};\n\t\t\t\tfor (const [key, child] of Object.entries(children)) {\n\t\t\t\t\tresult[key] = child.execute(data, options);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn this.state.value;\n\n\t\t\tcase \"template\": {\n\t\t\t\t// Pre-execution static validation if a schema is provided\n\t\t\t\tif (options?.schema) {\n\t\t\t\t\tconst analysis = this.analyze(\n\t\t\t\t\t\toptions.schema,\n\t\t\t\t\t\toptions.identifierSchemas,\n\t\t\t\t\t);\n\t\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext(options),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────\n\n\t/**\n\t * Analyzes and executes the template in a single call.\n\t *\n\t * Returns both the analysis result and the executed value.\n\t * If analysis fails, `value` is `undefined`.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - Additional options\n\t * @returns `{ analysis, value }`\n\t */\n\tanalyzeAndExecute(\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: {\n\t\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\t\tidentifierData?: Record<number, Record<string, unknown>>;\n\t\t},\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\treturn aggregateObjectAnalysisAndExecution(\n\t\t\t\t\tObject.keys(children),\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: key comes from Object.keys(children), access is guaranteed\n\t\t\t\t\t(key) => children[key]!.analyzeAndExecute(inputSchema, data, options),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tanalysis: {\n\t\t\t\t\t\tvalid: true,\n\t\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t\t},\n\t\t\t\t\tvalue: this.state.value,\n\t\t\t\t};\n\n\t\t\tcase \"template\": {\n\t\t\t\tconst analysis = this.analyze(inputSchema, options?.identifierSchemas);\n\n\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\treturn { analysis, value: undefined };\n\t\t\t\t}\n\n\t\t\t\tconst value = executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext({\n\t\t\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn { analysis, value };\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Internals ───────────────────────────────────────────────────────\n\n\t/**\n\t * Builds the execution context for `executeFromAst`.\n\t *\n\t * Uses lazy Handlebars compilation: the template is only compiled\n\t * on the first call that needs it (not for single expressions).\n\t */\n\tprivate buildExecutorContext(options?: ExecuteOptions): ExecutorContext {\n\t\treturn {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\tcompiledTemplate: this.getOrCompileHbs(),\n\t\t\thbs: this.options.hbs,\n\t\t\tcompilationCache: this.options.compilationCache,\n\t\t};\n\t}\n\n\t/**\n\t * Lazily compiles the Handlebars template and caches it.\n\t *\n\t * Compilation happens only once — subsequent calls return the\n\t * in-memory compiled template.\n\t *\n\t * Precondition: this method is only called from \"template\" mode.\n\t */\n\tprivate getOrCompileHbs(): HandlebarsTemplateDelegate {\n\t\tif (!this.hbsCompiled) {\n\t\t\t// In \"template\" mode, `this.template` returns the source string\n\t\t\tthis.hbsCompiled = this.options.hbs.compile(this.template, {\n\t\t\t\tnoEscape: true,\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t}\n\t\treturn this.hbsCompiled;\n\t}\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7E9B6C0E0074084D64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type Handlebars from "handlebars";
|
|
2
|
+
import type { JSONSchema7 } from "json-schema";
|
|
3
|
+
import type { AnalysisResult, ExecuteOptions, HelperDefinition, ValidationResult } from "./types.ts";
|
|
4
|
+
import { type LRUCache } from "./utils.ts";
|
|
5
|
+
/** Internal options passed by Typebars during compilation */
|
|
6
|
+
export interface CompiledTemplateOptions {
|
|
7
|
+
/** Custom helpers registered on the engine */
|
|
8
|
+
helpers: Map<string, HelperDefinition>;
|
|
9
|
+
/** Isolated Handlebars environment (with registered helpers) */
|
|
10
|
+
hbs: typeof Handlebars;
|
|
11
|
+
/** Compilation cache shared by the engine */
|
|
12
|
+
compilationCache: LRUCache<string, HandlebarsTemplateDelegate>;
|
|
13
|
+
}
|
|
14
|
+
export declare class CompiledTemplate {
|
|
15
|
+
/** Discriminated internal state */
|
|
16
|
+
private readonly state;
|
|
17
|
+
/** Options inherited from the parent Typebars instance */
|
|
18
|
+
private readonly options;
|
|
19
|
+
/** Compiled Handlebars template (lazy — created on the first `execute()` that needs it) */
|
|
20
|
+
private hbsCompiled;
|
|
21
|
+
/** The pre-parsed Handlebars AST — `null` in literal or object mode */
|
|
22
|
+
get ast(): hbs.AST.Program | null;
|
|
23
|
+
/** The original template source — empty string in literal or object mode */
|
|
24
|
+
get template(): string;
|
|
25
|
+
private constructor();
|
|
26
|
+
/**
|
|
27
|
+
* Creates a CompiledTemplate for a parsed Handlebars template.
|
|
28
|
+
*
|
|
29
|
+
* @param ast - The pre-parsed Handlebars AST
|
|
30
|
+
* @param source - The original template source
|
|
31
|
+
* @param options - Options inherited from Typebars
|
|
32
|
+
*/
|
|
33
|
+
static fromTemplate(ast: hbs.AST.Program, source: string, options: CompiledTemplateOptions): CompiledTemplate;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a CompiledTemplate in passthrough mode for a literal value
|
|
36
|
+
* (number, boolean, null). No parsing or compilation is performed.
|
|
37
|
+
*
|
|
38
|
+
* @param value - The primitive value
|
|
39
|
+
* @param options - Options inherited from Typebars
|
|
40
|
+
* @returns A CompiledTemplate that always returns `value`
|
|
41
|
+
*/
|
|
42
|
+
static fromLiteral(value: number | boolean | null, options: CompiledTemplateOptions): CompiledTemplate;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a CompiledTemplate in object mode, where each property is a
|
|
45
|
+
* child CompiledTemplate. All operations are recursively delegated
|
|
46
|
+
* to the children.
|
|
47
|
+
*
|
|
48
|
+
* @param children - The compiled child templates `{ [key]: CompiledTemplate }`
|
|
49
|
+
* @param options - Options inherited from Typebars
|
|
50
|
+
* @returns A CompiledTemplate that delegates to children
|
|
51
|
+
*/
|
|
52
|
+
static fromObject(children: Record<string, CompiledTemplate>, options: CompiledTemplateOptions): CompiledTemplate;
|
|
53
|
+
/**
|
|
54
|
+
* Statically analyzes this template against a JSON Schema v7.
|
|
55
|
+
*
|
|
56
|
+
* Returns an `AnalysisResult` containing:
|
|
57
|
+
* - `valid` — `true` if no errors
|
|
58
|
+
* - `diagnostics` — list of diagnostics (errors + warnings)
|
|
59
|
+
* - `outputSchema` — JSON Schema describing the return type
|
|
60
|
+
*
|
|
61
|
+
* Since the AST is pre-parsed, this method never re-parses the template.
|
|
62
|
+
*
|
|
63
|
+
* @param inputSchema - JSON Schema describing the available variables
|
|
64
|
+
* @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`
|
|
65
|
+
*/
|
|
66
|
+
analyze(inputSchema: JSONSchema7, identifierSchemas?: Record<number, JSONSchema7>): AnalysisResult;
|
|
67
|
+
/**
|
|
68
|
+
* Validates the template against a schema without returning the output type.
|
|
69
|
+
*
|
|
70
|
+
* This is an API shortcut for `analyze()` that only returns `valid` and
|
|
71
|
+
* `diagnostics`, without `outputSchema`. The full analysis (including type
|
|
72
|
+
* inference) is executed internally — this method provides no performance
|
|
73
|
+
* gain, only a simplified API.
|
|
74
|
+
*
|
|
75
|
+
* @param inputSchema - JSON Schema describing the available variables
|
|
76
|
+
* @param identifierSchemas - (optional) Schemas by identifier
|
|
77
|
+
*/
|
|
78
|
+
validate(inputSchema: JSONSchema7, identifierSchemas?: Record<number, JSONSchema7>): ValidationResult;
|
|
79
|
+
/**
|
|
80
|
+
* Executes this template with the provided data.
|
|
81
|
+
*
|
|
82
|
+
* The return type depends on the template structure:
|
|
83
|
+
* - Single expression `{{expr}}` → raw value (number, boolean, object…)
|
|
84
|
+
* - Mixed template or with blocks → `string`
|
|
85
|
+
* - Primitive literal → the value as-is
|
|
86
|
+
* - Object template → object with resolved values
|
|
87
|
+
*
|
|
88
|
+
* If a `schema` is provided in options, static analysis is performed
|
|
89
|
+
* before execution. A `TemplateAnalysisError` is thrown on errors.
|
|
90
|
+
*
|
|
91
|
+
* @param data - The context data for rendering
|
|
92
|
+
* @param options - Execution options (schema, identifierData, etc.)
|
|
93
|
+
* @returns The execution result
|
|
94
|
+
*/
|
|
95
|
+
execute(data: Record<string, unknown>, options?: ExecuteOptions): unknown;
|
|
96
|
+
/**
|
|
97
|
+
* Analyzes and executes the template in a single call.
|
|
98
|
+
*
|
|
99
|
+
* Returns both the analysis result and the executed value.
|
|
100
|
+
* If analysis fails, `value` is `undefined`.
|
|
101
|
+
*
|
|
102
|
+
* @param inputSchema - JSON Schema describing the available variables
|
|
103
|
+
* @param data - The context data for rendering
|
|
104
|
+
* @param options - Additional options
|
|
105
|
+
* @returns `{ analysis, value }`
|
|
106
|
+
*/
|
|
107
|
+
analyzeAndExecute(inputSchema: JSONSchema7, data: Record<string, unknown>, options?: {
|
|
108
|
+
identifierSchemas?: Record<number, JSONSchema7>;
|
|
109
|
+
identifierData?: Record<number, Record<string, unknown>>;
|
|
110
|
+
}): {
|
|
111
|
+
analysis: AnalysisResult;
|
|
112
|
+
value: unknown;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Builds the execution context for `executeFromAst`.
|
|
116
|
+
*
|
|
117
|
+
* Uses lazy Handlebars compilation: the template is only compiled
|
|
118
|
+
* on the first call that needs it (not for single expressions).
|
|
119
|
+
*/
|
|
120
|
+
private buildExecutorContext;
|
|
121
|
+
/**
|
|
122
|
+
* Lazily compiles the Handlebars template and caches it.
|
|
123
|
+
*
|
|
124
|
+
* Compilation happens only once — subsequent calls return the
|
|
125
|
+
* in-memory compiled template.
|
|
126
|
+
*
|
|
127
|
+
* Precondition: this method is only called from "template" mode.
|
|
128
|
+
*/
|
|
129
|
+
private getOrCompileHbs;
|
|
130
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{b as a}from"./chunk-kznb0bev.js";import"./chunk-1qwj7pjc.js";import"./chunk-qpzzr2rd.js";import"./chunk-6955jpr7.js";import"./chunk-1gm6cf0e.js";import"./chunk-ybh51hbe.js";import"./chunk-8g0d6h85.js";import"./chunk-4zv02svp.js";export{a as CompiledTemplate};
|
|
2
|
+
|
|
3
|
+
//# debugId=771271DE7F3CA9FE64756E2164756E21
|
|
4
|
+
//# sourceMappingURL=compiled-template.js.map
|