brut 0.15.0 → 0.16.0.pre

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 (400) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Dockerfile.dx +3 -6
  5. data/Gemfile.lock +1 -1
  6. data/brut-css/package-lock.json +2 -2
  7. data/brut-css/package.json +1 -1
  8. data/brut-js/package-lock.json +2 -2
  9. data/brut-js/package.json +1 -1
  10. data/brutrb.com/.vitepress/config.mjs +4 -3
  11. data/brutrb.com/deployment.md +23 -9
  12. data/brutrb.com/jobs.md +107 -7
  13. data/brutrb.com/recipes/dev-env-secrets.md +87 -0
  14. data/brutrb.com/roadmap.md +2 -7
  15. data/docs/404.html +2 -2
  16. data/docs/adrs.html +3 -3
  17. data/docs/ai.html +3 -3
  18. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  19. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  20. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  21. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  22. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  23. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  24. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  25. data/docs/api/Brut/BackEnd.html +1 -1
  26. data/docs/api/Brut/CLI/App.html +1 -1
  27. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  28. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  29. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  30. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  31. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  32. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  33. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  34. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  35. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  36. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  37. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  40. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  60. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  61. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  62. data/docs/api/Brut/CLI/Apps.html +1 -1
  63. data/docs/api/Brut/CLI/Command.html +1 -1
  64. data/docs/api/Brut/CLI/Error.html +1 -1
  65. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  66. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  67. data/docs/api/Brut/CLI/Executor.html +1 -1
  68. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  69. data/docs/api/Brut/CLI/Options.html +1 -1
  70. data/docs/api/Brut/CLI/Output.html +1 -1
  71. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  72. data/docs/api/Brut/CLI.html +1 -1
  73. data/docs/api/Brut/FactoryBot.html +1 -1
  74. data/docs/api/Brut/Framework/App.html +1 -1
  75. data/docs/api/Brut/Framework/Config.html +1 -1
  76. data/docs/api/Brut/Framework/Container.html +1 -1
  77. data/docs/api/Brut/Framework/Error.html +1 -1
  78. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  79. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  80. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  81. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  82. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  83. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  84. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  85. data/docs/api/Brut/Framework/Errors.html +1 -1
  86. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  87. data/docs/api/Brut/Framework/MCP.html +1 -1
  88. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  89. data/docs/api/Brut/Framework.html +1 -1
  90. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  91. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  92. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  93. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  94. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  95. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  96. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  97. data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +1 -1
  98. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  99. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
  100. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  101. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
  102. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
  103. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  105. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  107. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  108. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  109. data/docs/api/Brut/FrontEnd/CsrfProtector.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Forms/Button.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  117. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
  122. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  123. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
  124. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  127. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  128. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  129. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  132. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  135. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  136. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  137. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  138. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  139. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  147. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  148. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  149. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  150. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  151. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  152. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  153. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  154. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  155. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  156. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  157. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  158. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  161. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  162. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  163. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  166. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  167. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  168. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  169. data/docs/api/Brut/FrontEnd.html +1 -1
  170. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  171. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  172. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  173. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  174. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  175. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  176. data/docs/api/Brut/I18n.html +1 -1
  177. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  178. data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +1 -1
  179. data/docs/api/Brut/Instrumentation/Methods.html +1 -1
  180. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  181. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  182. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  183. data/docs/api/Brut/Instrumentation.html +1 -1
  184. data/docs/api/Brut/RubocopConfig.html +1 -1
  185. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  186. data/docs/api/Brut/SinatraHelpers.html +1 -1
  187. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  188. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  189. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  190. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  191. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  192. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  193. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  194. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  195. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  196. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  197. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  198. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  199. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  200. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  201. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  202. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  203. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  204. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  205. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  206. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  207. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  208. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  209. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  210. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  211. data/docs/api/Brut/SpecSupport.html +1 -1
  212. data/docs/api/Brut.html +1 -1
  213. data/docs/api/Clock.html +1 -1
  214. data/docs/api/ModuleName.html +1 -1
  215. data/docs/api/RichString.html +1 -1
  216. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  217. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  218. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  219. data/docs/api/Sequel/Extensions.html +1 -1
  220. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  221. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  222. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  223. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  224. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  225. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  226. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  227. data/docs/api/Sequel/Plugins.html +1 -1
  228. data/docs/api/Sequel.html +1 -1
  229. data/docs/api/_index.html +1 -1
  230. data/docs/api/file.README.html +1 -1
  231. data/docs/api/index.html +1 -1
  232. data/docs/api/top-level-namespace.html +1 -1
  233. data/docs/assets/{app.B0X8upRm.js → app.Dm7v_ouO.js} +1 -1
  234. data/docs/assets/{brut-js.md.CbJAe2Ky.js → brut-js.md.BMz0X1Rz.js} +1 -1
  235. data/docs/assets/chunks/@localSearchIndexroot.BiNFswvo.js +1 -0
  236. data/docs/assets/chunks/{VPLocalSearchBox.jLmhant1.js → VPLocalSearchBox.DQK6jQou.js} +1 -1
  237. data/docs/assets/chunks/{theme.CtVUdCdt.js → theme.BuExsdM9.js} +2 -2
  238. data/docs/assets/{components.md.C6nWgDP0.js → components.md.Dfd3w6UW.js} +3 -3
  239. data/docs/assets/{configuration.md.CpbYHWPb.js → configuration.md.DTYoV2Ea.js} +1 -1
  240. data/docs/assets/{forms.md.Bii91k3E.js → forms.md.DEkmJUvb.js} +1 -1
  241. data/docs/assets/{getting-started.md.ChAvueK7.js → getting-started.md.DO-4eoGW.js} +2 -2
  242. data/docs/assets/{tutorials_02-dialog.md.De6iTsWX.js → tutorials_02-dialog.md.D2vSjDVf.js} +1 -1
  243. data/docs/assets.html +3 -3
  244. data/docs/brut-js/api/AjaxSubmit.html +2 -2
  245. data/docs/brut-js/api/AjaxSubmit.js.html +2 -2
  246. data/docs/brut-js/api/Autosubmit.html +2 -2
  247. data/docs/brut-js/api/Autosubmit.js.html +2 -2
  248. data/docs/brut-js/api/BaseCustomElement.html +2 -2
  249. data/docs/brut-js/api/BaseCustomElement.js.html +2 -2
  250. data/docs/brut-js/api/BrutCustomElements.html +3 -3
  251. data/docs/brut-js/api/BufferedLogger.html +2 -2
  252. data/docs/brut-js/api/ConfirmSubmit.html +2 -2
  253. data/docs/brut-js/api/ConfirmSubmit.js.html +2 -2
  254. data/docs/brut-js/api/ConfirmationDialog.html +2 -2
  255. data/docs/brut-js/api/ConfirmationDialog.js.html +2 -2
  256. data/docs/brut-js/api/ConstraintViolationMessage.html +2 -2
  257. data/docs/brut-js/api/ConstraintViolationMessage.js.html +2 -2
  258. data/docs/brut-js/api/ConstraintViolationMessages.html +2 -2
  259. data/docs/brut-js/api/ConstraintViolationMessages.js.html +2 -2
  260. data/docs/brut-js/api/CopyToClipboard.html +2 -2
  261. data/docs/brut-js/api/CopyToClipboard.js.html +2 -2
  262. data/docs/brut-js/api/Form.html +2 -2
  263. data/docs/brut-js/api/Form.js.html +2 -2
  264. data/docs/brut-js/api/I18nTranslation.html +2 -2
  265. data/docs/brut-js/api/I18nTranslation.js.html +5 -2
  266. data/docs/brut-js/api/LocaleDetection.html +2 -2
  267. data/docs/brut-js/api/LocaleDetection.js.html +2 -2
  268. data/docs/brut-js/api/Logger.html +2 -2
  269. data/docs/brut-js/api/Logger.js.html +2 -2
  270. data/docs/brut-js/api/Message.html +2 -2
  271. data/docs/brut-js/api/Message.js.html +11 -5
  272. data/docs/brut-js/api/PrefixedLogger.html +2 -2
  273. data/docs/brut-js/api/RichString.html +9 -9
  274. data/docs/brut-js/api/RichString.js.html +6 -3
  275. data/docs/brut-js/api/Tabs.html +2 -2
  276. data/docs/brut-js/api/Tabs.js.html +2 -2
  277. data/docs/brut-js/api/Toast.html +270 -0
  278. data/docs/brut-js/api/Toast.js.html +153 -0
  279. data/docs/brut-js/api/Tracing.html +2 -2
  280. data/docs/brut-js/api/Tracing.js.html +2 -2
  281. data/docs/brut-js/api/external-CustomElementRegistry.html +3 -3
  282. data/docs/brut-js/api/external-Performance.html +3 -3
  283. data/docs/brut-js/api/external-Promise.html +3 -3
  284. data/docs/brut-js/api/external-ValidityState.html +3 -3
  285. data/docs/brut-js/api/external-Window.html +4 -4
  286. data/docs/brut-js/api/external-fetch.html +3 -3
  287. data/docs/brut-js/api/global.html +3 -3
  288. data/docs/brut-js/api/index.html +2 -2
  289. data/docs/brut-js/api/index.js.html +5 -2
  290. data/docs/brut-js/api/module-testing.html +2 -2
  291. data/docs/brut-js/api/testing.AssetMetadata.html +2 -2
  292. data/docs/brut-js/api/testing.AssetMetadataLoader.html +2 -2
  293. data/docs/brut-js/api/testing.CustomElementTest.html +2 -2
  294. data/docs/brut-js/api/testing.DOMCreator.html +2 -2
  295. data/docs/brut-js/api/testing_AssetMetadata.js.html +2 -2
  296. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +2 -2
  297. data/docs/brut-js/api/testing_CustomElementTest.js.html +2 -2
  298. data/docs/brut-js/api/testing_DOMCreator.js.html +2 -2
  299. data/docs/brut-js/api/testing_index.js.html +2 -2
  300. data/docs/brut-js.html +5 -5
  301. data/docs/business-logic.html +3 -3
  302. data/docs/cli.html +3 -3
  303. data/docs/components.html +7 -7
  304. data/docs/configuration.html +5 -5
  305. data/docs/css.html +3 -3
  306. data/docs/custom-element-tests.html +3 -3
  307. data/docs/database-access.html +3 -3
  308. data/docs/database-schema.html +3 -3
  309. data/docs/deployment.html +3 -3
  310. data/docs/dev-environment.html +3 -3
  311. data/docs/dir-structure.html +3 -3
  312. data/docs/doc-conventions.html +3 -3
  313. data/docs/end-to-end-tests.html +3 -3
  314. data/docs/features.html +3 -3
  315. data/docs/flash-and-session.html +3 -3
  316. data/docs/form-constraints.html +3 -3
  317. data/docs/forms.html +5 -5
  318. data/docs/getting-started.html +6 -6
  319. data/docs/handlers.html +3 -3
  320. data/docs/hashmap.json +1 -1
  321. data/docs/hooks.html +3 -3
  322. data/docs/i18n.html +3 -3
  323. data/docs/index.html +3 -3
  324. data/docs/instrumentation.html +3 -3
  325. data/docs/javascript.html +3 -3
  326. data/docs/jobs.html +3 -3
  327. data/docs/keyword-injection.html +3 -3
  328. data/docs/layouts.html +3 -3
  329. data/docs/lsp.html +3 -3
  330. data/docs/markdown-examples.html +3 -3
  331. data/docs/middleware.html +3 -3
  332. data/docs/overview.html +3 -3
  333. data/docs/pages.html +3 -3
  334. data/docs/recipes/alternate-layouts.html +3 -3
  335. data/docs/recipes/authentication.html +3 -3
  336. data/docs/recipes/custom-flash.html +3 -3
  337. data/docs/recipes/form-errors.html +3 -3
  338. data/docs/recipes/indexed-forms.html +3 -3
  339. data/docs/recipes/migrations.html +3 -3
  340. data/docs/recipes/text-field-component.html +3 -3
  341. data/docs/roadmap.html +3 -3
  342. data/docs/routes.html +3 -3
  343. data/docs/security.html +3 -3
  344. data/docs/seed-data.html +3 -3
  345. data/docs/space-time-continuum.html +3 -3
  346. data/docs/tutorial.html +3 -3
  347. data/docs/tutorials/01-intro.html +3 -3
  348. data/docs/tutorials/02-dialog.html +5 -5
  349. data/docs/unit-tests.html +3 -3
  350. data/docs/why.html +3 -3
  351. data/dx/bash_customizations +3 -4
  352. data/dx/build.pre +15 -0
  353. data/lib/brut/cli/apps/heroku_container_based_deploy.rb +41 -15
  354. data/lib/brut/cli/apps/test.rb +2 -0
  355. data/lib/brut/framework/container.rb +6 -4
  356. data/lib/brut/framework/mcp.rb +1 -1
  357. data/lib/brut/version.rb +1 -1
  358. data/mkbrut/Gemfile.lock +2 -2
  359. data/mkbrut/lib/mkbrut/add_segment.rb +38 -0
  360. data/mkbrut/lib/mkbrut/add_segment_options.rb +22 -0
  361. data/mkbrut/lib/mkbrut/app.rb +7 -2
  362. data/mkbrut/lib/mkbrut/base.rb +1 -0
  363. data/mkbrut/lib/mkbrut/cli.rb +90 -8
  364. data/mkbrut/lib/mkbrut/ops/insert_code_in_method.rb +19 -7
  365. data/mkbrut/lib/mkbrut/ops/insert_into_file.rb +36 -0
  366. data/mkbrut/lib/mkbrut/ops/prism_parsing_op.rb +14 -2
  367. data/mkbrut/lib/mkbrut/ops.rb +1 -0
  368. data/mkbrut/lib/mkbrut/segments/bare_bones.rb +8 -0
  369. data/mkbrut/lib/mkbrut/segments/demo.rb +8 -0
  370. data/mkbrut/lib/mkbrut/segments/heroku.rb +23 -3
  371. data/mkbrut/lib/mkbrut/segments/sidekiq.rb +136 -1
  372. data/mkbrut/lib/mkbrut/segments.rb +1 -1
  373. data/mkbrut/lib/mkbrut/version.rb +1 -1
  374. data/mkbrut/lib/mkbrut.rb +2 -0
  375. data/mkbrut/templates/Base/.gitignore +3 -0
  376. data/mkbrut/templates/Base/Dockerfile.dx +18 -21
  377. data/mkbrut/templates/Base/Gemfile.erb +1 -1
  378. data/mkbrut/templates/Base/bin/run +107 -67
  379. data/mkbrut/templates/Base/bin/run.run +4 -0
  380. data/mkbrut/templates/Base/bin/setup +32 -1
  381. data/mkbrut/templates/Base/dx/bash_customizations +0 -4
  382. data/mkbrut/templates/Base/package.json.erb +1 -1
  383. data/mkbrut/templates/segments/Heroku/deploy/Dockerfile +15 -15
  384. data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +5 -4
  385. data/mkbrut/templates/segments/Sidekiq/app/boot_sidekiq.rb +2 -0
  386. data/mkbrut/templates/segments/Sidekiq/app/config/sidekiq.yml +4 -0
  387. data/mkbrut/templates/segments/Sidekiq/app/src/back_end/jobs/app_job.rb +3 -0
  388. data/mkbrut/templates/segments/Sidekiq/app/src/back_end/jobs/example_job.rb +12 -0
  389. data/mkbrut/templates/segments/Sidekiq/app/src/back_end/segments/sidekiq_segment.rb +56 -0
  390. data/mkbrut/templates/segments/Sidekiq/bin/run.sidekiq +4 -0
  391. data/mkbrut/templates/segments/Sidekiq/specs/back_end/jobs/example_job.spec.rb +5 -0
  392. data/mkbrut/templates/segments/Sidekiq/specs/integration/sidekiq_works.spec.rb +38 -0
  393. metadata +34 -18
  394. data/docs/assets/chunks/@localSearchIndexroot.C0s1k0UQ.js +0 -1
  395. /data/docs/assets/{brut-js.md.CbJAe2Ky.lean.js → brut-js.md.BMz0X1Rz.lean.js} +0 -0
  396. /data/docs/assets/{components.md.C6nWgDP0.lean.js → components.md.Dfd3w6UW.lean.js} +0 -0
  397. /data/docs/assets/{configuration.md.CpbYHWPb.lean.js → configuration.md.DTYoV2Ea.lean.js} +0 -0
  398. /data/docs/assets/{forms.md.Bii91k3E.lean.js → forms.md.DEkmJUvb.lean.js} +0 -0
  399. /data/docs/assets/{getting-started.md.ChAvueK7.lean.js → getting-started.md.DO-4eoGW.lean.js} +0 -0
  400. /data/docs/assets/{tutorials_02-dialog.md.De6iTsWX.lean.js → tutorials_02-dialog.md.D2vSjDVf.lean.js} +0 -0
@@ -176,7 +176,7 @@ models in Log messages without having to explicitly fetch the id.</p>
176
176
  </div>
177
177
 
178
178
  <div id="footer">
179
- Generated on Tue Sep 9 14:11:14 2025 by
179
+ Generated on Tue Sep 16 13:23:59 2025 by
180
180
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
181
181
  0.9.37 (ruby-3.4.5).
182
182
  </div>
@@ -208,7 +208,7 @@ what type of thing an identifier represents.</p>
208
208
  </div>
209
209
 
210
210
  <div id="footer">
211
- Generated on Tue Sep 9 14:11:14 2025 by
211
+ Generated on Tue Sep 16 13:23:59 2025 by
212
212
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
213
213
  0.9.37 (ruby-3.4.5).
214
214
  </div>
@@ -192,7 +192,7 @@
192
192
  </div>
193
193
 
194
194
  <div id="footer">
195
- Generated on Tue Sep 9 14:11:14 2025 by
195
+ Generated on Tue Sep 16 13:23:59 2025 by
196
196
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
197
197
  0.9.37 (ruby-3.4.5).
198
198
  </div>
@@ -115,7 +115,7 @@
115
115
  </div>
116
116
 
117
117
  <div id="footer">
118
- Generated on Tue Sep 9 14:11:14 2025 by
118
+ Generated on Tue Sep 16 13:23:59 2025 by
119
119
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
120
120
  0.9.37 (ruby-3.4.5).
121
121
  </div>
@@ -107,7 +107,7 @@
107
107
  </div>
108
108
 
109
109
  <div id="footer">
110
- Generated on Tue Sep 9 14:11:14 2025 by
110
+ Generated on Tue Sep 16 13:23:59 2025 by
111
111
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
112
112
  0.9.37 (ruby-3.4.5).
113
113
  </div>
data/docs/api/Sequel.html CHANGED
@@ -107,7 +107,7 @@
107
107
  </div>
108
108
 
109
109
  <div id="footer">
110
- Generated on Tue Sep 9 14:11:14 2025 by
110
+ Generated on Tue Sep 16 13:23:59 2025 by
111
111
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
112
112
  0.9.37 (ruby-3.4.5).
113
113
  </div>
data/docs/api/_index.html CHANGED
@@ -1709,7 +1709,7 @@
1709
1709
  </div>
1710
1710
 
1711
1711
  <div id="footer">
1712
- Generated on Tue Sep 9 14:11:13 2025 by
1712
+ Generated on Tue Sep 16 13:23:59 2025 by
1713
1713
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
1714
1714
  0.9.37 (ruby-3.4.5).
1715
1715
  </div>
@@ -162,7 +162,7 @@ actions as well.</p>
162
162
  </div></div>
163
163
 
164
164
  <div id="footer">
165
- Generated on Tue Sep 9 14:11:13 2025 by
165
+ Generated on Tue Sep 16 13:23:59 2025 by
166
166
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
167
167
  0.9.37 (ruby-3.4.5).
168
168
  </div>
data/docs/api/index.html CHANGED
@@ -162,7 +162,7 @@ actions as well.</p>
162
162
  </div></div>
163
163
 
164
164
  <div id="footer">
165
- Generated on Tue Sep 9 14:11:13 2025 by
165
+ Generated on Tue Sep 16 13:23:59 2025 by
166
166
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
167
167
  0.9.37 (ruby-3.4.5).
168
168
  </div>
@@ -102,7 +102,7 @@
102
102
  </div>
103
103
 
104
104
  <div id="footer">
105
- Generated on Tue Sep 9 14:11:13 2025 by
105
+ Generated on Tue Sep 16 13:23:59 2025 by
106
106
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
107
107
  0.9.37 (ruby-3.4.5).
108
108
  </div>
@@ -1 +1 @@
1
- import{R as p}from"./chunks/theme.CtVUdCdt.js";import{R as s,a3 as i,a4 as u,a5 as c,a6 as l,a7 as f,a8 as d,a9 as m,aa as h,ab as g,ac as A,d as v,u as R,v as w,s as y,ad as C,ae as P,af as b,a2 as E}from"./chunks/framework.C4nOkCZI.js";function r(e){if(e.extends){const a=r(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const n=r(p),S=v({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=R();return w(()=>{y(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&C(),P(),b(),n.setup&&n.setup(),()=>E(n.Layout)}});async function T(){globalThis.__VITEPRESS__=!0;const e=_(),a=D();a.provide(u,e);const t=c(e.route);return a.provide(l,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),n.enhanceApp&&await n.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function D(){return A(S)}function _(){let e=s;return h(a=>{let t=g(a),o=null;return t&&(e&&(t=t.replace(/\.js$/,".lean.js")),o=import(t)),s&&(e=!1),o},n.NotFound)}s&&T().then(({app:e,router:a,data:t})=>{a.go().then(()=>{i(a.route,t.site),e.mount("#app")})});export{T as createApp};
1
+ import{R as p}from"./chunks/theme.BuExsdM9.js";import{R as s,a3 as i,a4 as u,a5 as c,a6 as l,a7 as f,a8 as d,a9 as m,aa as h,ab as g,ac as A,d as v,u as R,v as w,s as y,ad as C,ae as P,af as b,a2 as E}from"./chunks/framework.C4nOkCZI.js";function r(e){if(e.extends){const a=r(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const n=r(p),S=v({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=R();return w(()=>{y(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&C(),P(),b(),n.setup&&n.setup(),()=>E(n.Layout)}});async function T(){globalThis.__VITEPRESS__=!0;const e=_(),a=D();a.provide(u,e);const t=c(e.route);return a.provide(l,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),n.enhanceApp&&await n.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function D(){return A(S)}function _(){let e=s;return h(a=>{let t=g(a),o=null;return t&&(e&&(t=t.replace(/\.js$/,".lean.js")),o=import(t)),s&&(e=!1),o},n.NotFound)}s&&T().then(({app:e,router:a,data:t})=>{a.go().then(()=>{i(a.route,t.site),e.mount("#app")})});export{T as createApp};
@@ -9,4 +9,4 @@ import{_ as t,c as a,o as s,ag as o}from"./chunks/framework.C4nOkCZI.js";const i
9
9
  <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> button { </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;Submit&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
10
10
  <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
11
11
  <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
12
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><h3 id="custom-elements" tabindex="-1">Custom Elements <a class="header-anchor" href="#custom-elements" aria-label="Permalink to &quot;Custom Elements&quot;">​</a></h3><p>The JSDoc for these elements&#39; classes should provide complete documentation, however this is an overview of what each one does.</p><table tabindex="0"><thead><tr><th>Element</th><th>Purpose</th></tr></thead><tbody><tr><td><a href="/brut-js/api/AjaxSubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-ajax-submit&gt;</code></a></td><td>Allows submitting a form via Ajax. Handles the use of <code>fetch</code> and all possible cases, but you still provide the logic for what to do with the response.</td></tr><tr><td><a href="/brut-js/api/Autosubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-autosubmit&gt;</code></a></td><td>Auto submits a form when a <code>&lt;select&gt;</code>&#39;s option is chosen.</td></tr><tr><td><a href="/brut-js/api/ConfirmationDialog.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-confirmation-dialog&gt;</code></a></td><td>Enhances a <code>&lt;dialog&gt;</code> to make it easier to use as a generic confirmation with <a href="/brut-js/api/ConfirmSubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-confirm-submit&gt;</code></a></td></tr><tr><td><a href="/brut-js/api/ConfirmSubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-confirm-submit&gt;</code></a></td><td>Uses <code>window.confirm</code> or your owned styled <code>&lt;dialog&gt;</code> to confirm a button click.</td></tr><tr><td><a href="/brut-js/api/ConstraintViolationMessage.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv&gt;</code></a></td><td>Like <a href="/brut-js/api/Message.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-message&gt;</code></a> but specific to constraint violations, namely having additional logic for subsituting the field name in the message.</td></tr><tr><td><a href="/brut-js/api/ConstraintViolationMessages.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv-messages&gt;</code></a></td><td>Wraps <a href="/brut-js/api/ConstraintViolationMessage.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv&gt;</code></a> elements related to a single form input.</td></tr><tr><td><a href="/brut-js/api/CopyToClipboard.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-copy-to-clipboard&gt;</code></a></td><td>Allows the button inside it to copy text from another element onto the clipboard.</td></tr><tr><td><a href="/brut-js/api/Form.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-form&gt;</code></a></td><td>Manages client-side constraint violation UX unified with the server-side, as well as a few quality-of-life improvements for client-side violations and styling. See <a href="/forms.html#forms-and-constraint-violations">Forms</a>.</td></tr><tr><td><a href="/brut-js/api/I18nTranslation.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-i18n-translation&gt;</code></a></td><td>Holds the translated value for a single key in the web site visitor&#39;s locale.</td></tr><tr><td><a href="/brut-js/api/LocaleDetection.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-locale-detection&gt;</code></a></td><td>Sends an Ajax request to the server with the browser&#39;s reported locale and timezone. See <a href="/space-time-continuum.html#getting-timezone-from-the-browser">space-time continuum</a> for more details.</td></tr><tr><td><a href="/brut-js/api/Message.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-message&gt;</code></a></td><td>Shows a message using an <a href="/i18n.html">i18n</a> key to dynamically pull a localized message for client-side constraint violations.</td></tr><tr><td><a href="/brut-js/api/Tabs.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tabs&gt;</code></a></td><td>Uses ARIA roles related to a tab control and implements it client-side.</td></tr><tr><td><a href="/brut-js/api/Tracing.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tracing&gt;</code></a></td><td>Sends observability data back to the server to unify a server-side request with client-side tracing.</td></tr></tbody></table><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>BrutJS&#39;s elements were created only to solve specific issues in the apps Brut was initially used for. It&#39;s hoped that more elements will be added to provide a more feature-complete set of primitives to create client-side enhancements.</p></div><h3 id="creating-your-own-custom-elements" tabindex="-1">Creating Your Own Custom Elements <a class="header-anchor" href="#creating-your-own-custom-elements" aria-label="Permalink to &quot;Creating Your Own Custom Elements&quot;">​</a></h3><p>BrutJS includes a base class, <a href="/brut-js/api/BaseCustomElement.html"><code>BaseCustomElement</code></a>, you can use to create your own custom elements with a bit more help, but not too much.</p><p>The documentation for <code>BaseCustomElement</code> has an example, but here are the features you get (noting that you aren&#39;t abandoning the web platform&#39;s API, merely gaining a few additional quality-of-life improvements):</p><ul><li>The ability to add debugging statements that are disabled via markup, not commenting-out <code>console.log</code></li><li>Per-attribute change callbacks so you don&#39;t have to create <code>attributeChangedCallback</code> as a giant <code>if/else</code> block.</li><li>Default implementations of <code>connectedCallback</code> and <code>attributeChangedCallback</code> that call the template method <code>update</code>, thus allowing your element to centralize its logic in one place, regardless of how a state change was triggered.</li><li>Static <code>define()</code> method that defines your element based on its static <code>tagName</code> field. This allows richer interaction of elements, as you can do e.g. <code>document.querySelector(SomeOtherElement.tagName)</code> and better navigate changes to your code over time.</li></ul><p>If you are familiar with the API for autonomous custom elements, <code>BaseCustomElement</code> doesn&#39;t require learning much more. What you know already will be leveraged.</p><h3 id="removing-brutjs" tabindex="-1">Removing BrutJS <a class="header-anchor" href="#removing-brutjs" aria-label="Permalink to &quot;Removing BrutJS&quot;">​</a></h3><p>To remove BrutJS from your app, modify <code>app/src/front_end/js/index.js</code> to remove the <code>import</code> and call to <code>define()</code>. You can then remove it from your <code>package.json</code>.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>If you remove it like this, several features will not work, including locale detection, client-side observability, and client-side form validation UX.</p></div><h2 id="recommnded-practices" tabindex="-1">Recommnded Practices <a class="header-anchor" href="#recommnded-practices" aria-label="Permalink to &quot;Recommnded Practices&quot;">​</a></h2><h3 id="leaving-brutjs-in-your-app" tabindex="-1">Leaving BrutJS In Your App <a class="header-anchor" href="#leaving-brutjs-in-your-app" aria-label="Permalink to &quot;Leaving BrutJS In Your App&quot;">​</a></h3><p>BrutJS provides useful tools unrelated to single-page apps, or reactivity, or whatever else you might be concerned with in your client-side code. These features can work alongside whatever framework you want to use. Leave them in unless they are causing a specific problem.</p><h3 id="you-probably-don-t-need-a-single-page-app" tabindex="-1">You Probably Don&#39;t Need a Single-Page App <a class="header-anchor" href="#you-probably-don-t-need-a-single-page-app" aria-label="Permalink to &quot;You Probably Don&#39;t Need a Single-Page App&quot;">​</a></h3><p>Consider this decision tree from Alex Russell&#39;s <a href="https://infrequently.org/2024/11/if-not-react-then-what/" target="_blank" rel="noreferrer">If Not React, Then What?</a>:</p><p><img src="`+i+'" alt="Tree showing an SPA decision"></p><p>This is how Brut wants you to consider your app&#39;s architecture. <em>Many</em> apps do not have long-running sessions where visitors make lots of updates to data. Most so-called &quot;CRUD&quot; apps do not fall into this category. The visitor would be better served by a traditional app with server-side HTML generation and minimal interactivity. Visitors would also be better served with progressively enhanced features instead of massive JS payloads that show white screens on low bandwidth/low performance devices.</p><p>Thus, Brut recommends you design your app to work in a tranditional multi-page app sort of way, then <em>enhance</em> as needed using autonomous custom elements.</p><p>You can, of course, bring in whatever framework you like and use that in the normal way. BrutJS&#39;s custom elements should work with any framework.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>See <a href="/custom-element-tests.html">Testing Custom Elements</a>.</p><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to &quot;Technical Notes&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut&#39;s internals, the source code is always more correct.</p></div><p><em>Last Updated June 15, 2025</em></p><p>None.</p>',35)])])}const g=t(n,[["render",r]]);export{m as __pageData,g as default};
12
+ <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><h3 id="custom-elements" tabindex="-1">Custom Elements <a class="header-anchor" href="#custom-elements" aria-label="Permalink to &quot;Custom Elements&quot;">​</a></h3><p>The JSDoc for these elements&#39; classes should provide complete documentation, however this is an overview of what each one does.</p><table tabindex="0"><thead><tr><th>Element</th><th>Purpose</th></tr></thead><tbody><tr><td><a href="/brut-js/api/AjaxSubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-ajax-submit&gt;</code></a></td><td>Allows submitting a form via Ajax. Handles the use of <code>fetch</code> and all possible cases, but you still provide the logic for what to do with the response.</td></tr><tr><td><a href="/brut-js/api/Autosubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-autosubmit&gt;</code></a></td><td>Auto submits a form when a <code>&lt;select&gt;</code>&#39;s option is chosen.</td></tr><tr><td><a href="/brut-js/api/ConfirmationDialog.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-confirmation-dialog&gt;</code></a></td><td>Enhances a <code>&lt;dialog&gt;</code> to make it easier to use as a generic confirmation with <a href="/brut-js/api/ConfirmSubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-confirm-submit&gt;</code></a></td></tr><tr><td><a href="/brut-js/api/ConfirmSubmit.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-confirm-submit&gt;</code></a></td><td>Uses <code>window.confirm</code> or your owned styled <code>&lt;dialog&gt;</code> to confirm a button click.</td></tr><tr><td><a href="/brut-js/api/ConstraintViolationMessage.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv&gt;</code></a></td><td>Like <a href="/brut-js/api/Message.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-message&gt;</code></a> but specific to constraint violations, namely having additional logic for subsituting the field name in the message.</td></tr><tr><td><a href="/brut-js/api/ConstraintViolationMessages.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv-messages&gt;</code></a></td><td>Wraps <a href="/brut-js/api/ConstraintViolationMessage.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv&gt;</code></a> elements related to a single form input.</td></tr><tr><td><a href="/brut-js/api/CopyToClipboard.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-copy-to-clipboard&gt;</code></a></td><td>Allows the button inside it to copy text from another element onto the clipboard.</td></tr><tr><td><a href="/brut-js/api/Form.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-form&gt;</code></a></td><td>Manages client-side constraint violation UX unified with the server-side, as well as a few quality-of-life improvements for client-side violations and styling. See <a href="/forms.html#forms-and-constraint-violations">Forms</a>.</td></tr><tr><td><a href="/brut-js/api/I18nTranslation.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-i18n-translation&gt;</code></a></td><td>Holds the translated value for a single key in the web site visitor&#39;s locale.</td></tr><tr><td><a href="/brut-js/api/LocaleDetection.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-locale-detection&gt;</code></a></td><td>Sends an Ajax request to the server with the browser&#39;s reported locale and timezone. See <a href="/space-time-continuum.html#getting-timezone-from-the-browser">space-time continuum</a> for more details.</td></tr><tr><td><a href="/brut-js/api/Message.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-message&gt;</code></a></td><td>Shows a message using an <a href="/i18n.html">i18n</a> key to dynamically pull a localized message for client-side constraint violations.</td></tr><tr><td><a href="/brut-js/api/Tabs.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tabs&gt;</code></a></td><td>Uses ARIA roles related to a tab control and implements it client-side.</td></tr><tr><td><a href="/brut-js/api/Toast.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-toast&gt;</code></a></td><td>Support for managing i18-capable <a href="https://en.wikipedia.org/wiki/Pop-up_notification" target="_blank" rel="noreferrer">toast notifications</a></td></tr><tr><td><a href="/brut-js/api/Tracing.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tracing&gt;</code></a></td><td>Sends observability data back to the server to unify a server-side request with client-side tracing.</td></tr></tbody></table><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>BrutJS&#39;s elements were created only to solve specific issues in the apps Brut was initially used for. It&#39;s hoped that more elements will be added to provide a more feature-complete set of primitives to create client-side enhancements.</p></div><h3 id="creating-your-own-custom-elements" tabindex="-1">Creating Your Own Custom Elements <a class="header-anchor" href="#creating-your-own-custom-elements" aria-label="Permalink to &quot;Creating Your Own Custom Elements&quot;">​</a></h3><p>BrutJS includes a base class, <a href="/brut-js/api/BaseCustomElement.html"><code>BaseCustomElement</code></a>, you can use to create your own custom elements with a bit more help, but not too much.</p><p>The documentation for <code>BaseCustomElement</code> has an example, but here are the features you get (noting that you aren&#39;t abandoning the web platform&#39;s API, merely gaining a few additional quality-of-life improvements):</p><ul><li>The ability to add debugging statements that are disabled via markup, not commenting-out <code>console.log</code></li><li>Per-attribute change callbacks so you don&#39;t have to create <code>attributeChangedCallback</code> as a giant <code>if/else</code> block.</li><li>Default implementations of <code>connectedCallback</code> and <code>attributeChangedCallback</code> that call the template method <code>update</code>, thus allowing your element to centralize its logic in one place, regardless of how a state change was triggered.</li><li>Static <code>define()</code> method that defines your element based on its static <code>tagName</code> field. This allows richer interaction of elements, as you can do e.g. <code>document.querySelector(SomeOtherElement.tagName)</code> and better navigate changes to your code over time.</li></ul><p>If you are familiar with the API for autonomous custom elements, <code>BaseCustomElement</code> doesn&#39;t require learning much more. What you know already will be leveraged.</p><h3 id="removing-brutjs" tabindex="-1">Removing BrutJS <a class="header-anchor" href="#removing-brutjs" aria-label="Permalink to &quot;Removing BrutJS&quot;">​</a></h3><p>To remove BrutJS from your app, modify <code>app/src/front_end/js/index.js</code> to remove the <code>import</code> and call to <code>define()</code>. You can then remove it from your <code>package.json</code>.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>If you remove it like this, several features will not work, including locale detection, client-side observability, and client-side form validation UX.</p></div><h2 id="recommnded-practices" tabindex="-1">Recommnded Practices <a class="header-anchor" href="#recommnded-practices" aria-label="Permalink to &quot;Recommnded Practices&quot;">​</a></h2><h3 id="leaving-brutjs-in-your-app" tabindex="-1">Leaving BrutJS In Your App <a class="header-anchor" href="#leaving-brutjs-in-your-app" aria-label="Permalink to &quot;Leaving BrutJS In Your App&quot;">​</a></h3><p>BrutJS provides useful tools unrelated to single-page apps, or reactivity, or whatever else you might be concerned with in your client-side code. These features can work alongside whatever framework you want to use. Leave them in unless they are causing a specific problem.</p><h3 id="you-probably-don-t-need-a-single-page-app" tabindex="-1">You Probably Don&#39;t Need a Single-Page App <a class="header-anchor" href="#you-probably-don-t-need-a-single-page-app" aria-label="Permalink to &quot;You Probably Don&#39;t Need a Single-Page App&quot;">​</a></h3><p>Consider this decision tree from Alex Russell&#39;s <a href="https://infrequently.org/2024/11/if-not-react-then-what/" target="_blank" rel="noreferrer">If Not React, Then What?</a>:</p><p><img src="`+i+'" alt="Tree showing an SPA decision"></p><p>This is how Brut wants you to consider your app&#39;s architecture. <em>Many</em> apps do not have long-running sessions where visitors make lots of updates to data. Most so-called &quot;CRUD&quot; apps do not fall into this category. The visitor would be better served by a traditional app with server-side HTML generation and minimal interactivity. Visitors would also be better served with progressively enhanced features instead of massive JS payloads that show white screens on low bandwidth/low performance devices.</p><p>Thus, Brut recommends you design your app to work in a tranditional multi-page app sort of way, then <em>enhance</em> as needed using autonomous custom elements.</p><p>You can, of course, bring in whatever framework you like and use that in the normal way. BrutJS&#39;s custom elements should work with any framework.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>See <a href="/custom-element-tests.html">Testing Custom Elements</a>.</p><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to &quot;Technical Notes&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut&#39;s internals, the source code is always more correct.</p></div><p><em>Last Updated June 15, 2025</em></p><p>None.</p>',35)])])}const g=t(n,[["render",r]]);export{m as __pageData,g as default};