brut 0.0.20 → 0.0.22
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.
- checksums.yaml +4 -4
- data/.gitignore +24 -3
- data/.nvim.lua +1 -0
- data/Dockerfile.dx +12 -3
- data/Gemfile.lock +9 -7
- data/README.md +0 -7
- data/Rakefile +6 -4
- data/bin/dev +20 -0
- data/bin/docs +27 -0
- data/bin/setup +47 -1
- data/brut-css/.nvim.lua +1 -0
- data/brut-css/README.md +28 -0
- data/brut-css/bin/build +31 -0
- data/brut-css/bin/dev +1 -0
- data/brut-css/bin/docs +15 -0
- data/brut-css/bin/setup +5 -0
- data/brut-css/config/media-queries-all.css +15 -0
- data/brut-css/config/media-queries-minimal.css +5 -0
- data/brut-css/config/postcss.config.cjs +7 -0
- data/brut-css/config/pseudo-classes-all.css +9 -0
- data/brut-css/dx +1 -0
- data/brut-css/package-lock.json +3217 -0
- data/brut-css/package.json +36 -0
- data/brut-css/src/css/appearance.css +145 -0
- data/brut-css/src/css/border.css +522 -0
- data/brut-css/src/css/colors.css +3502 -0
- data/brut-css/src/css/dimensions.css +548 -0
- data/brut-css/src/css/flex.css +179 -0
- data/brut-css/src/css/index.css +13 -0
- data/brut-css/src/css/layout.css +120 -0
- data/brut-css/src/css/list.css +41 -0
- data/brut-css/src/css/positioning.css +354 -0
- data/brut-css/src/css/properties/colors.css +455 -0
- data/brut-css/src/css/properties/index.css +3 -0
- data/brut-css/src/css/properties/spacing.css +140 -0
- data/brut-css/src/css/properties/typography.css +224 -0
- data/brut-css/src/css/reset.css +107 -0
- data/brut-css/src/css/spacing.css +585 -0
- data/brut-css/src/css/typography.css +519 -0
- data/brut-css/src/css/utils.css +104 -0
- data/brut-css/src/docs/1_getting-started/1_overview.md +46 -0
- data/brut-css/src/docs/1_getting-started/2_installation.md +25 -0
- data/brut-css/src/docs/1_getting-started/3_core-concepts.md +75 -0
- data/brut-css/src/docs/1_getting-started/4_simple-example.md +132 -0
- data/brut-css/src/docs/1_getting-started/page.html.ejs +10 -0
- data/brut-css/src/docs/2_properties/page.html.ejs +71 -0
- data/brut-css/src/docs/3_classes/color-demo.html.ejs +31 -0
- data/brut-css/src/docs/3_classes/page.html.ejs +87 -0
- data/brut-css/src/docs/4_customization/1_design-system.md +36 -0
- data/brut-css/src/docs/4_customization/2_breakpoints.md +75 -0
- data/brut-css/src/docs/4_customization/3_pseudo-classes.md +74 -0
- data/brut-css/src/docs/4_customization/4_advanced-configuration.md +40 -0
- data/brut-css/src/docs/4_customization/page.html.ejs +10 -0
- data/brut-css/src/docs/docs.css +98 -0
- data/brut-css/src/docs/includes/body-and-header.html.ejs +30 -0
- data/brut-css/src/docs/includes/footer-and-rest.html.ejs +9 -0
- data/brut-css/src/docs/includes/head.html.ejs +5 -0
- data/brut-css/src/docs/includes/nav.html.ejs +10 -0
- data/brut-css/src/docs/index.html.ejs +32 -0
- data/brut-css/src/docs/prism-twilight.min.css +1 -0
- data/brut-css/src/js/Logger.js +71 -0
- data/brut-css/src/js/build.js +111 -0
- data/brut-css/src/js/cli/CLIArgError.js +7 -0
- data/brut-css/src/js/cli/Debug.js +27 -0
- data/brut-css/src/js/cli/DocsDir.js +16 -0
- data/brut-css/src/js/cli/DocsTemplateSourceDir.js +16 -0
- data/brut-css/src/js/cli/InputFile.js +31 -0
- data/brut-css/src/js/cli/MediaQueryConfigFile.js +10 -0
- data/brut-css/src/js/cli/OutputFile.js +22 -0
- data/brut-css/src/js/cli/ParsedArg.js +17 -0
- data/brut-css/src/js/cli/PathToBrutCSSRoot.js +19 -0
- data/brut-css/src/js/cli/PseudoClassConfigFile.js +11 -0
- data/brut-css/src/js/cli.js +108 -0
- data/brut-css/src/js/docGenerator.js +467 -0
- data/brut-css/src/js/mediaQueryConfigParser.js +98 -0
- data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +49 -0
- data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +42 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +9 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +185 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +8 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +7 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +73 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +9 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +4 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +8 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +12 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +4 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +8 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +5 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +9 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +49 -0
- data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +45 -0
- data/brut-css/src/js/pseudoClassConfigParser.js +145 -0
- data/brut-js/.projections.json +10 -0
- data/brut-js/README.md +118 -0
- data/brut-js/bin/build +10 -0
- data/brut-js/bin/ci +5 -0
- data/brut-js/bin/setup +5 -0
- data/brut-js/docs/README.md +8 -0
- data/brut-js/docs/jsdoc-plugins/customElementTag.js +8 -0
- data/brut-js/docs/jsdoc-theme/publish.js +692 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +25 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +202 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +2 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +28 -0
- data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +327 -0
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +111 -0
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +132 -0
- data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +10 -0
- data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +199 -0
- data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +143 -0
- data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +2 -0
- data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +13 -0
- data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +32 -0
- data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +38 -0
- data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +14 -0
- data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +38 -0
- data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +131 -0
- data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +14 -0
- data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +131 -0
- data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +108 -0
- data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +19 -0
- data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +8 -0
- data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +19 -0
- data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +7 -0
- data/brut-js/docs/jsdoc.config.json +23 -0
- data/brut-js/docs/package-lock.json +343 -0
- data/brut-js/docs/package.json +7 -0
- data/brut-js/package-lock.json +2171 -0
- data/brut-js/package.json +32 -0
- data/brut-js/specs/AjaxSubmit.spec.js +256 -0
- data/brut-js/specs/Autosubmit.spec.js +127 -0
- data/brut-js/specs/ConfirmSubmit.spec.js +193 -0
- data/brut-js/specs/ConstraintViolationMessage.spec.js +33 -0
- data/brut-js/specs/ConstraintViolationMessages.spec.js +29 -0
- data/brut-js/specs/CopyToClipboard.spec.js +35 -0
- data/brut-js/specs/Form.spec.js +181 -0
- data/brut-js/specs/I18nTranslation.spec.js +19 -0
- data/brut-js/specs/LocaleDetection.spec.js +22 -0
- data/brut-js/specs/Message.spec.js +15 -0
- data/brut-js/specs/SpecHelper.js +23 -0
- data/brut-js/specs/Tabs.spec.js +41 -0
- data/brut-js/specs/config/asset_metadata.json +7 -0
- data/brut-js/src/AjaxSubmit.js +384 -0
- data/brut-js/src/Autosubmit.js +63 -0
- data/brut-js/src/BaseCustomElement.js +261 -0
- data/brut-js/src/ConfirmSubmit.js +116 -0
- data/brut-js/src/ConfirmationDialog.js +143 -0
- data/brut-js/src/ConstraintViolationMessage.js +125 -0
- data/brut-js/src/ConstraintViolationMessages.js +98 -0
- data/brut-js/src/CopyToClipboard.js +96 -0
- data/brut-js/src/Form.js +151 -0
- data/brut-js/src/I18nTranslation.js +61 -0
- data/brut-js/src/LocaleDetection.js +117 -0
- data/brut-js/src/Logger.js +90 -0
- data/brut-js/src/Message.js +56 -0
- data/brut-js/src/RichString.js +113 -0
- data/brut-js/src/Tabs.js +168 -0
- data/brut-js/src/Tracing.js +247 -0
- data/brut-js/src/appForTestingOnly.js +15 -0
- data/brut-js/src/index.js +130 -0
- data/brut-js/src/testing/AssetMetadata.js +35 -0
- data/brut-js/src/testing/AssetMetadataLoader.js +25 -0
- data/brut-js/src/testing/CustomElementTest.js +235 -0
- data/brut-js/src/testing/DOMCreator.js +45 -0
- data/brut-js/src/testing/index.js +48 -0
- data/brutrb.com/.vitepress/config.mjs +106 -0
- data/brutrb.com/.vitepress/plugins/jsdocLinker.js +34 -0
- data/brutrb.com/.vitepress/plugins/rdocLinker.js +18 -0
- data/brutrb.com/.vitepress/theme/custom.css +7 -0
- data/brutrb.com/.vitepress/theme/index.js +18 -0
- data/brutrb.com/.vitepress/theme/style.css +149 -0
- data/brutrb.com/ai.md +68 -0
- data/brutrb.com/assets.md +138 -0
- data/brutrb.com/bin/build +5 -0
- data/brutrb.com/bin/deploy +7 -0
- data/brutrb.com/bin/dev +5 -0
- data/brutrb.com/bin/setup +5 -0
- data/brutrb.com/brut-js.md +117 -0
- data/brutrb.com/business-logic.md +55 -0
- data/brutrb.com/cli.md +278 -0
- data/brutrb.com/components.md +243 -0
- data/brutrb.com/configuration.md +257 -0
- data/brutrb.com/css.md +103 -0
- data/brutrb.com/custom-element-tests.md +149 -0
- data/brutrb.com/database-access.md +201 -0
- data/brutrb.com/database-schema.md +312 -0
- data/brutrb.com/deployment.md +66 -0
- data/brutrb.com/dev-environment.md +179 -0
- data/brutrb.com/doc-conventions.md +39 -0
- data/brutrb.com/end-to-end-tests.md +174 -0
- data/brutrb.com/flash-and-session.md +224 -0
- data/brutrb.com/forms.md +866 -0
- data/brutrb.com/getting-started.md +66 -0
- data/brutrb.com/handlers.md +153 -0
- data/brutrb.com/hooks.md +178 -0
- data/brutrb.com/i18n.md +188 -0
- data/brutrb.com/images/Makefile +10 -0
- data/brutrb.com/images/dev-env-overview.dot +54 -0
- data/brutrb.com/images/dev-env-overview.png +0 -0
- data/brutrb.com/images/dev-env-protocol.dot +37 -0
- data/brutrb.com/images/dev-env-protocol.png +0 -0
- data/brutrb.com/images/logo-300.png +0 -0
- data/brutrb.com/images/logo.png +0 -0
- data/brutrb.com/images/overview.graffle +0 -0
- data/brutrb.com/images/overview.png +0 -0
- data/brutrb.com/images/spa.dot +19 -0
- data/brutrb.com/images/spa.png +0 -0
- data/brutrb.com/images/workspace-protocol.dot +44 -0
- data/brutrb.com/images/workspace-protocol.png +0 -0
- data/brutrb.com/index.md +36 -0
- data/brutrb.com/instrumentation.md +183 -0
- data/brutrb.com/javascript.md +122 -0
- data/brutrb.com/jobs.md +14 -0
- data/{doc-src → brutrb.com}/keyword-injection.md +122 -68
- data/brutrb.com/markdown-examples.md +85 -0
- data/brutrb.com/middleware.md +80 -0
- data/brutrb.com/not-released.md +5 -0
- data/brutrb.com/overview.md +404 -0
- data/brutrb.com/package-lock.json +2404 -0
- data/brutrb.com/package.json +11 -0
- data/brutrb.com/pages.md +378 -0
- data/brutrb.com/public/images/logo-300.png +0 -0
- data/brutrb.com/public/images/logo.png +0 -0
- data/brutrb.com/routes.md +215 -0
- data/brutrb.com/security.md +105 -0
- data/brutrb.com/seed-data.md +63 -0
- data/brutrb.com/space-time-continuum.md +85 -0
- data/brutrb.com/tutorial.md +3 -0
- data/brutrb.com/unit-tests.md +148 -0
- data/docker-compose.dx.yml +6 -3
- data/docs/404.html +21 -0
- data/docs/CNAME +1 -0
- data/docs/ai.html +24 -0
- data/docs/api/Brut/BackEnd/SeedData.html +493 -0
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +214 -0
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +125 -0
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +125 -0
- data/docs/api/Brut/BackEnd/Sidekiq.html +125 -0
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +414 -0
- data/docs/api/Brut/BackEnd/Validators.html +128 -0
- data/docs/api/Brut/BackEnd.html +132 -0
- data/docs/api/Brut/CLI/App.html +1576 -0
- data/docs/api/Brut/CLI/AppRunner.html +491 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +264 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +306 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +262 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +314 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +183 -0
- data/docs/api/Brut/CLI/Apps/DB/Create.html +365 -0
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +357 -0
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +383 -0
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +335 -0
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +329 -0
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +347 -0
- data/docs/api/Brut/CLI/Apps/DB/Status.html +383 -0
- data/docs/api/Brut/CLI/Apps/DB.html +183 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +303 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +512 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +398 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +374 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +410 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +262 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +303 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +480 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +450 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +380 -0
- data/docs/api/Brut/CLI/Apps/Scaffold.html +253 -0
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +464 -0
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +407 -0
- data/docs/api/Brut/CLI/Apps/Test/JS.html +262 -0
- data/docs/api/Brut/CLI/Apps/Test/Run.html +578 -0
- data/docs/api/Brut/CLI/Apps/Test.html +253 -0
- data/docs/api/Brut/CLI/Apps.html +125 -0
- data/docs/api/Brut/CLI/Command.html +2342 -0
- data/docs/api/Brut/CLI/Error.html +139 -0
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +664 -0
- data/docs/api/Brut/CLI/ExecutionResults.html +675 -0
- data/docs/api/Brut/CLI/Executor.html +430 -0
- data/docs/api/Brut/CLI/InvalidOption.html +245 -0
- data/docs/api/Brut/CLI/Options.html +753 -0
- data/docs/api/Brut/CLI/Output.html +699 -0
- data/docs/api/Brut/CLI/SystemExecError.html +451 -0
- data/docs/api/Brut/CLI.html +263 -0
- data/docs/api/Brut/FactoryBot.html +225 -0
- data/docs/api/Brut/Framework/App.html +1097 -0
- data/docs/api/Brut/Framework/Config.html +1045 -0
- data/docs/api/Brut/Framework/Container.html +1379 -0
- data/docs/api/Brut/Framework/Error.html +140 -0
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +144 -0
- data/docs/api/Brut/Framework/Errors/Bug.html +234 -0
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +257 -0
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +273 -0
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +471 -0
- data/docs/api/Brut/Framework/Errors/NotFound.html +308 -0
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +234 -0
- data/docs/api/Brut/Framework/Errors.html +328 -0
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +392 -0
- data/docs/api/Brut/Framework/MCP.html +861 -0
- data/docs/api/Brut/Framework/ProjectEnvironment.html +648 -0
- data/docs/api/Brut/Framework.html +129 -0
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +317 -0
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +326 -0
- data/docs/api/Brut/FrontEnd/Component.html +365 -0
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +470 -0
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +518 -0
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +317 -0
- data/docs/api/Brut/FrontEnd/Components/Input.html +195 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +339 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +660 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +417 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +918 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +651 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +125 -0
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +367 -0
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +336 -0
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +655 -0
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +352 -0
- data/docs/api/Brut/FrontEnd/Components.html +135 -0
- data/docs/api/Brut/FrontEnd/Download.html +467 -0
- data/docs/api/Brut/FrontEnd/Flash.html +1150 -0
- data/docs/api/Brut/FrontEnd/Form.html +1157 -0
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +634 -0
- data/docs/api/Brut/FrontEnd/Forms/Input.html +615 -0
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +547 -0
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1318 -0
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +609 -0
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +587 -0
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +613 -0
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +582 -0
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +609 -0
- data/docs/api/Brut/FrontEnd/Forms.html +127 -0
- data/docs/api/Brut/FrontEnd/GenericResponse.html +377 -0
- data/docs/api/Brut/FrontEnd/Handler.html +442 -0
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +318 -0
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +336 -0
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +399 -0
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +354 -0
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +151 -0
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +315 -0
- data/docs/api/Brut/FrontEnd/Handlers.html +125 -0
- data/docs/api/Brut/FrontEnd/HandlingResults.html +339 -0
- data/docs/api/Brut/FrontEnd/HttpMethod.html +661 -0
- data/docs/api/Brut/FrontEnd/HttpStatus.html +496 -0
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +284 -0
- data/docs/api/Brut/FrontEnd/Layout.html +318 -0
- data/docs/api/Brut/FrontEnd/Middleware.html +135 -0
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +288 -0
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +292 -0
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +324 -0
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +372 -0
- data/docs/api/Brut/FrontEnd/Middlewares.html +125 -0
- data/docs/api/Brut/FrontEnd/Page.html +773 -0
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +797 -0
- data/docs/api/Brut/FrontEnd/Pages.html +125 -0
- data/docs/api/Brut/FrontEnd/RequestContext.html +1312 -0
- data/docs/api/Brut/FrontEnd/RouteHook.html +424 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +242 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +249 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +264 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +261 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +284 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +252 -0
- data/docs/api/Brut/FrontEnd/RouteHooks.html +115 -0
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +227 -0
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +305 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +324 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +319 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +315 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +315 -0
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +327 -0
- data/docs/api/Brut/FrontEnd/Routing/Route.html +761 -0
- data/docs/api/Brut/FrontEnd/Routing.html +927 -0
- data/docs/api/Brut/FrontEnd/Session.html +1195 -0
- data/docs/api/Brut/FrontEnd.html +134 -0
- data/docs/api/Brut/I18n/BaseMethods.html +931 -0
- data/docs/api/Brut/I18n/ForBackEnd.html +302 -0
- data/docs/api/Brut/I18n/ForCLI.html +302 -0
- data/docs/api/Brut/I18n/ForHTML.html +296 -0
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +316 -0
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +930 -0
- data/docs/api/Brut/I18n.html +127 -0
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +435 -0
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +286 -0
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +302 -0
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +864 -0
- data/docs/api/Brut/Instrumentation.html +126 -0
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +532 -0
- data/docs/api/Brut/SinatraHelpers.html +281 -0
- data/docs/api/Brut/SpecSupport/ClockSupport.html +383 -0
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +502 -0
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +503 -0
- data/docs/api/Brut/SpecSupport/E2eSupport.html +142 -0
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +403 -0
- data/docs/api/Brut/SpecSupport/FlashSupport.html +278 -0
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +401 -0
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +195 -0
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +160 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +553 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +439 -0
- data/docs/api/Brut/SpecSupport/Matchers.html +125 -0
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +335 -0
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +602 -0
- data/docs/api/Brut/SpecSupport/SessionSupport.html +196 -0
- data/docs/api/Brut/SpecSupport.html +129 -0
- data/docs/api/Brut.html +225 -0
- data/docs/api/Clock.html +603 -0
- data/docs/api/RichString.html +968 -0
- data/docs/api/SemanticLogger/Appender/Async.html +219 -0
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +115 -0
- data/docs/api/Sequel/Extensions/BrutMigrations.html +533 -0
- data/docs/api/Sequel/Extensions.html +117 -0
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +105 -0
- data/docs/api/Sequel/Plugins/CreatedAt.html +125 -0
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +207 -0
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +186 -0
- data/docs/api/Sequel/Plugins/ExternalId.html +218 -0
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +202 -0
- data/docs/api/Sequel/Plugins/FindBang.html +125 -0
- data/docs/api/Sequel/Plugins.html +117 -0
- data/docs/api/Sequel.html +117 -0
- data/docs/api/_index.html +1553 -0
- data/docs/api/class_list.html +54 -0
- data/docs/api/css/common.css +1 -0
- data/docs/api/css/full_list.css +58 -0
- data/docs/api/css/style.css +503 -0
- data/docs/api/file.README.html +127 -0
- data/docs/api/file_list.html +59 -0
- data/docs/api/frames.html +22 -0
- data/docs/api/index.html +127 -0
- data/docs/api/js/app.js +344 -0
- data/docs/api/js/full_list.js +242 -0
- data/docs/api/js/jquery.js +4 -0
- data/docs/api/method_list.html +3998 -0
- data/docs/api/top-level-namespace.html +112 -0
- data/docs/assets/ai.md.tZrjP9im.js +1 -0
- data/docs/assets/ai.md.tZrjP9im.lean.js +1 -0
- data/docs/assets/app.D_yaTITQ.js +1 -0
- data/docs/assets/assets.md.D3wunzLx.js +19 -0
- data/docs/assets/assets.md.D3wunzLx.lean.js +1 -0
- data/docs/assets/brut-js.md.o2DAO2s2.js +12 -0
- data/docs/assets/brut-js.md.o2DAO2s2.lean.js +1 -0
- data/docs/assets/business-logic.md.BY4hGy0m.js +1 -0
- data/docs/assets/business-logic.md.BY4hGy0m.lean.js +1 -0
- data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +1 -0
- data/docs/assets/chunks/VPLocalSearchBox.B2-ZzyTY.js +8 -0
- data/docs/assets/chunks/framework.1L-BeKqY.js +18 -0
- data/docs/assets/chunks/theme.CfGFVRvE.js +2 -0
- data/docs/assets/cli.md.RmeA2b0i.js +127 -0
- data/docs/assets/cli.md.RmeA2b0i.lean.js +1 -0
- data/docs/assets/components.md.eCttGlN-.js +104 -0
- data/docs/assets/components.md.eCttGlN-.lean.js +1 -0
- data/docs/assets/configuration.md.BRriU0cL.js +78 -0
- data/docs/assets/configuration.md.BRriU0cL.lean.js +1 -0
- data/docs/assets/css.md.DJgj2clw.js +21 -0
- data/docs/assets/css.md.DJgj2clw.lean.js +1 -0
- data/docs/assets/custom-element-tests.md.BrYJQEl3.js +69 -0
- data/docs/assets/custom-element-tests.md.BrYJQEl3.lean.js +1 -0
- data/docs/assets/database-access.md.C7l-Vuvb.js +63 -0
- data/docs/assets/database-access.md.C7l-Vuvb.lean.js +1 -0
- data/docs/assets/database-schema.md.BUjR0VS1.js +63 -0
- data/docs/assets/database-schema.md.BUjR0VS1.lean.js +1 -0
- data/docs/assets/deployment.md.Dbka4OTr.js +1 -0
- data/docs/assets/deployment.md.Dbka4OTr.lean.js +1 -0
- data/docs/assets/dev-env-overview.Gj7NWM8-.png +0 -0
- data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
- data/docs/assets/dev-environment.md.BNc8AYiK.js +11 -0
- data/docs/assets/dev-environment.md.BNc8AYiK.lean.js +1 -0
- data/docs/assets/doc-conventions.md.DCfRXXi-.js +1 -0
- data/docs/assets/doc-conventions.md.DCfRXXi-.lean.js +1 -0
- data/docs/assets/end-to-end-tests.md.yfQHC0b5.js +26 -0
- data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +1 -0
- data/docs/assets/flash-and-session.md.BXY8RvT0.js +93 -0
- data/docs/assets/flash-and-session.md.BXY8RvT0.lean.js +1 -0
- data/docs/assets/forms.md.CBTYQ_Cz.js +379 -0
- data/docs/assets/forms.md.CBTYQ_Cz.lean.js +1 -0
- data/docs/assets/getting-started.md.Bz2s1Vjb.js +2 -0
- data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +1 -0
- data/docs/assets/handlers.md.089DVD3v.js +69 -0
- data/docs/assets/handlers.md.089DVD3v.lean.js +1 -0
- data/docs/assets/hooks.md.C4-moMny.js +80 -0
- data/docs/assets/hooks.md.C4-moMny.lean.js +1 -0
- data/docs/assets/i18n.md.Do9i1qWl.js +23 -0
- data/docs/assets/i18n.md.Do9i1qWl.lean.js +1 -0
- data/docs/assets/index.md.B28EwVpq.js +1 -0
- data/docs/assets/index.md.B28EwVpq.lean.js +1 -0
- data/docs/assets/instrumentation.md.CL6ax7nT.js +35 -0
- data/docs/assets/instrumentation.md.CL6ax7nT.lean.js +1 -0
- data/docs/assets/javascript.md.GWbhRS51.js +31 -0
- data/docs/assets/javascript.md.GWbhRS51.lean.js +1 -0
- data/docs/assets/jobs.md.S-2amAYp.js +1 -0
- data/docs/assets/jobs.md.S-2amAYp.lean.js +1 -0
- data/docs/assets/keyword-injection.md.Dt2tKREs.js +25 -0
- data/docs/assets/keyword-injection.md.Dt2tKREs.lean.js +1 -0
- data/docs/assets/markdown-examples.md.CCFEQO44.js +33 -0
- data/docs/assets/markdown-examples.md.CCFEQO44.lean.js +1 -0
- data/docs/assets/middleware.md.Czz_UlJN.js +20 -0
- data/docs/assets/middleware.md.Czz_UlJN.lean.js +1 -0
- data/docs/assets/not-released.md.BBy28McC.js +1 -0
- data/docs/assets/not-released.md.BBy28McC.lean.js +1 -0
- data/docs/assets/overview.Da81cB9R.png +0 -0
- data/docs/assets/overview.md.CDalkuxV.js +133 -0
- data/docs/assets/overview.md.CDalkuxV.lean.js +1 -0
- data/docs/assets/pages.md.BE3kfOc5.js +122 -0
- data/docs/assets/pages.md.BE3kfOc5.lean.js +1 -0
- data/docs/assets/routes.md.BMM7peut.js +29 -0
- data/docs/assets/routes.md.BMM7peut.lean.js +1 -0
- data/docs/assets/security.md.C668yXCi.js +1 -0
- data/docs/assets/security.md.C668yXCi.lean.js +1 -0
- data/docs/assets/seed-data.md.BvFZlqIk.js +14 -0
- data/docs/assets/seed-data.md.BvFZlqIk.lean.js +1 -0
- data/docs/assets/spa.qejUdp-5.png +0 -0
- data/docs/assets/space-time-continuum.md.KPUIKysQ.js +1 -0
- data/docs/assets/space-time-continuum.md.KPUIKysQ.lean.js +1 -0
- data/docs/assets/style.D73IYGCX.css +1 -0
- data/docs/assets/tutorial.md.BnoGjrdK.js +1 -0
- data/docs/assets/tutorial.md.BnoGjrdK.lean.js +1 -0
- data/docs/assets/unit-tests.md.DUGrnLj5.js +13 -0
- data/docs/assets/unit-tests.md.DUGrnLj5.lean.js +1 -0
- data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
- data/docs/assets.html +42 -0
- data/docs/brut-css/brut.css +1 -0
- data/docs/brut-css/brut.max.css +22372 -0
- data/docs/brut-css/classes/appearances.html +783 -0
- data/docs/brut-css/classes/background-colors.html +3529 -0
- data/docs/brut-css/classes/border-colors.html +3529 -0
- data/docs/brut-css/classes/borders.html +2293 -0
- data/docs/brut-css/classes/dimensions.html +2581 -0
- data/docs/brut-css/classes/flex.html +917 -0
- data/docs/brut-css/classes/foreground-colors.html +3261 -0
- data/docs/brut-css/classes/junk-drawer.html +431 -0
- data/docs/brut-css/classes/layout.html +668 -0
- data/docs/brut-css/classes/lists.html +331 -0
- data/docs/brut-css/classes/positioning.html +1751 -0
- data/docs/brut-css/classes/spacings.html +2633 -0
- data/docs/brut-css/classes/typography.html +2206 -0
- data/docs/brut-css/customization/advanced-configuration.html +204 -0
- data/docs/brut-css/customization/breakpoints.html +227 -0
- data/docs/brut-css/customization/design-system.html +197 -0
- data/docs/brut-css/customization/pseudo-classes.html +228 -0
- data/docs/brut-css/docs.css +98 -0
- data/docs/brut-css/getting-started/core-concepts.html +234 -0
- data/docs/brut-css/getting-started/installation.html +190 -0
- data/docs/brut-css/getting-started/overview.html +210 -0
- data/docs/brut-css/getting-started/simple-example.html +285 -0
- data/docs/brut-css/index.html +193 -0
- data/docs/brut-css/prism-twilight.min.css +1 -0
- data/docs/brut-css/properties/colors.html +1548 -0
- data/docs/brut-css/properties/spacings.html +614 -0
- data/docs/brut-css/properties/typography.html +777 -0
- data/docs/brut-js/api/AjaxSubmit.html +374 -0
- data/docs/brut-js/api/AjaxSubmit.js.html +435 -0
- data/docs/brut-js/api/Autosubmit.html +192 -0
- data/docs/brut-js/api/Autosubmit.js.html +114 -0
- data/docs/brut-js/api/BaseCustomElement.html +1091 -0
- data/docs/brut-js/api/BaseCustomElement.js.html +312 -0
- data/docs/brut-js/api/BrutCustomElements.html +172 -0
- data/docs/brut-js/api/BufferedLogger.html +173 -0
- data/docs/brut-js/api/ConfirmSubmit.html +278 -0
- data/docs/brut-js/api/ConfirmSubmit.js.html +167 -0
- data/docs/brut-js/api/ConfirmationDialog.html +425 -0
- data/docs/brut-js/api/ConfirmationDialog.js.html +194 -0
- data/docs/brut-js/api/ConstraintViolationMessage.html +448 -0
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +176 -0
- data/docs/brut-js/api/ConstraintViolationMessages.html +590 -0
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +149 -0
- data/docs/brut-js/api/CopyToClipboard.html +345 -0
- data/docs/brut-js/api/CopyToClipboard.js.html +147 -0
- data/docs/brut-js/api/Form.html +294 -0
- data/docs/brut-js/api/Form.js.html +202 -0
- data/docs/brut-js/api/I18nTranslation.html +409 -0
- data/docs/brut-js/api/I18nTranslation.js.html +112 -0
- data/docs/brut-js/api/LocaleDetection.html +312 -0
- data/docs/brut-js/api/LocaleDetection.js.html +168 -0
- data/docs/brut-js/api/Logger.html +702 -0
- data/docs/brut-js/api/Logger.js.html +141 -0
- data/docs/brut-js/api/Message.html +238 -0
- data/docs/brut-js/api/Message.js.html +107 -0
- data/docs/brut-js/api/PrefixedLogger.html +369 -0
- data/docs/brut-js/api/RichString.html +1049 -0
- data/docs/brut-js/api/RichString.js.html +164 -0
- data/docs/brut-js/api/Tabs.html +295 -0
- data/docs/brut-js/api/Tabs.js.html +219 -0
- data/docs/brut-js/api/Tracing.html +277 -0
- data/docs/brut-js/api/Tracing.js.html +298 -0
- data/docs/brut-js/api/external-CustomElementRegistry.html +140 -0
- data/docs/brut-js/api/external-Performance.html +138 -0
- data/docs/brut-js/api/external-Promise.html +138 -0
- data/docs/brut-js/api/external-ValidityState.html +138 -0
- data/docs/brut-js/api/external-Window.html +233 -0
- data/docs/brut-js/api/external-fetch.html +138 -0
- data/docs/brut-js/api/global.html +400 -0
- data/docs/brut-js/api/index.html +168 -0
- data/docs/brut-js/api/index.js.html +181 -0
- data/docs/brut-js/api/module-testing.html +383 -0
- data/docs/brut-js/api/scripts/linenumber.js +25 -0
- data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- data/docs/brut-js/api/scripts/prettify/lang-css.js +2 -0
- data/docs/brut-js/api/scripts/prettify/prettify.js +28 -0
- data/docs/brut-js/api/styles/jsdoc-default.css +327 -0
- data/docs/brut-js/api/styles/prettify-jsdoc.css +111 -0
- data/docs/brut-js/api/styles/prettify-tomorrow.css +132 -0
- data/docs/brut-js/api/testing.AssetMetadata.html +172 -0
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +171 -0
- data/docs/brut-js/api/testing.CustomElementTest.html +679 -0
- data/docs/brut-js/api/testing.DOMCreator.html +171 -0
- data/docs/brut-js/api/testing_AssetMetadata.js.html +86 -0
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +76 -0
- data/docs/brut-js/api/testing_CustomElementTest.js.html +286 -0
- data/docs/brut-js/api/testing_DOMCreator.js.html +96 -0
- data/docs/brut-js/api/testing_index.js.html +99 -0
- data/docs/brut-js.html +35 -0
- data/docs/business-logic.html +24 -0
- data/docs/cli.html +150 -0
- data/docs/components.html +127 -0
- data/docs/configuration.html +101 -0
- data/docs/css.html +44 -0
- data/docs/custom-element-tests.html +92 -0
- data/docs/database-access.html +86 -0
- data/docs/database-schema.html +86 -0
- data/docs/deployment.html +24 -0
- data/docs/dev-environment.html +34 -0
- data/docs/doc-conventions.html +24 -0
- data/docs/end-to-end-tests.html +49 -0
- data/docs/flash-and-session.html +116 -0
- data/docs/forms.html +402 -0
- data/docs/getting-started.html +25 -0
- data/docs/handlers.html +92 -0
- data/docs/hashmap.json +1 -0
- data/docs/hooks.html +103 -0
- data/docs/i18n.html +46 -0
- data/docs/images/logo-300.png +0 -0
- data/docs/images/logo.png +0 -0
- data/docs/index.html +24 -0
- data/docs/instrumentation.html +58 -0
- data/docs/javascript.html +54 -0
- data/docs/jobs.html +24 -0
- data/docs/keyword-injection.html +48 -0
- data/docs/markdown-examples.html +56 -0
- data/docs/middleware.html +43 -0
- data/docs/not-released.html +24 -0
- data/docs/overview.html +156 -0
- data/docs/pages.html +145 -0
- data/docs/routes.html +52 -0
- data/docs/security.html +24 -0
- data/docs/seed-data.html +37 -0
- data/docs/space-time-continuum.html +24 -0
- data/docs/tutorial.html +24 -0
- data/docs/unit-tests.html +36 -0
- data/docs/vp-icons.css +1 -0
- data/lib/brut/back_end/seed_data.rb +19 -2
- data/lib/brut/back_end/sidekiq/middlewares/server.rb +2 -1
- data/lib/brut/back_end/sidekiq/middlewares.rb +2 -1
- data/lib/brut/back_end/sidekiq.rb +2 -1
- data/lib/brut/back_end/validator.rb +5 -1
- data/lib/brut/back_end.rb +4 -2
- data/lib/brut/cli/app_runner.rb +1 -1
- data/lib/brut/cli/apps/test.rb +5 -0
- data/lib/brut/cli.rb +4 -3
- data/lib/brut/factory_bot.rb +0 -5
- data/lib/brut/framework/app.rb +70 -5
- data/lib/brut/framework/config.rb +5 -3
- data/lib/brut/framework/container.rb +3 -2
- data/lib/brut/framework/errors.rb +12 -4
- data/lib/brut/framework/mcp.rb +58 -1
- data/lib/brut/framework/project_environment.rb +6 -2
- data/lib/brut/framework.rb +1 -1
- data/lib/brut/front_end/component.rb +69 -71
- data/lib/brut/front_end/components/constraint_violations.rb +1 -4
- data/lib/brut/front_end/components/form_tag.rb +1 -1
- data/lib/brut/front_end/components/input.rb +3 -3
- data/lib/brut/front_end/components/inputs/csrf_token.rb +1 -1
- data/lib/brut/front_end/components/inputs/{text_field.rb → input_tag.rb} +7 -9
- data/lib/brut/front_end/components/inputs/radio_button.rb +1 -1
- data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +187 -0
- data/lib/brut/front_end/components/inputs/{textarea.rb → textarea_tag.rb} +2 -2
- data/lib/brut/front_end/components/time_tag.rb +2 -1
- data/lib/brut/front_end/form.rb +4 -4
- data/lib/brut/front_end/forms/input.rb +2 -1
- data/lib/brut/front_end/forms/input_definition.rb +5 -2
- data/lib/brut/front_end/forms/radio_button_group_input.rb +2 -1
- data/lib/brut/front_end/forms/radio_button_group_input_definition.rb +2 -2
- data/lib/brut/front_end/forms/select_input.rb +2 -4
- data/lib/brut/front_end/forms/select_input_definition.rb +2 -2
- data/lib/brut/front_end/handler.rb +28 -26
- data/lib/brut/front_end/handlers/csp_reporting_handler.rb +5 -2
- data/lib/brut/front_end/handlers/instrumentation_handler.rb +8 -4
- data/lib/brut/front_end/handlers/locale_detection_handler.rb +9 -5
- data/lib/brut/front_end/handlers/missing_handler.rb +5 -2
- data/lib/brut/front_end/layout.rb +16 -0
- data/lib/brut/front_end/page.rb +52 -29
- data/lib/brut/front_end/request_context.rb +3 -2
- data/lib/brut/front_end/routing.rb +5 -1
- data/lib/brut/front_end.rb +4 -13
- data/lib/brut/i18n/base_methods.rb +167 -79
- data/lib/brut/i18n/for_back_end.rb +4 -0
- data/lib/brut/i18n/for_cli.rb +4 -0
- data/lib/brut/i18n/for_html.rb +32 -4
- data/lib/brut/i18n/http_accept_language.rb +47 -0
- data/lib/brut/instrumentation/open_telemetry.rb +36 -1
- data/lib/brut/instrumentation.rb +3 -5
- data/lib/brut/sinatra_helpers.rb +11 -3
- data/lib/brut/spec_support/component_support.rb +30 -16
- data/lib/brut/spec_support/e2e_support.rb +1 -1
- data/lib/brut/spec_support/e2e_test_server.rb +3 -0
- data/lib/brut/spec_support/general_support.rb +3 -0
- data/lib/brut/spec_support/handler_support.rb +6 -1
- data/lib/brut/spec_support/matcher.rb +1 -0
- data/lib/brut/spec_support/matchers/be_page_for.rb +1 -0
- data/lib/brut/spec_support/matchers/have_html_attribute.rb +1 -0
- data/lib/brut/spec_support/matchers/have_i18n_string.rb +2 -5
- data/lib/brut/spec_support/matchers/have_link_to.rb +1 -0
- data/lib/brut/spec_support/matchers/have_redirected_to.rb +1 -0
- data/lib/brut/spec_support/matchers/have_rendered.rb +1 -0
- data/lib/brut/spec_support/matchers/have_returned_rack_response.rb +44 -0
- data/lib/brut/spec_support.rb +1 -1
- data/lib/brut/version.rb +1 -1
- data/lib/brut.rb +5 -4
- data/lib/sequel/extensions/brut_migrations.rb +1 -1
- metadata +648 -13
- data/doc-src/architecture.md +0 -102
- data/doc-src/assets.md +0 -98
- data/doc-src/forms.md +0 -214
- data/doc-src/handlers.md +0 -83
- data/doc-src/javascript.md +0 -265
- data/doc-src/pages.md +0 -210
- data/doc-src/route-hooks.md +0 -59
- data/lib/brut/front_end/components/inputs/select.rb +0 -117
@@ -0,0 +1,113 @@
|
|
1
|
+
/** A wrapper around a string that provides useful utility functions
|
2
|
+
* not present in the standard library.
|
3
|
+
*
|
4
|
+
* @example
|
5
|
+
*
|
6
|
+
* const string = RichString.fromString(element.textContent)
|
7
|
+
* element.textContent = string.humanize().toString()
|
8
|
+
*/
|
9
|
+
class RichString {
|
10
|
+
/** Prefer this over the constructor, as this will
|
11
|
+
* wrap `possiblyDefinedStringOrRichString` only if necessary
|
12
|
+
* as well as handle `null`.
|
13
|
+
*
|
14
|
+
* @param {null|undefined|String|RichString} possiblyDefinedStringOrRichString - if `null`, `undefined`, or otherwise falsey, this method returns `null`. If a String, returns a new `RichString` wrapping it. If a `RichString`, returns the `RichString` unchanged.
|
15
|
+
*/
|
16
|
+
static fromString(possiblyDefinedStringOrRichString) {
|
17
|
+
if (possiblyDefinedStringOrRichString instanceof RichString) {
|
18
|
+
return possiblyDefinedStringOrRichString
|
19
|
+
}
|
20
|
+
if (!possiblyDefinedStringOrRichString) {
|
21
|
+
return null
|
22
|
+
}
|
23
|
+
return new RichString(String(possiblyDefinedStringOrRichString))
|
24
|
+
}
|
25
|
+
|
26
|
+
/** Prefer `fromString` */
|
27
|
+
constructor(string) {
|
28
|
+
if (typeof string !== "string") {
|
29
|
+
throw `You may only construct a RichString with a String, not a ${typeof string}`
|
30
|
+
}
|
31
|
+
this.string = string
|
32
|
+
}
|
33
|
+
|
34
|
+
/** Returns a `RichString` with the string capitalized. */
|
35
|
+
capitalize() {
|
36
|
+
return new RichString(this.string.charAt(0).toUpperCase() + this.string.slice(1))
|
37
|
+
}
|
38
|
+
|
39
|
+
/** Returns a `RichString` with the string un-capitalized. */
|
40
|
+
decapitalize() {
|
41
|
+
return new RichString(this.string.charAt(0).toLowerCase() + this.string.slice(1))
|
42
|
+
}
|
43
|
+
|
44
|
+
/** Returns a `RichString` with the string converted from snake or kebab case into camel case. */
|
45
|
+
camelize() {
|
46
|
+
// Taken from camelize npm module
|
47
|
+
return RichString.fromString(this.string.replace(/[_.-](\w|$)/g, function (_, x) {
|
48
|
+
return x.toUpperCase()
|
49
|
+
}))
|
50
|
+
}
|
51
|
+
|
52
|
+
/** Returns a 'humanized' `RichString`, which is basically a de-camelized version with the first letter
|
53
|
+
* capitalized.
|
54
|
+
*/
|
55
|
+
humanize() {
|
56
|
+
return this.decamlize({spacer: " "}).capitalize()
|
57
|
+
}
|
58
|
+
|
59
|
+
/** Returns a `RichString` with the string converted from camel case to snake or kebab case.
|
60
|
+
*
|
61
|
+
* @param {Object} parameters
|
62
|
+
* @param {string} parameters.spacer ["_"] - a string to use when joining words together.
|
63
|
+
*
|
64
|
+
*/
|
65
|
+
decamlize({spacer="_"} = {}) {
|
66
|
+
// Taken from decamelize NPM module
|
67
|
+
|
68
|
+
// Checking the second character is done later on. Therefore process shorter strings here.
|
69
|
+
if (this.string.length < 2) {
|
70
|
+
return new RichString(this.string.toLowerCase())
|
71
|
+
}
|
72
|
+
|
73
|
+
const replacement = `$1${spacer}$2`
|
74
|
+
|
75
|
+
// Split lowercase sequences followed by uppercase character.
|
76
|
+
// `dataForUSACounties` → `data_For_USACounties`
|
77
|
+
// `myURLstring → `my_URLstring`
|
78
|
+
const decamelized = this.string.replace(
|
79
|
+
/([\p{Lowercase_Letter}\d])(\p{Uppercase_Letter})/gu,
|
80
|
+
replacement,
|
81
|
+
)
|
82
|
+
|
83
|
+
// Split multiple uppercase characters followed by one or more lowercase characters.
|
84
|
+
// `my_URLstring` → `my_ur_lstring`
|
85
|
+
return new RichString(decamelized.
|
86
|
+
replace(
|
87
|
+
/(\p{Uppercase_Letter})(\p{Uppercase_Letter}\p{Lowercase_Letter}+)/gu,
|
88
|
+
replacement,
|
89
|
+
).
|
90
|
+
toLowerCase()
|
91
|
+
)
|
92
|
+
}
|
93
|
+
|
94
|
+
/** Return the underlying String value */
|
95
|
+
toString() { return this.string }
|
96
|
+
|
97
|
+
/** Return the underlying String value or null if the string is blank */
|
98
|
+
toStringOrNull() {
|
99
|
+
if (this.isBlank()) {
|
100
|
+
return null
|
101
|
+
}
|
102
|
+
else {
|
103
|
+
return this.string
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
/* Returns true if this string has only whitespace in it */
|
108
|
+
isBlank() {
|
109
|
+
return this.string.trim() == ""
|
110
|
+
}
|
111
|
+
|
112
|
+
}
|
113
|
+
export default RichString
|
data/brut-js/src/Tabs.js
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
import BaseCustomElement from "./BaseCustomElement"
|
2
|
+
|
3
|
+
/** Implements an in-page tab selector. It's intended to wrap a set of `<a>` or `<button>` elements
|
4
|
+
* that represent the tabs of a tabbed UI, as defined by ARIA roles.
|
5
|
+
*
|
6
|
+
* Each direct child must be an `<a>` or a `<button>`, though `<a>` is recommended.
|
7
|
+
* Any other elements are ignored. Each `<a>` or `<button>`
|
8
|
+
* (herafter referred to as "tab") must have the correct ARIA attributes:
|
9
|
+
*
|
10
|
+
* * `role="tab"`
|
11
|
+
* * `aria-selected` as true or false, depending on what tab is selected when the page is first rendered. This
|
12
|
+
* custom element will ensure this value is updated as different tabs are selected.
|
13
|
+
* * `tabindex` should be 0 if selected, -1 otherwise. This custom element will ensure this value is updated as
|
14
|
+
* different tabs are selected.
|
15
|
+
* * `aria-controls` to the ID or list of IDs of panels that should be shown when this tab is selected.
|
16
|
+
* * `id` to allow the `tab-panel` to refer back to this tab.
|
17
|
+
*
|
18
|
+
* This custom element will set click listeners on all tabs and, when clicked, hide all panels referred to by
|
19
|
+
* every tab (by setting the `hidden` attribute), then show only those panels referred to by the clicked
|
20
|
+
* tab. You can use CSS to style everything the way you like it.
|
21
|
+
*
|
22
|
+
* @property {boolean} tab-selection-pushes-and-restores-state if set, this custom element will use the
|
23
|
+
* history API to manage state. When a tab
|
24
|
+
* implemented by an `<a>` with an `href` is
|
25
|
+
* clicked, that `href` will be pushed into
|
26
|
+
* the state. When the back button is hit,
|
27
|
+
* this will select the previous tab as selected.
|
28
|
+
* Note that this will conflict with anything else
|
29
|
+
* on the page that manipulates state, so only
|
30
|
+
* set this if your UI is a "full page tab"
|
31
|
+
* style UI.
|
32
|
+
*
|
33
|
+
* @fires Tabs#brut:tabselected whenever the tab selection has changed
|
34
|
+
* @example
|
35
|
+
* <brut-tabs>
|
36
|
+
* <a role="tab" aria-selected="true" tabindex="0" aria-controls="inbox-panel" id="inbox-tab"
|
37
|
+
* href="?tab=inbox">Inbox</a>
|
38
|
+
* <a role="tab" aria-selected="false" tabindex="-1" aria-controls="drafts-panel" id="drafts-tab"
|
39
|
+
* href="?tab=drafts">Drafts</a>
|
40
|
+
* <a role="tab" aria-selected="false" tabindex="-1" aria-controls="spam-panel" id="spam-tab"
|
41
|
+
* href="?tab=spam">Spam</a>
|
42
|
+
* </brut-tabs>
|
43
|
+
* <section role="tabpanel" tabindex="0" id="inbox-panel">
|
44
|
+
* <h3>Inbox</h3>
|
45
|
+
* </section>
|
46
|
+
* <section role="tabpanel" tabindex="0" id="drafts-panel" hidden>
|
47
|
+
* <h3>Drafts</h3>
|
48
|
+
* </section>
|
49
|
+
* <section role="tabpanel" tabindex="0" id="spam-panel" hidden>
|
50
|
+
* <h3>Spam</h3>
|
51
|
+
* </section>
|
52
|
+
* <!-- if a user clicks on 'Drafts', the DOM will be updated to look
|
53
|
+
* effectively like so: -->
|
54
|
+
* <brut-tabs>
|
55
|
+
* <a role="tab" aria-selected="false" tabindex="-1" aria-controls="inbox-panel" id="inbox-tab"
|
56
|
+
* href="?tab=inbox">Inbox</a>
|
57
|
+
* <a role="tab" aria-selected="true" tabindex="0" aria-controls="drafts-panel" id="drafts-tab"
|
58
|
+
* href="?tab=drafts">Drafts</a>
|
59
|
+
* <a role="tab" aria-selected="false" tabindex="-1" aria-controls="spam-panel" id="spam-tab"
|
60
|
+
* href="?tab=spam">Spam</a>
|
61
|
+
* </brut-tabs>
|
62
|
+
* <section role="tabpanel" tabindex="0" id="inbox-panel" hidden>
|
63
|
+
* <h3>Inbox</h3>
|
64
|
+
* </section>
|
65
|
+
* <section role="tabpanel" tabindex="-1" id="drafts-panel">
|
66
|
+
* <h3>Drafts</h3>
|
67
|
+
* </section>
|
68
|
+
* <section role="tabpanel" tabindex="-1" id="spam-panel" hidden>
|
69
|
+
* <h3>Spam</h3>
|
70
|
+
* </section>
|
71
|
+
*
|
72
|
+
* @customElement brut-tabs
|
73
|
+
*/
|
74
|
+
class Tabs extends BaseCustomElement {
|
75
|
+
static tagName = "brut-tabs"
|
76
|
+
static observedAttributes = [
|
77
|
+
"tab-selection-pushes-and-restores-state",
|
78
|
+
"show-warnings",
|
79
|
+
]
|
80
|
+
|
81
|
+
tabSelectionPushesAndRestoresStateChangedCallback({newValue,oldValue}) {
|
82
|
+
this.#pushAndRestoreTabState = newValue != null
|
83
|
+
}
|
84
|
+
|
85
|
+
update() {
|
86
|
+
this.#tabs().forEach( (tab) => {
|
87
|
+
tab.addEventListener("click", this.#tabClicked)
|
88
|
+
})
|
89
|
+
}
|
90
|
+
|
91
|
+
#pushAndRestoreTabState = false
|
92
|
+
|
93
|
+
#tabClicked = (event) => {
|
94
|
+
event.preventDefault()
|
95
|
+
this.#setTabAsSelected(event.target)
|
96
|
+
event.preventDefault()
|
97
|
+
}
|
98
|
+
|
99
|
+
#reloadTab = (event) => {
|
100
|
+
const tab = document.getElementById(event.state.tabId)
|
101
|
+
if (tab) {
|
102
|
+
this.#setTabAsSelected(tab, { skipPushState: true })
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
#setTabAsSelected(selectedTab, { skipPushState = false } = {}) {
|
107
|
+
this.#tabs().forEach( (tab) => {
|
108
|
+
const tabPanels = []
|
109
|
+
const ariaControls = tab.getAttribute("aria-controls")
|
110
|
+
if (ariaControls) {
|
111
|
+
ariaControls.split(/\s+/).forEach( (id) => {
|
112
|
+
const panel = document.getElementById(id)
|
113
|
+
if (panel) {
|
114
|
+
tabPanels.push(panel)
|
115
|
+
}
|
116
|
+
else {
|
117
|
+
this.logger.warn("Tab %o references panel with id %s, but no such element exists with that id",tab,id)
|
118
|
+
}
|
119
|
+
})
|
120
|
+
}
|
121
|
+
if (tab == selectedTab) {
|
122
|
+
tab.setAttribute("aria-selected",true)
|
123
|
+
tab.setAttribute("tabindex","0")
|
124
|
+
tabPanels.forEach( (panel) => panel.removeAttribute("hidden") )
|
125
|
+
if (this.#pushAndRestoreTabState && !skipPushState) {
|
126
|
+
let href = tab.getAttribute("href") || ""
|
127
|
+
if (href.startsWith("?")) {
|
128
|
+
let hrefQueryString = href.slice(1)
|
129
|
+
const anchorIndex = hrefQueryString.indexOf("#")
|
130
|
+
if (anchorIndex != -1) {
|
131
|
+
hrefQueryString = hrefQueryString.slice(-1 * (hrefQueryString.length - anchorIndex - 1))
|
132
|
+
}
|
133
|
+
const currentQuery = new URLSearchParams(window.location.search)
|
134
|
+
const hrefQuery = new URLSearchParams(hrefQueryString)
|
135
|
+
hrefQuery.forEach( (value,key) => {
|
136
|
+
currentQuery.set(key,value)
|
137
|
+
})
|
138
|
+
href = "?" + currentQuery.toString() + (anchorIndex == -1 ? "" : hrefQueryString.slice(anchorIndex))
|
139
|
+
}
|
140
|
+
window.history.pushState({ tabId: tab.id },"",href)
|
141
|
+
window.addEventListener("popstate", this.#reloadTab)
|
142
|
+
}
|
143
|
+
this.dispatchEvent(new CustomEvent("brut:tabselected", { tabId: tab.id }))
|
144
|
+
}
|
145
|
+
else {
|
146
|
+
tab.setAttribute("aria-selected",false)
|
147
|
+
tab.setAttribute("tabindex","-1")
|
148
|
+
tabPanels.forEach( (panel) => panel.setAttribute("hidden", true) )
|
149
|
+
}
|
150
|
+
})
|
151
|
+
}
|
152
|
+
|
153
|
+
|
154
|
+
#tabs() {
|
155
|
+
const tabs = []
|
156
|
+
this.querySelectorAll("[role=tab]").forEach( (tab) => {
|
157
|
+
if ( (tab.tagName.toLowerCase() == "a") || (tab.tagName.toLowerCase() == "button") ) {
|
158
|
+
tabs.push(tab)
|
159
|
+
}
|
160
|
+
else {
|
161
|
+
this.logger.warn("An element with tag %s was assigned role=tab, and %s doesn't work that way. Use an <a> or a <button>",tab.tagName,this.constructor.name)
|
162
|
+
}
|
163
|
+
})
|
164
|
+
return tabs
|
165
|
+
}
|
166
|
+
|
167
|
+
}
|
168
|
+
export default Tabs
|
@@ -0,0 +1,247 @@
|
|
1
|
+
import { BaseCustomElement } from "brut-js"
|
2
|
+
|
3
|
+
/** Sends performance data to an endpoint in a Brut-powered app that is expected to save it as an Open Telemetry span.
|
4
|
+
* Uses the W3C-recommended headers "traceparent" and "tracestate" to do this.
|
5
|
+
*
|
6
|
+
* ### Supported Metrics
|
7
|
+
*
|
8
|
+
* Currently, this will attempt to send "navigation", "largest-contentful-paint", and "first-contentful-paint" back to the server.
|
9
|
+
* Not all browsers support these, so this element will send back as many as it can. It will wait for all supported metrics to be
|
10
|
+
* received before contacting the server. It will attempt to do this exactly once.
|
11
|
+
*
|
12
|
+
* ### Use
|
13
|
+
*
|
14
|
+
* To use this element, your page must have a `<meta>` element that contains the value for "traceparent". It is expected that your
|
15
|
+
* server will include this in server-generatd HTML. The Brut's `Brut::FrontEnd::Components::Traceparent` component will handle this
|
16
|
+
* for you. The value for "traceparent" is key to connecting the browser metrics to the back-end request that generated the page.
|
17
|
+
*
|
18
|
+
* The element also requires a `url` attribute to know where to send the data. By default, Brut is listening in
|
19
|
+
* `/__brut/instrumentation`. See the example.
|
20
|
+
*
|
21
|
+
* ### Durations vs Timestamps
|
22
|
+
*
|
23
|
+
* The performance API produces durations since an origin timestamp. Open Telemetry wants timestamps. In theory,
|
24
|
+
* `Performance.timeOrigin` is provided by the browser as a reference time when the page started doing anything.
|
25
|
+
* In practice, this value is incorrect on Firefox, so the element records a timestamp when it is created.
|
26
|
+
*
|
27
|
+
* When the data is merged back to the server span, the specific timestamps will not exactly match reality, however the durations will
|
28
|
+
* be accurate. Note that even if `Performance.timeOrigin` was correct, clock drift between client and server would make
|
29
|
+
* the timestamps inaccurate anyway.
|
30
|
+
*
|
31
|
+
* ### Encoding
|
32
|
+
*
|
33
|
+
* The spec for the "tracestate" header leaves open how the data is to be encoded. It supports multiple vendors using a key/value
|
34
|
+
* pair:
|
35
|
+
*
|
36
|
+
* tracestate: honeycomb=«encoded data»,newrelic=«encoded data»
|
37
|
+
*
|
38
|
+
* This element uses the vendor name "brut". The data is a Base64-encoded JSON blob containing the data.
|
39
|
+
*
|
40
|
+
* tracestate: brut=«Base64 encoded JSON»
|
41
|
+
*
|
42
|
+
* The values captured and format of the JSON map closely to Open Telemetry's browser instrumentation format.
|
43
|
+
* Of course, this element is many magnitudes smaller in size than Open Telemetry's, which is why it exists at all
|
44
|
+
*
|
45
|
+
* @example
|
46
|
+
* <!DOCTYPE html>
|
47
|
+
* <html>
|
48
|
+
* <head>
|
49
|
+
* <meta name="traceparent" content="293874293749237439843294">
|
50
|
+
* <brut-tracing url="/__brut/instrumentation"></brut-tracing>
|
51
|
+
* <!-- ... -->
|
52
|
+
* </head>
|
53
|
+
* <body>
|
54
|
+
* <!-- ... -->
|
55
|
+
* </body>
|
56
|
+
* </html>
|
57
|
+
*
|
58
|
+
* @property {string} url - the url where the trace information is to be sent.
|
59
|
+
*
|
60
|
+
* @see {@link https://www.w3.org/TR/trace-context/}
|
61
|
+
* @see external:Performance
|
62
|
+
*
|
63
|
+
* @customElement brut-tracing
|
64
|
+
*/
|
65
|
+
class Tracing extends BaseCustomElement {
|
66
|
+
static tagName = "brut-tracing"
|
67
|
+
|
68
|
+
static observedAttributes = [
|
69
|
+
"url",
|
70
|
+
"show-warnings",
|
71
|
+
]
|
72
|
+
|
73
|
+
|
74
|
+
#url = null
|
75
|
+
#sent = {}
|
76
|
+
#payload = {}
|
77
|
+
#timeOrigin = null
|
78
|
+
#supportedTypes = []
|
79
|
+
|
80
|
+
#performanceObserver = new PerformanceObserver( (entries) => {
|
81
|
+
const navigation = entries.getEntriesByType("navigation")[0]
|
82
|
+
if (navigation && navigation.loadEventEnd != 0 && !this.#payload.navigation) {
|
83
|
+
this.#payload.navigation = this.#parseNavigation(navigation)
|
84
|
+
}
|
85
|
+
const largestContentfulPaint = entries.getEntriesByType("largest-contentful-paint")
|
86
|
+
if (largestContentfulPaint.length > 0 && !this.#payload["largest-contentful-paint"]) {
|
87
|
+
this.#payload["largest-contentful-paint"] = this.#parseLargestContentfulPaint(largestContentfulPaint)
|
88
|
+
}
|
89
|
+
const paint = entries.getEntriesByName("first-contentful-paint", "paint")[0]
|
90
|
+
if (paint && !this.#payload.paint) {
|
91
|
+
this.#payload.paint = this.#parseFirstContentfulPaint(paint)
|
92
|
+
}
|
93
|
+
|
94
|
+
if ( this.#supportedTypes.every( (type) => this.#payload[type] ) ) {
|
95
|
+
this.#sendSpans()
|
96
|
+
this.#payload = {}
|
97
|
+
}
|
98
|
+
})
|
99
|
+
|
100
|
+
constructor() {
|
101
|
+
super()
|
102
|
+
this.#timeOrigin = Date.now()
|
103
|
+
this.#supportedTypes = [
|
104
|
+
"navigation",
|
105
|
+
"largest-contentful-paint",
|
106
|
+
"paint",
|
107
|
+
].filter( (type) => {
|
108
|
+
return PerformanceObserver.supportedEntryTypes.includes(type)
|
109
|
+
})
|
110
|
+
}
|
111
|
+
|
112
|
+
urlChangedCallback({newValue}) {
|
113
|
+
this.#url = newValue
|
114
|
+
}
|
115
|
+
|
116
|
+
update() {
|
117
|
+
this.#supportedTypes.forEach( (type) => {
|
118
|
+
this.#performanceObserver.observe({type: type, buffered: true})
|
119
|
+
})
|
120
|
+
}
|
121
|
+
|
122
|
+
#sendSpans() {
|
123
|
+
const headers = this.#initializerHeadersIfCanContinue()
|
124
|
+
if (!headers) {
|
125
|
+
return
|
126
|
+
}
|
127
|
+
const span = this.#payload.navigation
|
128
|
+
|
129
|
+
if (this.#payload.paint) {
|
130
|
+
span.events.push({
|
131
|
+
name: this.#payload.paint.name,
|
132
|
+
timestamp: this.#timeOrigin + this.#payload.paint.startTime
|
133
|
+
})
|
134
|
+
}
|
135
|
+
if (this.#payload["largest-contentful-paint"]) {
|
136
|
+
this.#payload["largest-contentful-paint"].forEach( (event) => {
|
137
|
+
span.events.push({
|
138
|
+
name: event.name,
|
139
|
+
timestamp: this.#timeOrigin + event.startTime,
|
140
|
+
attributes: {
|
141
|
+
"element.tag": event.element?.tagName,
|
142
|
+
"element.class": event.element?.className,
|
143
|
+
}
|
144
|
+
})
|
145
|
+
})
|
146
|
+
}
|
147
|
+
|
148
|
+
this.#sent[this.#url] = true
|
149
|
+
headers.append("tracestate",`brut=${window.btoa(JSON.stringify(span))}`)
|
150
|
+
const request = new Request(
|
151
|
+
this.#url,
|
152
|
+
{
|
153
|
+
headers: headers,
|
154
|
+
method: "GET",
|
155
|
+
}
|
156
|
+
)
|
157
|
+
fetch(request).then( (response) => {
|
158
|
+
if (!response.ok) {
|
159
|
+
console.warn("Problem sending instrumentation: %s/%s", response.status,response.statusText)
|
160
|
+
}
|
161
|
+
}).catch( (error) => {
|
162
|
+
console.warn("Problem sending instrumentation: %o", error)
|
163
|
+
})
|
164
|
+
}
|
165
|
+
|
166
|
+
#parseNavigation(navigation) {
|
167
|
+
const documentFetch = {
|
168
|
+
name: "browser.documentFetch",
|
169
|
+
start_timestamp: navigation.fetchStart + this.#timeOrigin,
|
170
|
+
end_timestamp: navigation.responseEnd + this.#timeOrigin,
|
171
|
+
attributes: {
|
172
|
+
"http.url": navigation.name,
|
173
|
+
},
|
174
|
+
}
|
175
|
+
const events = [
|
176
|
+
"fetchStart",
|
177
|
+
"unloadEventStart",
|
178
|
+
"unloadEventEnd",
|
179
|
+
"domInteractive",
|
180
|
+
"domInteractive",
|
181
|
+
"domContentLoadedEventStart",
|
182
|
+
"domContentLoadedEventEnd",
|
183
|
+
"domComplete",
|
184
|
+
"loadEventStart",
|
185
|
+
"loadEventEnd",
|
186
|
+
]
|
187
|
+
|
188
|
+
return {
|
189
|
+
name: "browser.documentLoad",
|
190
|
+
start_timestamp: navigation.fetchStart + this.#timeOrigin,
|
191
|
+
end_timestamp: navigation.loadEventEnd + this.#timeOrigin,
|
192
|
+
attributes: {
|
193
|
+
"http.url": navigation.name,
|
194
|
+
"http.user_agent": window.navigator.userAgent,
|
195
|
+
},
|
196
|
+
events: events.map( (eventName) => {
|
197
|
+
return {
|
198
|
+
name: eventName,
|
199
|
+
timestamp: this.#timeOrigin + navigation[eventName],
|
200
|
+
}
|
201
|
+
}),
|
202
|
+
spans: [
|
203
|
+
documentFetch
|
204
|
+
]
|
205
|
+
}
|
206
|
+
}
|
207
|
+
|
208
|
+
#parseFirstContentfulPaint(paint) {
|
209
|
+
return {
|
210
|
+
name: "browser.first-contentful-paint",
|
211
|
+
startTime: paint.startTime,
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
#parseLargestContentfulPaint(largestContentfulPaint) {
|
216
|
+
return largestContentfulPaint.map( (entry) => {
|
217
|
+
return {
|
218
|
+
name: "browser.largest-contentful-paint",
|
219
|
+
startTime: entry.startTime,
|
220
|
+
element: entry.element,
|
221
|
+
}
|
222
|
+
})
|
223
|
+
}
|
224
|
+
|
225
|
+
#initializerHeadersIfCanContinue() {
|
226
|
+
if (!this.#url) {
|
227
|
+
this.logger.info("No url set, no traces will be reported")
|
228
|
+
return
|
229
|
+
}
|
230
|
+
const $traceparent = document.querySelector("meta[name='traceparent']")
|
231
|
+
if (!$traceparent) {
|
232
|
+
this.logger.info("No <meta name='traceparent' ...> in the document, no traces can be reported")
|
233
|
+
return
|
234
|
+
}
|
235
|
+
if (this.#sent[this.#url]) {
|
236
|
+
this.logger.info("Already sent to %s", this.#url)
|
237
|
+
return
|
238
|
+
}
|
239
|
+
const traceparent = $traceparent.getAttribute("content")
|
240
|
+
if (!traceparent) {
|
241
|
+
this.logger.info("%o had no value for the content attribute, no traces can be reported",$traceparent)
|
242
|
+
return
|
243
|
+
}
|
244
|
+
return new Headers({ traceparent })
|
245
|
+
}
|
246
|
+
}
|
247
|
+
export default Tracing
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import * as BrutJS from "./index.js"
|
2
|
+
document.addEventListener("DOMContentLoaded", () => {
|
3
|
+
BrutJS.BrutCustomElements.define()
|
4
|
+
if (!HTMLDialogElement.prototype.showModal) {
|
5
|
+
HTMLDialogElement.prototype.showModal = function() {
|
6
|
+
this.open = true
|
7
|
+
}
|
8
|
+
}
|
9
|
+
if (!HTMLDialogElement.prototype.close) {
|
10
|
+
HTMLDialogElement.prototype.close = function(returnValue) {
|
11
|
+
this.open = false
|
12
|
+
this.returnValue = returnValue
|
13
|
+
}
|
14
|
+
}
|
15
|
+
})
|
@@ -0,0 +1,130 @@
|
|
1
|
+
import BaseCustomElement from "./BaseCustomElement"
|
2
|
+
import AjaxSubmit from "./AjaxSubmit"
|
3
|
+
import Autosubmit from "./Autosubmit"
|
4
|
+
import ConfirmSubmit from "./ConfirmSubmit"
|
5
|
+
import ConfirmationDialog from "./ConfirmationDialog"
|
6
|
+
import ConstraintViolationMessage from "./ConstraintViolationMessage"
|
7
|
+
import ConstraintViolationMessages from "./ConstraintViolationMessages"
|
8
|
+
import CopyToClipboard from "./CopyToClipboard"
|
9
|
+
import Form from "./Form"
|
10
|
+
import I18nTranslation from "./I18nTranslation"
|
11
|
+
import LocaleDetection from "./LocaleDetection"
|
12
|
+
import Message from "./Message"
|
13
|
+
import RichString from "./RichString"
|
14
|
+
import Tabs from "./Tabs"
|
15
|
+
import Tracing from "./Tracing"
|
16
|
+
|
17
|
+
/**
|
18
|
+
* This is the code for a test case. It may return a {@link external:Promise} if there is async behavior that must
|
19
|
+
* be waited-on to properly assert behavior.
|
20
|
+
*
|
21
|
+
* @callback testCodeCallback
|
22
|
+
*
|
23
|
+
* @param {Object} objects - objects passed into your test that you may need.
|
24
|
+
* @param {Window} objects.window - Access to the top-level window object. Note that this provided by JSDOM and is not exactly like the `Window` you'd get in your browser.
|
25
|
+
* @param {Document} objects.document - Access to the top-level document object. Note that this provided by JSDOM and is not exactly like the `Document` you'd get in your browser.
|
26
|
+
* @param {Object} objects.assert - The NodeJS assert object that you should use to assert behavior.
|
27
|
+
* @param {Object} objects.fetchRequests - An array of `Request` instances given to `fetch`. This will be updated as `fetch` is
|
28
|
+
* called and can be useful to assert the contents of what was requested via `fetch`.
|
29
|
+
*
|
30
|
+
* @example
|
31
|
+
* test("some test", ({document,assert}) => {
|
32
|
+
* const element = document.querySelector("div")
|
33
|
+
* assert(div.getAttribute("data-foo") != null)
|
34
|
+
* })
|
35
|
+
*
|
36
|
+
* @example
|
37
|
+
* test("some other test", ({document,window,assert}) => {
|
38
|
+
* const element = document.querySelector("div")
|
39
|
+
* assert.equal(window.history.state["foo"], "bar")
|
40
|
+
* })
|
41
|
+
*/
|
42
|
+
|
43
|
+
/**
|
44
|
+
* @external Performance
|
45
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Performance|Performance API}
|
46
|
+
*/
|
47
|
+
|
48
|
+
/**
|
49
|
+
* @external Promise
|
50
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise|Promise}
|
51
|
+
*/
|
52
|
+
/**
|
53
|
+
* @external fetch
|
54
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch|fetch}
|
55
|
+
*/
|
56
|
+
|
57
|
+
/**
|
58
|
+
* @external ValidityState
|
59
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState|ValidityState}
|
60
|
+
*/
|
61
|
+
|
62
|
+
/**
|
63
|
+
* The standard `CustomElementRegistry`
|
64
|
+
*
|
65
|
+
* @external CustomElementRegistry
|
66
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry|CustomElementRegistry}
|
67
|
+
*/
|
68
|
+
|
69
|
+
/**
|
70
|
+
* @external Window
|
71
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/|Window}
|
72
|
+
*/
|
73
|
+
|
74
|
+
/**
|
75
|
+
* @method confirm
|
76
|
+
* @memberof external:Window#
|
77
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm|confirm}
|
78
|
+
*/
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Class that can be used to automatically define all of brut's custom
|
82
|
+
* elements.
|
83
|
+
*/
|
84
|
+
class BrutCustomElements {
|
85
|
+
static elementClasses = []
|
86
|
+
static define() {
|
87
|
+
this.elementClasses.forEach( (e) => {
|
88
|
+
e.define()
|
89
|
+
})
|
90
|
+
}
|
91
|
+
static addElementClasses(...classes) {
|
92
|
+
this.elementClasses.push(...classes)
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
BrutCustomElements.addElementClasses(
|
97
|
+
// Ordering is important here - TBD how to make sure these are created in order
|
98
|
+
I18nTranslation,
|
99
|
+
CopyToClipboard,
|
100
|
+
Message,
|
101
|
+
ConfirmSubmit,
|
102
|
+
ConfirmationDialog,
|
103
|
+
ConstraintViolationMessages,
|
104
|
+
Form,
|
105
|
+
AjaxSubmit,
|
106
|
+
ConstraintViolationMessage,
|
107
|
+
Tabs,
|
108
|
+
LocaleDetection,
|
109
|
+
Autosubmit,
|
110
|
+
Tracing,
|
111
|
+
)
|
112
|
+
|
113
|
+
export {
|
114
|
+
AjaxSubmit,
|
115
|
+
Autosubmit,
|
116
|
+
BaseCustomElement,
|
117
|
+
BrutCustomElements,
|
118
|
+
ConfirmSubmit,
|
119
|
+
ConfirmationDialog,
|
120
|
+
ConstraintViolationMessage,
|
121
|
+
ConstraintViolationMessages,
|
122
|
+
Form,
|
123
|
+
I18nTranslation,
|
124
|
+
LocaleDetection,
|
125
|
+
Message,
|
126
|
+
RichString,
|
127
|
+
Tabs,
|
128
|
+
Tracing
|
129
|
+
}
|
130
|
+
|