@frontmcp/ui 0.5.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.
Files changed (393) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +438 -0
  3. package/package.json +147 -0
  4. package/src/adapters/index.d.ts +10 -0
  5. package/src/adapters/index.js +18 -0
  6. package/src/adapters/index.js.map +1 -0
  7. package/src/adapters/platform-meta.d.ts +165 -0
  8. package/src/adapters/platform-meta.js +310 -0
  9. package/src/adapters/platform-meta.js.map +1 -0
  10. package/src/base-template/bridge.d.ts +89 -0
  11. package/src/base-template/bridge.js +452 -0
  12. package/src/base-template/bridge.js.map +1 -0
  13. package/src/base-template/default-base-template.d.ts +91 -0
  14. package/src/base-template/default-base-template.js +435 -0
  15. package/src/base-template/default-base-template.js.map +1 -0
  16. package/src/base-template/index.d.ts +14 -0
  17. package/src/base-template/index.js +30 -0
  18. package/src/base-template/index.js.map +1 -0
  19. package/src/base-template/polyfills.d.ts +30 -0
  20. package/src/base-template/polyfills.js +190 -0
  21. package/src/base-template/polyfills.js.map +1 -0
  22. package/src/base-template/theme-styles.d.ts +73 -0
  23. package/src/base-template/theme-styles.js +95 -0
  24. package/src/base-template/theme-styles.js.map +1 -0
  25. package/src/bridge/adapters/base-adapter.d.ts +103 -0
  26. package/src/bridge/adapters/base-adapter.js +314 -0
  27. package/src/bridge/adapters/base-adapter.js.map +1 -0
  28. package/src/bridge/adapters/claude.adapter.d.ts +66 -0
  29. package/src/bridge/adapters/claude.adapter.js +145 -0
  30. package/src/bridge/adapters/claude.adapter.js.map +1 -0
  31. package/src/bridge/adapters/ext-apps.adapter.d.ts +142 -0
  32. package/src/bridge/adapters/ext-apps.adapter.js +416 -0
  33. package/src/bridge/adapters/ext-apps.adapter.js.map +1 -0
  34. package/src/bridge/adapters/gemini.adapter.d.ts +63 -0
  35. package/src/bridge/adapters/gemini.adapter.js +160 -0
  36. package/src/bridge/adapters/gemini.adapter.js.map +1 -0
  37. package/src/bridge/adapters/generic.adapter.d.ts +55 -0
  38. package/src/bridge/adapters/generic.adapter.js +108 -0
  39. package/src/bridge/adapters/generic.adapter.js.map +1 -0
  40. package/src/bridge/adapters/index.d.ts +25 -0
  41. package/src/bridge/adapters/index.js +65 -0
  42. package/src/bridge/adapters/index.js.map +1 -0
  43. package/src/bridge/adapters/openai.adapter.d.ts +64 -0
  44. package/src/bridge/adapters/openai.adapter.js +194 -0
  45. package/src/bridge/adapters/openai.adapter.js.map +1 -0
  46. package/src/bridge/core/adapter-registry.d.ts +121 -0
  47. package/src/bridge/core/adapter-registry.js +271 -0
  48. package/src/bridge/core/adapter-registry.js.map +1 -0
  49. package/src/bridge/core/bridge-factory.d.ts +198 -0
  50. package/src/bridge/core/bridge-factory.js +428 -0
  51. package/src/bridge/core/bridge-factory.js.map +1 -0
  52. package/src/bridge/core/index.d.ts +9 -0
  53. package/src/bridge/core/index.js +22 -0
  54. package/src/bridge/core/index.js.map +1 -0
  55. package/src/bridge/index.d.ts +61 -0
  56. package/src/bridge/index.js +94 -0
  57. package/src/bridge/index.js.map +1 -0
  58. package/src/bridge/runtime/iife-generator.d.ts +61 -0
  59. package/src/bridge/runtime/iife-generator.js +940 -0
  60. package/src/bridge/runtime/iife-generator.js.map +1 -0
  61. package/src/bridge/runtime/index.d.ts +8 -0
  62. package/src/bridge/runtime/index.js +16 -0
  63. package/src/bridge/runtime/index.js.map +1 -0
  64. package/src/bridge/types.d.ts +385 -0
  65. package/src/bridge/types.js +11 -0
  66. package/src/bridge/types.js.map +1 -0
  67. package/src/build/cdn-resources.d.ts +140 -0
  68. package/src/build/cdn-resources.js +314 -0
  69. package/src/build/cdn-resources.js.map +1 -0
  70. package/src/build/index.d.ts +294 -0
  71. package/src/build/index.js +325 -0
  72. package/src/build/index.js.map +1 -0
  73. package/src/build/widget-manifest.d.ts +212 -0
  74. package/src/build/widget-manifest.js +652 -0
  75. package/src/build/widget-manifest.js.map +1 -0
  76. package/src/bundler/bundler.d.ts +110 -0
  77. package/src/bundler/bundler.js +432 -0
  78. package/src/bundler/bundler.js.map +1 -0
  79. package/src/bundler/cache.d.ts +172 -0
  80. package/src/bundler/cache.js +250 -0
  81. package/src/bundler/cache.js.map +1 -0
  82. package/src/bundler/index.d.ts +41 -0
  83. package/src/bundler/index.js +73 -0
  84. package/src/bundler/index.js.map +1 -0
  85. package/src/bundler/sandbox/enclave-adapter.d.ts +120 -0
  86. package/src/bundler/sandbox/enclave-adapter.js +339 -0
  87. package/src/bundler/sandbox/enclave-adapter.js.map +1 -0
  88. package/src/bundler/sandbox/executor.d.ts +13 -0
  89. package/src/bundler/sandbox/executor.js +22 -0
  90. package/src/bundler/sandbox/executor.js.map +1 -0
  91. package/src/bundler/sandbox/policy.d.ts +61 -0
  92. package/src/bundler/sandbox/policy.js +238 -0
  93. package/src/bundler/sandbox/policy.js.map +1 -0
  94. package/src/bundler/types.d.ts +347 -0
  95. package/src/bundler/types.js +132 -0
  96. package/src/bundler/types.js.map +1 -0
  97. package/src/components/alert.d.ts +71 -0
  98. package/src/components/alert.js +189 -0
  99. package/src/components/alert.js.map +1 -0
  100. package/src/components/alert.schema.d.ts +114 -0
  101. package/src/components/alert.schema.js +105 -0
  102. package/src/components/alert.schema.js.map +1 -0
  103. package/src/components/avatar.d.ts +76 -0
  104. package/src/components/avatar.js +176 -0
  105. package/src/components/avatar.js.map +1 -0
  106. package/src/components/avatar.schema.d.ts +169 -0
  107. package/src/components/avatar.schema.js +103 -0
  108. package/src/components/avatar.schema.js.map +1 -0
  109. package/src/components/badge.d.ts +70 -0
  110. package/src/components/badge.js +149 -0
  111. package/src/components/badge.js.map +1 -0
  112. package/src/components/badge.schema.d.ts +109 -0
  113. package/src/components/badge.schema.js +96 -0
  114. package/src/components/badge.schema.js.map +1 -0
  115. package/src/components/button.d.ts +111 -0
  116. package/src/components/button.js +336 -0
  117. package/src/components/button.js.map +1 -0
  118. package/src/components/button.schema.d.ts +148 -0
  119. package/src/components/button.schema.js +121 -0
  120. package/src/components/button.schema.js.map +1 -0
  121. package/src/components/card.d.ts +60 -0
  122. package/src/components/card.js +117 -0
  123. package/src/components/card.js.map +1 -0
  124. package/src/components/card.schema.d.ts +113 -0
  125. package/src/components/card.schema.js +98 -0
  126. package/src/components/card.schema.js.map +1 -0
  127. package/src/components/form.d.ts +239 -0
  128. package/src/components/form.js +420 -0
  129. package/src/components/form.js.map +1 -0
  130. package/src/components/form.schema.d.ts +441 -0
  131. package/src/components/form.schema.js +406 -0
  132. package/src/components/form.schema.js.map +1 -0
  133. package/src/components/index.d.ts +29 -0
  134. package/src/components/index.js +98 -0
  135. package/src/components/index.js.map +1 -0
  136. package/src/components/list.d.ts +127 -0
  137. package/src/components/list.js +279 -0
  138. package/src/components/list.js.map +1 -0
  139. package/src/components/list.schema.d.ts +134 -0
  140. package/src/components/list.schema.js +168 -0
  141. package/src/components/list.schema.js.map +1 -0
  142. package/src/components/modal.d.ts +111 -0
  143. package/src/components/modal.js +260 -0
  144. package/src/components/modal.js.map +1 -0
  145. package/src/components/modal.schema.d.ts +186 -0
  146. package/src/components/modal.schema.js +167 -0
  147. package/src/components/modal.schema.js.map +1 -0
  148. package/src/components/table.d.ts +105 -0
  149. package/src/components/table.js +283 -0
  150. package/src/components/table.js.map +1 -0
  151. package/src/components/table.schema.d.ts +159 -0
  152. package/src/components/table.schema.js +173 -0
  153. package/src/components/table.schema.js.map +1 -0
  154. package/src/handlebars/helpers.d.ts +348 -0
  155. package/src/handlebars/helpers.js +605 -0
  156. package/src/handlebars/helpers.js.map +1 -0
  157. package/src/handlebars/index.d.ts +193 -0
  158. package/src/handlebars/index.js +350 -0
  159. package/src/handlebars/index.js.map +1 -0
  160. package/src/index.d.ts +50 -0
  161. package/src/index.js +192 -0
  162. package/src/index.js.map +1 -0
  163. package/src/layouts/base.d.ts +88 -0
  164. package/src/layouts/base.js +227 -0
  165. package/src/layouts/base.js.map +1 -0
  166. package/src/layouts/index.d.ts +7 -0
  167. package/src/layouts/index.js +25 -0
  168. package/src/layouts/index.js.map +1 -0
  169. package/src/layouts/presets.d.ts +133 -0
  170. package/src/layouts/presets.js +277 -0
  171. package/src/layouts/presets.js.map +1 -0
  172. package/src/pages/consent.d.ts +116 -0
  173. package/src/pages/consent.js +218 -0
  174. package/src/pages/consent.js.map +1 -0
  175. package/src/pages/error.d.ts +100 -0
  176. package/src/pages/error.js +263 -0
  177. package/src/pages/error.js.map +1 -0
  178. package/src/pages/index.d.ts +8 -0
  179. package/src/pages/index.js +27 -0
  180. package/src/pages/index.js.map +1 -0
  181. package/src/react/Alert.d.ts +101 -0
  182. package/src/react/Alert.js +51 -0
  183. package/src/react/Alert.js.map +1 -0
  184. package/src/react/Badge.d.ts +100 -0
  185. package/src/react/Badge.js +55 -0
  186. package/src/react/Badge.js.map +1 -0
  187. package/src/react/Button.d.ts +108 -0
  188. package/src/react/Button.js +52 -0
  189. package/src/react/Button.js.map +1 -0
  190. package/src/react/Card.d.ts +103 -0
  191. package/src/react/Card.js +55 -0
  192. package/src/react/Card.js.map +1 -0
  193. package/src/react/hooks/context.d.ts +178 -0
  194. package/src/react/hooks/context.js +287 -0
  195. package/src/react/hooks/context.js.map +1 -0
  196. package/src/react/hooks/index.d.ts +41 -0
  197. package/src/react/hooks/index.js +61 -0
  198. package/src/react/hooks/index.js.map +1 -0
  199. package/src/react/hooks/tools.d.ts +283 -0
  200. package/src/react/hooks/tools.js +465 -0
  201. package/src/react/hooks/tools.js.map +1 -0
  202. package/src/react/index.d.ts +80 -0
  203. package/src/react/index.js +113 -0
  204. package/src/react/index.js.map +1 -0
  205. package/src/react/types.d.ts +105 -0
  206. package/src/react/types.js +12 -0
  207. package/src/react/types.js.map +1 -0
  208. package/src/react/utils.d.ts +42 -0
  209. package/src/react/utils.js +99 -0
  210. package/src/react/utils.js.map +1 -0
  211. package/src/registry/index.d.ts +45 -0
  212. package/src/registry/index.js +67 -0
  213. package/src/registry/index.js.map +1 -0
  214. package/src/registry/render-template.d.ts +86 -0
  215. package/src/registry/render-template.js +239 -0
  216. package/src/registry/render-template.js.map +1 -0
  217. package/src/registry/tool-ui.registry.d.ts +260 -0
  218. package/src/registry/tool-ui.registry.js +438 -0
  219. package/src/registry/tool-ui.registry.js.map +1 -0
  220. package/src/registry/uri-utils.d.ts +55 -0
  221. package/src/registry/uri-utils.js +97 -0
  222. package/src/registry/uri-utils.js.map +1 -0
  223. package/src/render/index.d.ts +7 -0
  224. package/src/render/index.js +14 -0
  225. package/src/render/index.js.map +1 -0
  226. package/src/render/prerender.d.ts +56 -0
  227. package/src/render/prerender.js +98 -0
  228. package/src/render/prerender.js.map +1 -0
  229. package/src/renderers/cache.d.ts +144 -0
  230. package/src/renderers/cache.js +240 -0
  231. package/src/renderers/cache.js.map +1 -0
  232. package/src/renderers/html.renderer.d.ts +122 -0
  233. package/src/renderers/html.renderer.js +204 -0
  234. package/src/renderers/html.renderer.js.map +1 -0
  235. package/src/renderers/index.d.ts +35 -0
  236. package/src/renderers/index.js +70 -0
  237. package/src/renderers/index.js.map +1 -0
  238. package/src/renderers/mdx.renderer.d.ts +119 -0
  239. package/src/renderers/mdx.renderer.js +305 -0
  240. package/src/renderers/mdx.renderer.js.map +1 -0
  241. package/src/renderers/react.renderer.d.ts +95 -0
  242. package/src/renderers/react.renderer.js +260 -0
  243. package/src/renderers/react.renderer.js.map +1 -0
  244. package/src/renderers/registry.d.ts +133 -0
  245. package/src/renderers/registry.js +232 -0
  246. package/src/renderers/registry.js.map +1 -0
  247. package/src/renderers/types.d.ts +341 -0
  248. package/src/renderers/types.js +9 -0
  249. package/src/renderers/types.js.map +1 -0
  250. package/src/renderers/utils/detect.d.ts +106 -0
  251. package/src/renderers/utils/detect.js +267 -0
  252. package/src/renderers/utils/detect.js.map +1 -0
  253. package/src/renderers/utils/hash.d.ts +39 -0
  254. package/src/renderers/utils/hash.js +75 -0
  255. package/src/renderers/utils/hash.js.map +1 -0
  256. package/src/renderers/utils/index.d.ts +8 -0
  257. package/src/renderers/utils/index.js +28 -0
  258. package/src/renderers/utils/index.js.map +1 -0
  259. package/src/renderers/utils/transpiler.d.ts +88 -0
  260. package/src/renderers/utils/transpiler.js +215 -0
  261. package/src/renderers/utils/transpiler.js.map +1 -0
  262. package/src/runtime/adapters/html.adapter.d.ts +58 -0
  263. package/src/runtime/adapters/html.adapter.js +131 -0
  264. package/src/runtime/adapters/html.adapter.js.map +1 -0
  265. package/src/runtime/adapters/index.d.ts +25 -0
  266. package/src/runtime/adapters/index.js +54 -0
  267. package/src/runtime/adapters/index.js.map +1 -0
  268. package/src/runtime/adapters/mdx.adapter.d.ts +72 -0
  269. package/src/runtime/adapters/mdx.adapter.js +241 -0
  270. package/src/runtime/adapters/mdx.adapter.js.map +1 -0
  271. package/src/runtime/adapters/react.adapter.d.ts +69 -0
  272. package/src/runtime/adapters/react.adapter.js +245 -0
  273. package/src/runtime/adapters/react.adapter.js.map +1 -0
  274. package/src/runtime/adapters/types.d.ts +94 -0
  275. package/src/runtime/adapters/types.js +11 -0
  276. package/src/runtime/adapters/types.js.map +1 -0
  277. package/src/runtime/csp.d.ts +37 -0
  278. package/src/runtime/csp.js +140 -0
  279. package/src/runtime/csp.js.map +1 -0
  280. package/src/runtime/index.d.ts +16 -0
  281. package/src/runtime/index.js +72 -0
  282. package/src/runtime/index.js.map +1 -0
  283. package/src/runtime/mcp-bridge.d.ts +100 -0
  284. package/src/runtime/mcp-bridge.js +581 -0
  285. package/src/runtime/mcp-bridge.js.map +1 -0
  286. package/src/runtime/renderer-runtime.d.ts +132 -0
  287. package/src/runtime/renderer-runtime.js +389 -0
  288. package/src/runtime/renderer-runtime.js.map +1 -0
  289. package/src/runtime/sanitizer.d.ts +171 -0
  290. package/src/runtime/sanitizer.js +318 -0
  291. package/src/runtime/sanitizer.js.map +1 -0
  292. package/src/runtime/types.d.ts +414 -0
  293. package/src/runtime/types.js +12 -0
  294. package/src/runtime/types.js.map +1 -0
  295. package/src/runtime/wrapper.d.ts +375 -0
  296. package/src/runtime/wrapper.js +1793 -0
  297. package/src/runtime/wrapper.js.map +1 -0
  298. package/src/styles/index.d.ts +7 -0
  299. package/src/styles/index.js +11 -0
  300. package/src/styles/index.js.map +1 -0
  301. package/src/styles/variants.d.ts +50 -0
  302. package/src/styles/variants.js +175 -0
  303. package/src/styles/variants.js.map +1 -0
  304. package/src/theme/cdn.d.ts +194 -0
  305. package/src/theme/cdn.js +375 -0
  306. package/src/theme/cdn.js.map +1 -0
  307. package/src/theme/index.d.ts +17 -0
  308. package/src/theme/index.js +57 -0
  309. package/src/theme/index.js.map +1 -0
  310. package/src/theme/platforms.d.ts +106 -0
  311. package/src/theme/platforms.js +161 -0
  312. package/src/theme/platforms.js.map +1 -0
  313. package/src/theme/presets/github-openai.d.ts +49 -0
  314. package/src/theme/presets/github-openai.js +189 -0
  315. package/src/theme/presets/github-openai.js.map +1 -0
  316. package/src/theme/presets/index.d.ts +10 -0
  317. package/src/theme/presets/index.js +17 -0
  318. package/src/theme/presets/index.js.map +1 -0
  319. package/src/theme/theme.d.ts +395 -0
  320. package/src/theme/theme.js +332 -0
  321. package/src/theme/theme.js.map +1 -0
  322. package/src/tool-template/builder.d.ts +212 -0
  323. package/src/tool-template/builder.js +397 -0
  324. package/src/tool-template/builder.js.map +1 -0
  325. package/src/tool-template/index.d.ts +15 -0
  326. package/src/tool-template/index.js +38 -0
  327. package/src/tool-template/index.js.map +1 -0
  328. package/src/types/index.d.ts +13 -0
  329. package/src/types/index.js +26 -0
  330. package/src/types/index.js.map +1 -0
  331. package/src/types/ui-config.d.ts +357 -0
  332. package/src/types/ui-config.js +12 -0
  333. package/src/types/ui-config.js.map +1 -0
  334. package/src/types/ui-runtime.d.ts +965 -0
  335. package/src/types/ui-runtime.js +117 -0
  336. package/src/types/ui-runtime.js.map +1 -0
  337. package/src/validation/error-box.d.ts +55 -0
  338. package/src/validation/error-box.js +75 -0
  339. package/src/validation/error-box.js.map +1 -0
  340. package/src/validation/index.d.ts +12 -0
  341. package/src/validation/index.js +21 -0
  342. package/src/validation/index.js.map +1 -0
  343. package/src/validation/wrapper.d.ts +96 -0
  344. package/src/validation/wrapper.js +117 -0
  345. package/src/validation/wrapper.js.map +1 -0
  346. package/src/web-components/core/attribute-parser.d.ts +85 -0
  347. package/src/web-components/core/attribute-parser.js +189 -0
  348. package/src/web-components/core/attribute-parser.js.map +1 -0
  349. package/src/web-components/core/base-element.d.ts +197 -0
  350. package/src/web-components/core/base-element.js +289 -0
  351. package/src/web-components/core/base-element.js.map +1 -0
  352. package/src/web-components/core/index.d.ts +8 -0
  353. package/src/web-components/core/index.js +18 -0
  354. package/src/web-components/core/index.js.map +1 -0
  355. package/src/web-components/elements/fmcp-alert.d.ts +45 -0
  356. package/src/web-components/elements/fmcp-alert.js +93 -0
  357. package/src/web-components/elements/fmcp-alert.js.map +1 -0
  358. package/src/web-components/elements/fmcp-badge.d.ts +46 -0
  359. package/src/web-components/elements/fmcp-badge.js +99 -0
  360. package/src/web-components/elements/fmcp-badge.js.map +1 -0
  361. package/src/web-components/elements/fmcp-button.d.ts +124 -0
  362. package/src/web-components/elements/fmcp-button.js +233 -0
  363. package/src/web-components/elements/fmcp-button.js.map +1 -0
  364. package/src/web-components/elements/fmcp-card.d.ts +52 -0
  365. package/src/web-components/elements/fmcp-card.js +115 -0
  366. package/src/web-components/elements/fmcp-card.js.map +1 -0
  367. package/src/web-components/elements/fmcp-input.d.ts +95 -0
  368. package/src/web-components/elements/fmcp-input.js +248 -0
  369. package/src/web-components/elements/fmcp-input.js.map +1 -0
  370. package/src/web-components/elements/fmcp-select.d.ts +99 -0
  371. package/src/web-components/elements/fmcp-select.js +243 -0
  372. package/src/web-components/elements/fmcp-select.js.map +1 -0
  373. package/src/web-components/elements/index.d.ts +12 -0
  374. package/src/web-components/elements/index.js +34 -0
  375. package/src/web-components/elements/index.js.map +1 -0
  376. package/src/web-components/index.d.ts +49 -0
  377. package/src/web-components/index.js +75 -0
  378. package/src/web-components/index.js.map +1 -0
  379. package/src/web-components/register.d.ts +56 -0
  380. package/src/web-components/register.js +80 -0
  381. package/src/web-components/register.js.map +1 -0
  382. package/src/web-components/types.d.ts +121 -0
  383. package/src/web-components/types.js +25 -0
  384. package/src/web-components/types.js.map +1 -0
  385. package/src/widgets/index.d.ts +7 -0
  386. package/src/widgets/index.js +24 -0
  387. package/src/widgets/index.js.map +1 -0
  388. package/src/widgets/progress.d.ts +132 -0
  389. package/src/widgets/progress.js +303 -0
  390. package/src/widgets/progress.js.map +1 -0
  391. package/src/widgets/resource.d.ts +162 -0
  392. package/src/widgets/resource.js +340 -0
  393. package/src/widgets/resource.js.map +1 -0
@@ -0,0 +1,605 @@
1
+ "use strict";
2
+ /**
3
+ * Handlebars Built-in Helpers
4
+ *
5
+ * Provides a collection of helper functions for Handlebars templates.
6
+ * Includes formatting, escaping, conditionals, and iteration helpers.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.builtinHelpers = void 0;
12
+ exports.escapeHtml = escapeHtml;
13
+ exports.formatDate = formatDate;
14
+ exports.formatCurrency = formatCurrency;
15
+ exports.formatNumber = formatNumber;
16
+ exports.jsonEmbed = jsonEmbed;
17
+ exports.json = json;
18
+ exports.eq = eq;
19
+ exports.ne = ne;
20
+ exports.gt = gt;
21
+ exports.gte = gte;
22
+ exports.lt = lt;
23
+ exports.lte = lte;
24
+ exports.and = and;
25
+ exports.or = or;
26
+ exports.not = not;
27
+ exports.first = first;
28
+ exports.last = last;
29
+ exports.length = length;
30
+ exports.includes = includes;
31
+ exports.join = join;
32
+ exports.uppercase = uppercase;
33
+ exports.lowercase = lowercase;
34
+ exports.capitalize = capitalize;
35
+ exports.truncate = truncate;
36
+ exports.defaultValue = defaultValue;
37
+ exports.uniqueId = uniqueId;
38
+ exports.resetUniqueIdCounter = resetUniqueIdCounter;
39
+ exports.classNames = classNames;
40
+ /**
41
+ * Escape HTML special characters to prevent XSS.
42
+ *
43
+ * @param str - String to escape
44
+ * @returns Escaped string safe for HTML output
45
+ *
46
+ * @example
47
+ * ```handlebars
48
+ * {{escapeHtml output.userInput}}
49
+ * ```
50
+ */
51
+ function escapeHtml(str) {
52
+ if (str === null || str === undefined) {
53
+ return '';
54
+ }
55
+ const s = String(str);
56
+ return s
57
+ .replace(/&/g, '&')
58
+ .replace(/</g, '&lt;')
59
+ .replace(/>/g, '&gt;')
60
+ .replace(/"/g, '&quot;')
61
+ .replace(/'/g, '&#39;');
62
+ }
63
+ /**
64
+ * Format a date for display.
65
+ *
66
+ * @param date - Date object, ISO string, or timestamp
67
+ * @param format - Optional format string (default: localized date)
68
+ * @returns Formatted date string
69
+ *
70
+ * @example
71
+ * ```handlebars
72
+ * {{formatDate output.createdAt}}
73
+ * {{formatDate output.createdAt "short"}}
74
+ * {{formatDate output.createdAt "long"}}
75
+ * ```
76
+ */
77
+ function formatDate(date, format) {
78
+ if (date === null || date === undefined) {
79
+ return '';
80
+ }
81
+ let dateObj;
82
+ if (date instanceof Date) {
83
+ dateObj = date;
84
+ }
85
+ else if (typeof date === 'string' || typeof date === 'number') {
86
+ dateObj = new Date(date);
87
+ }
88
+ else {
89
+ return String(date);
90
+ }
91
+ if (isNaN(dateObj.getTime())) {
92
+ return String(date);
93
+ }
94
+ const options = {};
95
+ switch (format) {
96
+ case 'short':
97
+ options.dateStyle = 'short';
98
+ break;
99
+ case 'medium':
100
+ options.dateStyle = 'medium';
101
+ break;
102
+ case 'long':
103
+ options.dateStyle = 'long';
104
+ break;
105
+ case 'full':
106
+ options.dateStyle = 'full';
107
+ break;
108
+ case 'time':
109
+ options.timeStyle = 'short';
110
+ break;
111
+ case 'datetime':
112
+ options.dateStyle = 'medium';
113
+ options.timeStyle = 'short';
114
+ break;
115
+ case 'iso':
116
+ return dateObj.toISOString();
117
+ case 'relative':
118
+ return formatRelativeDate(dateObj);
119
+ default:
120
+ options.dateStyle = 'medium';
121
+ }
122
+ return new Intl.DateTimeFormat('en-US', options).format(dateObj);
123
+ }
124
+ /**
125
+ * Format a date relative to now.
126
+ */
127
+ function formatRelativeDate(date) {
128
+ const now = new Date();
129
+ const diffMs = now.getTime() - date.getTime();
130
+ const diffSecs = Math.floor(diffMs / 1000);
131
+ const diffMins = Math.floor(diffSecs / 60);
132
+ const diffHours = Math.floor(diffMins / 60);
133
+ const diffDays = Math.floor(diffHours / 24);
134
+ if (diffSecs < 60) {
135
+ return 'just now';
136
+ }
137
+ else if (diffMins < 60) {
138
+ return `${diffMins} minute${diffMins === 1 ? '' : 's'} ago`;
139
+ }
140
+ else if (diffHours < 24) {
141
+ return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
142
+ }
143
+ else if (diffDays < 7) {
144
+ return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
145
+ }
146
+ else {
147
+ return formatDate(date, 'medium');
148
+ }
149
+ }
150
+ /**
151
+ * Format a number as currency.
152
+ *
153
+ * @param amount - Numeric amount
154
+ * @param currency - ISO 4217 currency code (default: 'USD')
155
+ * @returns Formatted currency string
156
+ *
157
+ * @example
158
+ * ```handlebars
159
+ * {{formatCurrency output.price}}
160
+ * {{formatCurrency output.price "EUR"}}
161
+ * ```
162
+ */
163
+ function formatCurrency(amount, currency) {
164
+ if (amount === null || amount === undefined) {
165
+ return '';
166
+ }
167
+ const num = typeof amount === 'number' ? amount : parseFloat(String(amount));
168
+ if (isNaN(num)) {
169
+ return String(amount);
170
+ }
171
+ return new Intl.NumberFormat('en-US', {
172
+ style: 'currency',
173
+ currency: typeof currency === 'string' ? currency : 'USD',
174
+ }).format(num);
175
+ }
176
+ /**
177
+ * Format a number with locale-aware separators.
178
+ *
179
+ * @param value - Number to format
180
+ * @param decimals - Number of decimal places (optional)
181
+ * @returns Formatted number string
182
+ *
183
+ * @example
184
+ * ```handlebars
185
+ * {{formatNumber output.count}}
186
+ * {{formatNumber output.percentage 2}}
187
+ * ```
188
+ */
189
+ function formatNumber(value, decimals) {
190
+ if (value === null || value === undefined) {
191
+ return '';
192
+ }
193
+ const num = typeof value === 'number' ? value : parseFloat(String(value));
194
+ if (isNaN(num)) {
195
+ return String(value);
196
+ }
197
+ const options = {};
198
+ if (typeof decimals === 'number') {
199
+ options.minimumFractionDigits = decimals;
200
+ options.maximumFractionDigits = decimals;
201
+ }
202
+ return new Intl.NumberFormat('en-US', options).format(num);
203
+ }
204
+ /**
205
+ * Safely embed JSON data in HTML.
206
+ * Escapes script-breaking characters.
207
+ *
208
+ * @param data - Data to serialize
209
+ * @returns JSON string safe for embedding
210
+ *
211
+ * @example
212
+ * ```handlebars
213
+ * <script type="application/json" id="data">
214
+ * {{{jsonEmbed output}}}
215
+ * </script>
216
+ * ```
217
+ */
218
+ function jsonEmbed(data) {
219
+ const json = JSON.stringify(data ?? null);
220
+ // Escape characters that could break out of script tags
221
+ return json
222
+ .replace(/</g, '\\u003c')
223
+ .replace(/>/g, '\\u003e')
224
+ .replace(/&/g, '\\u0026')
225
+ .replace(/\u2028/g, '\\u2028')
226
+ .replace(/\u2029/g, '\\u2029');
227
+ }
228
+ /**
229
+ * Convert data to JSON string.
230
+ *
231
+ * @param data - Data to serialize
232
+ * @param pretty - Whether to pretty-print (optional)
233
+ * @returns JSON string
234
+ *
235
+ * @example
236
+ * ```handlebars
237
+ * <pre>{{json output true}}</pre>
238
+ * ```
239
+ */
240
+ function json(data, pretty) {
241
+ return JSON.stringify(data ?? null, null, pretty ? 2 : undefined);
242
+ }
243
+ /**
244
+ * Equality comparison.
245
+ *
246
+ * @param a - First value
247
+ * @param b - Second value
248
+ * @returns true if values are equal
249
+ *
250
+ * @example
251
+ * ```handlebars
252
+ * {{#if (eq output.status "active")}}Active{{/if}}
253
+ * ```
254
+ */
255
+ function eq(a, b) {
256
+ return a === b;
257
+ }
258
+ /**
259
+ * Inequality comparison.
260
+ *
261
+ * @param a - First value
262
+ * @param b - Second value
263
+ * @returns true if values are not equal
264
+ *
265
+ * @example
266
+ * ```handlebars
267
+ * {{#if (ne output.status "deleted")}}Visible{{/if}}
268
+ * ```
269
+ */
270
+ function ne(a, b) {
271
+ return a !== b;
272
+ }
273
+ /**
274
+ * Greater than comparison.
275
+ *
276
+ * @param a - First value
277
+ * @param b - Second value
278
+ * @returns true if a > b
279
+ *
280
+ * @example
281
+ * ```handlebars
282
+ * {{#if (gt output.count 10)}}Many items{{/if}}
283
+ * ```
284
+ */
285
+ function gt(a, b) {
286
+ return Number(a) > Number(b);
287
+ }
288
+ /**
289
+ * Greater than or equal comparison.
290
+ *
291
+ * @param a - First value
292
+ * @param b - Second value
293
+ * @returns true if a >= b
294
+ */
295
+ function gte(a, b) {
296
+ return Number(a) >= Number(b);
297
+ }
298
+ /**
299
+ * Less than comparison.
300
+ *
301
+ * @param a - First value
302
+ * @param b - Second value
303
+ * @returns true if a < b
304
+ *
305
+ * @example
306
+ * ```handlebars
307
+ * {{#if (lt output.remaining 5)}}Running low{{/if}}
308
+ * ```
309
+ */
310
+ function lt(a, b) {
311
+ return Number(a) < Number(b);
312
+ }
313
+ /**
314
+ * Less than or equal comparison.
315
+ *
316
+ * @param a - First value
317
+ * @param b - Second value
318
+ * @returns true if a <= b
319
+ */
320
+ function lte(a, b) {
321
+ return Number(a) <= Number(b);
322
+ }
323
+ /**
324
+ * Logical AND.
325
+ *
326
+ * @param a - First value
327
+ * @param b - Second value
328
+ * @returns true if both values are truthy
329
+ *
330
+ * @example
331
+ * ```handlebars
332
+ * {{#if (and output.enabled output.visible)}}Show content{{/if}}
333
+ * ```
334
+ */
335
+ function and(a, b) {
336
+ return Boolean(a) && Boolean(b);
337
+ }
338
+ /**
339
+ * Logical OR.
340
+ *
341
+ * @param a - First value
342
+ * @param b - Second value
343
+ * @returns true if either value is truthy
344
+ *
345
+ * @example
346
+ * ```handlebars
347
+ * {{#if (or output.featured output.promoted)}}Highlight{{/if}}
348
+ * ```
349
+ */
350
+ function or(a, b) {
351
+ return Boolean(a) || Boolean(b);
352
+ }
353
+ /**
354
+ * Logical NOT.
355
+ *
356
+ * @param value - Value to negate
357
+ * @returns Negated boolean
358
+ *
359
+ * @example
360
+ * ```handlebars
361
+ * {{#if (not output.hidden)}}Visible{{/if}}
362
+ * ```
363
+ */
364
+ function not(value) {
365
+ return !value;
366
+ }
367
+ /**
368
+ * Get the first element of an array.
369
+ *
370
+ * @param arr - Array
371
+ * @returns First element
372
+ *
373
+ * @example
374
+ * ```handlebars
375
+ * {{first output.items}}
376
+ * ```
377
+ */
378
+ function first(arr) {
379
+ if (!Array.isArray(arr))
380
+ return undefined;
381
+ return arr[0];
382
+ }
383
+ /**
384
+ * Get the last element of an array.
385
+ *
386
+ * @param arr - Array
387
+ * @returns Last element
388
+ *
389
+ * @example
390
+ * ```handlebars
391
+ * {{last output.items}}
392
+ * ```
393
+ */
394
+ function last(arr) {
395
+ if (!Array.isArray(arr))
396
+ return undefined;
397
+ return arr[arr.length - 1];
398
+ }
399
+ /**
400
+ * Get the length of an array or string.
401
+ *
402
+ * @param value - Array or string
403
+ * @returns Length
404
+ *
405
+ * @example
406
+ * ```handlebars
407
+ * {{length output.items}} items
408
+ * ```
409
+ */
410
+ function length(value) {
411
+ if (Array.isArray(value))
412
+ return value.length;
413
+ if (typeof value === 'string')
414
+ return value.length;
415
+ return 0;
416
+ }
417
+ /**
418
+ * Check if a value is in an array.
419
+ *
420
+ * @param arr - Array to search
421
+ * @param value - Value to find
422
+ * @returns true if value is in array
423
+ *
424
+ * @example
425
+ * ```handlebars
426
+ * {{#if (includes output.tags "featured")}}Featured{{/if}}
427
+ * ```
428
+ */
429
+ function includes(arr, value) {
430
+ if (!Array.isArray(arr))
431
+ return false;
432
+ return arr.includes(value);
433
+ }
434
+ /**
435
+ * Join array elements with a separator.
436
+ *
437
+ * @param arr - Array to join
438
+ * @param separator - Separator string (default: ', ')
439
+ * @returns Joined string
440
+ *
441
+ * @example
442
+ * ```handlebars
443
+ * {{join output.tags ", "}}
444
+ * ```
445
+ */
446
+ function join(arr, separator) {
447
+ if (!Array.isArray(arr))
448
+ return '';
449
+ return arr.join(typeof separator === 'string' ? separator : ', ');
450
+ }
451
+ /**
452
+ * Convert to uppercase.
453
+ *
454
+ * @param str - String to convert
455
+ * @returns Uppercase string
456
+ *
457
+ * @example
458
+ * ```handlebars
459
+ * {{uppercase output.status}}
460
+ * ```
461
+ */
462
+ function uppercase(str) {
463
+ return String(str ?? '').toUpperCase();
464
+ }
465
+ /**
466
+ * Convert to lowercase.
467
+ *
468
+ * @param str - String to convert
469
+ * @returns Lowercase string
470
+ *
471
+ * @example
472
+ * ```handlebars
473
+ * {{lowercase output.name}}
474
+ * ```
475
+ */
476
+ function lowercase(str) {
477
+ return String(str ?? '').toLowerCase();
478
+ }
479
+ /**
480
+ * Capitalize the first letter.
481
+ *
482
+ * @param str - String to capitalize
483
+ * @returns Capitalized string
484
+ *
485
+ * @example
486
+ * ```handlebars
487
+ * {{capitalize output.name}}
488
+ * ```
489
+ */
490
+ function capitalize(str) {
491
+ const s = String(str ?? '');
492
+ return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
493
+ }
494
+ /**
495
+ * Truncate a string to a maximum length.
496
+ *
497
+ * @param str - String to truncate
498
+ * @param maxLength - Maximum length
499
+ * @param suffix - Suffix to add if truncated (default: '...')
500
+ * @returns Truncated string
501
+ *
502
+ * @example
503
+ * ```handlebars
504
+ * {{truncate output.description 100}}
505
+ * ```
506
+ */
507
+ function truncate(str, maxLength, suffix) {
508
+ const s = String(str ?? '');
509
+ const len = typeof maxLength === 'number' ? maxLength : 50;
510
+ const suf = typeof suffix === 'string' ? suffix : '...';
511
+ if (s.length <= len)
512
+ return s;
513
+ return s.slice(0, len - suf.length) + suf;
514
+ }
515
+ /**
516
+ * Provide a default value if the input is falsy.
517
+ *
518
+ * @param value - Value to check
519
+ * @param defaultValue - Default value if falsy
520
+ * @returns Value or default
521
+ *
522
+ * @example
523
+ * ```handlebars
524
+ * {{default output.name "Unknown"}}
525
+ * ```
526
+ */
527
+ function defaultValue(value, defaultValue) {
528
+ return value || defaultValue;
529
+ }
530
+ /**
531
+ * Generate a unique ID.
532
+ *
533
+ * @param prefix - Optional prefix
534
+ * @returns Unique ID string
535
+ *
536
+ * @example
537
+ * ```handlebars
538
+ * <div id="{{uniqueId 'widget'}}">...</div>
539
+ * ```
540
+ */
541
+ let idCounter = 0;
542
+ function uniqueId(prefix) {
543
+ const id = ++idCounter;
544
+ return prefix ? `${prefix}-${id}` : `id-${id}`;
545
+ }
546
+ /**
547
+ * Reset the unique ID counter (for testing).
548
+ */
549
+ function resetUniqueIdCounter() {
550
+ idCounter = 0;
551
+ }
552
+ /**
553
+ * Conditionally join class names.
554
+ *
555
+ * @param classes - Class names (falsy values are filtered)
556
+ * @returns Space-separated class string
557
+ *
558
+ * @example
559
+ * ```handlebars
560
+ * <div class="{{classNames 'base' (if active 'active') (if disabled 'disabled')}}">
561
+ * ```
562
+ */
563
+ function classNames(...classes) {
564
+ return classes.filter(Boolean).map(String).join(' ');
565
+ }
566
+ /**
567
+ * Collection of all built-in helpers.
568
+ */
569
+ exports.builtinHelpers = {
570
+ // Escaping
571
+ escapeHtml,
572
+ // Formatting
573
+ formatDate,
574
+ formatCurrency,
575
+ formatNumber,
576
+ json,
577
+ jsonEmbed,
578
+ // Comparison
579
+ eq,
580
+ ne,
581
+ gt,
582
+ gte,
583
+ lt,
584
+ lte,
585
+ // Logical
586
+ and,
587
+ or,
588
+ not,
589
+ // Array
590
+ first,
591
+ last,
592
+ length,
593
+ includes,
594
+ join,
595
+ // String
596
+ uppercase,
597
+ lowercase,
598
+ capitalize,
599
+ truncate,
600
+ // Utility
601
+ default: defaultValue,
602
+ uniqueId,
603
+ classNames,
604
+ };
605
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/handlebars/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Handlebars Built-in Helpers\n *\n * Provides a collection of helper functions for Handlebars templates.\n * Includes formatting, escaping, conditionals, and iteration helpers.\n *\n * @packageDocumentation\n */\n\n/**\n * Helper function type for Handlebars.\n * Allows any function signature since Handlebars helpers vary widely.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HelperFunction = (...args: any[]) => string | boolean | unknown;\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param str - String to escape\n * @returns Escaped string safe for HTML output\n *\n * @example\n * ```handlebars\n * {{escapeHtml output.userInput}}\n * ```\n */\nexport function escapeHtml(str: unknown): string {\n if (str === null || str === undefined) {\n return '';\n }\n\n const s = String(str);\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n/**\n * Format a date for display.\n *\n * @param date - Date object, ISO string, or timestamp\n * @param format - Optional format string (default: localized date)\n * @returns Formatted date string\n *\n * @example\n * ```handlebars\n * {{formatDate output.createdAt}}\n * {{formatDate output.createdAt \"short\"}}\n * {{formatDate output.createdAt \"long\"}}\n * ```\n */\nexport function formatDate(date: unknown, format?: string): string {\n if (date === null || date === undefined) {\n return '';\n }\n\n let dateObj: Date;\n\n if (date instanceof Date) {\n dateObj = date;\n } else if (typeof date === 'string' || typeof date === 'number') {\n dateObj = new Date(date);\n } else {\n return String(date);\n }\n\n if (isNaN(dateObj.getTime())) {\n return String(date);\n }\n\n const options: Intl.DateTimeFormatOptions = {};\n\n switch (format) {\n case 'short':\n options.dateStyle = 'short';\n break;\n case 'medium':\n options.dateStyle = 'medium';\n break;\n case 'long':\n options.dateStyle = 'long';\n break;\n case 'full':\n options.dateStyle = 'full';\n break;\n case 'time':\n options.timeStyle = 'short';\n break;\n case 'datetime':\n options.dateStyle = 'medium';\n options.timeStyle = 'short';\n break;\n case 'iso':\n return dateObj.toISOString();\n case 'relative':\n return formatRelativeDate(dateObj);\n default:\n options.dateStyle = 'medium';\n }\n\n return new Intl.DateTimeFormat('en-US', options).format(dateObj);\n}\n\n/**\n * Format a date relative to now.\n */\nfunction formatRelativeDate(date: Date): string {\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSecs = Math.floor(diffMs / 1000);\n const diffMins = Math.floor(diffSecs / 60);\n const diffHours = Math.floor(diffMins / 60);\n const diffDays = Math.floor(diffHours / 24);\n\n if (diffSecs < 60) {\n return 'just now';\n } else if (diffMins < 60) {\n return `${diffMins} minute${diffMins === 1 ? '' : 's'} ago`;\n } else if (diffHours < 24) {\n return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;\n } else if (diffDays < 7) {\n return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;\n } else {\n return formatDate(date, 'medium');\n }\n}\n\n/**\n * Format a number as currency.\n *\n * @param amount - Numeric amount\n * @param currency - ISO 4217 currency code (default: 'USD')\n * @returns Formatted currency string\n *\n * @example\n * ```handlebars\n * {{formatCurrency output.price}}\n * {{formatCurrency output.price \"EUR\"}}\n * ```\n */\nexport function formatCurrency(amount: unknown, currency?: string): string {\n if (amount === null || amount === undefined) {\n return '';\n }\n\n const num = typeof amount === 'number' ? amount : parseFloat(String(amount));\n\n if (isNaN(num)) {\n return String(amount);\n }\n\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: typeof currency === 'string' ? currency : 'USD',\n }).format(num);\n}\n\n/**\n * Format a number with locale-aware separators.\n *\n * @param value - Number to format\n * @param decimals - Number of decimal places (optional)\n * @returns Formatted number string\n *\n * @example\n * ```handlebars\n * {{formatNumber output.count}}\n * {{formatNumber output.percentage 2}}\n * ```\n */\nexport function formatNumber(value: unknown, decimals?: number): string {\n if (value === null || value === undefined) {\n return '';\n }\n\n const num = typeof value === 'number' ? value : parseFloat(String(value));\n\n if (isNaN(num)) {\n return String(value);\n }\n\n const options: Intl.NumberFormatOptions = {};\n if (typeof decimals === 'number') {\n options.minimumFractionDigits = decimals;\n options.maximumFractionDigits = decimals;\n }\n\n return new Intl.NumberFormat('en-US', options).format(num);\n}\n\n/**\n * Safely embed JSON data in HTML.\n * Escapes script-breaking characters.\n *\n * @param data - Data to serialize\n * @returns JSON string safe for embedding\n *\n * @example\n * ```handlebars\n * <script type=\"application/json\" id=\"data\">\n * {{{jsonEmbed output}}}\n * </script>\n * ```\n */\nexport function jsonEmbed(data: unknown): string {\n const json = JSON.stringify(data ?? null);\n // Escape characters that could break out of script tags\n return json\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Convert data to JSON string.\n *\n * @param data - Data to serialize\n * @param pretty - Whether to pretty-print (optional)\n * @returns JSON string\n *\n * @example\n * ```handlebars\n * <pre>{{json output true}}</pre>\n * ```\n */\nexport function json(data: unknown, pretty?: boolean): string {\n return JSON.stringify(data ?? null, null, pretty ? 2 : undefined);\n}\n\n/**\n * Equality comparison.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if values are equal\n *\n * @example\n * ```handlebars\n * {{#if (eq output.status \"active\")}}Active{{/if}}\n * ```\n */\nexport function eq(a: unknown, b: unknown): boolean {\n return a === b;\n}\n\n/**\n * Inequality comparison.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if values are not equal\n *\n * @example\n * ```handlebars\n * {{#if (ne output.status \"deleted\")}}Visible{{/if}}\n * ```\n */\nexport function ne(a: unknown, b: unknown): boolean {\n return a !== b;\n}\n\n/**\n * Greater than comparison.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if a > b\n *\n * @example\n * ```handlebars\n * {{#if (gt output.count 10)}}Many items{{/if}}\n * ```\n */\nexport function gt(a: unknown, b: unknown): boolean {\n return Number(a) > Number(b);\n}\n\n/**\n * Greater than or equal comparison.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if a >= b\n */\nexport function gte(a: unknown, b: unknown): boolean {\n return Number(a) >= Number(b);\n}\n\n/**\n * Less than comparison.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if a < b\n *\n * @example\n * ```handlebars\n * {{#if (lt output.remaining 5)}}Running low{{/if}}\n * ```\n */\nexport function lt(a: unknown, b: unknown): boolean {\n return Number(a) < Number(b);\n}\n\n/**\n * Less than or equal comparison.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if a <= b\n */\nexport function lte(a: unknown, b: unknown): boolean {\n return Number(a) <= Number(b);\n}\n\n/**\n * Logical AND.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if both values are truthy\n *\n * @example\n * ```handlebars\n * {{#if (and output.enabled output.visible)}}Show content{{/if}}\n * ```\n */\nexport function and(a: unknown, b: unknown): boolean {\n return Boolean(a) && Boolean(b);\n}\n\n/**\n * Logical OR.\n *\n * @param a - First value\n * @param b - Second value\n * @returns true if either value is truthy\n *\n * @example\n * ```handlebars\n * {{#if (or output.featured output.promoted)}}Highlight{{/if}}\n * ```\n */\nexport function or(a: unknown, b: unknown): boolean {\n return Boolean(a) || Boolean(b);\n}\n\n/**\n * Logical NOT.\n *\n * @param value - Value to negate\n * @returns Negated boolean\n *\n * @example\n * ```handlebars\n * {{#if (not output.hidden)}}Visible{{/if}}\n * ```\n */\nexport function not(value: unknown): boolean {\n return !value;\n}\n\n/**\n * Get the first element of an array.\n *\n * @param arr - Array\n * @returns First element\n *\n * @example\n * ```handlebars\n * {{first output.items}}\n * ```\n */\nexport function first<T>(arr: T[]): T | undefined {\n if (!Array.isArray(arr)) return undefined;\n return arr[0];\n}\n\n/**\n * Get the last element of an array.\n *\n * @param arr - Array\n * @returns Last element\n *\n * @example\n * ```handlebars\n * {{last output.items}}\n * ```\n */\nexport function last<T>(arr: T[]): T | undefined {\n if (!Array.isArray(arr)) return undefined;\n return arr[arr.length - 1];\n}\n\n/**\n * Get the length of an array or string.\n *\n * @param value - Array or string\n * @returns Length\n *\n * @example\n * ```handlebars\n * {{length output.items}} items\n * ```\n */\nexport function length(value: unknown): number {\n if (Array.isArray(value)) return value.length;\n if (typeof value === 'string') return value.length;\n return 0;\n}\n\n/**\n * Check if a value is in an array.\n *\n * @param arr - Array to search\n * @param value - Value to find\n * @returns true if value is in array\n *\n * @example\n * ```handlebars\n * {{#if (includes output.tags \"featured\")}}Featured{{/if}}\n * ```\n */\nexport function includes(arr: unknown, value: unknown): boolean {\n if (!Array.isArray(arr)) return false;\n return arr.includes(value);\n}\n\n/**\n * Join array elements with a separator.\n *\n * @param arr - Array to join\n * @param separator - Separator string (default: ', ')\n * @returns Joined string\n *\n * @example\n * ```handlebars\n * {{join output.tags \", \"}}\n * ```\n */\nexport function join(arr: unknown, separator?: string): string {\n if (!Array.isArray(arr)) return '';\n return arr.join(typeof separator === 'string' ? separator : ', ');\n}\n\n/**\n * Convert to uppercase.\n *\n * @param str - String to convert\n * @returns Uppercase string\n *\n * @example\n * ```handlebars\n * {{uppercase output.status}}\n * ```\n */\nexport function uppercase(str: unknown): string {\n return String(str ?? '').toUpperCase();\n}\n\n/**\n * Convert to lowercase.\n *\n * @param str - String to convert\n * @returns Lowercase string\n *\n * @example\n * ```handlebars\n * {{lowercase output.name}}\n * ```\n */\nexport function lowercase(str: unknown): string {\n return String(str ?? '').toLowerCase();\n}\n\n/**\n * Capitalize the first letter.\n *\n * @param str - String to capitalize\n * @returns Capitalized string\n *\n * @example\n * ```handlebars\n * {{capitalize output.name}}\n * ```\n */\nexport function capitalize(str: unknown): string {\n const s = String(str ?? '');\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\n/**\n * Truncate a string to a maximum length.\n *\n * @param str - String to truncate\n * @param maxLength - Maximum length\n * @param suffix - Suffix to add if truncated (default: '...')\n * @returns Truncated string\n *\n * @example\n * ```handlebars\n * {{truncate output.description 100}}\n * ```\n */\nexport function truncate(str: unknown, maxLength?: number, suffix?: string): string {\n const s = String(str ?? '');\n const len = typeof maxLength === 'number' ? maxLength : 50;\n const suf = typeof suffix === 'string' ? suffix : '...';\n\n if (s.length <= len) return s;\n return s.slice(0, len - suf.length) + suf;\n}\n\n/**\n * Provide a default value if the input is falsy.\n *\n * @param value - Value to check\n * @param defaultValue - Default value if falsy\n * @returns Value or default\n *\n * @example\n * ```handlebars\n * {{default output.name \"Unknown\"}}\n * ```\n */\nexport function defaultValue(value: unknown, defaultValue: unknown): unknown {\n return value || defaultValue;\n}\n\n/**\n * Generate a unique ID.\n *\n * @param prefix - Optional prefix\n * @returns Unique ID string\n *\n * @example\n * ```handlebars\n * <div id=\"{{uniqueId 'widget'}}\">...</div>\n * ```\n */\nlet idCounter = 0;\nexport function uniqueId(prefix?: string): string {\n const id = ++idCounter;\n return prefix ? `${prefix}-${id}` : `id-${id}`;\n}\n\n/**\n * Reset the unique ID counter (for testing).\n */\nexport function resetUniqueIdCounter(): void {\n idCounter = 0;\n}\n\n/**\n * Conditionally join class names.\n *\n * @param classes - Class names (falsy values are filtered)\n * @returns Space-separated class string\n *\n * @example\n * ```handlebars\n * <div class=\"{{classNames 'base' (if active 'active') (if disabled 'disabled')}}\">\n * ```\n */\nexport function classNames(...classes: unknown[]): string {\n return classes.filter(Boolean).map(String).join(' ');\n}\n\n/**\n * Collection of all built-in helpers.\n */\nexport const builtinHelpers: Record<string, HelperFunction> = {\n // Escaping\n escapeHtml,\n\n // Formatting\n formatDate,\n formatCurrency,\n formatNumber,\n json,\n jsonEmbed,\n\n // Comparison\n eq,\n ne,\n gt,\n gte,\n lt,\n lte,\n\n // Logical\n and,\n or,\n not,\n\n // Array\n first,\n last,\n length,\n includes,\n join,\n\n // String\n uppercase,\n lowercase,\n capitalize,\n truncate,\n\n // Utility\n default: defaultValue,\n uniqueId,\n classNames,\n};\n"]}