brut 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (689) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +24 -3
  3. data/.nvim.lua +1 -0
  4. data/Dockerfile.dx +12 -3
  5. data/Gemfile.lock +9 -7
  6. data/README.md +0 -7
  7. data/Rakefile +6 -4
  8. data/bin/dev +20 -0
  9. data/bin/docs +27 -0
  10. data/bin/setup +47 -1
  11. data/brut-css/.nvim.lua +1 -0
  12. data/brut-css/README.md +28 -0
  13. data/brut-css/bin/build +31 -0
  14. data/brut-css/bin/dev +1 -0
  15. data/brut-css/bin/docs +15 -0
  16. data/brut-css/bin/setup +5 -0
  17. data/brut-css/config/media-queries-all.css +15 -0
  18. data/brut-css/config/media-queries-minimal.css +5 -0
  19. data/brut-css/config/postcss.config.cjs +7 -0
  20. data/brut-css/config/pseudo-classes-all.css +9 -0
  21. data/brut-css/dx +1 -0
  22. data/brut-css/package-lock.json +3217 -0
  23. data/brut-css/package.json +36 -0
  24. data/brut-css/src/css/appearance.css +145 -0
  25. data/brut-css/src/css/border.css +522 -0
  26. data/brut-css/src/css/colors.css +3502 -0
  27. data/brut-css/src/css/dimensions.css +548 -0
  28. data/brut-css/src/css/flex.css +179 -0
  29. data/brut-css/src/css/index.css +13 -0
  30. data/brut-css/src/css/layout.css +120 -0
  31. data/brut-css/src/css/list.css +41 -0
  32. data/brut-css/src/css/positioning.css +354 -0
  33. data/brut-css/src/css/properties/colors.css +455 -0
  34. data/brut-css/src/css/properties/index.css +3 -0
  35. data/brut-css/src/css/properties/spacing.css +140 -0
  36. data/brut-css/src/css/properties/typography.css +224 -0
  37. data/brut-css/src/css/reset.css +107 -0
  38. data/brut-css/src/css/spacing.css +585 -0
  39. data/brut-css/src/css/typography.css +519 -0
  40. data/brut-css/src/css/utils.css +104 -0
  41. data/brut-css/src/docs/1_getting-started/1_overview.md +46 -0
  42. data/brut-css/src/docs/1_getting-started/2_installation.md +25 -0
  43. data/brut-css/src/docs/1_getting-started/3_core-concepts.md +75 -0
  44. data/brut-css/src/docs/1_getting-started/4_simple-example.md +132 -0
  45. data/brut-css/src/docs/1_getting-started/page.html.ejs +10 -0
  46. data/brut-css/src/docs/2_properties/page.html.ejs +71 -0
  47. data/brut-css/src/docs/3_classes/color-demo.html.ejs +31 -0
  48. data/brut-css/src/docs/3_classes/page.html.ejs +87 -0
  49. data/brut-css/src/docs/4_customization/1_design-system.md +36 -0
  50. data/brut-css/src/docs/4_customization/2_breakpoints.md +75 -0
  51. data/brut-css/src/docs/4_customization/3_pseudo-classes.md +74 -0
  52. data/brut-css/src/docs/4_customization/4_advanced-configuration.md +40 -0
  53. data/brut-css/src/docs/4_customization/page.html.ejs +10 -0
  54. data/brut-css/src/docs/docs.css +98 -0
  55. data/brut-css/src/docs/includes/body-and-header.html.ejs +30 -0
  56. data/brut-css/src/docs/includes/footer-and-rest.html.ejs +9 -0
  57. data/brut-css/src/docs/includes/head.html.ejs +5 -0
  58. data/brut-css/src/docs/includes/nav.html.ejs +10 -0
  59. data/brut-css/src/docs/index.html.ejs +32 -0
  60. data/brut-css/src/docs/prism-twilight.min.css +1 -0
  61. data/brut-css/src/js/Logger.js +71 -0
  62. data/brut-css/src/js/build.js +111 -0
  63. data/brut-css/src/js/cli/CLIArgError.js +7 -0
  64. data/brut-css/src/js/cli/Debug.js +27 -0
  65. data/brut-css/src/js/cli/DocsDir.js +16 -0
  66. data/brut-css/src/js/cli/DocsTemplateSourceDir.js +16 -0
  67. data/brut-css/src/js/cli/InputFile.js +31 -0
  68. data/brut-css/src/js/cli/MediaQueryConfigFile.js +10 -0
  69. data/brut-css/src/js/cli/OutputFile.js +22 -0
  70. data/brut-css/src/js/cli/ParsedArg.js +17 -0
  71. data/brut-css/src/js/cli/PathToBrutCSSRoot.js +19 -0
  72. data/brut-css/src/js/cli/PseudoClassConfigFile.js +11 -0
  73. data/brut-css/src/js/cli.js +108 -0
  74. data/brut-css/src/js/docGenerator.js +467 -0
  75. data/brut-css/src/js/mediaQueryConfigParser.js +98 -0
  76. data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +49 -0
  77. data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +42 -0
  78. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +9 -0
  79. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +185 -0
  80. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +8 -0
  81. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +7 -0
  82. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +73 -0
  83. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +9 -0
  84. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +4 -0
  85. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +8 -0
  86. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +12 -0
  87. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +4 -0
  88. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +8 -0
  89. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +5 -0
  90. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +9 -0
  91. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +49 -0
  92. data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +45 -0
  93. data/brut-css/src/js/pseudoClassConfigParser.js +145 -0
  94. data/brut-js/.projections.json +10 -0
  95. data/brut-js/README.md +118 -0
  96. data/brut-js/bin/build +10 -0
  97. data/brut-js/bin/ci +5 -0
  98. data/brut-js/bin/setup +5 -0
  99. data/brut-js/docs/README.md +8 -0
  100. data/brut-js/docs/jsdoc-plugins/customElementTag.js +8 -0
  101. data/brut-js/docs/jsdoc-theme/publish.js +692 -0
  102. data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +25 -0
  103. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +202 -0
  104. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +2 -0
  105. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +28 -0
  106. data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +327 -0
  107. data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +111 -0
  108. data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +132 -0
  109. data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +10 -0
  110. data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +199 -0
  111. data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +143 -0
  112. data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +2 -0
  113. data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +13 -0
  114. data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +32 -0
  115. data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +38 -0
  116. data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +14 -0
  117. data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +38 -0
  118. data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +131 -0
  119. data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +14 -0
  120. data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +131 -0
  121. data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +108 -0
  122. data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +19 -0
  123. data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +8 -0
  124. data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +19 -0
  125. data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +7 -0
  126. data/brut-js/docs/jsdoc.config.json +23 -0
  127. data/brut-js/docs/package-lock.json +343 -0
  128. data/brut-js/docs/package.json +7 -0
  129. data/brut-js/package-lock.json +2171 -0
  130. data/brut-js/package.json +32 -0
  131. data/brut-js/specs/AjaxSubmit.spec.js +256 -0
  132. data/brut-js/specs/Autosubmit.spec.js +127 -0
  133. data/brut-js/specs/ConfirmSubmit.spec.js +193 -0
  134. data/brut-js/specs/ConstraintViolationMessage.spec.js +33 -0
  135. data/brut-js/specs/ConstraintViolationMessages.spec.js +29 -0
  136. data/brut-js/specs/CopyToClipboard.spec.js +35 -0
  137. data/brut-js/specs/Form.spec.js +181 -0
  138. data/brut-js/specs/I18nTranslation.spec.js +19 -0
  139. data/brut-js/specs/LocaleDetection.spec.js +22 -0
  140. data/brut-js/specs/Message.spec.js +15 -0
  141. data/brut-js/specs/SpecHelper.js +23 -0
  142. data/brut-js/specs/Tabs.spec.js +41 -0
  143. data/brut-js/specs/config/asset_metadata.json +7 -0
  144. data/brut-js/src/AjaxSubmit.js +384 -0
  145. data/brut-js/src/Autosubmit.js +63 -0
  146. data/brut-js/src/BaseCustomElement.js +261 -0
  147. data/brut-js/src/ConfirmSubmit.js +116 -0
  148. data/brut-js/src/ConfirmationDialog.js +143 -0
  149. data/brut-js/src/ConstraintViolationMessage.js +125 -0
  150. data/brut-js/src/ConstraintViolationMessages.js +98 -0
  151. data/brut-js/src/CopyToClipboard.js +96 -0
  152. data/brut-js/src/Form.js +151 -0
  153. data/brut-js/src/I18nTranslation.js +61 -0
  154. data/brut-js/src/LocaleDetection.js +117 -0
  155. data/brut-js/src/Logger.js +90 -0
  156. data/brut-js/src/Message.js +56 -0
  157. data/brut-js/src/RichString.js +113 -0
  158. data/brut-js/src/Tabs.js +168 -0
  159. data/brut-js/src/Tracing.js +247 -0
  160. data/brut-js/src/appForTestingOnly.js +15 -0
  161. data/brut-js/src/index.js +130 -0
  162. data/brut-js/src/testing/AssetMetadata.js +35 -0
  163. data/brut-js/src/testing/AssetMetadataLoader.js +25 -0
  164. data/brut-js/src/testing/CustomElementTest.js +235 -0
  165. data/brut-js/src/testing/DOMCreator.js +45 -0
  166. data/brut-js/src/testing/index.js +48 -0
  167. data/brutrb.com/.vitepress/config.mjs +106 -0
  168. data/brutrb.com/.vitepress/plugins/jsdocLinker.js +34 -0
  169. data/brutrb.com/.vitepress/plugins/rdocLinker.js +18 -0
  170. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  171. data/brutrb.com/.vitepress/theme/index.js +18 -0
  172. data/brutrb.com/.vitepress/theme/style.css +149 -0
  173. data/brutrb.com/ai.md +68 -0
  174. data/brutrb.com/assets.md +138 -0
  175. data/brutrb.com/bin/build +5 -0
  176. data/brutrb.com/bin/deploy +7 -0
  177. data/brutrb.com/bin/dev +5 -0
  178. data/brutrb.com/bin/setup +5 -0
  179. data/brutrb.com/brut-js.md +117 -0
  180. data/brutrb.com/business-logic.md +55 -0
  181. data/brutrb.com/cli.md +278 -0
  182. data/brutrb.com/components.md +243 -0
  183. data/brutrb.com/configuration.md +257 -0
  184. data/brutrb.com/css.md +103 -0
  185. data/brutrb.com/custom-element-tests.md +149 -0
  186. data/brutrb.com/database-access.md +201 -0
  187. data/brutrb.com/database-schema.md +312 -0
  188. data/brutrb.com/deployment.md +66 -0
  189. data/brutrb.com/dev-environment.md +179 -0
  190. data/brutrb.com/doc-conventions.md +39 -0
  191. data/brutrb.com/end-to-end-tests.md +174 -0
  192. data/brutrb.com/flash-and-session.md +224 -0
  193. data/brutrb.com/forms.md +866 -0
  194. data/brutrb.com/getting-started.md +66 -0
  195. data/brutrb.com/handlers.md +153 -0
  196. data/brutrb.com/hooks.md +178 -0
  197. data/brutrb.com/i18n.md +188 -0
  198. data/brutrb.com/images/Makefile +10 -0
  199. data/brutrb.com/images/dev-env-overview.dot +54 -0
  200. data/brutrb.com/images/dev-env-overview.png +0 -0
  201. data/brutrb.com/images/dev-env-protocol.dot +37 -0
  202. data/brutrb.com/images/dev-env-protocol.png +0 -0
  203. data/brutrb.com/images/logo-300.png +0 -0
  204. data/brutrb.com/images/logo.png +0 -0
  205. data/brutrb.com/images/overview.graffle +0 -0
  206. data/brutrb.com/images/overview.png +0 -0
  207. data/brutrb.com/images/spa.dot +19 -0
  208. data/brutrb.com/images/spa.png +0 -0
  209. data/brutrb.com/images/workspace-protocol.dot +44 -0
  210. data/brutrb.com/images/workspace-protocol.png +0 -0
  211. data/brutrb.com/index.md +36 -0
  212. data/brutrb.com/instrumentation.md +183 -0
  213. data/brutrb.com/javascript.md +122 -0
  214. data/brutrb.com/jobs.md +14 -0
  215. data/brutrb.com/keyword-injection.md +237 -0
  216. data/brutrb.com/markdown-examples.md +85 -0
  217. data/brutrb.com/middleware.md +80 -0
  218. data/brutrb.com/not-released.md +5 -0
  219. data/brutrb.com/overview.md +404 -0
  220. data/brutrb.com/package-lock.json +2404 -0
  221. data/brutrb.com/package.json +11 -0
  222. data/brutrb.com/pages.md +378 -0
  223. data/brutrb.com/public/images/logo-300.png +0 -0
  224. data/brutrb.com/public/images/logo.png +0 -0
  225. data/brutrb.com/routes.md +215 -0
  226. data/brutrb.com/security.md +105 -0
  227. data/brutrb.com/seed-data.md +63 -0
  228. data/brutrb.com/space-time-continuum.md +85 -0
  229. data/brutrb.com/tutorial.md +3 -0
  230. data/brutrb.com/unit-tests.md +148 -0
  231. data/docker-compose.dx.yml +6 -3
  232. data/docs/404.html +21 -0
  233. data/docs/CNAME +1 -0
  234. data/docs/ai.html +24 -0
  235. data/docs/api/Brut/BackEnd/SeedData.html +493 -0
  236. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +214 -0
  237. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +125 -0
  238. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +125 -0
  239. data/docs/api/Brut/BackEnd/Sidekiq.html +125 -0
  240. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +414 -0
  241. data/docs/api/Brut/BackEnd/Validators.html +128 -0
  242. data/docs/api/Brut/BackEnd.html +132 -0
  243. data/docs/api/Brut/CLI/App.html +1576 -0
  244. data/docs/api/Brut/CLI/AppRunner.html +491 -0
  245. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +264 -0
  246. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +306 -0
  247. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +262 -0
  248. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +314 -0
  249. data/docs/api/Brut/CLI/Apps/BuildAssets.html +183 -0
  250. data/docs/api/Brut/CLI/Apps/DB/Create.html +365 -0
  251. data/docs/api/Brut/CLI/Apps/DB/Drop.html +357 -0
  252. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +383 -0
  253. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +335 -0
  254. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +329 -0
  255. data/docs/api/Brut/CLI/Apps/DB/Seed.html +347 -0
  256. data/docs/api/Brut/CLI/Apps/DB/Status.html +383 -0
  257. data/docs/api/Brut/CLI/Apps/DB.html +183 -0
  258. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +303 -0
  259. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +512 -0
  260. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +398 -0
  261. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +374 -0
  262. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +410 -0
  263. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +262 -0
  264. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +303 -0
  265. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +480 -0
  266. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +450 -0
  267. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +380 -0
  268. data/docs/api/Brut/CLI/Apps/Scaffold.html +253 -0
  269. data/docs/api/Brut/CLI/Apps/Test/Audit.html +464 -0
  270. data/docs/api/Brut/CLI/Apps/Test/E2e.html +407 -0
  271. data/docs/api/Brut/CLI/Apps/Test/JS.html +262 -0
  272. data/docs/api/Brut/CLI/Apps/Test/Run.html +578 -0
  273. data/docs/api/Brut/CLI/Apps/Test.html +253 -0
  274. data/docs/api/Brut/CLI/Apps.html +125 -0
  275. data/docs/api/Brut/CLI/Command.html +2342 -0
  276. data/docs/api/Brut/CLI/Error.html +139 -0
  277. data/docs/api/Brut/CLI/ExecutionResults/Result.html +664 -0
  278. data/docs/api/Brut/CLI/ExecutionResults.html +675 -0
  279. data/docs/api/Brut/CLI/Executor.html +430 -0
  280. data/docs/api/Brut/CLI/InvalidOption.html +245 -0
  281. data/docs/api/Brut/CLI/Options.html +753 -0
  282. data/docs/api/Brut/CLI/Output.html +699 -0
  283. data/docs/api/Brut/CLI/SystemExecError.html +451 -0
  284. data/docs/api/Brut/CLI.html +263 -0
  285. data/docs/api/Brut/FactoryBot.html +225 -0
  286. data/docs/api/Brut/Framework/App.html +1097 -0
  287. data/docs/api/Brut/Framework/Config.html +1045 -0
  288. data/docs/api/Brut/Framework/Container.html +1379 -0
  289. data/docs/api/Brut/Framework/Error.html +140 -0
  290. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +144 -0
  291. data/docs/api/Brut/Framework/Errors/Bug.html +234 -0
  292. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +257 -0
  293. data/docs/api/Brut/Framework/Errors/MissingParameter.html +273 -0
  294. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +471 -0
  295. data/docs/api/Brut/Framework/Errors/NotFound.html +308 -0
  296. data/docs/api/Brut/Framework/Errors/NotImplemented.html +234 -0
  297. data/docs/api/Brut/Framework/Errors.html +328 -0
  298. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +392 -0
  299. data/docs/api/Brut/Framework/MCP.html +861 -0
  300. data/docs/api/Brut/Framework/ProjectEnvironment.html +648 -0
  301. data/docs/api/Brut/Framework.html +129 -0
  302. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +317 -0
  303. data/docs/api/Brut/FrontEnd/Component/Helpers.html +326 -0
  304. data/docs/api/Brut/FrontEnd/Component.html +365 -0
  305. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +470 -0
  306. data/docs/api/Brut/FrontEnd/Components/FormTag.html +518 -0
  307. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +317 -0
  308. data/docs/api/Brut/FrontEnd/Components/Input.html +195 -0
  309. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +339 -0
  310. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +660 -0
  311. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +417 -0
  312. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +918 -0
  313. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +651 -0
  314. data/docs/api/Brut/FrontEnd/Components/Inputs.html +125 -0
  315. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +367 -0
  316. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +336 -0
  317. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +655 -0
  318. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +352 -0
  319. data/docs/api/Brut/FrontEnd/Components.html +135 -0
  320. data/docs/api/Brut/FrontEnd/Download.html +467 -0
  321. data/docs/api/Brut/FrontEnd/Flash.html +1150 -0
  322. data/docs/api/Brut/FrontEnd/Form.html +1157 -0
  323. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +634 -0
  324. data/docs/api/Brut/FrontEnd/Forms/Input.html +615 -0
  325. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +547 -0
  326. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1318 -0
  327. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +609 -0
  328. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +587 -0
  329. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +613 -0
  330. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +582 -0
  331. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +609 -0
  332. data/docs/api/Brut/FrontEnd/Forms.html +127 -0
  333. data/docs/api/Brut/FrontEnd/GenericResponse.html +377 -0
  334. data/docs/api/Brut/FrontEnd/Handler.html +442 -0
  335. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +318 -0
  336. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +336 -0
  337. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +399 -0
  338. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +354 -0
  339. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +151 -0
  340. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +315 -0
  341. data/docs/api/Brut/FrontEnd/Handlers.html +125 -0
  342. data/docs/api/Brut/FrontEnd/HandlingResults.html +339 -0
  343. data/docs/api/Brut/FrontEnd/HttpMethod.html +661 -0
  344. data/docs/api/Brut/FrontEnd/HttpStatus.html +496 -0
  345. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +284 -0
  346. data/docs/api/Brut/FrontEnd/Layout.html +318 -0
  347. data/docs/api/Brut/FrontEnd/Middleware.html +135 -0
  348. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +288 -0
  349. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +292 -0
  350. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +324 -0
  351. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +372 -0
  352. data/docs/api/Brut/FrontEnd/Middlewares.html +125 -0
  353. data/docs/api/Brut/FrontEnd/Page.html +773 -0
  354. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +797 -0
  355. data/docs/api/Brut/FrontEnd/Pages.html +125 -0
  356. data/docs/api/Brut/FrontEnd/RequestContext.html +1312 -0
  357. data/docs/api/Brut/FrontEnd/RouteHook.html +424 -0
  358. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +242 -0
  359. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +249 -0
  360. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +264 -0
  361. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +261 -0
  362. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +284 -0
  363. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +252 -0
  364. data/docs/api/Brut/FrontEnd/RouteHooks.html +115 -0
  365. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +227 -0
  366. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +305 -0
  367. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +324 -0
  368. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +319 -0
  369. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +315 -0
  370. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +315 -0
  371. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +327 -0
  372. data/docs/api/Brut/FrontEnd/Routing/Route.html +761 -0
  373. data/docs/api/Brut/FrontEnd/Routing.html +927 -0
  374. data/docs/api/Brut/FrontEnd/Session.html +1195 -0
  375. data/docs/api/Brut/FrontEnd.html +134 -0
  376. data/docs/api/Brut/I18n/BaseMethods.html +931 -0
  377. data/docs/api/Brut/I18n/ForBackEnd.html +302 -0
  378. data/docs/api/Brut/I18n/ForCLI.html +302 -0
  379. data/docs/api/Brut/I18n/ForHTML.html +296 -0
  380. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +316 -0
  381. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +930 -0
  382. data/docs/api/Brut/I18n.html +127 -0
  383. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +435 -0
  384. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +286 -0
  385. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +302 -0
  386. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +864 -0
  387. data/docs/api/Brut/Instrumentation.html +126 -0
  388. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +532 -0
  389. data/docs/api/Brut/SinatraHelpers.html +281 -0
  390. data/docs/api/Brut/SpecSupport/ClockSupport.html +383 -0
  391. data/docs/api/Brut/SpecSupport/ComponentSupport.html +502 -0
  392. data/docs/api/Brut/SpecSupport/E2ETestServer.html +503 -0
  393. data/docs/api/Brut/SpecSupport/E2eSupport.html +142 -0
  394. data/docs/api/Brut/SpecSupport/EnhancedNode.html +403 -0
  395. data/docs/api/Brut/SpecSupport/FlashSupport.html +278 -0
  396. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +401 -0
  397. data/docs/api/Brut/SpecSupport/GeneralSupport.html +195 -0
  398. data/docs/api/Brut/SpecSupport/HandlerSupport.html +160 -0
  399. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +553 -0
  400. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +439 -0
  401. data/docs/api/Brut/SpecSupport/Matchers.html +125 -0
  402. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +335 -0
  403. data/docs/api/Brut/SpecSupport/RSpecSetup.html +602 -0
  404. data/docs/api/Brut/SpecSupport/SessionSupport.html +196 -0
  405. data/docs/api/Brut/SpecSupport.html +129 -0
  406. data/docs/api/Brut.html +225 -0
  407. data/docs/api/Clock.html +603 -0
  408. data/docs/api/RichString.html +968 -0
  409. data/docs/api/SemanticLogger/Appender/Async.html +219 -0
  410. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +115 -0
  411. data/docs/api/Sequel/Extensions/BrutMigrations.html +533 -0
  412. data/docs/api/Sequel/Extensions.html +117 -0
  413. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +105 -0
  414. data/docs/api/Sequel/Plugins/CreatedAt.html +125 -0
  415. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +207 -0
  416. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +186 -0
  417. data/docs/api/Sequel/Plugins/ExternalId.html +218 -0
  418. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +202 -0
  419. data/docs/api/Sequel/Plugins/FindBang.html +125 -0
  420. data/docs/api/Sequel/Plugins.html +117 -0
  421. data/docs/api/Sequel.html +117 -0
  422. data/docs/api/_index.html +1553 -0
  423. data/docs/api/class_list.html +54 -0
  424. data/docs/api/css/common.css +1 -0
  425. data/docs/api/css/full_list.css +58 -0
  426. data/docs/api/css/style.css +503 -0
  427. data/docs/api/file.README.html +127 -0
  428. data/docs/api/file_list.html +59 -0
  429. data/docs/api/frames.html +22 -0
  430. data/docs/api/index.html +127 -0
  431. data/docs/api/js/app.js +344 -0
  432. data/docs/api/js/full_list.js +242 -0
  433. data/docs/api/js/jquery.js +4 -0
  434. data/docs/api/method_list.html +3998 -0
  435. data/docs/api/top-level-namespace.html +112 -0
  436. data/docs/assets/ai.md.tZrjP9im.js +1 -0
  437. data/docs/assets/ai.md.tZrjP9im.lean.js +1 -0
  438. data/docs/assets/app.D_yaTITQ.js +1 -0
  439. data/docs/assets/assets.md.D3wunzLx.js +19 -0
  440. data/docs/assets/assets.md.D3wunzLx.lean.js +1 -0
  441. data/docs/assets/brut-js.md.o2DAO2s2.js +12 -0
  442. data/docs/assets/brut-js.md.o2DAO2s2.lean.js +1 -0
  443. data/docs/assets/business-logic.md.BY4hGy0m.js +1 -0
  444. data/docs/assets/business-logic.md.BY4hGy0m.lean.js +1 -0
  445. data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +1 -0
  446. data/docs/assets/chunks/VPLocalSearchBox.B2-ZzyTY.js +8 -0
  447. data/docs/assets/chunks/framework.1L-BeKqY.js +18 -0
  448. data/docs/assets/chunks/theme.CfGFVRvE.js +2 -0
  449. data/docs/assets/cli.md.RmeA2b0i.js +127 -0
  450. data/docs/assets/cli.md.RmeA2b0i.lean.js +1 -0
  451. data/docs/assets/components.md.eCttGlN-.js +104 -0
  452. data/docs/assets/components.md.eCttGlN-.lean.js +1 -0
  453. data/docs/assets/configuration.md.BRriU0cL.js +78 -0
  454. data/docs/assets/configuration.md.BRriU0cL.lean.js +1 -0
  455. data/docs/assets/css.md.DJgj2clw.js +21 -0
  456. data/docs/assets/css.md.DJgj2clw.lean.js +1 -0
  457. data/docs/assets/custom-element-tests.md.BrYJQEl3.js +69 -0
  458. data/docs/assets/custom-element-tests.md.BrYJQEl3.lean.js +1 -0
  459. data/docs/assets/database-access.md.C7l-Vuvb.js +63 -0
  460. data/docs/assets/database-access.md.C7l-Vuvb.lean.js +1 -0
  461. data/docs/assets/database-schema.md.BUjR0VS1.js +63 -0
  462. data/docs/assets/database-schema.md.BUjR0VS1.lean.js +1 -0
  463. data/docs/assets/deployment.md.Dbka4OTr.js +1 -0
  464. data/docs/assets/deployment.md.Dbka4OTr.lean.js +1 -0
  465. data/docs/assets/dev-env-overview.Gj7NWM8-.png +0 -0
  466. data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
  467. data/docs/assets/dev-environment.md.BNc8AYiK.js +11 -0
  468. data/docs/assets/dev-environment.md.BNc8AYiK.lean.js +1 -0
  469. data/docs/assets/doc-conventions.md.DCfRXXi-.js +1 -0
  470. data/docs/assets/doc-conventions.md.DCfRXXi-.lean.js +1 -0
  471. data/docs/assets/end-to-end-tests.md.yfQHC0b5.js +26 -0
  472. data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +1 -0
  473. data/docs/assets/flash-and-session.md.BXY8RvT0.js +93 -0
  474. data/docs/assets/flash-and-session.md.BXY8RvT0.lean.js +1 -0
  475. data/docs/assets/forms.md.CBTYQ_Cz.js +379 -0
  476. data/docs/assets/forms.md.CBTYQ_Cz.lean.js +1 -0
  477. data/docs/assets/getting-started.md.Bz2s1Vjb.js +2 -0
  478. data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +1 -0
  479. data/docs/assets/handlers.md.089DVD3v.js +69 -0
  480. data/docs/assets/handlers.md.089DVD3v.lean.js +1 -0
  481. data/docs/assets/hooks.md.C4-moMny.js +80 -0
  482. data/docs/assets/hooks.md.C4-moMny.lean.js +1 -0
  483. data/docs/assets/i18n.md.Do9i1qWl.js +23 -0
  484. data/docs/assets/i18n.md.Do9i1qWl.lean.js +1 -0
  485. data/docs/assets/index.md.B28EwVpq.js +1 -0
  486. data/docs/assets/index.md.B28EwVpq.lean.js +1 -0
  487. data/docs/assets/instrumentation.md.CL6ax7nT.js +35 -0
  488. data/docs/assets/instrumentation.md.CL6ax7nT.lean.js +1 -0
  489. data/docs/assets/javascript.md.GWbhRS51.js +31 -0
  490. data/docs/assets/javascript.md.GWbhRS51.lean.js +1 -0
  491. data/docs/assets/jobs.md.S-2amAYp.js +1 -0
  492. data/docs/assets/jobs.md.S-2amAYp.lean.js +1 -0
  493. data/docs/assets/keyword-injection.md.Dt2tKREs.js +25 -0
  494. data/docs/assets/keyword-injection.md.Dt2tKREs.lean.js +1 -0
  495. data/docs/assets/markdown-examples.md.CCFEQO44.js +33 -0
  496. data/docs/assets/markdown-examples.md.CCFEQO44.lean.js +1 -0
  497. data/docs/assets/middleware.md.Czz_UlJN.js +20 -0
  498. data/docs/assets/middleware.md.Czz_UlJN.lean.js +1 -0
  499. data/docs/assets/not-released.md.BBy28McC.js +1 -0
  500. data/docs/assets/not-released.md.BBy28McC.lean.js +1 -0
  501. data/docs/assets/overview.Da81cB9R.png +0 -0
  502. data/docs/assets/overview.md.CDalkuxV.js +133 -0
  503. data/docs/assets/overview.md.CDalkuxV.lean.js +1 -0
  504. data/docs/assets/pages.md.BE3kfOc5.js +122 -0
  505. data/docs/assets/pages.md.BE3kfOc5.lean.js +1 -0
  506. data/docs/assets/routes.md.BMM7peut.js +29 -0
  507. data/docs/assets/routes.md.BMM7peut.lean.js +1 -0
  508. data/docs/assets/security.md.C668yXCi.js +1 -0
  509. data/docs/assets/security.md.C668yXCi.lean.js +1 -0
  510. data/docs/assets/seed-data.md.BvFZlqIk.js +14 -0
  511. data/docs/assets/seed-data.md.BvFZlqIk.lean.js +1 -0
  512. data/docs/assets/spa.qejUdp-5.png +0 -0
  513. data/docs/assets/space-time-continuum.md.KPUIKysQ.js +1 -0
  514. data/docs/assets/space-time-continuum.md.KPUIKysQ.lean.js +1 -0
  515. data/docs/assets/style.D73IYGCX.css +1 -0
  516. data/docs/assets/tutorial.md.BnoGjrdK.js +1 -0
  517. data/docs/assets/tutorial.md.BnoGjrdK.lean.js +1 -0
  518. data/docs/assets/unit-tests.md.DUGrnLj5.js +13 -0
  519. data/docs/assets/unit-tests.md.DUGrnLj5.lean.js +1 -0
  520. data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
  521. data/docs/assets.html +42 -0
  522. data/docs/brut-css/brut.css +1 -0
  523. data/docs/brut-css/brut.max.css +22372 -0
  524. data/docs/brut-css/classes/appearances.html +783 -0
  525. data/docs/brut-css/classes/background-colors.html +3529 -0
  526. data/docs/brut-css/classes/border-colors.html +3529 -0
  527. data/docs/brut-css/classes/borders.html +2293 -0
  528. data/docs/brut-css/classes/dimensions.html +2581 -0
  529. data/docs/brut-css/classes/flex.html +917 -0
  530. data/docs/brut-css/classes/foreground-colors.html +3261 -0
  531. data/docs/brut-css/classes/junk-drawer.html +431 -0
  532. data/docs/brut-css/classes/layout.html +668 -0
  533. data/docs/brut-css/classes/lists.html +331 -0
  534. data/docs/brut-css/classes/positioning.html +1751 -0
  535. data/docs/brut-css/classes/spacings.html +2633 -0
  536. data/docs/brut-css/classes/typography.html +2206 -0
  537. data/docs/brut-css/customization/advanced-configuration.html +204 -0
  538. data/docs/brut-css/customization/breakpoints.html +227 -0
  539. data/docs/brut-css/customization/design-system.html +197 -0
  540. data/docs/brut-css/customization/pseudo-classes.html +228 -0
  541. data/docs/brut-css/docs.css +98 -0
  542. data/docs/brut-css/getting-started/core-concepts.html +234 -0
  543. data/docs/brut-css/getting-started/installation.html +190 -0
  544. data/docs/brut-css/getting-started/overview.html +210 -0
  545. data/docs/brut-css/getting-started/simple-example.html +285 -0
  546. data/docs/brut-css/index.html +193 -0
  547. data/docs/brut-css/prism-twilight.min.css +1 -0
  548. data/docs/brut-css/properties/colors.html +1548 -0
  549. data/docs/brut-css/properties/spacings.html +614 -0
  550. data/docs/brut-css/properties/typography.html +777 -0
  551. data/docs/brut-js/api/AjaxSubmit.html +374 -0
  552. data/docs/brut-js/api/AjaxSubmit.js.html +435 -0
  553. data/docs/brut-js/api/Autosubmit.html +192 -0
  554. data/docs/brut-js/api/Autosubmit.js.html +114 -0
  555. data/docs/brut-js/api/BaseCustomElement.html +1091 -0
  556. data/docs/brut-js/api/BaseCustomElement.js.html +312 -0
  557. data/docs/brut-js/api/BrutCustomElements.html +172 -0
  558. data/docs/brut-js/api/BufferedLogger.html +173 -0
  559. data/docs/brut-js/api/ConfirmSubmit.html +278 -0
  560. data/docs/brut-js/api/ConfirmSubmit.js.html +167 -0
  561. data/docs/brut-js/api/ConfirmationDialog.html +425 -0
  562. data/docs/brut-js/api/ConfirmationDialog.js.html +194 -0
  563. data/docs/brut-js/api/ConstraintViolationMessage.html +448 -0
  564. data/docs/brut-js/api/ConstraintViolationMessage.js.html +176 -0
  565. data/docs/brut-js/api/ConstraintViolationMessages.html +590 -0
  566. data/docs/brut-js/api/ConstraintViolationMessages.js.html +149 -0
  567. data/docs/brut-js/api/CopyToClipboard.html +345 -0
  568. data/docs/brut-js/api/CopyToClipboard.js.html +147 -0
  569. data/docs/brut-js/api/Form.html +294 -0
  570. data/docs/brut-js/api/Form.js.html +202 -0
  571. data/docs/brut-js/api/I18nTranslation.html +409 -0
  572. data/docs/brut-js/api/I18nTranslation.js.html +112 -0
  573. data/docs/brut-js/api/LocaleDetection.html +312 -0
  574. data/docs/brut-js/api/LocaleDetection.js.html +168 -0
  575. data/docs/brut-js/api/Logger.html +702 -0
  576. data/docs/brut-js/api/Logger.js.html +141 -0
  577. data/docs/brut-js/api/Message.html +238 -0
  578. data/docs/brut-js/api/Message.js.html +107 -0
  579. data/docs/brut-js/api/PrefixedLogger.html +369 -0
  580. data/docs/brut-js/api/RichString.html +1049 -0
  581. data/docs/brut-js/api/RichString.js.html +164 -0
  582. data/docs/brut-js/api/Tabs.html +295 -0
  583. data/docs/brut-js/api/Tabs.js.html +219 -0
  584. data/docs/brut-js/api/Tracing.html +277 -0
  585. data/docs/brut-js/api/Tracing.js.html +298 -0
  586. data/docs/brut-js/api/external-CustomElementRegistry.html +140 -0
  587. data/docs/brut-js/api/external-Performance.html +138 -0
  588. data/docs/brut-js/api/external-Promise.html +138 -0
  589. data/docs/brut-js/api/external-ValidityState.html +138 -0
  590. data/docs/brut-js/api/external-Window.html +233 -0
  591. data/docs/brut-js/api/external-fetch.html +138 -0
  592. data/docs/brut-js/api/global.html +400 -0
  593. data/docs/brut-js/api/index.html +168 -0
  594. data/docs/brut-js/api/index.js.html +181 -0
  595. data/docs/brut-js/api/module-testing.html +383 -0
  596. data/docs/brut-js/api/scripts/linenumber.js +25 -0
  597. data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +202 -0
  598. data/docs/brut-js/api/scripts/prettify/lang-css.js +2 -0
  599. data/docs/brut-js/api/scripts/prettify/prettify.js +28 -0
  600. data/docs/brut-js/api/styles/jsdoc-default.css +327 -0
  601. data/docs/brut-js/api/styles/prettify-jsdoc.css +111 -0
  602. data/docs/brut-js/api/styles/prettify-tomorrow.css +132 -0
  603. data/docs/brut-js/api/testing.AssetMetadata.html +172 -0
  604. data/docs/brut-js/api/testing.AssetMetadataLoader.html +171 -0
  605. data/docs/brut-js/api/testing.CustomElementTest.html +679 -0
  606. data/docs/brut-js/api/testing.DOMCreator.html +171 -0
  607. data/docs/brut-js/api/testing_AssetMetadata.js.html +86 -0
  608. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +76 -0
  609. data/docs/brut-js/api/testing_CustomElementTest.js.html +286 -0
  610. data/docs/brut-js/api/testing_DOMCreator.js.html +96 -0
  611. data/docs/brut-js/api/testing_index.js.html +99 -0
  612. data/docs/brut-js.html +35 -0
  613. data/docs/business-logic.html +24 -0
  614. data/docs/cli.html +150 -0
  615. data/docs/components.html +127 -0
  616. data/docs/configuration.html +101 -0
  617. data/docs/css.html +44 -0
  618. data/docs/custom-element-tests.html +92 -0
  619. data/docs/database-access.html +86 -0
  620. data/docs/database-schema.html +86 -0
  621. data/docs/deployment.html +24 -0
  622. data/docs/dev-environment.html +34 -0
  623. data/docs/doc-conventions.html +24 -0
  624. data/docs/end-to-end-tests.html +49 -0
  625. data/docs/flash-and-session.html +116 -0
  626. data/docs/forms.html +402 -0
  627. data/docs/getting-started.html +25 -0
  628. data/docs/handlers.html +92 -0
  629. data/docs/hashmap.json +1 -0
  630. data/docs/hooks.html +103 -0
  631. data/docs/i18n.html +46 -0
  632. data/docs/images/logo-300.png +0 -0
  633. data/docs/images/logo.png +0 -0
  634. data/docs/index.html +24 -0
  635. data/docs/instrumentation.html +58 -0
  636. data/docs/javascript.html +54 -0
  637. data/docs/jobs.html +24 -0
  638. data/docs/keyword-injection.html +48 -0
  639. data/docs/markdown-examples.html +56 -0
  640. data/docs/middleware.html +43 -0
  641. data/docs/not-released.html +24 -0
  642. data/docs/overview.html +156 -0
  643. data/docs/pages.html +145 -0
  644. data/docs/routes.html +52 -0
  645. data/docs/security.html +24 -0
  646. data/docs/seed-data.html +37 -0
  647. data/docs/space-time-continuum.html +24 -0
  648. data/docs/tutorial.html +24 -0
  649. data/docs/unit-tests.html +36 -0
  650. data/docs/vp-icons.css +1 -0
  651. data/lib/brut/cli/app_runner.rb +1 -1
  652. data/lib/brut/cli/apps/test.rb +5 -0
  653. data/lib/brut/framework/mcp.rb +0 -4
  654. data/lib/brut/front_end/component.rb +59 -84
  655. data/lib/brut/front_end/components/constraint_violations.rb +1 -4
  656. data/lib/brut/front_end/components/form_tag.rb +1 -1
  657. data/lib/brut/front_end/components/input.rb +3 -3
  658. data/lib/brut/front_end/components/inputs/csrf_token.rb +1 -1
  659. data/lib/brut/front_end/components/inputs/{text_field.rb → input_tag.rb} +6 -8
  660. data/lib/brut/front_end/components/inputs/radio_button.rb +1 -1
  661. data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +187 -0
  662. data/lib/brut/front_end/components/inputs/{textarea.rb → textarea_tag.rb} +2 -2
  663. data/lib/brut/front_end/components/time_tag.rb +2 -1
  664. data/lib/brut/front_end/form.rb +4 -4
  665. data/lib/brut/front_end/forms/input.rb +2 -1
  666. data/lib/brut/front_end/forms/input_definition.rb +5 -2
  667. data/lib/brut/front_end/forms/radio_button_group_input.rb +2 -1
  668. data/lib/brut/front_end/forms/radio_button_group_input_definition.rb +2 -2
  669. data/lib/brut/front_end/forms/select_input.rb +2 -4
  670. data/lib/brut/front_end/forms/select_input_definition.rb +2 -2
  671. data/lib/brut/front_end/handler.rb +28 -26
  672. data/lib/brut/front_end/handlers/csp_reporting_handler.rb +5 -2
  673. data/lib/brut/front_end/handlers/instrumentation_handler.rb +8 -4
  674. data/lib/brut/front_end/handlers/locale_detection_handler.rb +9 -5
  675. data/lib/brut/front_end/handlers/missing_handler.rb +5 -2
  676. data/lib/brut/front_end/page.rb +4 -6
  677. data/lib/brut/front_end/request_context.rb +3 -2
  678. data/lib/brut/i18n/base_methods.rb +136 -82
  679. data/lib/brut/i18n/for_back_end.rb +1 -0
  680. data/lib/brut/i18n/for_cli.rb +1 -0
  681. data/lib/brut/i18n/for_html.rb +32 -4
  682. data/lib/brut/instrumentation/open_telemetry.rb +12 -2
  683. data/lib/brut/sinatra_helpers.rb +10 -3
  684. data/lib/brut/spec_support/component_support.rb +18 -18
  685. data/lib/brut/spec_support/e2e_test_server.rb +3 -0
  686. data/lib/brut/version.rb +1 -1
  687. data/lib/sequel/extensions/brut_migrations.rb +12 -9
  688. metadata +647 -5
  689. data/lib/brut/front_end/components/inputs/select.rb +0 -117
@@ -0,0 +1,98 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+ import RichString from "./RichString"
3
+ import ConstraintViolationMessage from "./ConstraintViolationMessage"
4
+
5
+ /**
6
+ * Custom element to translate keys from {@link external:ValidityState} into
7
+ * actual messges for a human. This works by inserting `<brut-cv>` elements
8
+ * as children, where the key represents the particular errors present in the `ValidityState` passed
9
+ * to `createMessages`. Note that this will not insert an element for `customError`, since there can't be
10
+ * any sort of general error message to correspond to that.
11
+ *
12
+ * @property {string} input-name if set, this indicates this element contains constraint violation messages
13
+ * for the input with this name inside the form this element is in. Currently doesn't affect
14
+ * this element's behavior, however AjaxSubmit will use it to locate where it
15
+ * should insert server-side errors.
16
+ *
17
+ * @see Form
18
+ * @see ConstraintViolationMessage
19
+ * @see AjaxSubmit
20
+ *
21
+ * @customElement brut-cv-messages
22
+ */
23
+ class ConstraintViolationMessages extends BaseCustomElement {
24
+ static tagName = "brut-cv-messages"
25
+
26
+ static observedAttributes = [
27
+ "show-warnings",
28
+ "input-name",
29
+ ]
30
+
31
+ inputNameChangedCallback({newValue}) {
32
+ // attribute listed for documentation purposes only
33
+ }
34
+
35
+ /**
36
+ * Creates error messages based on the passed `ValidityState` and input name.
37
+ *
38
+ * This should be called as part of a Form validation event to provide a customized UX for
39
+ * the error messages, beyond what the browser would do by default. The keys used are the same
40
+ * as the attributes of a `ValidityState`, so for example, a range underflow would mean that `validity.rangeUnderflow` would return
41
+ * true. Thus, a `<brut-cv>` would be created with `key="cv.fe.rangeUnderflow"`.
42
+ *
43
+ * The `cv.fe` is hard-coded to be consistent with Brut's server-side translation management.
44
+ *
45
+ * @param {ValidityState} validityState - the return from an element's `validity` when it's found to have constraint violations.
46
+ * @param {String} inputName - the element's `name`.
47
+ */
48
+ createMessages({validityState,inputName}) {
49
+ const errors = this.#VALIDITY_STATE_ATTRIBUTES.filter( (attribute) => validityState[attribute] )
50
+ this.clearClientSideMessages()
51
+ errors.forEach( (key) => {
52
+ const options = {
53
+ key: key,
54
+ "input-name": inputName,
55
+ }
56
+ const showWarnings = this.getAttribute("show-warnings")
57
+ if (showWarnings != null) {
58
+ options["show-warnings"] = showWarnings
59
+ }
60
+ const element = ConstraintViolationMessage.createElement(document,options)
61
+ this.appendChild(element)
62
+ })
63
+ }
64
+
65
+ /**
66
+ * Clear any client-side messages previously inserted by another element.
67
+ * This is useful to remove potentially out-of-date messages to replace with up-to-date ones.
68
+ */
69
+ clearClientSideMessages() {
70
+ this.querySelectorAll(ConstraintViolationMessage.clientSideSelector()).forEach( (element) => {
71
+ this.removeChild(element)
72
+ })
73
+ }
74
+
75
+ /**
76
+ * Clear any server-side messages previously inserted by another element or rendered from the server.
77
+ * This is useful to remove potentially out-of-date messages to replace with up-to-date ones.
78
+ */
79
+ clearServerSideMessages() {
80
+ this.querySelectorAll(ConstraintViolationMessage.serverSideSelector()).forEach( (element) => {
81
+ this.removeChild(element)
82
+ })
83
+ }
84
+
85
+ #VALIDITY_STATE_ATTRIBUTES = [
86
+ "badInput",
87
+ "patternMismatch",
88
+ "rangeOverflow",
89
+ "rangeUnderflow",
90
+ "stepMismatch",
91
+ "tooLong",
92
+ "tooShort",
93
+ "typeMismatch",
94
+ "valueMissing",
95
+ // customError omitted, since it makes no sense as a general error key to look up
96
+ ]
97
+ }
98
+ export default ConstraintViolationMessages
@@ -0,0 +1,96 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+
3
+ /** Wraps a `<BUTTON>` that will copy text from another element into the system clipboard. It will set various attributes on itself
4
+ * to allow styling the states of the process.
5
+ *
6
+ * Overall, the flow is as follows:
7
+ *
8
+ * 1. When the button is clicked, its default is prevented, and the element with the id of the `element` attribute is located.
9
+ * 2. If found, this element gets the `copying` attribute set.
10
+ * 3. If the copy completes successfully:
11
+ * a. `copying` is removed
12
+ * b. `copied` is set
13
+ * c. `copied` is scheduled for removal in 2000ms or the number of milliseconds in the `copied-lifetime` attribute.
14
+ * 4. If the copy failed:
15
+ * a. `copying` is removed
16
+ * b. `errored` is set
17
+ * c. The `brut:copyfailed` event is fired. It's detail contains a `text:` value with the text that was attempted to be copied.
18
+ *
19
+ * The intention is to use these attributes to style whatever UX you want.
20
+ *
21
+ * @property {string} element - ID of the element whose `textContent` is what will be copied to the clipboard.
22
+ * @property {number} copied-lifetime - number of milliseconds to wait before clearing the `copied` attribute after a successful copy.
23
+ * @property {boolean} copying - Set after a copy is initiated, but before it completes
24
+ * @property {boolean} copied - Set after a copy is completed successfully
25
+ * @property {boolean} errored - Set after a copy is fails
26
+ *
27
+ * @fires brut:copyfailed Fired when the copy fails to complete
28
+ *
29
+ * @example
30
+ * <pre><code id="code">dx/exec bin/setup</code></pre>
31
+ * <brut-copy-to-clipboard element="code">
32
+ * <button>Copy</button>
33
+ * </brut-copy-to-clipboard>
34
+ *
35
+ * @customElement brut-copy-to-clipboard
36
+ */
37
+ class CopyToClipboard extends BaseCustomElement {
38
+ static tagName = "brut-copy-to-clipboard"
39
+
40
+ static observedAttributes = [
41
+ "element",
42
+ "copied-lifetime",
43
+ ]
44
+
45
+ #elementId = null
46
+ #copiedLifetime = 2000
47
+
48
+ #copyCode = (event) => {
49
+ event.preventDefault()
50
+
51
+ const element = document.getElementById(this.#elementId)
52
+
53
+ if (!element) {
54
+ this.logger.info("No element with id %s, so nothing to copy",this.#elementId)
55
+ return
56
+ }
57
+ this.setAttribute("copying",true)
58
+
59
+ const text = element.textContent
60
+
61
+ navigator.clipboard.writeText(text).then( () => {
62
+ this.setAttribute("copied", true)
63
+ }).catch( (e) => {
64
+ this.setAttribute("errored", true)
65
+ this.dispatchEvent(new CustomEvent("brut:copyfailed", { detail: { text: text }}))
66
+ }).finally( () => {
67
+ this.removeAttribute("copying")
68
+ setTimeout( () => this.removeAttribute("copied"), 2000)
69
+ })
70
+ }
71
+
72
+ update() {
73
+ const button = this.querySelector("button")
74
+ if (button) {
75
+ button.addEventListener("click", this.#copyCode)
76
+ }
77
+ else {
78
+ this.logger.info("There is no button, so no way to initiate a copy")
79
+ }
80
+ }
81
+
82
+ elementChangedCallback({newValue}) {
83
+ this.#elementId = newValue
84
+ }
85
+
86
+ copiedLifetimeChangedCallback({newValue}) {
87
+ const newValueAsInt = parseInt(newValue)
88
+ if (!isNaN(newValueAsInt)) {
89
+ this.#copiedLifetime = newValueAsInt
90
+ }
91
+ else {
92
+ this.logger.info("Value '%s' for copied-lifetime is not a number. Ignoring it",newValue)
93
+ }
94
+ }
95
+ }
96
+ export default CopyToClipboard
@@ -0,0 +1,151 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+ import RichString from "./RichString"
3
+ import AjaxSubmit from "./AjaxSubmit"
4
+ import ConstraintViolationMessages from "./ConstraintViolationMessages"
5
+
6
+ /** A web component that enhances a form it contains to make constraint validations
7
+ * easier to manage and control.
8
+ *
9
+ * This provides two main features:
10
+ *
11
+ * * While the `:user-invalid` selector allows you to target inputs that have been interacted
12
+ * with (thus avoiding issues when using `:invalid`), this still creates the experience of a
13
+ * user tabbing off of a control and getting an error message. If, instead, you only
14
+ * want to show these errors when a submit has been attempted, this element will
15
+ * set `submitted-invalid` on itself when that happens, thus allowing you to target invalid
16
+ * fields only after a submission attempt.
17
+ * * You may wish to control the messaging of client-side constraint violations
18
+ * beyond what the browser gives you. Assuming your `INPUT` tags are inside a container
19
+ * like `LABEL`, a `brut-cv` tag found in that container
20
+ * (i.e. a sibling of your `INPUT`) will be modified to contain error messages specific
21
+ * to the {@link external:ValidityState} of the control.
22
+ *
23
+ * @fires brut:invalid Fired when any element is found to be invalid
24
+ * @fires brut:valid Fired when no element is found to be invalid. This should be reliable to know
25
+ * when constraint violations have cleared.
26
+ *
27
+ * @example <caption>Basic Structure Required</caption>
28
+ * <brut-form>
29
+ * <form ...>
30
+ * <label>
31
+ * <input type="text" required name="username">
32
+ * <brut-cv-messages>
33
+ * </brut-cv-messages>
34
+ * </label>
35
+ * <div> <!-- container need not be a label -->
36
+ * <input type="text" required minlength="4" name="alias">
37
+ * <brut-cv-messages>
38
+ * </brut-cv-messages>
39
+ * </div>
40
+ * <button>Submit</button>
41
+ * </form>
42
+ * </brut-form>
43
+ * <!-- after a submit of this form, the HTML will effectively be as follows -->
44
+ * <brut-form submitted-invalid>
45
+ * <form ...>
46
+ * <label>
47
+ * <input type="text" required name="username">
48
+ * <brut-cv-messages>
49
+ * <brut-cv>This field is required</brut-cv>
50
+ * </brut-cv-messages>
51
+ * </label>
52
+ * <div> <!-- container need not be a label -->
53
+ * <input type="text" required minlength="4" name="alias">
54
+ * <brut-cv-messages>
55
+ * <brut-cv>This field is required</brut-cv>
56
+ * </brut-cv-messages>
57
+ * </div>
58
+ * <button>Submit</button>
59
+ * </form>
60
+ * </brut-form>
61
+ *
62
+ * @property {boolean} submitted-invalid - set by this element when the form is submitted. Does not trigger any behavior and can be used in CSS.
63
+ * @see ConstraintViolationMessages
64
+ *
65
+ * @customElement brut-form
66
+ */
67
+ class Form extends BaseCustomElement {
68
+ static tagName = "brut-form"
69
+ static observedAttributes = [
70
+ "submitted-invalid",
71
+ "show-warnings",
72
+ ]
73
+
74
+ #markFormSubmittedInvalid = (event) => {
75
+ this.setAttribute("submitted-invalid","")
76
+ }
77
+ #updateValidity = (event) => {
78
+ this.#updateErrorMessages(event)
79
+ }
80
+ #sendValid = () => {
81
+ this.dispatchEvent(new CustomEvent("brut:valid"))
82
+ }
83
+ #sendInvalid = () => {
84
+ this.dispatchEvent(new CustomEvent("brut:invalid"))
85
+ }
86
+
87
+ submittedInvalidChangedCallback() {}
88
+
89
+ update() {
90
+ const forms = this.querySelectorAll("form")
91
+ if (forms.length == 0) {
92
+ this.logger.warn("Didn't find any forms. Ignoring")
93
+ return
94
+ }
95
+ forms.forEach( (form) => {
96
+ Array.from(form.elements).forEach( (formElement) => {
97
+ formElement.addEventListener("invalid", this.#updateValidity)
98
+ formElement.addEventListener("invalid", this.#markFormSubmittedInvalid)
99
+ formElement.addEventListener("input", this.#updateValidity)
100
+ })
101
+ form.querySelectorAll(AjaxSubmit.tagName).forEach( (ajaxSubmits) => {
102
+ ajaxSubmits.addEventListener("brut:submitok", this.#sendValid)
103
+ ajaxSubmits.addEventListener("brut:submitinvalid", this.#sendInvalid)
104
+ })
105
+ })
106
+ }
107
+
108
+ #updateErrorMessages(event) {
109
+ const element = event.target
110
+ const selector = ConstraintViolationMessages.tagName
111
+ let errorLabels = element.parentNode.querySelectorAll(selector)
112
+ if (errorLabels.length == 0) {
113
+ if (element.name && element.form) {
114
+ const moreGeneralSelector = `${ConstraintViolationMessages.tagName}[input-name='${element.name}']`
115
+ errorLabels = element.form.querySelectorAll(moreGeneralSelector)
116
+ if (errorLabels.length == 0) {
117
+ this.logger.warn(`Did not find any elements matching ${selector} or ${moreGeneralSelector}, so no error messages will be shown`)
118
+ }
119
+ }
120
+ else {
121
+ this.logger.warn("Did not find any elements matching %s and the form element has %s %s",
122
+ selector,
123
+ element.name ? "no name" : "a name, but",
124
+ element.form ? "no form" : "though has a form")
125
+ }
126
+ }
127
+ if (errorLabels.length == 0) {
128
+ return
129
+ }
130
+ let anyErrors = false
131
+ errorLabels.forEach( (errorLabel) => {
132
+ if (element.validity.valid) {
133
+ errorLabel.clearClientSideMessages()
134
+ }
135
+ else {
136
+ anyErrors = true
137
+ errorLabel.createMessages({
138
+ validityState: element.validity,
139
+ inputName: element.name
140
+ })
141
+ }
142
+ })
143
+ if (anyErrors) {
144
+ this.#sendInvalid()
145
+ }
146
+ else {
147
+ this.#sendValid()
148
+ }
149
+ }
150
+ }
151
+ export default Form
@@ -0,0 +1,61 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+
3
+ /** Manages a translation based on a key and value, with the value potentially having interpolation.
4
+ *
5
+ * This is intended to be server-rendered with the subset of keys the server manages that are relevant
6
+ * to the front-end. Any other code on the page can then locate an element with the desired key and
7
+ * call `translation` to get the human-readable key. It is assumed that the server would render
8
+ * in the language required by the visitor, so there is no need to first select by locale.
9
+ *
10
+ * @property {string} key - an i18n key, presumably dot-delimited, however it can be any valid attribute value.
11
+ * @property {string} value - the value of the key, in the browser's locale. It may contain placeholders for interpolation using `%{«placeholder»}` syntax.
12
+ *
13
+ * @example
14
+ * <brut-i18n-translation key="greeting" value="Hello %{username}"></brut-i18n-translation>
15
+ *
16
+ * @customElement brut-i18n-translation
17
+ */
18
+ class I18nTranslation extends BaseCustomElement {
19
+ static tagName = "brut-i18n-translation"
20
+
21
+ static observedAttributes = [
22
+ "show-warnings",
23
+ "key",
24
+ "value",
25
+ ]
26
+
27
+ #key = null
28
+ #value = ""
29
+
30
+ keyChangedCallback({newValue}) {
31
+ this.#key = newValue
32
+ }
33
+
34
+ valueChangedCallback({newValue}) {
35
+ this.#value = newValue ? String(newValue) : ""
36
+ }
37
+
38
+ /**
39
+ * Called by other JavaScript to get the translated string.
40
+ * @param {Object} interpolatedValues - Object where the keys are placeholders in the string for interpolation and the values are
41
+ * the values to replace. Placeholders not in the translated value are ignored. Missing placeholders won't cause an error, but the
42
+ * placeholder will be present verbatim in the translated string.
43
+ *
44
+ * @example
45
+ * const element = document.querySeletor("brut-i18n-translation[key='greeting']")
46
+ * if (element) {
47
+ * const translation = element.translation({ username: "Pat" })
48
+ * alert(translation) // Shows 'Hello Pat'
49
+ * }
50
+ */
51
+ translation(interpolatedValues) {
52
+ return this.#value.replaceAll(/%\{([^}%]+)\}/g, (match,key) => {
53
+ if (interpolatedValues[key]) {
54
+ return interpolatedValues[key]
55
+ }
56
+ return match
57
+ })
58
+ }
59
+
60
+ }
61
+ export default I18nTranslation
@@ -0,0 +1,117 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+
3
+ /**
4
+ * Send the locale and timezone from the browser to a configured endpoint on the server. This allows
5
+ * the server to have access to a reasonable guess as to the website visitor's locale/timezone.
6
+ *
7
+ * Note that this will not contact the server if both `locale-from-server` and `timezone-from-server` are
8
+ * set. Further note that this will only contact the server once per page load, unless `url` is changed.
9
+ *
10
+ * @property {String} locale-from-server - omit this if the server doesn't know the visitor's locale. If both this and `timezone-from-server` are set, the server will not be contacted.
11
+ * @property {String} timezone-from-server - omit this if the server doesn't know the visitor's timezone. If both this and `locale-from-server` are set, the server will not be contacted.
12
+ * @property {URL} url - the url to send information to on the server.
13
+ * @property {number} timeout-before-ping-ms - MS to wait until this element contacts the server. A value of 0 will contact the server immediately. The default is 1,000, meaning this element will wait 1 second before contacting the server.
14
+ *
15
+ * @example <caption>When no information about the visitor is known</caption>
16
+ * <brut-locale-detection url="__brut/locale-detection"></brut-locale-detection>
17
+ *
18
+ * @example <caption>When all information about the visitor is known</caption>
19
+ * <brut-locale-detection
20
+ * url="__brut/locale-detection"
21
+ * locale-from-server="en-US"
22
+ * timezone-from-server="America/New_York">
23
+ * </brut-locale-detection>
24
+ *
25
+ * @customElement brut-locale-detection
26
+ */
27
+ class LocaleDetection extends BaseCustomElement {
28
+ static tagName = "brut-locale-detection"
29
+
30
+ static observedAttributes = [
31
+ "locale-from-server",
32
+ "timezone-from-server",
33
+ "url",
34
+ "timeout-before-ping-ms",
35
+ "show-warnings",
36
+ ]
37
+
38
+ #localeFromServer = null
39
+ #timezoneFromServer = null
40
+ #reportingURL = null
41
+ #timeoutBeforePing = 1000
42
+ #serverContacted = false
43
+
44
+ localeFromServerChangedCallback({newValue}) {
45
+ this.#localeFromServer = newValue
46
+ }
47
+
48
+ timezoneFromServerChangedCallback({newValue}) {
49
+ this.#timezoneFromServer = newValue
50
+ }
51
+
52
+ urlChangedCallback({newValue}) {
53
+ if (this.#serverContacted) {
54
+ this.#serverContacted = false
55
+ }
56
+ this.#reportingURL = newValue
57
+ }
58
+
59
+ timeoutBeforePingMsChangedCallback({newValue}) {
60
+ this.#timeoutBeforePing = newValue
61
+ }
62
+
63
+ update() {
64
+ if (this.#timeoutBeforePing == 0) {
65
+ this.#pingServerWithLocaleInfo()
66
+ }
67
+ else {
68
+ setTimeout(this.#pingServerWithLocaleInfo.bind(this), this.#timeoutBeforePing)
69
+ }
70
+ }
71
+
72
+ #pingServerWithLocaleInfo() {
73
+ if (!this.#reportingURL) {
74
+ this.logger.info("no url= set, so nowhere to report to")
75
+ return
76
+ }
77
+ if (this.#localeFromServer && this.#timezoneFromServer) {
78
+ this.logger.info("locale and timezone both set, not contacting server")
79
+ return
80
+ }
81
+
82
+ if (this.#serverContacted) {
83
+ this.logger.info("server has already been contacted at the given url, not doing it again")
84
+ return
85
+ }
86
+ this.#serverContacted = true
87
+
88
+ const formatOptions = Intl.DateTimeFormat().resolvedOptions()
89
+ const request = new Request(
90
+ this.#reportingURL,
91
+ {
92
+ headers: {
93
+ "Content-Type": "application/json",
94
+ },
95
+ method: "POST",
96
+ body: JSON.stringify({
97
+ locale: formatOptions.locale,
98
+ timeZone: formatOptions.timeZone,
99
+ }),
100
+ }
101
+ )
102
+
103
+ window.fetch(request).then( (response) => {
104
+ if (response.ok) {
105
+ this.logger.info("Server gave us the OK")
106
+ }
107
+ else {
108
+ console.warn(response)
109
+ }
110
+ }).catch( (e) => {
111
+ console.warn(e)
112
+ })
113
+ }
114
+
115
+
116
+ }
117
+ export default LocaleDetection
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Abstract interface for logging information from a component.
3
+ * This is intended to allow prefixed messages to be optionally shown
4
+ * in the console to help debug.
5
+ *
6
+ * @see BufferedLogger
7
+ * @see PrefixedLogger
8
+ * @see BaseCustomElement#logger
9
+ */
10
+ class Logger {
11
+ /** Create a logger for the given prefix.
12
+ *
13
+ * @param {string|false} stringOrFalse - if false,returns a {@link BufferedLogger}. Otherwise, returns a {@link PrefixedLogger} using the param's value as the prefix.
14
+ *
15
+ * @returns {Logger}
16
+ */
17
+ static forPrefix(stringOrFalse) {
18
+ if (!stringOrFalse) {
19
+ return new BufferedLogger()
20
+ }
21
+ else {
22
+ return new PrefixedLogger(stringOrFalse)
23
+ }
24
+ }
25
+
26
+ /** Subclasses must implement this.
27
+ *
28
+ * @param {string} level - 'info' or 'warn' to indicate the logging level
29
+ * @param {...*} args - args to pass directly to console.log
30
+ */
31
+ log() {
32
+ throw `Subclass must implement`
33
+ }
34
+
35
+ /** Log an informational bit of information */
36
+ info(...args) { this.log("info",...args) }
37
+ /** Log a warning */
38
+ warn(...args) { this.log("warn",...args) }
39
+ }
40
+
41
+ /** Logger that buffers, but does not print, its logged messages.
42
+ * The reason it buffers them is to allow custom elements to retroatively log
43
+ * information captured before warnings were turned on.
44
+ */
45
+ class BufferedLogger extends Logger {
46
+ constructor() {
47
+ super()
48
+ this.messages = []
49
+ }
50
+ log(...args) {
51
+ this.messages.push(args)
52
+ }
53
+ }
54
+
55
+ /** Log information to the JavaScript console.
56
+ */
57
+ class PrefixedLogger extends Logger {
58
+ /** Create a PrefixedLogger.
59
+ *
60
+ * @param {string|true} prefixOrTrue - if true, uses the prefix `"debug"`, otherwise uses the param as the prefix to all
61
+ * messages output.
62
+ */
63
+ constructor(prefixOrTrue) {
64
+ super()
65
+ this.prefix = prefixOrTrue === true ? "debug" : prefixOrTrue
66
+ }
67
+
68
+ /** Dumps hte contents of a {@link BufferedLogger} to this logger's output.
69
+ *
70
+ * @param {BufferedLogger} bufferedLogger - a logger with pent-up messages, waiting to be logged.
71
+ */
72
+ dump(bufferedLogger) {
73
+ if (bufferedLogger instanceof BufferedLogger) {
74
+ bufferedLogger.messages.forEach( (args) => {
75
+ this.log(...args)
76
+ })
77
+ }
78
+ }
79
+
80
+ log(level,...args) {
81
+ if (typeof(args[0]) === "string") {
82
+ const message = `[prefix:${this.prefix}]:${args[0]}`
83
+ console[level](message,...(args.slice(1)))
84
+ }
85
+ else {
86
+ console[level](this.prefix,...args)
87
+ }
88
+ }
89
+ }
90
+ export default Logger
@@ -0,0 +1,56 @@
1
+ import BaseCustomElement from "./BaseCustomElement"
2
+ import RichString from "./RichString"
3
+ import I18nTranslation from "./I18nTranslation"
4
+
5
+ /** Renders a translated message for a given key, handling all the needed interpolation based
6
+ * on the existence of `<brut-i18n-translation>` elements on the page.
7
+ *
8
+ * When the `key` attribute has a value, this element will locate the `<brut-i18-translation>` element and call `translate`. Note that
9
+ * interpolation is not supported.
10
+ *
11
+ * @property {string} key - the i18n translation key to use. It must map to the `key` of a `<brut-i18n-translation>` on the page or
12
+ * the element will not render any text.
13
+ *
14
+ * @see I18nTranslation
15
+ * @see ConstraintViolationMessage
16
+ *
17
+ * @customElement brut-message
18
+ */
19
+ class Message extends BaseCustomElement {
20
+ static tagName = "brut-message"
21
+ static observedAttributes = [
22
+ "show-warnings",
23
+ "key",
24
+ ]
25
+
26
+ static createElement(document,attributes) {
27
+ const element = document.createElement(Message.tagName)
28
+ element.setAttribute("key",attributes.key)
29
+ element.setAttribute("show-warnings",attributes["show-warnings"])
30
+ return element
31
+ }
32
+
33
+ #key = null
34
+
35
+ keyChangedCallback({newValue}) {
36
+ this.#key = newValue
37
+ }
38
+
39
+ update() {
40
+ if (!this.#key) {
41
+ this.logger.info("No key attribute, so can't do anything")
42
+ return
43
+ }
44
+
45
+ const selector = `${I18nTranslation.tagName}[key='${this.#key}']`
46
+ const translation = document.querySelector(selector)
47
+ if (!translation) {
48
+ this.logger.info("Could not find translation based on selector '%s'",selector)
49
+ return
50
+ }
51
+
52
+ this.textContent = RichString.fromString(translation.translation()).capitalize().toString()
53
+ }
54
+ }
55
+
56
+ export default Message