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,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/utils.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { JSONSchema7 } from \"json-schema\";\nimport type { AnalysisResult, TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Utilitaires ─────────────────────────────────────────────────────────────\n// Fonctions et classes utilitaires partagées par les différents modules\n// du moteur de template.\n\n// ─── Deep Equality ───────────────────────────────────────────────────────────\n// Comparaison structurelle profonde pour des valeurs JSON-compatibles.\n// Plus robuste que `JSON.stringify` car indépendant de l'ordre des clés\n// et sans allocation de strings intermédiaires.\n\n/**\n * Compare récursivement deux valeurs JSON-compatibles.\n *\n * @param a - Première valeur\n * @param b - Seconde valeur\n * @returns `true` si les deux valeurs sont structurellement identiques\n *\n * @example\n * ```\n * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // → true\n * deepEqual([1, 2], [1, 2]) // → true\n * deepEqual({ a: 1 }, { a: 2 }) // → false\n * ```\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n\t// Identité stricte (couvre primitives, même ref, NaN !== NaN volontaire)\n\tif (a === b) return true;\n\n\t// null est typeof \"object\" en JS — on le traite à part\n\tif (a === null || b === null) return false;\n\tif (typeof a !== typeof b) return false;\n\n\t// ── Tableaux ────────────────────────────────────────────────────────────\n\tif (Array.isArray(a)) {\n\t\tif (!Array.isArray(b)) return false;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// ── Objets ──────────────────────────────────────────────────────────────\n\tif (typeof a === \"object\") {\n\t\tconst objA = a as Record<string, unknown>;\n\t\tconst objB = b as Record<string, unknown>;\n\t\tconst keysA = Object.keys(objA);\n\t\tconst keysB = Object.keys(objB);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!(key in objB) || !deepEqual(objA[key], objB[key])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Primitives différentes (déjà couvert par a === b au début)\n\treturn false;\n}\n\n// ─── LRU Cache ───────────────────────────────────────────────────────────────\n// Cache à capacité fixe avec éviction LRU (Least Recently Used).\n// Utilise l'ordre d'insertion de `Map` pour tracker l'accès : l'entrée\n// la plus ancienne est toujours en première position.\n\n/**\n * Cache LRU simple à capacité fixe.\n *\n * @example\n * ```\n * const cache = new LRUCache<string, number>(2);\n * cache.set(\"a\", 1);\n * cache.set(\"b\", 2);\n * cache.get(\"a\"); // → 1 (marque \"a\" comme récemment utilisé)\n * cache.set(\"c\", 3); // évince \"b\" (le moins récemment utilisé)\n * cache.get(\"b\"); // → undefined\n * ```\n */\nexport class LRUCache<K, V> {\n\tprivate readonly cache = new Map<K, V>();\n\n\tconstructor(private readonly capacity: number) {\n\t\tif (capacity < 1) {\n\t\t\tthrow new Error(\"LRUCache capacity must be at least 1\");\n\t\t}\n\t}\n\n\t/**\n\t * Récupère une valeur du cache. Retourne `undefined` si absente.\n\t * Marque l'entrée comme récemment utilisée.\n\t */\n\tget(key: K): V | undefined {\n\t\tif (!this.cache.has(key)) return undefined;\n\n\t\t// Déplacer en fin de Map (= plus récent)\n\t\tconst value = this.cache.get(key) as V;\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, value);\n\t\treturn value;\n\t}\n\n\t/**\n\t * Insère ou met à jour une valeur dans le cache.\n\t * Si le cache est plein, évince l'entrée la moins récemment utilisée.\n\t */\n\tset(key: K, value: V): void {\n\t\tif (this.cache.has(key)) {\n\t\t\tthis.cache.delete(key);\n\t\t} else if (this.cache.size >= this.capacity) {\n\t\t\t// Évince la première entrée (la plus ancienne)\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey !== undefined) {\n\t\t\t\tthis.cache.delete(oldestKey);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(key, value);\n\t}\n\n\t/**\n\t * Vérifie si une clé existe dans le cache (sans modifier l'ordre LRU).\n\t */\n\thas(key: K): boolean {\n\t\treturn this.cache.has(key);\n\t}\n\n\t/**\n\t * Supprime une entrée du cache.\n\t * @returns `true` si l'entrée existait et a été supprimée\n\t */\n\tdelete(key: K): boolean {\n\t\treturn this.cache.delete(key);\n\t}\n\n\t/** Vide entièrement le cache. */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/** Nombre d'entrées actuellement dans le cache. */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n}\n\n// ─── Extraction de snippet source ────────────────────────────────────────────\n// Utilisé pour enrichir les diagnostics avec le fragment de template\n// qui a causé l'erreur.\n\n/**\n * Extrait un fragment de template autour d'une position donnée.\n *\n * @param template - Le template source complet\n * @param loc - La position (ligne/colonne, 1-based) de l'erreur\n * @returns Le fragment de code correspondant (trimé)\n */\nexport function extractSourceSnippet(\n\ttemplate: string,\n\tloc: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t},\n): string {\n\tconst lines = template.split(\"\\n\");\n\tconst startLine = loc.start.line - 1; // 0-based\n\tconst endLine = loc.end.line - 1;\n\n\tif (startLine < 0 || startLine >= lines.length) return \"\";\n\n\tif (startLine === endLine) {\n\t\t// Même ligne — extraire la portion entre start.column et end.column\n\t\tconst line = lines[startLine] ?? \"\";\n\t\treturn line.trim();\n\t}\n\n\t// Multi-lignes — retourner les lignes concernées\n\tconst clampedEnd = Math.min(endLine, lines.length - 1);\n\treturn lines\n\t\t.slice(startLine, clampedEnd + 1)\n\t\t.map((l) => l.trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\n// ─── Schema Properties ──────────────────────────────────────────────────────\n// Utilitaire pour lister les propriétés disponibles dans un schema,\n// utilisé pour enrichir les messages d'erreur (suggestions).\n\n/**\n * Liste les noms de propriétés déclarées dans un JSON Schema.\n * Retourne un tableau vide si le schema n'a pas de `properties`.\n */\nexport function getSchemaPropertyNames(schema: JSONSchema7): string[] {\n\tconst names = new Set<string>();\n\n\t// Propriétés directes\n\tif (schema.properties) {\n\t\tfor (const key of Object.keys(schema.properties)) {\n\t\t\tnames.add(key);\n\t\t}\n\t}\n\n\t// Propriétés dans les combinateurs\n\tfor (const combinator of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[combinator];\n\t\tif (branches) {\n\t\t\tfor (const branch of branches) {\n\t\t\t\tif (typeof branch === \"boolean\") continue;\n\t\t\t\tif (branch.properties) {\n\t\t\t\t\tfor (const key of Object.keys(branch.properties)) {\n\t\t\t\t\t\tnames.add(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(names).sort();\n}\n\n// ─── Agrégation d'analyses d'objets ──────────────────────────────────────────\n// Factorise le pattern commun de récursion sur un objet template :\n// itérer les clés, analyser chaque entrée via un callback, accumuler\n// les diagnostics, construire le outputSchema objet.\n//\n// Utilisé par :\n// - `analyzer.ts` (analyzeObjectTemplate)\n// - `Typebars.analyzeObject()` (typebars.ts)\n// - `CompiledTemplate.analyze()` en mode objet (compiled-template.ts)\n\n/**\n * Agrège les résultats d'analyse d'un ensemble d'entrées nommées en un\n * seul `AnalysisResult` avec un `outputSchema` de type objet.\n *\n * @param keys - Les clés de l'objet à analyser\n * @param analyzeEntry - Callback qui analyse une entrée par sa clé\n * @returns Un `AnalysisResult` agrégé\n *\n * @example\n * ```\n * aggregateObjectAnalysis(\n * Object.keys(template),\n * (key) => analyze(template[key], inputSchema),\n * );\n * ```\n */\nexport function aggregateObjectAnalysis(\n\tkeys: string[],\n\tanalyzeEntry: (key: string) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = analyzeEntry(key);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\tproperties[key] = child.outputSchema;\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n}\n\n/**\n * Agrège les résultats d'analyse **et** d'exécution d'un ensemble d'entrées\n * nommées. Retourne à la fois l'`AnalysisResult` agrégé et l'objet des\n * valeurs exécutées (ou `undefined` si au moins une entrée est invalide).\n *\n * @param keys - Les clés de l'objet\n * @param processEntry - Callback qui analyse et exécute une entrée par sa clé\n * @returns `{ analysis, value }` agrégés\n */\nexport function aggregateObjectAnalysisAndExecution(\n\tkeys: string[],\n\tprocessEntry: (key: string) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tconst resultValues: Record<string, unknown> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = processEntry(key);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\tproperties[key] = child.analysis.outputSchema;\n\t\tresultValues[key] = child.value;\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7D2B6D65A23A9D4664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{C as O}from"./chunk-vka4e61h.js";import{Q}from"./chunk-fhvf5y4x.js";import V from"handlebars";var Y=/^(.+):(\d+)$/,Z=/^-?\d+(\.\d+)?$/,K=new Q(128);function N(k){let q=K.get(k);if(q)return q;try{let z=V.parse(k);return K.set(k,z),z}catch(z){let G=z instanceof Error?z.message:String(z),J=G.match(/line\s+(\d+).*?column\s+(\d+)/i),X=J?{line:parseInt(J[1]??"0",10),column:parseInt(J[2]??"0",10)}:void 0;throw new O(G,X)}}function P(k){try{return V.parse(k)}catch(q){let z=q instanceof Error?q.message:String(q),G=z.match(/line\s+(\d+).*?column\s+(\d+)/i),J=G?{line:parseInt(G[1]??"0",10),column:parseInt(G[2]??"0",10)}:void 0;throw new O(z,J)}}function H(){K.clear()}function U(k){let{body:q}=k;return q.length===1&&q[0]?.type==="MustacheStatement"}function _(k){if(k.type==="PathExpression")return k.parts;return[]}function w(k){if(k.type!=="PathExpression")return!1;let q=k;return q.original==="this"||q.original==="."}function W(k){return k.body.filter((q)=>!(q.type==="ContentStatement"&&q.value.trim()===""))}function A(k){let q=W(k);if(q.length===1&&q[0]?.type==="BlockStatement")return q[0];return null}function C(k){let q=W(k);if(q.length===1&&q[0]?.type==="MustacheStatement")return q[0];return null}function S(k){return k.body.every((q)=>q.type==="ContentStatement"||q.type==="MustacheStatement"&&q.params.length===0&&!q.hash)}function $(k){if(Z.test(k))return"number";if(k==="true"||k==="false")return"boolean";if(k==="null")return"null";return null}function T(k){let q=k.trim(),z=$(q);if(z==="number")return Number(q);if(z==="boolean")return q==="true";if(z==="null")return null;return k}function D(k){let q=k.match(Y);if(q)return{key:q[1]??k,identifier:parseInt(q[2]??"0",10)};return{key:k,identifier:null}}function L(k){if(k.length===0)return{cleanSegments:[],identifier:null};let q=k[k.length-1],z=D(q);if(z.identifier!==null)return{cleanSegments:[...k.slice(0,-1),z.key],identifier:z.identifier};return{cleanSegments:k,identifier:null}}
|
|
2
|
+
export{N as n,P as o,H as p,U as q,_ as r,w as s,W as t,A as u,C as v,S as w,$ as x,T as y,D as z,L as A};
|
|
3
|
+
|
|
4
|
+
//# debugId=3C85E965B62779F664756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-ggdfaqhe.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/parser.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import Handlebars from \"handlebars\";\nimport { TemplateParseError } from \"./errors.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Regex pour détecter un identifiant de template (ex: \"meetingId:1\") ──────\n// L'identifiant est toujours un entier positif ou zéro, séparé du nom de la\n// variable par un `:`. Le `:` et le nombre sont sur le **dernier** segment\n// du chemin (Handlebars split sur les `.`).\nconst IDENTIFIER_RE = /^(.+):(\\d+)$/;\n\n// ─── Template Parser ─────────────────────────────────────────────────────────\n// Wrapper mince autour du parser Handlebars. On centralise ici l'appel au\n// parser pour :\n// 1. Encapsuler les erreurs dans notre hiérarchie (`TemplateParseError`)\n// 2. Exposer des helpers d'introspection sur l'AST (ex: `isSingleExpression`)\n// 3. Isoler la dépendance directe à Handlebars du reste du code\n// 4. Cacher les ASTs parsés via un LRU cache pour éviter les re-parsings\n\n// ─── Regex pour détecter un littéral numérique (entier ou décimal, signé) ────\n// Conservateur volontairement : pas de notation scientifique (1e5), pas de\n// hex (0xFF), pas de séparateurs (1_000). On veut reconnaître uniquement ce\n// qu'un humain écrirait comme valeur numérique dans un template.\nconst NUMERIC_LITERAL_RE = /^-?\\d+(\\.\\d+)?$/;\n\n// ─── Cache global d'AST ──────────────────────────────────────────────────────\n// Le parsing Handlebars est coûteux. Ce cache module-level évite de re-parser\n// le même template string lors d'appels répétés à `parse()`.\n// Taille par défaut : 128 entrées (suffisant pour la majorité des usages).\nconst globalAstCache = new LRUCache<string, hbs.AST.Program>(128);\n\n/**\n * Parse un template string et retourne l'AST Handlebars.\n *\n * Les résultats sont cachés automatiquement : appeler `parse()` deux fois\n * avec le même template ne re-parse pas.\n *\n * @param template - La chaîne de template à parser (ex: `\"Hello {{name}}\"`)\n * @returns L'AST racine (`hbs.AST.Program`)\n * @throws {TemplateParseError} si la syntaxe du template est invalide\n */\nexport function parse(template: string): hbs.AST.Program {\n\t// Vérifier le cache en premier\n\tconst cached = globalAstCache.get(template);\n\tif (cached) return cached;\n\n\ttry {\n\t\tconst ast = Handlebars.parse(template);\n\t\tglobalAstCache.set(template, ast);\n\t\treturn ast;\n\t} catch (error: unknown) {\n\t\t// Handlebars lève une Error classique avec un message descriptif.\n\t\t// On la transforme en TemplateParseError pour un traitement uniforme.\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\n\t\t// Handlebars inclut parfois la position dans le message, on tente\n\t\t// de l'extraire pour enrichir notre erreur.\n\t\tconst locMatch = message.match(/line\\s+(\\d+).*?column\\s+(\\d+)/i);\n\t\tconst loc = locMatch\n\t\t\t? {\n\t\t\t\t\tline: parseInt(locMatch[1] ?? \"0\", 10),\n\t\t\t\t\tcolumn: parseInt(locMatch[2] ?? \"0\", 10),\n\t\t\t\t}\n\t\t\t: undefined;\n\n\t\tthrow new TemplateParseError(message, loc);\n\t}\n}\n\n/**\n * Parse un template sans utiliser le cache. Utile pour les benchmarks\n * ou quand on veut un AST frais garanti.\n *\n * @param template - La chaîne de template à parser\n * @returns L'AST racine (`hbs.AST.Program`)\n * @throws {TemplateParseError} si la syntaxe du template est invalide\n */\nexport function parseUncached(template: string): hbs.AST.Program {\n\ttry {\n\t\treturn Handlebars.parse(template);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst locMatch = message.match(/line\\s+(\\d+).*?column\\s+(\\d+)/i);\n\t\tconst loc = locMatch\n\t\t\t? {\n\t\t\t\t\tline: parseInt(locMatch[1] ?? \"0\", 10),\n\t\t\t\t\tcolumn: parseInt(locMatch[2] ?? \"0\", 10),\n\t\t\t\t}\n\t\t\t: undefined;\n\t\tthrow new TemplateParseError(message, loc);\n\t}\n}\n\n/**\n * Vide le cache global d'AST. Utile pour les tests ou pour libérer\n * la mémoire si beaucoup de templates uniques ont été parsés.\n */\nexport function clearParseCache(): void {\n\tglobalAstCache.clear();\n}\n\n/**\n * Détermine si l'AST représente un template constitué d'une seule expression\n * `{{expression}}` sans aucun contenu textuel autour.\n *\n * C'est important pour l'inférence de type de retour :\n * - Template `{{value}}` → retourne le type brut de `value` (number, object…)\n * - Template `Hello {{name}}` → retourne toujours `string` (concaténation)\n *\n * @param ast - L'AST parsé du template\n * @returns `true` si le template est une expression unique\n */\nexport function isSingleExpression(ast: hbs.AST.Program): boolean {\n\tconst { body } = ast;\n\n\t// Exactement un nœud, et c'est un MustacheStatement (pas un block, pas du texte)\n\treturn body.length === 1 && body[0]?.type === \"MustacheStatement\";\n}\n\n/**\n * Extrait les segments de chemin d'un `PathExpression` Handlebars.\n *\n * Handlebars décompose `user.address.city` en `{ parts: [\"user\", \"address\", \"city\"] }`.\n * Cette fonction extrait ces segments de manière sûre.\n *\n * @param expr - L'expression dont on veut le chemin\n * @returns Les segments du chemin, ou un tableau vide si l'expression n'est\n * pas un `PathExpression`\n */\nexport function extractPathSegments(expr: hbs.AST.Expression): string[] {\n\tif (expr.type === \"PathExpression\") {\n\t\treturn (expr as hbs.AST.PathExpression).parts;\n\t}\n\treturn [];\n}\n\n/**\n * Vérifie si une expression AST est un `PathExpression` pointant vers `this`\n * (utilisé à l'intérieur des blocs `{{#each}}`).\n */\nexport function isThisExpression(expr: hbs.AST.Expression): boolean {\n\tif (expr.type !== \"PathExpression\") return false;\n\tconst path = expr as hbs.AST.PathExpression;\n\treturn path.original === \"this\" || path.original === \".\";\n}\n\n// ─── Filtrage des nœuds significatifs ────────────────────────────────────────\n// Dans un AST Handlebars, le formatage (retours à la ligne, indentation)\n// produit des `ContentStatement` dont la valeur est purement du whitespace.\n// Ces nœuds n'ont aucun impact sémantique et doivent être ignorés lors de\n// l'inférence de type pour détecter les cas \"effectivement un seul bloc\" ou\n// \"effectivement une seule expression\".\n\n/**\n * Retourne les statements significatifs d'un Program en éliminant les\n * `ContentStatement` constitués uniquement de whitespace.\n */\nexport function getEffectiveBody(\n\tprogram: hbs.AST.Program,\n): hbs.AST.Statement[] {\n\treturn program.body.filter(\n\t\t(s) =>\n\t\t\t!(\n\t\t\t\ts.type === \"ContentStatement\" &&\n\t\t\t\t(s as hbs.AST.ContentStatement).value.trim() === \"\"\n\t\t\t),\n\t);\n}\n\n/**\n * Détermine si un Program est effectivement constitué d'un seul\n * `BlockStatement` (en ignorant le whitespace autour).\n *\n * Exemples reconnus :\n * ```\n * {{#if x}}...{{/if}}\n *\n * {{#each items}}...{{/each}}\n * ```\n *\n * @returns Le `BlockStatement` unique ou `null` si le programme contient\n * d'autres nœuds significatifs.\n */\nexport function getEffectivelySingleBlock(\n\tprogram: hbs.AST.Program,\n): hbs.AST.BlockStatement | null {\n\tconst effective = getEffectiveBody(program);\n\tif (effective.length === 1 && effective[0]?.type === \"BlockStatement\") {\n\t\treturn effective[0] as hbs.AST.BlockStatement;\n\t}\n\treturn null;\n}\n\n/**\n * Détermine si un Program est effectivement constitué d'une seule\n * `MustacheStatement` (en ignorant le whitespace autour).\n *\n * Exemple : ` {{age}} ` → true\n */\nexport function getEffectivelySingleExpression(\n\tprogram: hbs.AST.Program,\n): hbs.AST.MustacheStatement | null {\n\tconst effective = getEffectiveBody(program);\n\tif (effective.length === 1 && effective[0]?.type === \"MustacheStatement\") {\n\t\treturn effective[0] as hbs.AST.MustacheStatement;\n\t}\n\treturn null;\n}\n\n// ─── Détection du fast-path ──────────────────────────────────────────────────\n// Pour les templates constitués uniquement de texte et d'expressions simples\n// (pas de blocs, pas de helpers avec paramètres), on peut court-circuiter\n// Handlebars et faire un simple remplacement de variables.\n\n/**\n * Détermine si un AST peut être exécuté via le fast-path (concaténation\n * directe sans passer par `Handlebars.compile()`).\n *\n * Le fast-path est possible quand le template ne contient que :\n * - Des `ContentStatement` (texte statique)\n * - Des `MustacheStatement` simples (sans params, sans hash)\n *\n * Cela exclut :\n * - Les blocs (`{{#if}}`, `{{#each}}`, etc.)\n * - Les helpers inline (`{{uppercase name}}`)\n * - Les sub-expressions\n *\n * @param ast - L'AST parsé du template\n * @returns `true` si le template peut utiliser le fast-path\n */\nexport function canUseFastPath(ast: hbs.AST.Program): boolean {\n\treturn ast.body.every(\n\t\t(s) =>\n\t\t\ts.type === \"ContentStatement\" ||\n\t\t\t(s.type === \"MustacheStatement\" &&\n\t\t\t\t(s as hbs.AST.MustacheStatement).params.length === 0 &&\n\t\t\t\t!(s as hbs.AST.MustacheStatement).hash),\n\t);\n}\n\n// ─── Détection de littéraux dans le contenu textuel ──────────────────────────\n// Quand un programme ne contient que des ContentStatements (pas d'expressions),\n// on essaie de détecter si le texte concaténé et trimé est un littéral typé\n// (nombre, booléen, null). Cela permet d'inférer correctement le type de\n// branches comme `{{#if x}} 42 {{/if}}`.\n\n/**\n * Tente de détecter le type d'un littéral textuel brut.\n *\n * @param text - Le texte trimé d'un ContentStatement ou d'un groupe de ContentStatements\n * @returns Le type JSON Schema détecté, ou `null` si c'est du texte libre (string).\n */\nexport function detectLiteralType(\n\ttext: string,\n): \"number\" | \"boolean\" | \"null\" | null {\n\tif (NUMERIC_LITERAL_RE.test(text)) return \"number\";\n\tif (text === \"true\" || text === \"false\") return \"boolean\";\n\tif (text === \"null\") return \"null\";\n\treturn null;\n}\n\n/**\n * Coerce une string brute issue du rendu Handlebars vers son type réel\n * si elle représente un littéral (number, boolean, null).\n * Retourne la string trimée sinon.\n */\nexport function coerceLiteral(raw: string): unknown {\n\tconst trimmed = raw.trim();\n\tconst type = detectLiteralType(trimmed);\n\tif (type === \"number\") return Number(trimmed);\n\tif (type === \"boolean\") return trimmed === \"true\";\n\tif (type === \"null\") return null;\n\t// Pas un littéral typé → on retourne la string brute sans la trimer,\n\t// car le whitespace peut être significatif (ex: résultat d'un #each).\n\treturn raw;\n}\n\n// ─── Template Identifier Parsing ─────────────────────────────────────────────\n// Syntaxe `{{key:N}}` où N est un entier positif ou zéro.\n// L'identifiant permet de résoudre une variable depuis une source de données\n// spécifique (ex: un nœud de workflow identifié par son numéro).\n\n/** Résultat du parsing d'un segment de chemin avec identifiant potentiel */\nexport interface ParsedIdentifier {\n\t/** Le nom de la variable, sans le suffixe `:N` */\n\tkey: string;\n\t/** L'identifiant numérique, ou `null` si absent */\n\tidentifier: number | null;\n}\n\n/**\n * Parse un segment de chemin individuel pour en extraire la clé et\n * l'identifiant optionnel.\n *\n * @param segment - Un segment de chemin brut (ex: `\"meetingId:1\"` ou `\"meetingId\"`)\n * @returns Un objet `{ key, identifier }`\n *\n * @example\n * ```\n * parseIdentifier(\"meetingId:1\") // → { key: \"meetingId\", identifier: 1 }\n * parseIdentifier(\"meetingId\") // → { key: \"meetingId\", identifier: null }\n * parseIdentifier(\"meetingId:0\") // → { key: \"meetingId\", identifier: 0 }\n * ```\n */\nexport function parseIdentifier(segment: string): ParsedIdentifier {\n\tconst match = segment.match(IDENTIFIER_RE);\n\tif (match) {\n\t\treturn {\n\t\t\tkey: match[1] ?? segment,\n\t\t\tidentifier: parseInt(match[2] ?? \"0\", 10),\n\t\t};\n\t}\n\treturn { key: segment, identifier: null };\n}\n\n/** Résultat de l'extraction de l'identifiant sur une expression complète */\nexport interface ExpressionIdentifier {\n\t/** Segments de chemin nettoyés (sans le suffixe `:N` sur le dernier) */\n\tcleanSegments: string[];\n\t/** L'identifiant numérique extrait du dernier segment, ou `null` */\n\tidentifier: number | null;\n}\n\n/**\n * Extrait l'identifiant d'une expression complète (tableau de segments).\n *\n * L'identifiant est toujours sur le **dernier** segment du chemin, car\n * Handlebars split sur les `.` avant le `:`.\n *\n * @param segments - Les segments bruts du chemin (ex: `[\"user\", \"name:1\"]`)\n * @returns Un objet `{ cleanSegments, identifier }`\n *\n * @example\n * ```\n * extractExpressionIdentifier([\"meetingId:1\"])\n * // → { cleanSegments: [\"meetingId\"], identifier: 1 }\n *\n * extractExpressionIdentifier([\"user\", \"name:1\"])\n * // → { cleanSegments: [\"user\", \"name\"], identifier: 1 }\n *\n * extractExpressionIdentifier([\"meetingId\"])\n * // → { cleanSegments: [\"meetingId\"], identifier: null }\n * ```\n */\nexport function extractExpressionIdentifier(\n\tsegments: string[],\n): ExpressionIdentifier {\n\tif (segments.length === 0) {\n\t\treturn { cleanSegments: [], identifier: null };\n\t}\n\n\tconst lastSegment = segments[segments.length - 1] as string;\n\tconst parsed = parseIdentifier(lastSegment);\n\n\tif (parsed.identifier !== null) {\n\t\tconst cleanSegments = [...segments.slice(0, -1), parsed.key];\n\t\treturn { cleanSegments, identifier: parsed.identifier };\n\t}\n\n\treturn { cleanSegments: segments, identifier: null };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "3C85E965B62779F664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{e as X,g as Y,h as Z,i as _,j as $,k,l as A,o as B}from"./chunk-9mg6qfrs.js";import{u as Q,v as R,w as U}from"./chunk-qh2r1pa1.js";function g(b,j,w){let q=X(b),z={root:j,current:j,diagnostics:[],identifierSchemas:w},F=O(q,z);return{valid:!z.diagnostics.some((K)=>K.severity==="error"),diagnostics:z.diagnostics,outputSchema:U(F)}}function C(b,j){for(let w of b.body)P(w,j)}function P(b,j){switch(b.type){case"ContentStatement":break;case"MustacheStatement":D(b,j);break;case"BlockStatement":N(b,j);break;case"CommentStatement":break;default:H(j,"warning",`Unsupported AST node type: "${b.type}"`,b)}}function D(b,j){if(b.path.type==="SubExpression"){H(j,"warning","Sub-expressions are not statically analyzable",b);return}L(b.path,j,b)}function M(b){return b.params[0]}function N(b,j){let w=V(b);switch(w){case"if":case"unless":W(b,j);break;case"each":T(b,j);break;case"with":u(b,j);break;default:if(H(j,"warning",`Unknown block helper "{{#${w}}}" — cannot analyze statically`,b),C(b.program,j),b.inverse)C(b.inverse,j)}}function W(b,j){let w=M(b);if(w)L(w,j,b);else H(j,"error",`"{{#${V(b)}}}" requires an argument`,b);if(C(b.program,j),b.inverse)C(b.inverse,j)}function T(b,j){let w=M(b);if(!w){if(H(j,"error",'"{{#each}}" requires an argument',b),C(b.program,{...j,current:{}}),b.inverse)C(b.inverse,j);return}let q=L(w,j,b);if(!q){if(C(b.program,{...j,current:{}}),b.inverse)C(b.inverse,j);return}let z=R(q,j.root);if(!z){if(H(j,"error",`"{{#each}}" expects an array, but resolved schema has type "${I(q)}"`,b),C(b.program,{...j,current:{}}),b.inverse)C(b.inverse,j);return}if(C(b.program,{...j,current:z}),b.inverse)C(b.inverse,j)}function u(b,j){let w=M(b);if(!w){if(H(j,"error",'"{{#with}}" requires an argument',b),C(b.program,{...j,current:{}}),b.inverse)C(b.inverse,j);return}let q=L(w,j,b);if(!q){if(C(b.program,{...j,current:{}}),b.inverse)C(b.inverse,j);return}if(C(b.program,{...j,current:q}),b.inverse)C(b.inverse,j)}function E(b,j){return L(b,j)??{}}function O(b,j){let w=_(b);if(w.length===0)return{type:"string"};let q=k(b);if(q)return E(q.path,j);let z=$(b);if(z)return y(z,j);if(w.every((G)=>G.type==="ContentStatement")){let G=w.map((J)=>J.value).join("").trim();if(G==="")return{type:"string"};let K=A(G);if(K)return{type:K}}return C(b,j),{type:"string"}}function y(b,j){switch(V(b)){case"if":case"unless":{let q=M(b);if(q)L(q,j,b);let z=O(b.program,j);if(b.inverse){let F=O(b.inverse,j),G=JSON.stringify(z),K=JSON.stringify(F);if(G===K)return z;return U({oneOf:[z,F]})}return z}case"each":{let q=M(b);if(q){let z=L(q,j,b);if(z){let F=R(z,j.root);if(F)C(b.program,{...j,current:F});else H(j,"error",`"{{#each}}" expects an array, but resolved schema has type "${I(z)}"`,b)}}return{type:"string"}}case"with":{let q=M(b);if(q){let z=L(q,j,b);if(z)return O(b.program,{...j,current:z})}return O(b.program,{...j,current:{}})}default:{if(C(b.program,j),b.inverse)C(b.inverse,j);return{type:"string"}}}}function L(b,j,w){if(Z(b))return j.current;let q=Y(b);if(q.length===0){if(b.type==="StringLiteral")return{type:"string"};if(b.type==="NumberLiteral")return{type:"number"};if(b.type==="BooleanLiteral")return{type:"boolean"};if(b.type==="NullLiteral")return{type:"null"};if(b.type==="UndefinedLiteral")return{};H(j,"warning",`Expression of type "${b.type}" cannot be statically analyzed`,w??b);return}let{cleanSegments:z,identifier:F}=B(q);if(F!==null)return v(z,F,j,w??b);let G=Q(j.current,z);if(G===void 0){let K=z.join(".");H(j,"error",`Property "${K}" does not exist in the context schema`,w??b);return}return G}function v(b,j,w,q){let z=b.join(".");if(!w.identifierSchemas){H(w,"error",`Property "${z}:${j}" uses an identifier but no identifier schemas were provided`,q);return}let F=w.identifierSchemas[j];if(!F){H(w,"error",`Property "${z}:${j}" references identifier ${j} but no schema exists for this identifier`,q);return}let G=Q(F,b);if(G===void 0){H(w,"error",`Property "${z}" does not exist in the schema for identifier ${j}`,q);return}return G}function V(b){if(b.path.type==="PathExpression")return b.path.original;return""}function H(b,j,w,q){let z={severity:j,message:w};if(q&&"loc"in q&&q.loc)z.loc={start:{line:q.loc.start.line,column:q.loc.start.column},end:{line:q.loc.end.line,column:q.loc.end.column}};b.diagnostics.push(z)}function I(b){if(b.type)return Array.isArray(b.type)?b.type.join(" | "):b.type;if(b.oneOf)return"oneOf(...)";if(b.anyOf)return"anyOf(...)";if(b.allOf)return"allOf(...)";if(b.enum)return"enum";return"unknown"}
|
|
2
|
+
export{g as a,y as b};
|
|
3
|
+
|
|
4
|
+
//# debugId=BE231BA8C982D31264756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-hc1jnqaw.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/analyzer.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\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\tJSONSchema7,\n\tTemplateDiagnostic,\n} from \"./types.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// Deux responsabilités :\n// 1. **Validation** — vérifier que chaque référence du template existe dans le\n// schema et que les constructions (if, each, with) sont utilisées sur des\n// types compatibles.\n// 2. **Inférence du type de retour** — produire un JSON Schema décrivant le\n// type de la valeur retournée par l'exécution du template.\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) */\n\tcurrent: JSONSchema7;\n\t/** Accumulateur de diagnostics */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** Schemas par identifiant de template (pour la syntaxe {{key:N}}) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\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 * @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: string,\n\tinputSchema: JSONSchema7,\n\tidentifierSchemas?: Record<number, JSONSchema7>,\n): AnalysisResult {\n\tconst ast = parse(template);\n\n\tconst ctx: AnalysisContext = {\n\t\troot: inputSchema,\n\t\tcurrent: inputSchema,\n\t\tdiagnostics: [],\n\t\tidentifierSchemas,\n\t};\n\n\t// Inférer le type de sortie en parcourant l'AST.\n\t// On distingue 3 cas (du plus spécifique au plus général) :\n\t//\n\t// 1. Expression unique `{{expr}}`\n\t// → le type de retour est celui de l'expression (number, object, …)\n\t//\n\t// 2. Bloc unique `{{#if …}}…{{/if}}` (éventuellement entouré de whitespace)\n\t// → on délègue à `inferBlockType` qui analyse les branches\n\t//\n\t// 3. Template mixte (texte + expressions, blocs multiples, …)\n\t// → le résultat est toujours une string (concaténation)\n\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 de l'AST (validation) ──────────────────────────────────────────\n// Ces fonctions parcourent l'AST pour valider les références et collecter\n// les diagnostics. Elles ne retournent pas de type — c'est le rôle des\n// fonctions `infer*`.\n\n/**\n * Valide un `Program` (corps d'un template ou d'un bloc).\n * Parcourt séquentiellement chaque statement.\n */\nfunction validateProgram(program: hbs.AST.Program, ctx: AnalysisContext): void {\n\tfor (const stmt of program.body) {\n\t\tvalidateStatement(stmt, ctx);\n\t}\n}\n\n/**\n * Dispatche la validation d'un statement selon son type AST.\n */\nfunction validateStatement(\n\tstmt: hbs.AST.Statement,\n\tctx: AnalysisContext,\n): void {\n\tswitch (stmt.type) {\n\t\tcase \"ContentStatement\":\n\t\t\t// Texte statique — rien à valider\n\t\t\tbreak;\n\n\t\tcase \"MustacheStatement\":\n\t\t\tvalidateMustache(stmt as hbs.AST.MustacheStatement, ctx);\n\t\t\tbreak;\n\n\t\tcase \"BlockStatement\":\n\t\t\tvalidateBlock(stmt as hbs.AST.BlockStatement, ctx);\n\t\t\tbreak;\n\n\t\tcase \"CommentStatement\":\n\t\t\t// Commentaire Handlebars {{!-- ... --}} — ignoré\n\t\t\tbreak;\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\"warning\",\n\t\t\t\t`Unsupported AST node type: \"${stmt.type}\"`,\n\t\t\t\tstmt,\n\t\t\t);\n\t}\n}\n\n/**\n * Valide une expression moustache `{{expression}}`.\n * Vérifie que le chemin référencé existe dans le schema courant.\n */\nfunction validateMustache(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): void {\n\t// Les SubExpressions (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\"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\tresolveExpressionWithDiagnostics(stmt.path, ctx, stmt);\n}\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 * Valide un bloc `{{#helper}}...{{/helper}}`.\n * Supporte : `if`, `unless`, `each`, `with`.\n */\nfunction validateBlock(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): void {\n\tconst helperName = getBlockHelperName(stmt);\n\n\tswitch (helperName) {\n\t\tcase \"if\":\n\t\tcase \"unless\":\n\t\t\tvalidateIfBlock(stmt, ctx);\n\t\t\tbreak;\n\n\t\tcase \"each\":\n\t\t\tvalidateEachBlock(stmt, ctx);\n\t\t\tbreak;\n\n\t\tcase \"with\":\n\t\t\tvalidateWithBlock(stmt, ctx);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"warning\",\n\t\t\t\t`Unknown block helper \"{{#${helperName}}}\" — cannot analyze statically`,\n\t\t\t\tstmt,\n\t\t\t);\n\t\t\t// On valide quand même le corps avec le contexte courant (best-effort)\n\t\t\tvalidateProgram(stmt.program, ctx);\n\t\t\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n\t}\n}\n\n/**\n * Valide un bloc `{{#if condition}}...{{else}}...{{/if}}`.\n * La condition doit référencer un chemin valide dans le schema.\n */\nfunction validateIfBlock(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): void {\n\tconst arg = getBlockArgument(stmt);\n\tif (arg) {\n\t\t// Valider que la condition référence un chemin existant\n\t\tresolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t} else {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"error\",\n\t\t\t`\"{{#${getBlockHelperName(stmt)}}}\" requires an argument`,\n\t\t\tstmt,\n\t\t);\n\t}\n\n\t// Le contexte ne change pas dans un if/unless — on valide les deux branches\n\tvalidateProgram(stmt.program, ctx);\n\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n}\n\n/**\n * Valide un bloc `{{#each items}}...{{/each}}`.\n * `items` doit référencer un tableau dans le schema.\n * Le corps du bloc est validé avec le schema des éléments du tableau.\n */\nfunction validateEachBlock(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): void {\n\tconst arg = getBlockArgument(stmt);\n\tif (!arg) {\n\t\taddDiagnostic(ctx, \"error\", `\"{{#each}}\" requires an argument`, stmt);\n\t\tvalidateProgram(stmt.program, { ...ctx, current: {} });\n\t\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n\t\treturn;\n\t}\n\n\tconst schema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\tif (!schema) {\n\t\t// Le chemin n'a pas pu être résolu — on a déjà émis un diagnostic.\n\t\t// On valide le corps avec un schema vide (best-effort).\n\t\tvalidateProgram(stmt.program, { ...ctx, current: {} });\n\t\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n\t\treturn;\n\t}\n\n\t// Résoudre le schema des éléments du tableau\n\tconst itemSchema = resolveArrayItems(schema, ctx.root);\n\tif (!itemSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"error\",\n\t\t\t`\"{{#each}}\" expects an array, but resolved schema has type \"${schemaTypeLabel(schema)}\"`,\n\t\t\tstmt,\n\t\t);\n\t\tvalidateProgram(stmt.program, { ...ctx, current: {} });\n\t\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n\t\treturn;\n\t}\n\n\t// Valider le corps avec le schema des éléments comme contexte\n\tvalidateProgram(stmt.program, { ...ctx, current: itemSchema });\n\n\t// La branche inverse ({{else}}) garde le contexte parent\n\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n}\n\n/**\n * Valide un bloc `{{#with object}}...{{/with}}`.\n * `object` doit référencer un objet dans le schema.\n * Le corps du bloc est validé avec ce sous-schema comme contexte.\n */\nfunction validateWithBlock(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): void {\n\tconst arg = getBlockArgument(stmt);\n\tif (!arg) {\n\t\taddDiagnostic(ctx, \"error\", `\"{{#with}}\" requires an argument`, stmt);\n\t\tvalidateProgram(stmt.program, { ...ctx, current: {} });\n\t\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n\t\treturn;\n\t}\n\n\tconst schema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\tif (!schema) {\n\t\tvalidateProgram(stmt.program, { ...ctx, current: {} });\n\t\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n\t\treturn;\n\t}\n\n\t// Valider le corps avec le sous-schema comme nouveau contexte\n\tvalidateProgram(stmt.program, { ...ctx, current: schema });\n\tif (stmt.inverse) validateProgram(stmt.inverse, ctx);\n}\n\n// ─── Inférence de type ───────────────────────────────────────────────────────\n// Ces fonctions déterminent le JSON Schema de sortie du template.\n\nfunction inferExpressionType(\n\texpr: hbs.AST.Expression,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst schema = resolveExpressionWithDiagnostics(expr, ctx);\n\t// Si le chemin n'a pas pu être résolu, on retourne un schema vide (unknown).\n\t// Le diagnostic a déjà été émis par resolveExpressionWithDiagnostics.\n\treturn schema ?? {};\n}\n\n/**\n * Infère le type de sortie d'un `Program` (corps d'un template ou d'un bloc).\n *\n * - Si le programme est vide → `{ type: \"string\" }` (string vide)\n * - Si le programme contient un seul MustacheStatement → type de l'expression\n * - Sinon → `{ type: \"string\" }` (concaténation)\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 inferExpressionType(singleExpr.path, 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 valide pour collecter les diagnostics, le résultat est toujours string.\n\tvalidateProgram(program, ctx);\n\treturn { type: \"string\" };\n}\n\n/**\n * Infère le type de sortie d'un bloc `{{#if}}...{{else}}...{{/if}}`.\n * Le résultat est l'union des types des deux branches.\n */\nexport function inferBlockType(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = getBlockHelperName(stmt);\n\n\tswitch (helperName) {\n\t\tcase \"if\":\n\t\tcase \"unless\": {\n\t\t\t// Valider la condition (argument du bloc, pas le nom du helper)\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (arg) {\n\t\t\t\tresolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\t}\n\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// Union des deux branches\n\t\t\t\tconst thenJson = JSON.stringify(thenType);\n\t\t\t\tconst elseJson = JSON.stringify(elseType);\n\t\t\t\tif (thenJson === elseJson) return thenType;\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 peut être undefined (string vide\n\t\t\t// en Handlebars, mais conceptuellement c'est optionnel).\n\t\t\treturn thenType;\n\t\t}\n\n\t\tcase \"each\": {\n\t\t\t// Valider et résoudre le schema de la collection (argument du bloc)\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (arg) {\n\t\t\t\tconst collectionSchema = resolveExpressionWithDiagnostics(\n\t\t\t\t\targ,\n\t\t\t\t\tctx,\n\t\t\t\t\tstmt,\n\t\t\t\t);\n\t\t\t\tif (collectionSchema) {\n\t\t\t\t\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\t\t\t\t\tif (itemSchema) {\n\t\t\t\t\t\t// Valider le corps avec le contexte des éléments\n\t\t\t\t\t\tvalidateProgram(stmt.program, { ...ctx, current: itemSchema });\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// La cible n'est pas un tableau — même diagnostic que validateEachBlock\n\t\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t`\"{{#each}}\" expects an array, but resolved schema has type \"${schemaTypeLabel(collectionSchema)}\"`,\n\t\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Un each concatène les rendus → toujours string\n\t\t\treturn { type: \"string\" };\n\t\t}\n\n\t\tcase \"with\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (arg) {\n\t\t\t\tconst innerSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\t\tif (innerSchema) {\n\t\t\t\t\treturn inferProgramType(stmt.program, {\n\t\t\t\t\t\t...ctx,\n\t\t\t\t\t\tcurrent: innerSchema,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn inferProgramType(stmt.program, { ...ctx, current: {} });\n\t\t}\n\n\t\tdefault: {\n\t\t\t// Helper inconnu — on ne peut rien inférer\n\t\t\tvalidateProgram(stmt.program, ctx);\n\t\t\tif (stmt.inverse) validateProgram(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\"warning\",\n\t\t\t`Expression of type \"${expr.type}\" cannot be statically analyzed`,\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\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}\" does not exist in the context schema`,\n\t\t\tparentNode ?? expr,\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\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" uses an identifier but no identifier schemas were provided`,\n\t\t\tnode,\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\"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);\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\taddDiagnostic(\n\t\t\tctx,\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);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n// ─── Utilitaires ─────────────────────────────────────────────────────────────\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 * Ajoute un diagnostic au contexte d'analyse.\n */\nfunction addDiagnostic(\n\tctx: AnalysisContext,\n\tseverity: \"error\" | \"warning\",\n\tmessage: string,\n\tnode?: hbs.AST.Node,\n): void {\n\tconst diagnostic: TemplateDiagnostic = { severity, message };\n\n\t// Extraire la position si disponible dans le nœud AST\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}\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"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "BE231BA8C982D31264756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{d as N}from"./chunk-1qwj7pjc.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-kznb0bev.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,5 @@
|
|
|
1
|
+
import{P as M}from"./chunk-fhvf5y4x.js";function G(A,z){if(!A.$ref)return A;let w=A.$ref,j=w.match(/^#\/(definitions|\$defs)\/(.+)$/);if(!j)throw Error(`Unsupported $ref format: "${w}". Only internal #/definitions/ references are supported.`);let B=j[1],D=j[2]??"",F=B==="definitions"?z.definitions:z.$defs;if(!F||!(D in F))throw Error(`Cannot resolve $ref "${w}": definition "${D}" not found.`);let H=F[D];if(!H||typeof H==="boolean")throw Error(`Cannot resolve $ref "${w}": definition "${D}" not found.`);return G(H,z)}function L(A,z,w){let j=G(A,w);if(j.properties&&z in j.properties){let H=j.properties[z];if(H&&typeof H!=="boolean")return G(H,w);if(H===!0)return{}}if(j.additionalProperties!==void 0&&j.additionalProperties!==!1){if(j.additionalProperties===!0)return{};return G(j.additionalProperties,w)}let B=j.type;if((B==="array"||Array.isArray(B)&&B.includes("array"))&&z==="length")return{type:"integer"};let F=N(j,z,w);if(F)return F;return}function N(A,z,w){if(A.allOf){let j=A.allOf.filter((B)=>typeof B!=="boolean").map((B)=>L(B,z,w)).filter((B)=>B!==void 0);if(j.length===1)return j[0];if(j.length>1)return{allOf:j}}for(let j of["anyOf","oneOf"]){if(!A[j])continue;let B=A[j].filter((D)=>typeof D!=="boolean").map((D)=>L(D,z,w)).filter((D)=>D!==void 0);if(B.length===1)return B[0];if(B.length>1)return{[j]:B}}return}function P(A,z){if(z.length===0)return G(A,A);let w=G(A,A),j=A;for(let B of z){let D=L(w,B,j);if(D===void 0)return;w=D}return w}function Q(A,z){let w=G(A,z),j=w.type;if(!(j==="array"||Array.isArray(j)&&j.includes("array"))&&w.items===void 0)return;if(w.items===void 0)return{};if(typeof w.items==="boolean")return{};if(Array.isArray(w.items)){let D=w.items.filter((F)=>typeof F!=="boolean").map((F)=>G(F,z));if(D.length===0)return{};return{oneOf:D}}return G(w.items,z)}function J(A){for(let z of["oneOf","anyOf"]){let w=A[z];if(w&&w.length===1){let j=w[0];if(j!==void 0&&typeof j!=="boolean")return J(j)}}if(A.allOf&&A.allOf.length===1){let z=A.allOf[0];if(z!==void 0&&typeof z!=="boolean")return J(z)}for(let z of["oneOf","anyOf"]){let w=A[z];if(w&&w.length>1){let j=[];for(let B of w){if(typeof B==="boolean")continue;if(!j.some((F)=>M(F,B)))j.push(J(B))}if(j.length===1)return j[0];return{...A,[z]:j}}}return A}
|
|
2
|
+
export{G as L,P as M,Q as N,J as O};
|
|
3
|
+
|
|
4
|
+
//# debugId=0F300C79E490F4DC64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-mx8neh7q.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/schema-resolver.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { JSONSchema7 } from \"json-schema\";\nimport { deepEqual } from \"./utils.ts\";\n\n// ─── JSON Schema Resolver ────────────────────────────────────────────────────\n// Utilitaire pour naviguer dans un JSON Schema Draft v7 en suivant un chemin\n// de propriétés (ex: [\"user\", \"address\", \"city\"]).\n//\n// Gère :\n// - La résolution de `$ref` (références internes #/definitions/...)\n// - La navigation dans `properties`\n// - La navigation dans `items` (éléments d'un tableau)\n// - Les combinateurs `allOf`, `anyOf`, `oneOf` (recherche dans chaque branche)\n// - `additionalProperties` quand la propriété n'est pas déclarée\n\n// ─── Résolution de $ref ──────────────────────────────────────────────────────\n// Supporte uniquement les références internes au format `#/definitions/Foo`\n// ou `#/$defs/Foo` (JSON Schema Draft 2019+). Les $ref distantes (URL) ne\n// sont pas prises en charge — ce n'est pas le rôle d'un moteur de template.\n\n/**\n * Résout récursivement les `$ref` d'un schema en utilisant le schema racine\n * comme source de définitions.\n */\nexport function resolveRef(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 {\n\tif (!schema.$ref) return schema;\n\n\tconst ref = schema.$ref;\n\n\t// Format attendu : #/definitions/Name ou #/$defs/Name\n\tconst match = ref.match(/^#\\/(definitions|\\$defs)\\/(.+)$/);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`Unsupported $ref format: \"${ref}\". Only internal #/definitions/ references are supported.`,\n\t\t);\n\t}\n\n\tconst defsKey = match[1] as \"definitions\" | \"$defs\";\n\tconst name = match[2] ?? \"\";\n\n\tconst defs = defsKey === \"definitions\" ? root.definitions : root.$defs;\n\n\tif (!defs || !(name in defs)) {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\n\t// Résolution récursive au cas où la définition elle-même contient un $ref\n\tconst def = defs[name];\n\tif (!def || typeof def === \"boolean\") {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\treturn resolveRef(def, root);\n}\n\n// ─── Navigation par segment de chemin ────────────────────────────────────────\n\n/**\n * Résout un seul segment de chemin (un nom de propriété) dans un schema.\n * Retourne le sous-schema correspondant ou `undefined` si le chemin est invalide.\n *\n * @param schema - Le schema courant (déjà résolu, sans $ref)\n * @param segment - Le nom de la propriété à résoudre\n * @param root - Le schema racine (pour résoudre d'éventuels $ref internes)\n */\nfunction resolveSegment(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// 1. Propriétés explicites\n\tif (resolved.properties && segment in resolved.properties) {\n\t\tconst prop = resolved.properties[segment];\n\t\tif (prop && typeof prop !== \"boolean\") return resolveRef(prop, root);\n\t\tif (prop === true) return {};\n\t}\n\n\t// 2. additionalProperties (quand la propriété n'est pas déclarée)\n\tif (\n\t\tresolved.additionalProperties !== undefined &&\n\t\tresolved.additionalProperties !== false\n\t) {\n\t\tif (resolved.additionalProperties === true) {\n\t\t\t// additionalProperties: true → on ne sait rien du type\n\t\t\treturn {};\n\t\t}\n\t\treturn resolveRef(resolved.additionalProperties, root);\n\t}\n\n\t// 3. Propriétés intrinsèques des tableaux (ex: `.length`)\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (isArray && segment === \"length\") {\n\t\treturn { type: \"integer\" };\n\t}\n\n\t// 4. Combinateurs — on cherche dans chaque branche\n\tconst combinatorResult = resolveInCombinators(resolved, segment, root);\n\tif (combinatorResult) return combinatorResult;\n\n\treturn undefined;\n}\n\n/**\n * Cherche un segment dans les branches `allOf`, `anyOf`, `oneOf`.\n * Retourne le premier sous-schema trouvé ou `undefined`.\n * Pour `allOf`, on fusionne les résultats trouvés en un `allOf`.\n */\nfunction resolveInCombinators(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\t// allOf : la propriété peut être définie dans n'importe quelle branche,\n\t// et toutes les contraintes s'appliquent simultanément.\n\tif (schema.allOf) {\n\t\tconst matches = schema.allOf\n\t\t\t.filter((b): b is JSONSchema7 => typeof b !== \"boolean\")\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { allOf: matches };\n\t}\n\n\t// anyOf / oneOf : la propriété peut venir de n'importe quelle branche.\n\tfor (const key of [\"anyOf\", \"oneOf\"] as const) {\n\t\tif (!schema[key]) continue;\n\t\tconst matches = schema[key]\n\t\t\t.filter((b): b is JSONSchema7 => typeof b !== \"boolean\")\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { [key]: matches };\n\t}\n\n\treturn undefined;\n}\n\n// ─── API publique ────────────────────────────────────────────────────────────\n\n/**\n * Résout un chemin complet (ex: [\"user\", \"address\", \"city\"]) dans un JSON\n * Schema et retourne le sous-schema correspondant.\n *\n * @param schema - Le schema racine décrivant le contexte du template\n * @param path - Tableau de segments (noms de propriétés)\n * @returns Le sous-schema au bout du chemin, ou `undefined` si le chemin\n * ne peut pas être résolu.\n *\n * @example\n * ```\n * const schema = {\n * type: \"object\",\n * properties: {\n * user: {\n * type: \"object\",\n * properties: {\n * name: { type: \"string\" }\n * }\n * }\n * }\n * };\n * resolveSchemaPath(schema, [\"user\", \"name\"]);\n * // → { type: \"string\" }\n * ```\n */\nexport function resolveSchemaPath(\n\tschema: JSONSchema7,\n\tpath: string[],\n): JSONSchema7 | undefined {\n\tif (path.length === 0) return resolveRef(schema, schema);\n\n\tlet current: JSONSchema7 = resolveRef(schema, schema);\n\tconst root = schema;\n\n\tfor (const segment of path) {\n\t\tconst next = resolveSegment(current, segment, root);\n\t\tif (next === undefined) return undefined;\n\t\tcurrent = next;\n\t}\n\n\treturn current;\n}\n\n/**\n * Résout le schema des éléments d'un tableau.\n * Si le schema n'est pas de type `array` ou n'a pas de `items`, retourne `undefined`.\n *\n * @param schema - Le schema d'un tableau\n * @param root - Le schema racine (pour résoudre les $ref)\n */\nexport function resolveArrayItems(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// Vérification que c'est bien un tableau\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (!isArray && resolved.items === undefined) {\n\t\treturn undefined;\n\t}\n\n\tif (resolved.items === undefined) {\n\t\t// array sans items → éléments de type inconnu\n\t\treturn {};\n\t}\n\n\t// items peut être un boolean (true = anything, false = nothing)\n\tif (typeof resolved.items === \"boolean\") {\n\t\treturn {};\n\t}\n\n\t// items peut être un schema unique ou un tuple (tableau de schemas).\n\t// Pour les boucles de template, on traite le cas d'un schema unique.\n\tif (Array.isArray(resolved.items)) {\n\t\t// Tuple : on crée un oneOf de tous les types possibles\n\t\tconst schemas = resolved.items\n\t\t\t.filter((item): item is JSONSchema7 => typeof item !== \"boolean\")\n\t\t\t.map((item) => resolveRef(item, root));\n\t\tif (schemas.length === 0) return {};\n\t\treturn { oneOf: schemas };\n\t}\n\n\treturn resolveRef(resolved.items, root);\n}\n\n/**\n * Simplifie un schema de sortie pour éviter les constructions inutilement\n * complexes (ex: `oneOf` avec un seul élément, doublons, etc.).\n *\n * Utilise `deepEqual` pour la déduplication — plus robuste et performant\n * que `JSON.stringify` (indépendant de l'ordre des clés, sans allocation\n * de strings intermédiaires).\n */\nexport function simplifySchema(schema: JSONSchema7): JSONSchema7 {\n\t// oneOf / anyOf avec un seul élément → on déplie\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tconst arr = schema[key];\n\t\tif (arr && arr.length === 1) {\n\t\t\tconst first = arr[0];\n\t\t\tif (first !== undefined && typeof first !== \"boolean\")\n\t\t\t\treturn simplifySchema(first);\n\t\t}\n\t}\n\n\t// allOf avec un seul élément → on déplie\n\tif (schema.allOf && schema.allOf.length === 1) {\n\t\tconst first = schema.allOf[0];\n\t\tif (first !== undefined && typeof first !== \"boolean\")\n\t\t\treturn simplifySchema(first);\n\t}\n\n\t// Déduplique les entrées identiques dans oneOf/anyOf\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tconst arr = schema[key];\n\t\tif (arr && arr.length > 1) {\n\t\t\tconst unique: JSONSchema7[] = [];\n\t\t\tfor (const entry of arr) {\n\t\t\t\tif (typeof entry === \"boolean\") continue;\n\t\t\t\t// Utilisation de deepEqual au lieu de JSON.stringify pour la\n\t\t\t\t// comparaison structurelle — plus robuste (ordre des clés) et\n\t\t\t\t// plus performant (pas d'allocation de strings).\n\t\t\t\tconst isDuplicate = unique.some((existing) =>\n\t\t\t\t\tdeepEqual(existing, entry),\n\t\t\t\t);\n\t\t\t\tif (!isDuplicate) {\n\t\t\t\t\tunique.push(simplifySchema(entry));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (unique.length === 1) return unique[0] as JSONSchema7;\n\t\t\treturn { ...schema, [key]: unique };\n\t\t}\n\t}\n\n\treturn schema;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "0F300C79E490F4DC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{j as T,k as y}from"./chunk-6955jpr7.js";import{A as L,n as j,q as w,r as O,s as k,u as F,v as R,w as U,y as _}from"./chunk-ggdfaqhe.js";import{E as A}from"./chunk-vka4e61h.js";import{Q as V}from"./chunk-fhvf5y4x.js";import P from"handlebars";var C=new V(128);function I(q,M,z){if(y(q))return S(q,M,z);if(T(q))return q;let G=j(q);return W(G,q,M,{identifierData:z})}function S(q,M,z){let G={};for(let[K,J]of Object.entries(q))G[K]=I(J,M,z);return G}function W(q,M,z,G){let K=G?.identifierData;if(w(q)){let N=q.body[0];if(N.params.length===0&&!N.hash)return $(N.path,z,K)}let J=R(q);if(J&&J.params.length===0&&!J.hash)return $(J.path,z,K);if(J&&(J.params.length>0||J.hash)){let N=Y(z,K),X=Z(M,N,G);return _(X)}if(U(q)&&q.body.length>1)return v(q,z,K);if(F(q)){let N=Y(z,K),X=Z(M,N,G);return _(X)}let H=Y(z,K);return Z(M,H,G)}function v(q,M,z){let G="";for(let K of q.body)if(K.type==="ContentStatement")G+=K.value;else if(K.type==="MustacheStatement"){let J=$(K.path,M,z);if(J!=null)G+=String(J)}return G}function $(q,M,z){if(k(q))return M;if(q.type==="StringLiteral")return q.value;if(q.type==="NumberLiteral")return q.value;if(q.type==="BooleanLiteral")return q.value;if(q.type==="NullLiteral")return null;if(q.type==="UndefinedLiteral")return;let G=O(q);if(G.length===0)throw new A(`Cannot resolve expression of type "${q.type}"`);let{cleanSegments:K,identifier:J}=L(G);if(J!==null&&z){let Q=z[J];if(Q)return B(Q,K);return}if(J!==null&&!z)return;return B(M,K)}function B(q,M){let z=q;for(let G of M){if(z===null||z===void 0)return;if(typeof z!=="object")return;z=z[G]}return z}function Y(q,M){if(!M)return q;let z={...q};for(let[G,K]of Object.entries(M))for(let[J,Q]of Object.entries(K))z[`${J}:${G}`]=Q;return z}function Z(q,M,z){try{if(z?.compiledTemplate)return z.compiledTemplate(M);let G=z?.compilationCache??C,K=z?.hbs??P,J=G.get(q);if(!J)J=K.compile(q,{noEscape:!0,strict:!1}),G.set(q,J);return J(M)}catch(G){let K=G instanceof Error?G.message:String(G);throw new A(K)}}function g(){C.clear()}
|
|
2
|
+
export{I as f,W as g,B as h,g as i};
|
|
3
|
+
|
|
4
|
+
//# debugId=22DB3A05EDDFCF4C64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-p3xzf1ew.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/executor.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import Handlebars from \"handlebars\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport type { TemplateInput, TemplateInputObject } from \"./types.ts\";\nimport { isLiteralInput, isObjectInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Exécute un template Handlebars avec des données réelles.\n//\n// Quatre modes d'exécution (du plus rapide au plus général) :\n//\n// 1. **Expression unique** (`{{value}}` ou ` {{value}} `) → retourne la\n// valeur brute sans conversion en string. Cela permet de préserver le type\n// original (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (texte + expressions simples, pas de blocs ni helpers) →\n// concaténation directe sans passer par Handlebars.compile(). Jusqu'à\n// 10-100x plus rapide pour les templates simples comme `Hello {{name}}`.\n//\n// 3. **Bloc unique** (`{{#if x}}10{{else}}20{{/if}}` éventuellement entouré\n// de whitespace) → rendu via Handlebars puis coercion intelligente du\n// résultat (détection de littéraux number, boolean, null).\n//\n// 4. **Template mixte** (texte + blocs multiples, helpers, …) →\n// délègue à Handlebars qui produit toujours une string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Les templates compilés par Handlebars sont cachés dans un LRU cache pour\n// éviter la recompilation coûteuse lors d'appels répétés.\n//\n// Deux niveaux de cache :\n// - **Cache global** (module-level) pour les fonctions standalone `execute()`\n// - **Cache d'instance** pour `Typebars` (passé via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// La syntaxe `{{key:N}}` permet de résoudre une variable depuis une source\n// de données spécifique, identifiée par un entier N. Le paramètre optionnel\n// `identifierData` fournit un mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Contexte optionnel pour l'exécution (utilisé par Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Données par identifiant `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Template Handlebars pré-compilé (pour CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Environnement Handlebars isolé (pour les helpers custom) */\n\thbs?: typeof Handlebars;\n\t/** Cache de compilation partagé par l'engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n// ─── Cache global de compilation ─────────────────────────────────────────────\n// Utilisé par la fonction standalone `execute()` et `renderWithHandlebars()`.\n// Les instances de `Typebars` utilisent leur propre cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── API publique (backward-compatible) ──────────────────────────────────────\n\n/**\n * Exécute un template avec les données fournies et retourne le résultat.\n *\n * Le type de retour dépend de la structure du template :\n * - Expression unique `{{expr}}` → valeur brute (any)\n * - Bloc unique → valeur coercée (number, boolean, null ou string)\n * - Template mixte → `string`\n *\n * @param template - La chaîne de template\n * @param data - Les données de contexte principal\n * @param identifierData - (optionnel) Données par identifiant `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\tif (isObjectInput(template)) {\n\t\treturn executeObjectTemplate(template, data, identifierData);\n\t}\n\tif (isLiteralInput(template)) return template;\n\tconst ast = parse(template);\n\treturn executeFromAst(ast, template, data, { identifierData });\n}\n\n/**\n * Exécute un objet template récursivement (version standalone).\n * Chaque propriété est exécutée individuellement et le résultat est un objet\n * avec la même structure mais les valeurs résolues.\n */\nfunction executeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(template)) {\n\t\tresult[key] = execute(value, data, identifierData);\n\t}\n\treturn result;\n}\n\n// ─── API interne (pour Typebars / CompiledTemplate) ────────────────────\n\n/**\n * Exécute un template à partir d'un AST déjà parsé.\n *\n * Cette fonction est le cœur de l'exécution. Elle est utilisée par :\n * - `execute()` (wrapper backward-compatible)\n * - `CompiledTemplate.execute()` (avec AST pré-parsé et cache)\n * - `Typebars.execute()` (avec cache et helpers)\n *\n * @param ast - L'AST Handlebars déjà parsé\n * @param template - Le template source (pour la compilation Handlebars si nécessaire)\n * @param data - Les données de contexte principal\n * @param ctx - Contexte d'exécution optionnel\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Cas 1 : expression unique stricte `{{expr}}` ─────────────────────\n\t// On exclut les helper calls (params > 0 ou hash) car ils doivent\n\t// passer par Handlebars pour être exécutés correctement.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\n\t\t}\n\t}\n\n\t// ── Cas 1b : expression unique avec whitespace autour ` {{expr}} ` ──\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(singleExpr.path, data, identifierData);\n\t}\n\n\t// ── Cas 1c : expression unique avec helper (params > 0) ──────────────\n\t// Ex: `{{ divide accountIds.length 10 }}` ou `{{ math a \"+\" b }}`\n\t// Le helper retourne une valeur typée mais Handlebars la convertit en\n\t// string. On rend via Handlebars puis on coerce le résultat pour\n\t// retrouver le type original (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Cas 2 : fast-path pour templates simples (texte + expressions) ────\n\t// Si le template ne contient que du texte et des expressions simples\n\t// (pas de blocs, pas de helpers avec paramètres), on peut faire une\n\t// concaténation directe sans passer par Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Cas 3 : bloc unique (éventuellement entouré de whitespace) ────────\n\t// On rend via Handlebars puis on tente de coercer le résultat vers le\n\t// type littéral détecté (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Cas 4 : template mixte → string ───────────────────────────────────\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\treturn renderWithHandlebars(template, merged, ctx);\n}\n\n// ─── Fast-path execution ─────────────────────────────────────────────────────\n// Pour les templates constitués uniquement de texte et d'expressions simples\n// (pas de blocs, pas de helpers), on court-circuite Handlebars et on fait\n// une concaténation directe. C'est significativement plus rapide.\n\n/**\n * Exécute un template via le fast-path (concaténation directe).\n *\n * Pré-condition : `canUseFastPath(ast)` doit retourner `true`.\n *\n * @param ast - L'AST du template (que ContentStatement et MustacheStatement simples)\n * @param data - Les données de contexte\n * @param identifierData - Données par identifiant (optionnel)\n * @returns La string résultante\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars convertit les valeurs en string pour le rendu.\n\t\t\t// On reproduit ce comportement : null/undefined → \"\", sinon String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Résolution directe d'expression ─────────────────────────────────────────\n// Utilisé pour les templates à expression unique et le fast-path, afin de\n// retourner la valeur brute sans passer par le moteur Handlebars.\n\n/**\n * Résout une expression AST en suivant le chemin dans les données.\n *\n * Si l'expression contient un identifiant (ex: `meetingId:1`), la résolution\n * se fait dans `identifierData[1]` au lieu de `data`.\n *\n * @param expr - L'expression AST à résoudre\n * @param data - Le contexte de données principal\n * @param identifierData - Données par identifiant (optionnel)\n * @returns La valeur brute pointée par l'expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\t// this / . → retourne le contexte entier\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// PathExpression — navigation par segments dans l'objet data\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extraire l'identifiant éventuel du dernier segment\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// La source n'existe pas → undefined (comme une clé manquante)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template utilise un identifiant mais aucune identifierData fournie\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigue dans un objet de données en suivant un chemin de segments.\n *\n * @param data - L'objet de données\n * @param segments - Les segments du chemin (ex: `[\"user\", \"address\", \"city\"]`)\n * @returns La valeur au bout du chemin, ou `undefined` si un segment\n * intermédiaire est null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Fusion des données ──────────────────────────────────────────────────────\n// Pour le rendu Handlebars (templates mixtes / blocs), on ne peut pas\n// intercepter la résolution expression par expression. On fusionne donc\n// les données identifiées dans l'objet principal sous la forme `\"key:N\"`.\n//\n// Handlebars parse `{{meetingId:1}}` comme un PathExpression avec un seul\n// segment `\"meetingId:1\"`, donc il cherchera la clé `\"meetingId:1\"` dans\n// l'objet de données — ce qui correspond exactement à notre format aplati.\n\n/**\n * Fusionne les données principales avec les données identifiées.\n *\n * @param data - Données principales\n * @param identifierData - Données par identifiant\n * @returns Un objet fusionné où les données identifiées sont sous la forme `\"key:N\"`\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tif (!identifierData) return data;\n\n\tconst merged: Record<string, unknown> = { ...data };\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Rendu Handlebars ────────────────────────────────────────────────────────\n// Pour les templates complexes (blocs, helpers), on délègue à Handlebars.\n// La compilation est cachée pour éviter les recompilations coûteuses.\n\n/**\n * Compile et exécute un template via Handlebars.\n *\n * Utilise un cache de compilation (LRU) pour éviter de recompiler le même\n * template lors d'appels répétés. Le cache est soit :\n * - Le cache global (pour la fonction standalone `execute()`)\n * - Le cache d'instance fourni via `ExecutorContext` (pour `Typebars`)\n *\n * @param template - La chaîne de template\n * @param data - Les données de contexte\n * @param ctx - Contexte d'exécution optionnel (cache, env Handlebars)\n * @returns Toujours une string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Utiliser le template pré-compilé si disponible (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Chercher dans le cache (instance ou global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Désactive le HTML-escaping par défaut — ce moteur n'est pas\n\t\t\t\t// spécifique au HTML, on veut les valeurs brutes.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Mode strict : lève une erreur si un chemin n'existe pas dans les données.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Vide le cache global de compilation Handlebars.\n * Utile pour les tests ou pour libérer la mémoire.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "22DB3A05EDDFCF4C64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
function G(w,A){if(!w.$ref)return w;let B=w.$ref,j=B.match(/^#\/(definitions|\$defs)\/(.+)$/);if(!j)throw Error(`Unsupported $ref format: "${B}". Only internal #/definitions/ references are supported.`);let E=j[1],F=j[2]??"",H=E==="definitions"?A.definitions:A.$defs;if(!H||!(F in H))throw Error(`Cannot resolve $ref "${B}": definition "${F}" not found.`);let M=H[F];if(!M)throw Error(`Cannot resolve $ref "${B}": definition "${F}" not found.`);return G(M,A)}function L(w,A,B){let j=G(w,B);if(j.properties&&A in j.properties){let F=j.properties[A];if(F)return G(F,B)}if(j.additionalProperties!==void 0&&j.additionalProperties!==!1){if(j.additionalProperties===!0)return{};return G(j.additionalProperties,B)}let E=N(j,A,B);if(E)return E;return}function N(w,A,B){if(w.allOf){let j=w.allOf.map((E)=>L(E,A,B)).filter((E)=>E!==void 0);if(j.length===1)return j[0];if(j.length>1)return{allOf:j}}for(let j of["anyOf","oneOf"]){if(!w[j])continue;let E=w[j].map((F)=>L(F,A,B)).filter((F)=>F!==void 0);if(E.length===1)return E[0];if(E.length>1)return{[j]:E}}return}function P(w,A){if(A.length===0)return G(w,w);let B=G(w,w),j=w;for(let E of A){let F=L(B,E,j);if(F===void 0)return;B=F}return B}function Q(w,A){let B=G(w,A),j=B.type;if(!(j==="array"||Array.isArray(j)&&j.includes("array"))&&B.items===void 0)return;if(B.items===void 0)return{};if(Array.isArray(B.items))return{oneOf:B.items.map((F)=>G(F,A))};return G(B.items,A)}function J(w){for(let A of["oneOf","anyOf"])if(w[A]&&w[A].length===1)return J(w[A][0]);if(w.allOf&&w.allOf.length===1)return J(w.allOf[0]);for(let A of["oneOf","anyOf"])if(w[A]&&w[A].length>1){let B=new Set,j=[];for(let E of w[A]){let F=JSON.stringify(E);if(!B.has(F))B.add(F),j.push(J(E))}if(j.length===1)return j[0];return{...w,[A]:j}}return w}
|
|
2
|
+
export{G as t,P as u,Q as v,J as w};
|
|
3
|
+
|
|
4
|
+
//# debugId=BE90011647E1C81A64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-qh2r1pa1.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/schema-resolver.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { JSONSchema7 } from \"./types.ts\";\n\n// ─── JSON Schema Resolver ────────────────────────────────────────────────────\n// Utilitaire pour naviguer dans un JSON Schema Draft v7 en suivant un chemin\n// de propriétés (ex: [\"user\", \"address\", \"city\"]).\n//\n// Gère :\n// - La résolution de `$ref` (références internes #/definitions/...)\n// - La navigation dans `properties`\n// - La navigation dans `items` (éléments d'un tableau)\n// - Les combinateurs `allOf`, `anyOf`, `oneOf` (recherche dans chaque branche)\n// - `additionalProperties` quand la propriété n'est pas déclarée\n\n// ─── Résolution de $ref ──────────────────────────────────────────────────────\n// Supporte uniquement les références internes au format `#/definitions/Foo`\n// ou `#/$defs/Foo` (JSON Schema Draft 2019+). Les $ref distantes (URL) ne\n// sont pas prises en charge — ce n'est pas le rôle d'un moteur de template.\n\n/**\n * Résout récursivement les `$ref` d'un schema en utilisant le schema racine\n * comme source de définitions.\n */\nexport function resolveRef(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 {\n\tif (!schema.$ref) return schema;\n\n\tconst ref = schema.$ref;\n\n\t// Format attendu : #/definitions/Name ou #/$defs/Name\n\tconst match = ref.match(/^#\\/(definitions|\\$defs)\\/(.+)$/);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`Unsupported $ref format: \"${ref}\". Only internal #/definitions/ references are supported.`,\n\t\t);\n\t}\n\n\tconst defsKey = match[1] as \"definitions\" | \"$defs\";\n\tconst name = match[2] ?? \"\";\n\n\tconst defs =\n\t\tdefsKey === \"definitions\"\n\t\t\t? root.definitions\n\t\t\t: ((root as Record<string, unknown>).$defs as\n\t\t\t\t\t| Record<string, JSONSchema7>\n\t\t\t\t\t| undefined);\n\n\tif (!defs || !(name in defs)) {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\n\t// Résolution récursive au cas où la définition elle-même contient un $ref\n\tconst def = defs[name];\n\tif (!def) {\n\t\tthrow new Error(\n\t\t\t`Cannot resolve $ref \"${ref}\": definition \"${name}\" not found.`,\n\t\t);\n\t}\n\treturn resolveRef(def, root);\n}\n\n// ─── Navigation par segment de chemin ────────────────────────────────────────\n\n/**\n * Résout un seul segment de chemin (un nom de propriété) dans un schema.\n * Retourne le sous-schema correspondant ou `undefined` si le chemin est invalide.\n *\n * @param schema - Le schema courant (déjà résolu, sans $ref)\n * @param segment - Le nom de la propriété à résoudre\n * @param root - Le schema racine (pour résoudre d'éventuels $ref internes)\n */\nfunction resolveSegment(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// 1. Propriétés explicites\n\tif (resolved.properties && segment in resolved.properties) {\n\t\tconst prop = resolved.properties[segment];\n\t\tif (prop) return resolveRef(prop, root);\n\t}\n\n\t// 2. additionalProperties (quand la propriété n'est pas déclarée)\n\tif (\n\t\tresolved.additionalProperties !== undefined &&\n\t\tresolved.additionalProperties !== false\n\t) {\n\t\tif (resolved.additionalProperties === true) {\n\t\t\t// additionalProperties: true → on ne sait rien du type\n\t\t\treturn {};\n\t\t}\n\t\treturn resolveRef(resolved.additionalProperties, root);\n\t}\n\n\t// 3. Combinateurs — on cherche dans chaque branche\n\tconst combinatorResult = resolveInCombinators(resolved, segment, root);\n\tif (combinatorResult) return combinatorResult;\n\n\treturn undefined;\n}\n\n/**\n * Cherche un segment dans les branches `allOf`, `anyOf`, `oneOf`.\n * Retourne le premier sous-schema trouvé ou `undefined`.\n * Pour `allOf`, on fusionne les résultats trouvés en un `allOf`.\n */\nfunction resolveInCombinators(\n\tschema: JSONSchema7,\n\tsegment: string,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\t// allOf : la propriété peut être définie dans n'importe quelle branche,\n\t// et toutes les contraintes s'appliquent simultanément.\n\tif (schema.allOf) {\n\t\tconst matches = schema.allOf\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { allOf: matches };\n\t}\n\n\t// anyOf / oneOf : la propriété peut venir de n'importe quelle branche.\n\tfor (const key of [\"anyOf\", \"oneOf\"] as const) {\n\t\tif (!schema[key]) continue;\n\t\tconst matches = schema[key]\n\t\t\t.map((branch) => resolveSegment(branch, segment, root))\n\t\t\t.filter((s): s is JSONSchema7 => s !== undefined);\n\n\t\tif (matches.length === 1) return matches[0] as JSONSchema7;\n\t\tif (matches.length > 1) return { [key]: matches };\n\t}\n\n\treturn undefined;\n}\n\n// ─── API publique ────────────────────────────────────────────────────────────\n\n/**\n * Résout un chemin complet (ex: [\"user\", \"address\", \"city\"]) dans un JSON\n * Schema et retourne le sous-schema correspondant.\n *\n * @param schema - Le schema racine décrivant le contexte du template\n * @param path - Tableau de segments (noms de propriétés)\n * @returns Le sous-schema au bout du chemin, ou `undefined` si le chemin\n * ne peut pas être résolu.\n *\n * @example\n * ```\n * const schema = {\n * type: \"object\",\n * properties: {\n * user: {\n * type: \"object\",\n * properties: {\n * name: { type: \"string\" }\n * }\n * }\n * }\n * };\n * resolveSchemaPath(schema, [\"user\", \"name\"]);\n * // → { type: \"string\" }\n * ```\n */\nexport function resolveSchemaPath(\n\tschema: JSONSchema7,\n\tpath: string[],\n): JSONSchema7 | undefined {\n\tif (path.length === 0) return resolveRef(schema, schema);\n\n\tlet current: JSONSchema7 = resolveRef(schema, schema);\n\tconst root = schema;\n\n\tfor (const segment of path) {\n\t\tconst next = resolveSegment(current, segment, root);\n\t\tif (next === undefined) return undefined;\n\t\tcurrent = next;\n\t}\n\n\treturn current;\n}\n\n/**\n * Résout le schema des éléments d'un tableau.\n * Si le schema n'est pas de type `array` ou n'a pas de `items`, retourne `undefined`.\n *\n * @param schema - Le schema d'un tableau\n * @param root - Le schema racine (pour résoudre les $ref)\n */\nexport function resolveArrayItems(\n\tschema: JSONSchema7,\n\troot: JSONSchema7,\n): JSONSchema7 | undefined {\n\tconst resolved = resolveRef(schema, root);\n\n\t// Vérification que c'est bien un tableau\n\tconst schemaType = resolved.type;\n\tconst isArray =\n\t\tschemaType === \"array\" ||\n\t\t(Array.isArray(schemaType) && schemaType.includes(\"array\"));\n\n\tif (!isArray && resolved.items === undefined) {\n\t\treturn undefined;\n\t}\n\n\tif (resolved.items === undefined) {\n\t\t// array sans items → éléments de type inconnu\n\t\treturn {};\n\t}\n\n\t// items peut être un schema unique ou un tuple (tableau de schemas).\n\t// Pour les boucles de template, on traite le cas d'un schema unique.\n\tif (Array.isArray(resolved.items)) {\n\t\t// Tuple : on crée un oneOf de tous les types possibles\n\t\treturn { oneOf: resolved.items.map((item) => resolveRef(item, root)) };\n\t}\n\n\treturn resolveRef(resolved.items, root);\n}\n\n/**\n * Simplifie un schema de sortie pour éviter les constructions inutilement\n * complexes (ex: `oneOf` avec un seul élément, doublons, etc.).\n */\nexport function simplifySchema(schema: JSONSchema7): JSONSchema7 {\n\t// oneOf / anyOf avec un seul élément → on déplie\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tif (schema[key] && schema[key].length === 1) {\n\t\t\treturn simplifySchema(schema[key][0] as JSONSchema7);\n\t\t}\n\t}\n\n\t// allOf avec un seul élément → on déplie\n\tif (schema.allOf && schema.allOf.length === 1) {\n\t\treturn simplifySchema(schema.allOf[0] as JSONSchema7);\n\t}\n\n\t// Déduplique les entrées identiques dans oneOf/anyOf\n\tfor (const key of [\"oneOf\", \"anyOf\"] as const) {\n\t\tif (schema[key] && schema[key].length > 1) {\n\t\t\tconst seen = new Set<string>();\n\t\t\tconst unique: JSONSchema7[] = [];\n\t\t\tfor (const entry of schema[key]) {\n\t\t\t\tconst serialized = JSON.stringify(entry);\n\t\t\t\tif (!seen.has(serialized)) {\n\t\t\t\t\tseen.add(serialized);\n\t\t\t\t\tunique.push(simplifySchema(entry));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (unique.length === 1) return unique[0] as JSONSchema7;\n\t\t\treturn { ...schema, [key]: unique };\n\t\t}\n\t}\n\n\treturn schema;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "AAsBO,SAAS,CAAU,CACzB,EACA,EACc,CACd,GAAI,CAAC,EAAO,KAAM,OAAO,EAEzB,IAAM,EAAM,EAAO,KAGb,EAAQ,EAAI,MAAM,iCAAiC,EACzD,GAAI,CAAC,EACJ,MAAU,MACT,6BAA6B,4DAC9B,EAGD,IAAM,EAAU,EAAM,GAChB,EAAO,EAAM,IAAM,GAEnB,EACL,IAAY,cACT,EAAK,YACH,EAAiC,MAIvC,GAAI,CAAC,GAAQ,EAAE,KAAQ,GACtB,MAAU,MACT,wBAAwB,mBAAqB,eAC9C,EAID,IAAM,EAAM,EAAK,GACjB,GAAI,CAAC,EACJ,MAAU,MACT,wBAAwB,mBAAqB,eAC9C,EAED,OAAO,EAAW,EAAK,CAAI,EAa5B,SAAS,CAAc,CACtB,EACA,EACA,EAC0B,CAC1B,IAAM,EAAW,EAAW,EAAQ,CAAI,EAGxC,GAAI,EAAS,YAAc,KAAW,EAAS,WAAY,CAC1D,IAAM,EAAO,EAAS,WAAW,GACjC,GAAI,EAAM,OAAO,EAAW,EAAM,CAAI,EAIvC,GACC,EAAS,uBAAyB,QAClC,EAAS,uBAAyB,GACjC,CACD,GAAI,EAAS,uBAAyB,GAErC,MAAO,CAAC,EAET,OAAO,EAAW,EAAS,qBAAsB,CAAI,EAItD,IAAM,EAAmB,EAAqB,EAAU,EAAS,CAAI,EACrE,GAAI,EAAkB,OAAO,EAE7B,OAQD,SAAS,CAAoB,CAC5B,EACA,EACA,EAC0B,CAG1B,GAAI,EAAO,MAAO,CACjB,IAAM,EAAU,EAAO,MACrB,IAAI,CAAC,IAAW,EAAe,EAAQ,EAAS,CAAI,CAAC,EACrD,OAAO,CAAC,IAAwB,IAAM,MAAS,EAEjD,GAAI,EAAQ,SAAW,EAAG,OAAO,EAAQ,GACzC,GAAI,EAAQ,OAAS,EAAG,MAAO,CAAE,MAAO,CAAQ,EAIjD,QAAW,IAAO,CAAC,QAAS,OAAO,EAAY,CAC9C,GAAI,CAAC,EAAO,GAAM,SAClB,IAAM,EAAU,EAAO,GACrB,IAAI,CAAC,IAAW,EAAe,EAAQ,EAAS,CAAI,CAAC,EACrD,OAAO,CAAC,IAAwB,IAAM,MAAS,EAEjD,GAAI,EAAQ,SAAW,EAAG,OAAO,EAAQ,GACzC,GAAI,EAAQ,OAAS,EAAG,MAAO,EAAG,GAAM,CAAQ,EAGjD,OA+BM,SAAS,CAAiB,CAChC,EACA,EAC0B,CAC1B,GAAI,EAAK,SAAW,EAAG,OAAO,EAAW,EAAQ,CAAM,EAEvD,IAAI,EAAuB,EAAW,EAAQ,CAAM,EAC9C,EAAO,EAEb,QAAW,KAAW,EAAM,CAC3B,IAAM,EAAO,EAAe,EAAS,EAAS,CAAI,EAClD,GAAI,IAAS,OAAW,OACxB,EAAU,EAGX,OAAO,EAUD,SAAS,CAAiB,CAChC,EACA,EAC0B,CAC1B,IAAM,EAAW,EAAW,EAAQ,CAAI,EAGlC,EAAa,EAAS,KAK5B,GAAI,EAHH,IAAe,SACd,MAAM,QAAQ,CAAU,GAAK,EAAW,SAAS,OAAO,IAE1C,EAAS,QAAU,OAClC,OAGD,GAAI,EAAS,QAAU,OAEtB,MAAO,CAAC,EAKT,GAAI,MAAM,QAAQ,EAAS,KAAK,EAE/B,MAAO,CAAE,MAAO,EAAS,MAAM,IAAI,CAAC,IAAS,EAAW,EAAM,CAAI,CAAC,CAAE,EAGtE,OAAO,EAAW,EAAS,MAAO,CAAI,EAOhC,SAAS,CAAc,CAAC,EAAkC,CAEhE,QAAW,IAAO,CAAC,QAAS,OAAO,EAClC,GAAI,EAAO,IAAQ,EAAO,GAAK,SAAW,EACzC,OAAO,EAAe,EAAO,GAAK,EAAiB,EAKrD,GAAI,EAAO,OAAS,EAAO,MAAM,SAAW,EAC3C,OAAO,EAAe,EAAO,MAAM,EAAiB,EAIrD,QAAW,IAAO,CAAC,QAAS,OAAO,EAClC,GAAI,EAAO,IAAQ,EAAO,GAAK,OAAS,EAAG,CAC1C,IAAM,EAAO,IAAI,IACX,EAAwB,CAAC,EAC/B,QAAW,KAAS,EAAO,GAAM,CAChC,IAAM,EAAa,KAAK,UAAU,CAAK,EACvC,GAAI,CAAC,EAAK,IAAI,CAAU,EACvB,EAAK,IAAI,CAAU,EACnB,EAAO,KAAK,EAAe,CAAK,CAAC,EAGnC,GAAI,EAAO,SAAW,EAAG,OAAO,EAAO,GACvC,MAAO,IAAK,GAAS,GAAM,CAAO,EAIpC,OAAO",
|
|
8
|
+
"debugId": "BE90011647E1C81A64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{j as T,k as H}from"./chunk-6955jpr7.js";import{n as B,o as j,p as w,q as O,s as k,t as F,u as R,w as Z,y as U}from"./chunk-1gm6cf0e.js";import{C as $}from"./chunk-ybh51hbe.js";import{N as L}from"./chunk-4zv02svp.js";import C from"handlebars";var P=new L(128);function I(q,M,z){if(H(q))return S(q,M,z);if(T(q))return q;let G=B(q);return W(G,q,M,{identifierData:z})}function S(q,M,z){let G={};for(let[K,J]of Object.entries(q))G[K]=I(J,M,z);return G}function W(q,M,z,G){let K=G?.identifierData;if(j(q)){let N=q.body[0];if(N.params.length===0&&!N.hash)return _(N.path,z,K)}let J=F(q);if(J&&J.params.length===0&&!J.hash)return _(J.path,z,K);if(J&&(J.params.length>0||J.hash)){let N=X(z,K),V=Y(M,N,G);return Z(V)}if(R(q)&&q.body.length>1)return v(q,z,K);if(k(q)){let N=X(z,K),V=Y(M,N,G);return Z(V)}let y=X(z,K);return Y(M,y,G)}function v(q,M,z){let G="";for(let K of q.body)if(K.type==="ContentStatement")G+=K.value;else if(K.type==="MustacheStatement"){let J=_(K.path,M,z);if(J!=null)G+=String(J)}return G}function _(q,M,z){if(O(q))return M;if(q.type==="StringLiteral")return q.value;if(q.type==="NumberLiteral")return q.value;if(q.type==="BooleanLiteral")return q.value;if(q.type==="NullLiteral")return null;if(q.type==="UndefinedLiteral")return;let G=w(q);if(G.length===0)throw new $(`Cannot resolve expression of type "${q.type}"`);let{cleanSegments:K,identifier:J}=U(G);if(J!==null&&z){let Q=z[J];if(Q)return A(Q,K);return}if(J!==null&&!z)return;return A(M,K)}function A(q,M){let z=q;for(let G of M){if(z===null||z===void 0)return;if(typeof z!=="object")return;z=z[G]}return z}function X(q,M){if(!M)return q;let z={...q};for(let[G,K]of Object.entries(M))for(let[J,Q]of Object.entries(K))z[`${J}:${G}`]=Q;return z}function Y(q,M,z){try{if(z?.compiledTemplate)return z.compiledTemplate(M);let G=z?.compilationCache??P,K=z?.hbs??C,J=G.get(q);if(!J)J=K.compile(q,{noEscape:!0,strict:!1}),G.set(q,J);return J(M)}catch(G){let K=G instanceof Error?G.message:String(G);throw new $(K)}}function g(){P.clear()}
|
|
2
|
+
export{I as f,W as g,A as h,g as i};
|
|
3
|
+
|
|
4
|
+
//# debugId=1D7E919F3953731C64756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-qpzzr2rd.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/executor.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import Handlebars from \"handlebars\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport type { TemplateInput, TemplateInputObject } from \"./types.ts\";\nimport { isLiteralInput, isObjectInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\tif (isObjectInput(template)) {\n\t\treturn executeObjectTemplate(template, data, identifierData);\n\t}\n\tif (isLiteralInput(template)) return template;\n\tconst ast = parse(template);\n\treturn executeFromAst(ast, template, data, { identifierData });\n}\n\n/**\n * Executes an object template recursively (standalone version).\n * Each property is executed individually and the result is an object\n * with the same structure but resolved values.\n */\nfunction executeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(template)) {\n\t\tresult[key] = execute(value, data, identifierData);\n\t}\n\treturn result;\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(singleExpr.path, data, identifierData);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 4: mixed template → string ──────────────────────────────────\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\treturn renderWithHandlebars(template, merged, ctx);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tif (!identifierData) return data;\n\n\tconst merged: Record<string, unknown> = { ...data };\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "1D7E919F3953731C64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class A extends Error{constructor(j){super(j);this.name="TemplateError"}toJSON(){return{name:this.name,message:this.message}}}class F extends A{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 G extends A{diagnostics;errors;warnings;errorCount;warningCount;constructor(j){let k=j.filter((z)=>z.severity==="error"),q=j.filter((z)=>z.severity==="warning"),B=k.map((z)=>I(z)).join(`
|
|
2
|
+
`);super(`Static analysis failed with ${k.length} error(s):
|
|
3
|
+
${B}`);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 H extends A{constructor(j){super(`Runtime error: ${j}`);this.name="TemplateRuntimeError"}}function I(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 J(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 K(j,k,q){return`"{{#${j}}}" expects ${k}, but resolved schema has type "${q}"`}function M(j){return`"{{#${j}}}" requires an argument`}function O(j){return`Unknown block helper "{{#${j}}}" — cannot analyze statically`}function Q(j){return`Expression of type "${j}" cannot be statically analyzed`}function R(j,k){if(!j)return"MISSING_IDENTIFIER_SCHEMAS";if(!k)return"UNKNOWN_IDENTIFIER";return"IDENTIFIER_PROPERTY_NOT_FOUND"}
|
|
4
|
+
export{A as B,F as C,G as D,H as E,J as F,K as G,M as H,O as I,Q as J,R as K};
|
|
5
|
+
|
|
6
|
+
//# debugId=47521E188A2A57ED64756E2164756E21
|
|
7
|
+
//# sourceMappingURL=chunk-vka4e61h.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { DiagnosticCode, TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Classe de base ──────────────────────────────────────────────────────────\n// Toutes les erreurs du moteur de template héritent de cette classe pour\n// permettre un `catch` ciblé : `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 * Sérialise l'erreur en un objet JSON-compatible, adapté à l'envoi\n\t * vers un frontend ou un système de logging structuré.\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// ─── Erreur de parsing ───────────────────────────────────────────────────────\n// Levée quand Handlebars ne parvient pas à parser le template (syntaxe invalide).\n\nexport class TemplateParseError extends TemplateError {\n\tconstructor(\n\t\tmessage: string,\n\t\t/** Position approximative de l'erreur dans le source */\n\t\tpublic readonly loc?: { line: number; column: number },\n\t\t/** Fragment du template source autour de l'erreur */\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// ─── Erreur d'analyse statique ───────────────────────────────────────────────\n// Levée en mode strict quand l'analyse produit au moins une erreur.\n// Contient la liste complète des diagnostics pour inspection détaillée.\n\nexport class TemplateAnalysisError extends TemplateError {\n\t/** Liste complète des diagnostics (erreurs + warnings) */\n\tpublic readonly diagnostics: TemplateDiagnostic[];\n\n\t/** Uniquement les diagnostics de sévérité \"error\" */\n\tpublic readonly errors: TemplateDiagnostic[];\n\n\t/** Uniquement les diagnostics de sévérité \"warning\" */\n\tpublic readonly warnings: TemplateDiagnostic[];\n\n\t/** Nombre total d'erreurs */\n\tpublic readonly errorCount: number;\n\n\t/** Nombre total de 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 * Sérialise l'erreur d'analyse en un objet JSON-compatible.\n\t *\n\t * Conçu pour être envoyé directement à un frontend :\n\t * ```\n\t * res.status(400).json(error.toJSON());\n\t * ```\n\t *\n\t * Structure retournée :\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// ─── Erreur d'exécution ──────────────────────────────────────────────────────\n// Levée quand l'exécution du template échoue (accès à une propriété\n// inexistante en mode strict, type inattendu, 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// ─── Utilitaires internes ────────────────────────────────────────────────────\n\n/**\n * Formate une ligne de diagnostic pour le message résumé d'une\n * `TemplateAnalysisError`.\n *\n * Produit un format lisible :\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// ─── Factory pour les erreurs courantes ──────────────────────────────────────\n// Ces fonctions simplifient la création d'erreurs typées à travers le code.\n\n/**\n * Crée un diagnostic structuré pour une propriété inexistante.\n * Utilisé par l'analyseur pour produire des messages d'erreur enrichis.\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 * Crée un message pour une incompatibilité de type.\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 * Crée un message pour un argument manquant sur un helper de bloc.\n */\nexport function createMissingArgumentMessage(helperName: string): string {\n\treturn `\"{{#${helperName}}}\" requires an argument`;\n}\n\n/**\n * Crée un message pour un helper de bloc inconnu.\n */\nexport function createUnknownHelperMessage(helperName: string): string {\n\treturn `Unknown block helper \"{{#${helperName}}}\" — cannot analyze statically`;\n}\n\n/**\n * Crée un message pour une expression non analysable.\n */\nexport function createUnanalyzableMessage(nodeType: string): string {\n\treturn `Expression of type \"${nodeType}\" cannot be statically analyzed`;\n}\n\n/**\n * Détermine le DiagnosticCode approprié pour une erreur d'identifiant.\n */\nexport function getIdentifierErrorCode(\n\thasSchemas: boolean,\n\thasSpecificSchema: boolean,\n): DiagnosticCode {\n\tif (!hasSchemas) return \"MISSING_IDENTIFIER_SCHEMAS\";\n\tif (!hasSpecificSchema) return \"UNKNOWN_IDENTIFIER\";\n\treturn \"IDENTIFIER_PROPERTY_NOT_FOUND\";\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,mCAMxB,SAAS,CAAsB,CACrC,EACA,EACiB,CACjB,GAAI,CAAC,EAAY,MAAO,6BACxB,GAAI,CAAC,EAAmB,MAAO,qBAC/B,MAAO",
|
|
8
|
+
"debugId": "47521E188A2A57ED64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{b as z}from"./chunk-z6yh5qvc.js";import{d as _}from"./chunk-awgj10qg.js";import{g as F}from"./chunk-qpzzr2rd.js";import{j as Z,k as $,l as U}from"./chunk-6955jpr7.js";import{n as E}from"./chunk-1gm6cf0e.js";import{B as C}from"./chunk-ybh51hbe.js";import{N as x,Q as L,R as g}from"./chunk-4zv02svp.js";import R from"handlebars";var j=new Set(["+","-","*","/","%","**"]);function K(q){if(typeof q==="number")return q;if(typeof q==="string"){let B=Number(q);return Number.isNaN(B)?0:B}return 0}function P(q,B,G){switch(B){case"+":return q+G;case"-":return q-G;case"*":return q*G;case"/":return G===0?1/0:q/G;case"%":return G===0?NaN:q%G;case"**":return q**G}}class Y{static HELPER_NAMES=["add","subtract","sub","multiply","mul","divide","div","modulo","mod","pow","abs","ceil","floor","round","sqrt","min","max","math"];static HELPER_NAMES_SET=new Set(Y.HELPER_NAMES);static getDefinitions(){let q=new Map;return Y.registerBinaryOperators(q),Y.registerUnaryFunctions(q),Y.registerMinMax(q),Y.registerGenericMath(q),q}static registerBinaryOperators(q){let B={fn:(V,X)=>K(V)+K(X),params:[{name:"a",type:{type:"number"},description:"First operand"},{name:"b",type:{type:"number"},description:"Second operand"}],returnType:{type:"number"},description:"Adds two numbers: {{ add a b }}"};q.set("add",B);let G={fn:(V,X)=>K(V)-K(X),params:[{name:"a",type:{type:"number"},description:"Value to subtract from"},{name:"b",type:{type:"number"},description:"Value to subtract"}],returnType:{type:"number"},description:"Subtracts b from a: {{ subtract a b }}"};q.set("subtract",G),q.set("sub",G);let J={fn:(V,X)=>K(V)*K(X),params:[{name:"a",type:{type:"number"},description:"First factor"},{name:"b",type:{type:"number"},description:"Second factor"}],returnType:{type:"number"},description:"Multiplies two numbers: {{ multiply a b }}"};q.set("multiply",J),q.set("mul",J);let Q={fn:(V,X)=>{let w=K(X);return w===0?1/0:K(V)/w},params:[{name:"a",type:{type:"number"},description:"Dividend"},{name:"b",type:{type:"number"},description:"Divisor"}],returnType:{type:"number"},description:"Divides a by b: {{ divide a b }}. Returns Infinity if b is 0."};q.set("divide",Q),q.set("div",Q);let W={fn:(V,X)=>{let w=K(X);return w===0?NaN:K(V)%w},params:[{name:"a",type:{type:"number"},description:"Dividend"},{name:"b",type:{type:"number"},description:"Divisor"}],returnType:{type:"number"},description:"Returns the remainder of a divided by b: {{ modulo a b }}"};q.set("modulo",W),q.set("mod",W),q.set("pow",{fn:(V,X)=>K(V)**K(X),params:[{name:"base",type:{type:"number"},description:"The base"},{name:"exponent",type:{type:"number"},description:"The exponent"}],returnType:{type:"number"},description:"Raises base to the power of exponent: {{ pow base exponent }}"})}static registerUnaryFunctions(q){q.set("abs",{fn:(B)=>Math.abs(K(B)),params:[{name:"value",type:{type:"number"},description:"The number"}],returnType:{type:"number"},description:"Returns the absolute value: {{ abs value }}"}),q.set("ceil",{fn:(B)=>Math.ceil(K(B)),params:[{name:"value",type:{type:"number"},description:"The number to round up"}],returnType:{type:"number"},description:"Rounds up to the nearest integer: {{ ceil value }}"}),q.set("floor",{fn:(B)=>Math.floor(K(B)),params:[{name:"value",type:{type:"number"},description:"The number to round down"}],returnType:{type:"number"},description:"Rounds down to the nearest integer: {{ floor value }}"}),q.set("round",{fn:(B,G)=>{let J=K(B);if(G===void 0||G===null||typeof G==="object")return Math.round(J);let W=10**K(G);return Math.round(J*W)/W},params:[{name:"value",type:{type:"number"},description:"The number to round"},{name:"precision",type:{type:"number"},description:"Number of decimal places (default: 0)",optional:!0}],returnType:{type:"number"},description:"Rounds to the nearest integer or to a given precision: {{ round value }} or {{ round value 2 }}"}),q.set("sqrt",{fn:(B)=>Math.sqrt(K(B)),params:[{name:"value",type:{type:"number"},description:"The number"}],returnType:{type:"number"},description:"Returns the square root: {{ sqrt value }}"})}static registerMinMax(q){q.set("min",{fn:(B,G)=>Math.min(K(B),K(G)),params:[{name:"a",type:{type:"number"},description:"First number"},{name:"b",type:{type:"number"},description:"Second number"}],returnType:{type:"number"},description:"Returns the smaller of two numbers: {{ min a b }}"}),q.set("max",{fn:(B,G)=>Math.max(K(B),K(G)),params:[{name:"a",type:{type:"number"},description:"First number"},{name:"b",type:{type:"number"},description:"Second number"}],returnType:{type:"number"},description:"Returns the larger of two numbers: {{ max a b }}"})}static registerGenericMath(q){q.set("math",{fn:(B,G,J)=>{let Q=String(G);if(!j.has(Q))throw Error(`[math helper] Unknown operator "${Q}". Supported: ${[...j].join(", ")} `);return P(K(B),Q,K(J))},params:[{name:"a",type:{type:"number"},description:"Left operand"},{name:"operator",type:{type:"string",enum:["+","-","*","/","%","**"]},description:'Arithmetic operator: "+", "-", "*", "/", "%", "**"'},{name:"b",type:{type:"number"},description:"Right operand"}],returnType:{type:"number"},description:'Generic math helper with operator as parameter: {{ math a "+" b }}, {{ math a "/" b }}. Supported operators: +, -, *, /, %, **'})}static register(q){let B=Y.getDefinitions();for(let[G,J]of B)q.registerHelper(G,J)}static unregister(q){for(let B of Y.HELPER_NAMES)q.unregisterHelper(B)}static getHelperNames(){return Y.HELPER_NAMES}static isMathHelper(q){return Y.HELPER_NAMES_SET.has(q)}}class k{hbs;astCache;compilationCache;helpers=new Map;constructor(q={}){if(this.hbs=R.create(),this.astCache=new x(q.astCacheSize??256),this.compilationCache=new x(q.compilationCacheSize??256),Y.register(this),q.helpers)for(let B of q.helpers){let{name:G,...J}=B;this.registerHelper(G,J)}}compile(q){if($(q)){let J={};for(let[Q,W]of Object.entries(q))J[Q]=this.compile(W);return z.fromObject(J,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if(Z(q))return z.fromLiteral(q,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache});let B=this.getCachedAst(q),G={helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache};return z.fromTemplate(B,q,G)}analyze(q,B,G){if($(q))return L(Object.keys(q),(Q)=>this.analyze(q[Q],B,G));if(Z(q))return{valid:!0,diagnostics:[],outputSchema:U(q)};let J=this.getCachedAst(q);return _(J,q,B,{identifierSchemas:G,helpers:this.helpers})}validate(q,B,G){let J=this.analyze(q,B,G);return{valid:J.valid,diagnostics:J.diagnostics}}isValidSyntax(q){if($(q))return Object.values(q).every((B)=>this.isValidSyntax(B));if(Z(q))return!0;try{return this.getCachedAst(q),!0}catch{return!1}}execute(q,B,G){if($(q)){let Q={};for(let[W,V]of Object.entries(q))Q[W]=this.execute(V,B,G);return Q}if(Z(q))return q;let J=this.getCachedAst(q);if(G?.schema){let Q=_(J,q,G.schema,{identifierSchemas:G.identifierSchemas,helpers:this.helpers});if(!Q.valid)throw new C(Q.diagnostics)}return F(J,q,B,{identifierData:G?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache})}analyzeAndExecute(q,B,G,J){if($(q))return g(Object.keys(q),(X)=>this.analyzeAndExecute(q[X],B,G,J));if(Z(q))return{analysis:{valid:!0,diagnostics:[],outputSchema:U(q)},value:q};let Q=this.getCachedAst(q),W=_(Q,q,B,{identifierSchemas:J?.identifierSchemas,helpers:this.helpers});if(!W.valid)return{analysis:W,value:void 0};let V=F(Q,q,G,{identifierData:J?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache});return{analysis:W,value:V}}registerHelper(q,B){return this.helpers.set(q,B),this.hbs.registerHelper(q,B.fn),this.compilationCache.clear(),this}unregisterHelper(q){return this.helpers.delete(q),this.hbs.unregisterHelper(q),this.compilationCache.clear(),this}hasHelper(q){return this.helpers.has(q)}clearCaches(){this.astCache.clear(),this.compilationCache.clear()}getCachedAst(q){let B=this.astCache.get(q);if(!B)B=E(q),this.astCache.set(q,B);return B}}
|
|
2
|
+
export{k as a};
|
|
3
|
+
|
|
4
|
+
//# debugId=073ABADC78560F0964756E2164756E21
|
|
5
|
+
//# sourceMappingURL=chunk-xbvk4ygq.js.map
|