Showing preview only (4,312K chars total). Download the full file or copy to clipboard to get everything.
Repository: athena-framework/athena
Branch: master
Commit: ac9732d451be
Files: 1321
Total size: 3.8 MB
Directory structure:
gitextract_igikmbi3/
├── .ameba.yml
├── .changes/
│ ├── clock/
│ │ ├── v0.2.0.md
│ │ └── v0.3.0.md
│ ├── console/
│ │ ├── v0.4.1.md
│ │ ├── v0.4.2.md
│ │ └── v0.4.3.md
│ ├── contracts/
│ │ └── v0.1.0.md
│ ├── dependency-injection/
│ │ ├── v0.4.3.md
│ │ ├── v0.4.4.md
│ │ └── v0.4.5.md
│ ├── dotenv/
│ │ ├── v0.2.0.md
│ │ └── v0.2.1.md
│ ├── event-dispatcher/
│ │ ├── v0.3.1.md
│ │ ├── v0.4.0.md
│ │ └── v0.4.1.md
│ ├── framework/
│ │ ├── v0.20.1.md
│ │ ├── v0.21.0.md
│ │ ├── v0.21.1.md
│ │ └── v0.22.0.md
│ ├── header.tpl.md
│ ├── http/
│ │ └── v0.1.0.md
│ ├── http-kernel/
│ │ └── v0.1.0.md
│ ├── image-size/
│ │ └── v0.1.4.md
│ ├── mercure/
│ │ └── v0.1.0.md
│ ├── mercure-bundle/
│ │ └── v0.1.0.md
│ ├── mime/
│ │ ├── v0.2.0.md
│ │ └── v0.2.1.md
│ ├── negotiation/
│ │ └── v0.2.0.md
│ ├── routing/
│ │ ├── v0.1.10.md
│ │ ├── v0.1.11.md
│ │ ├── v0.1.12.md
│ │ └── v0.2.0.md
│ ├── serializer/
│ │ ├── v0.4.1.md
│ │ ├── v0.4.2.md
│ │ └── v0.4.3.md
│ ├── spec/
│ │ ├── v0.3.11.md
│ │ ├── v0.4.0.md
│ │ ├── v0.4.1.md
│ │ └── v0.4.2.md
│ ├── unreleased/
│ │ ├── .gitkeep
│ │ └── event-dispatcher-Changed-20260502-225424.yaml
│ └── validator/
│ ├── v0.4.0.md
│ ├── v0.4.1.md
│ └── v0.5.0.md
├── .changie.yaml
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build_and_publish_docs.yml
│ ├── ci.yml
│ ├── sync.yml
│ └── tag_and_create_release.yml
├── .gitignore
├── .python-version
├── .typos.toml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── UPGRADING.md
├── codecov.yml
├── docs/
│ ├── README.md
│ ├── api_reference.md
│ ├── bundle_reference.md
│ ├── css/
│ │ ├── index.css
│ │ └── monorepo.css
│ ├── getting_started/
│ │ ├── README.md
│ │ ├── commands.md
│ │ ├── configuration.md
│ │ ├── error_handling.md
│ │ ├── middleware.md
│ │ ├── routing.md
│ │ ├── testing.md
│ │ └── validation.md
│ ├── guides/
│ │ ├── README.md
│ │ └── proxies.md
│ ├── index.cr
│ ├── templates/
│ │ └── crystal/
│ │ └── material/
│ │ ├── schema.html
│ │ └── type.html
│ └── why_athena.md
├── gen_doc_stubs.py
├── justfile
├── mkdocs-common.yml
├── mkdocs.yml
├── pyproject.toml
├── scripts/
│ └── test.sh
├── shard.dev.yml
├── shard.prod.yml
├── shard.yml
└── src/
├── bundles/
│ └── mercure/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── shard.yml
│ ├── spec/
│ │ ├── authorization_spec.cr
│ │ ├── bundle_spec.cr
│ │ ├── discovery_spec.cr
│ │ ├── listeners/
│ │ │ ├── add_link_header_spec.cr
│ │ │ └── set_cookie_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-mercure_bundle.cr
│ ├── authorization.cr
│ ├── discovery.cr
│ └── listeners/
│ ├── add_link_header.cr
│ └── set_cookie.cr
└── components/
├── clock/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-clock_spec.cr
│ │ ├── aware_spec.cr
│ │ ├── mock_clock_spec.cr
│ │ ├── native_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-clock.cr
│ ├── aware.cr
│ ├── interface.cr
│ ├── native.cr
│ └── spec.cr
├── console/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── application_spec.cr
│ │ ├── application_tester_spec.cr
│ │ ├── command_spec.cr
│ │ ├── command_tester_spec.cr
│ │ ├── commands/
│ │ │ ├── complete_spec.cr
│ │ │ ├── dump_completion_spec.cr
│ │ │ ├── help_spec.cr
│ │ │ ├── lazy_spec.cr
│ │ │ └── list_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── completion/
│ │ │ ├── input_spec.cr
│ │ │ └── output/
│ │ │ ├── bash_spec.cr
│ │ │ ├── completion_output_test_case.cr
│ │ │ ├── fish_spec.cr
│ │ │ └── zsh_spec.cr
│ │ ├── cursor_spec.cr
│ │ ├── descriptor/
│ │ │ ├── abstract_descriptor_test_case.cr
│ │ │ ├── application_spec.cr
│ │ │ ├── object_provider.cr
│ │ │ └── text_spec.cr
│ │ ├── fixtures/
│ │ │ ├── applications/
│ │ │ │ ├── descriptor1.cr
│ │ │ │ └── descriptor2.cr
│ │ │ ├── commands/
│ │ │ │ ├── annotation_configured.cr
│ │ │ │ ├── annotation_configured_aliases.cr
│ │ │ │ ├── annotation_configured_hidden.cr
│ │ │ │ ├── annotation_configured_hidden_field.cr
│ │ │ │ ├── bar_buc.cr
│ │ │ │ ├── descriptor1.cr
│ │ │ │ ├── descriptor2.cr
│ │ │ │ ├── descriptor3.cr
│ │ │ │ ├── descriptor4.cr
│ │ │ │ ├── foo.cr
│ │ │ │ ├── foo1.cr
│ │ │ │ ├── foo2.cr
│ │ │ │ ├── foo3.cr
│ │ │ │ ├── foo4.cr
│ │ │ │ ├── foo6.cr
│ │ │ │ ├── foo_bar.cr
│ │ │ │ ├── foo_hidden.cr
│ │ │ │ ├── foo_opt.cr
│ │ │ │ ├── foo_same_case_lowercase.cr
│ │ │ │ ├── foo_same_case_uppercase.cr
│ │ │ │ ├── foo_subnamespaced1.cr
│ │ │ │ ├── foo_subnamespaced2.cr
│ │ │ │ ├── foo_without_alias.cr
│ │ │ │ ├── io.cr
│ │ │ │ ├── test.cr
│ │ │ │ ├── test_ambiguous_command_registering1.cr
│ │ │ │ └── test_ambiguous_command_registering2.cr
│ │ │ ├── helper/
│ │ │ │ └── table/
│ │ │ │ ├── borderless.txt
│ │ │ │ ├── borderless_vertical.txt
│ │ │ │ ├── box.txt
│ │ │ │ ├── compact.txt
│ │ │ │ ├── compact_vertical.txt
│ │ │ │ ├── default.txt
│ │ │ │ ├── default_cells_with_colspan.txt
│ │ │ │ ├── default_cells_with_formatting_tags.txt
│ │ │ │ ├── default_cells_with_non_formatting_tags.txt
│ │ │ │ ├── default_cells_with_rowspan.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_alignment.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_custom_format.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_fgbg.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_line_breaks.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_no_separators.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_separator_in_rowspan.txt
│ │ │ │ ├── default_colspan_and_table_cell_with_comment_style.txt
│ │ │ │ ├── default_formatted_row_with_line_breaks.txt
│ │ │ │ ├── default_headerless.txt
│ │ │ │ ├── default_line_break_after_colspan_cell.txt
│ │ │ │ ├── default_line_breaks_after_colspan_cell.txt
│ │ │ │ ├── default_missing_cell_values.txt
│ │ │ │ ├── default_multiline_cells.txt
│ │ │ │ ├── default_multiple_header_lines.txt
│ │ │ │ ├── default_no_rows.txt
│ │ │ │ ├── default_row_with_multiple_cells.txt
│ │ │ │ ├── double_box_separator.txt
│ │ │ │ ├── markdown.txt
│ │ │ │ └── suggested_vertical.txt
│ │ │ ├── style/
│ │ │ │ ├── backslashes.txt
│ │ │ │ ├── block.txt
│ │ │ │ ├── block_line_endings.txt
│ │ │ │ ├── block_no_prefix_type.txt
│ │ │ │ ├── block_padding.txt
│ │ │ │ ├── block_prefix_no_type.txt
│ │ │ │ ├── blocks.txt
│ │ │ │ ├── closing_tag.txt
│ │ │ │ ├── definition_list.txt
│ │ │ │ ├── emojis.txt
│ │ │ │ ├── empty_buffer.txt
│ │ │ │ ├── horizontal_table.txt
│ │ │ │ ├── long_line_block.txt
│ │ │ │ ├── long_line_block_wrapping.txt
│ │ │ │ ├── long_line_comment.txt
│ │ │ │ ├── long_line_comment_decorated.txt
│ │ │ │ ├── multi_line_block.txt
│ │ │ │ ├── nested_tag_prefix.txt
│ │ │ │ ├── non_interactive_question.txt
│ │ │ │ ├── table.txt
│ │ │ │ ├── table_horizontal.txt
│ │ │ │ ├── table_vertical.txt
│ │ │ │ ├── text_block_blank_line.txt
│ │ │ │ ├── title_block.txt
│ │ │ │ ├── titles.txt
│ │ │ │ └── titles_text.txt
│ │ │ └── text/
│ │ │ ├── application_1.txt
│ │ │ ├── application_2.txt
│ │ │ ├── application_alternative_namespace.txt
│ │ │ ├── application_filtered_namespace.txt
│ │ │ ├── application_renderexception1.txt
│ │ │ ├── application_renderexception2.txt
│ │ │ ├── application_renderexception3.txt
│ │ │ ├── application_renderexception3_decorated.txt
│ │ │ ├── application_renderexception4.txt
│ │ │ ├── application_renderexception_doublewidth1.txt
│ │ │ ├── application_renderexception_escapeslines.txt
│ │ │ ├── application_renderexception_linebreaks.txt
│ │ │ ├── application_renderexception_synopsis_escapeslines.txt
│ │ │ ├── application_run1.txt
│ │ │ ├── application_run2.txt
│ │ │ ├── application_run3.txt
│ │ │ ├── application_run4.txt
│ │ │ ├── application_run5.txt
│ │ │ ├── command_1.txt
│ │ │ ├── command_2.txt
│ │ │ ├── input_argument_1.txt
│ │ │ ├── input_argument_2.txt
│ │ │ ├── input_argument_3.txt
│ │ │ ├── input_argument_4.txt
│ │ │ ├── input_argument_with_style.txt
│ │ │ ├── input_definition_1.txt
│ │ │ ├── input_definition_2.txt
│ │ │ ├── input_definition_3.txt
│ │ │ ├── input_definition_4.txt
│ │ │ ├── input_option_1.txt
│ │ │ ├── input_option_2.txt
│ │ │ ├── input_option_3.txt
│ │ │ ├── input_option_4.txt
│ │ │ ├── input_option_5.txt
│ │ │ ├── input_option_6.txt
│ │ │ ├── input_option_with_style.txt
│ │ │ └── input_option_with_style_array.txt
│ │ ├── formatter/
│ │ │ ├── null_spec.cr
│ │ │ ├── null_style_spec.cr
│ │ │ ├── output_formatter_spec.cr
│ │ │ ├── output_formatter_style_spec.cr
│ │ │ └── output_formatter_style_stack_spec.cr
│ │ ├── helper/
│ │ │ ├── abstract_question_helper_test_case.cr
│ │ │ ├── athena_question_spec.cr
│ │ │ ├── formatter_spec.cr
│ │ │ ├── helper_set_spec.cr
│ │ │ ├── helper_spec.cr
│ │ │ ├── output_wrapper_spec.cr
│ │ │ ├── progress_bar_spec.cr
│ │ │ ├── progress_indicator_spec.cr
│ │ │ ├── question_spec.cr
│ │ │ ├── table_spec.cr
│ │ │ └── table_style_spec.cr
│ │ ├── input/
│ │ │ ├── argument_spec.cr
│ │ │ ├── argv_spec.cr
│ │ │ ├── definition_spec.cr
│ │ │ ├── hash_spec.cr
│ │ │ ├── input_spec.cr
│ │ │ ├── option_spec.cr
│ │ │ ├── string_line_spec.cr
│ │ │ └── value/
│ │ │ ├── array_spec.cr
│ │ │ ├── bool_spec.cr
│ │ │ ├── nil_spec.cr
│ │ │ ├── number_spec.cr
│ │ │ └── string_spec.cr
│ │ ├── output/
│ │ │ ├── console_section_output_spec.cr
│ │ │ ├── io_spec.cr
│ │ │ ├── null_spec.cr
│ │ │ └── output_spec.cr
│ │ ├── question/
│ │ │ ├── choice_spec.cr
│ │ │ ├── confirmation_spec.cr
│ │ │ ├── multiple_choice_spec.cr
│ │ │ └── question_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── style/
│ │ │ └── athena_style_spec.cr
│ │ └── terminal_spec.cr
│ └── src/
│ ├── annotations.cr
│ ├── application.cr
│ ├── athena-console.cr
│ ├── command.cr
│ ├── commands/
│ │ ├── complete.cr
│ │ ├── dump_completion.cr
│ │ ├── generic.cr
│ │ ├── help.cr
│ │ ├── lazy.cr
│ │ └── list.cr
│ ├── completion/
│ │ ├── input.cr
│ │ ├── output/
│ │ │ ├── bash.cr
│ │ │ ├── completion.bash
│ │ │ ├── completion.fish
│ │ │ ├── completion.zsh
│ │ │ ├── fish.cr
│ │ │ ├── interface.cr
│ │ │ └── zsh.cr
│ │ └── suggestions.cr
│ ├── cursor.cr
│ ├── descriptor/
│ │ ├── application.cr
│ │ ├── context.cr
│ │ ├── descriptor.cr
│ │ ├── interface.cr
│ │ └── text.cr
│ ├── exception/
│ │ ├── command_not_found.cr
│ │ ├── invalid_argument.cr
│ │ ├── invalid_option.cr
│ │ ├── logic.cr
│ │ ├── missing_input.cr
│ │ ├── namespace_not_found.cr
│ │ └── runtime.cr
│ ├── ext/
│ │ └── terminal.cr
│ ├── formatter/
│ │ ├── interface.cr
│ │ ├── null.cr
│ │ ├── null_style.cr
│ │ ├── output.cr
│ │ ├── output_formatter_style_stack.cr
│ │ ├── output_style.cr
│ │ ├── output_style_interface.cr
│ │ └── wrappable_interface.cr
│ ├── helper/
│ │ ├── athena_question.cr
│ │ ├── descriptor_helper.cr
│ │ ├── formatter.cr
│ │ ├── helper.cr
│ │ ├── helper_set.cr
│ │ ├── interface.cr
│ │ ├── output_wrapper.cr
│ │ ├── progress_bar.cr
│ │ ├── progress_indicator.cr
│ │ ├── question.cr
│ │ ├── table.cr
│ │ ├── table_cell_style.cr
│ │ └── table_style.cr
│ ├── input/
│ │ ├── argument.cr
│ │ ├── argv.cr
│ │ ├── definition.cr
│ │ ├── hash.cr
│ │ ├── input.cr
│ │ ├── interface.cr
│ │ ├── option.cr
│ │ ├── streamable.cr
│ │ ├── string_line.cr
│ │ └── value/
│ │ ├── array.cr
│ │ ├── bool.cr
│ │ ├── nil.cr
│ │ ├── number.cr
│ │ ├── string.cr
│ │ └── value.cr
│ ├── loader/
│ │ ├── factory.cr
│ │ └── interface.cr
│ ├── output/
│ │ ├── console_output.cr
│ │ ├── console_output_interface.cr
│ │ ├── interface.cr
│ │ ├── io.cr
│ │ ├── null.cr
│ │ ├── output.cr
│ │ ├── section.cr
│ │ ├── sized_buffer.cr
│ │ ├── type.cr
│ │ └── verbosity.cr
│ ├── question/
│ │ ├── abstract_choice.cr
│ │ ├── base.cr
│ │ ├── choice.cr
│ │ ├── confirmation.cr
│ │ ├── multiple_choice.cr
│ │ └── question.cr
│ ├── spec/
│ │ └── expectations/
│ │ └── command_is_successful.cr
│ ├── spec.cr
│ ├── style/
│ │ ├── athena.cr
│ │ ├── interface.cr
│ │ └── output.cr
│ └── terminal.cr
├── contracts/
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ └── .gitkeep
│ └── src/
│ ├── alias.cr
│ ├── athena-contracts.cr
│ ├── contracts/
│ │ └── event_dispatcher/
│ │ ├── event.cr
│ │ ├── interface.cr
│ │ └── stoppable_event.cr
│ └── event_dispatcher.cr
├── dependency_injection/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── abstract_bundle_spec.cr
│ │ ├── athena-dependency_injection_spec.cr
│ │ ├── compiler_passes/
│ │ │ ├── auto_wire_spec.cr
│ │ │ ├── define_getters_spec.cr
│ │ │ ├── inline_service_definitions_spec.cr
│ │ │ ├── merge_configs_spec.cr
│ │ │ ├── merge_extension_config_spec.cr
│ │ │ ├── namespaced_spec.cr
│ │ │ ├── normalize_definitions_spec.cr
│ │ │ ├── optional_services_spec.cr
│ │ │ ├── parameters_spec.cr
│ │ │ ├── process_aliases_spec.cr
│ │ │ ├── process_auto_configurations_spec.cr
│ │ │ ├── process_bindings_spec.cr
│ │ │ ├── process_parameters_spec.cr
│ │ │ ├── proxy_spec.cr
│ │ │ ├── register_services_spec.cr
│ │ │ ├── remove_unused_services_spec.cr
│ │ │ ├── resolve_parameter_placeholders_spec.cr
│ │ │ ├── resolve_values_spec.cr
│ │ │ ├── untyped_with_default_spec.cr
│ │ │ ├── validate_arguments_spec.cr
│ │ │ └── validate_generics_spec.cr
│ │ ├── extension_spec.cr
│ │ ├── spec_helper.cr
│ │ └── spec_spec.cr
│ └── src/
│ ├── abstract_bundle.cr
│ ├── annotation_configurations.cr
│ ├── annotations.cr
│ ├── athena-dependency_injection.cr
│ ├── compiler_passes/
│ │ ├── analyze_service_references.cr
│ │ ├── auto_wire.cr
│ │ ├── define_getters.cr
│ │ ├── inline_service_definitions.cr
│ │ ├── merge_configs.cr
│ │ ├── merge_extension_config.cr
│ │ ├── normalize_definitions.cr
│ │ ├── process_aliases.cr
│ │ ├── process_annotation_bindings.cr
│ │ ├── process_autoconfigure_annotations.cr
│ │ ├── process_bindings.cr
│ │ ├── process_parameters.cr
│ │ ├── process_tags.cr
│ │ ├── register_services.cr
│ │ ├── remove_unused_services.cr
│ │ ├── resolve_parameter_placeholders.cr
│ │ ├── resolve_tagged_iterators.cr
│ │ ├── resolve_values.cr
│ │ ├── validate_arguments.cr
│ │ └── validate_generics.cr
│ ├── extension.cr
│ ├── proxy.cr
│ ├── service_container.cr
│ └── spec.cr
├── dotenv/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-dotenv_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-dotenv.cr
│ └── exception/
│ ├── format.cr
│ ├── logic.cr
│ └── path.cr
├── event_dispatcher/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── callable_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── event_dispatcher_spec.cr
│ │ ├── generic_event_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── annotations.cr
│ ├── athena-event_dispatcher.cr
│ ├── callable.cr
│ ├── event.cr
│ ├── event_dispatcher.cr
│ ├── event_dispatcher_interface.cr
│ ├── generic_event.cr
│ └── spec.cr
├── framework/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── .gitkeep
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── argument_resolver_controller_spec.cr
│ │ ├── assets/
│ │ │ ├── file-big.txt
│ │ │ ├── file-small.txt
│ │ │ ├── foo.txt
│ │ │ ├── greeting.ecr
│ │ │ ├── layout.ecr
│ │ │ └── openssl/
│ │ │ ├── openssl.crt
│ │ │ └── openssl.key
│ │ ├── athena_spec.cr
│ │ ├── bundle_spec.cr
│ │ ├── commands/
│ │ │ ├── debug_event_dispatcher_spec.cr
│ │ │ ├── debug_router_match_spec.cr
│ │ │ └── debug_router_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── controller/
│ │ │ ├── redirect_spec.cr
│ │ │ └── value_resolvers/
│ │ │ ├── enum_spec.cr
│ │ │ ├── query_parameter_spec.cr
│ │ │ ├── request_body_spec.cr
│ │ │ ├── time_spec.cr
│ │ │ └── uuid_spec.cr
│ │ ├── controller_spec.cr
│ │ ├── controllers/
│ │ │ ├── argument_resolver_controller.cr
│ │ │ ├── custom_annotation_controller.cr
│ │ │ ├── file_upload_controller.cr
│ │ │ ├── prefix_controller.cr
│ │ │ ├── routing_controller.cr
│ │ │ └── view_controller.cr
│ │ ├── custom_annotation_spec.cr
│ │ ├── ext/
│ │ │ ├── console/
│ │ │ │ └── register_commands_spec.cr
│ │ │ └── routing/
│ │ │ └── annotation_route_loader_spec.cr
│ │ ├── file_parser_spec.cr
│ │ ├── file_upload_controller_spec.cr
│ │ ├── listeners/
│ │ │ ├── cors_spec.cr
│ │ │ ├── file_spec.cr
│ │ │ ├── format_spec.cr
│ │ │ └── view_spec.cr
│ │ ├── prefix_spec.cr
│ │ ├── routing_spec.cr
│ │ ├── spec/
│ │ │ ├── expectations/
│ │ │ │ ├── request/
│ │ │ │ │ └── attribute_equals_spec.cr
│ │ │ │ └── response/
│ │ │ │ ├── cookie_value_equals_spec.cr
│ │ │ │ ├── format_equals_spec.cr
│ │ │ │ ├── has_cookie_spec.cr
│ │ │ │ ├── has_header_spec.cr
│ │ │ │ ├── has_status_spec.cr
│ │ │ │ ├── header_equals_spec.cr
│ │ │ │ ├── is_redirected_spec.cr
│ │ │ │ ├── is_successful_spec.cr
│ │ │ │ └── is_unprocessable_spec.cr
│ │ │ └── web_test_case_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── view/
│ │ │ ├── context_spec.cr
│ │ │ ├── format_negotiator_spec.cr
│ │ │ ├── view_handler_spec.cr
│ │ │ └── view_spec.cr
│ │ └── view_controller_spec.cr
│ └── src/
│ ├── annotation_resolver.cr
│ ├── annotations.cr
│ ├── athena.cr
│ ├── bundle.cr
│ ├── commands/
│ │ ├── debug_event_dispatcher.cr
│ │ ├── debug_router.cr
│ │ └── debug_router_match.cr
│ ├── compiler_passes/
│ │ └── expose_controller_services.cr
│ ├── controller/
│ │ ├── redirect.cr
│ │ └── value_resolvers/
│ │ ├── enum.cr
│ │ ├── interface.cr
│ │ ├── query_parameter.cr
│ │ ├── request_body.cr
│ │ ├── time.cr
│ │ └── uuid.cr
│ ├── controller.cr
│ ├── ext/
│ │ ├── clock.cr
│ │ ├── console/
│ │ │ ├── application.cr
│ │ │ ├── compiler_passes/
│ │ │ │ └── register_commands.cr
│ │ │ ├── container_command_loader.cr
│ │ │ ├── descriptor/
│ │ │ │ ├── descriptor.cr
│ │ │ │ └── text.cr
│ │ │ └── helper/
│ │ │ └── descriptor_helper.cr
│ │ ├── console.cr
│ │ ├── event_dispatcher.cr
│ │ ├── http.cr
│ │ ├── http_kernel.cr
│ │ ├── routing/
│ │ │ ├── annotation_route_loader.cr
│ │ │ ├── redirectable_url_matcher.cr
│ │ │ └── router.cr
│ │ ├── routing.cr
│ │ ├── serializer.cr
│ │ ├── validator/
│ │ │ └── validation_failed_exception.cr
│ │ └── validator.cr
│ ├── file_parser.cr
│ ├── listeners/
│ │ ├── cors.cr
│ │ ├── file.cr
│ │ ├── format.cr
│ │ └── view.cr
│ ├── logging.cr
│ ├── spec/
│ │ ├── abstract_browser.cr
│ │ ├── api_test_case.cr
│ │ ├── expectations/
│ │ │ ├── http.cr
│ │ │ ├── request/
│ │ │ │ └── attribute_equals.cr
│ │ │ └── response/
│ │ │ ├── base.cr
│ │ │ ├── cookie_value_equals.cr
│ │ │ ├── format_equals.cr
│ │ │ ├── has_cookie.cr
│ │ │ ├── has_header.cr
│ │ │ ├── has_status.cr
│ │ │ ├── header_equals.cr
│ │ │ ├── is_redirected.cr
│ │ │ ├── is_successful.cr
│ │ │ └── is_unprocessable.cr
│ │ ├── http_browser.cr
│ │ └── web_test_case.cr
│ ├── spec.cr
│ └── view/
│ ├── configurable_view_handler_interface.cr
│ ├── context.cr
│ ├── format_handler_interface.cr
│ ├── format_negotiator.cr
│ ├── view.cr
│ ├── view_handler.cr
│ └── view_handler_interface.cr
├── http/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── assets/
│ │ │ ├── .unknownextension
│ │ │ ├── case-sensitive-mime-type.xlsm
│ │ │ ├── directory/
│ │ │ │ └── .empty
│ │ │ ├── file-big.txt
│ │ │ ├── file-small.txt
│ │ │ ├── foo.txt
│ │ │ ├── fööö.html
│ │ │ ├── test
│ │ │ └── webkitdirectory/
│ │ │ ├── nested/
│ │ │ │ └── test.txt
│ │ │ └── test.txt
│ │ ├── binary_file_response_spec.cr
│ │ ├── ext/
│ │ │ └── conversion_types_spec.cr
│ │ ├── file_spec.cr
│ │ ├── header_utils_spec.cr
│ │ ├── ip_utils_spec.cr
│ │ ├── parameter_bag_spec.cr
│ │ ├── redirect_response_spec.cr
│ │ ├── request_matcher/
│ │ │ ├── attributes_spec.cr
│ │ │ ├── header_spec.cr
│ │ │ ├── hostname_spec.cr
│ │ │ ├── method_spec.cr
│ │ │ ├── path_spec.cr
│ │ │ └── query_parameter_spec.cr
│ │ ├── request_matcher_spec.cr
│ │ ├── request_spec.cr
│ │ ├── response_headers_spec.cr
│ │ ├── response_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── streamed_response_spec.cr
│ │ └── uploaded_file_spec.cr
│ └── src/
│ ├── abstract_file.cr
│ ├── athena-http.cr
│ ├── binary_file_response.cr
│ ├── exception/
│ │ ├── conflicting_headers.cr
│ │ ├── file.cr
│ │ ├── file_not_found.cr
│ │ ├── file_size_limit_exceeded.cr
│ │ ├── logic.cr
│ │ ├── request_exception_interface.cr
│ │ └── suspicious_operation.cr
│ ├── ext/
│ │ └── conversion_types.cr
│ ├── file.cr
│ ├── header_utils.cr
│ ├── ip_utils.cr
│ ├── parameter_bag.cr
│ ├── redirect_response.cr
│ ├── request.cr
│ ├── request_matcher/
│ │ ├── attributes.cr
│ │ ├── header.cr
│ │ ├── hostname.cr
│ │ ├── method.cr
│ │ ├── path.cr
│ │ └── query_parameter.cr
│ ├── request_matcher.cr
│ ├── request_store.cr
│ ├── response.cr
│ ├── response_headers.cr
│ ├── streamed_response.cr
│ └── uploaded_file.cr
├── http_kernel/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── controller/
│ │ │ ├── argument_resolver_spec.cr
│ │ │ ├── parameter_metadata_spec.cr
│ │ │ └── value_resolvers/
│ │ │ ├── default_value_spec.cr
│ │ │ ├── request_attribute_spec.cr
│ │ │ └── request_spec.cr
│ │ ├── error_renderer_spec.cr
│ │ ├── exception/
│ │ │ ├── bad_gateway_spec.cr
│ │ │ ├── http_exception_spec.cr
│ │ │ ├── service_unavailable_spec.cr
│ │ │ ├── too_many_requests_spec.cr
│ │ │ └── unauthorized_spec.cr
│ │ ├── http_kernel_spec.cr
│ │ ├── listeners/
│ │ │ └── error_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── action.cr
│ ├── action_resolver.cr
│ ├── action_resolver_interface.cr
│ ├── athena-http_kernel.cr
│ ├── controller/
│ │ ├── argument_resolver.cr
│ │ ├── argument_resolver_interface.cr
│ │ ├── parameter_metadata.cr
│ │ └── value_resolvers/
│ │ ├── default_value.cr
│ │ ├── interface.cr
│ │ ├── request.cr
│ │ └── request_attribute.cr
│ ├── error_renderer.cr
│ ├── error_renderer_interface.cr
│ ├── events/
│ │ ├── action_event.cr
│ │ ├── exception_event.cr
│ │ ├── request_aware.cr
│ │ ├── request_event.cr
│ │ ├── response_event.cr
│ │ ├── settable_response.cr
│ │ ├── terminate_event.cr
│ │ └── view_event.cr
│ ├── exception/
│ │ ├── bad_gateway.cr
│ │ ├── bad_request.cr
│ │ ├── conflict.cr
│ │ ├── forbidden.cr
│ │ ├── gone.cr
│ │ ├── http_exception.cr
│ │ ├── length_required.cr
│ │ ├── logic.cr
│ │ ├── method_not_allowed.cr
│ │ ├── not_acceptable.cr
│ │ ├── not_found.cr
│ │ ├── not_implemented.cr
│ │ ├── precondition_failed.cr
│ │ ├── service_unavailable.cr
│ │ ├── stop_format_listener.cr
│ │ ├── too_many_requests.cr
│ │ ├── unauthorized.cr
│ │ ├── unprocessable_entity.cr
│ │ └── unsupported_media_type.cr
│ ├── http_kernel.cr
│ └── listeners/
│ ├── error.cr
│ └── routing.cr
├── image_size/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-image_size_spec.cr
│ │ ├── images/
│ │ │ ├── cur/
│ │ │ │ └── 32x256_8_0.cur
│ │ │ ├── mng/
│ │ │ │ └── 61x42_0_0.mng
│ │ │ ├── png/
│ │ │ │ └── 192x110_8_0.apng
│ │ │ ├── psd/
│ │ │ │ └── 16x20_8_3.psd
│ │ │ ├── swf/
│ │ │ │ └── 450x200_0_0.swf
│ │ │ └── tiff/
│ │ │ ├── big-endian.68x49_8_1.tiff
│ │ │ └── little-endian.40x68_8_1.tiff
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-image_size.cr
│ ├── extractors/
│ │ ├── abstract_ico.cr
│ │ ├── abstract_png.cr
│ │ ├── abstract_tiff.cr
│ │ ├── apng.cr
│ │ ├── bmp.cr
│ │ ├── cur.cr
│ │ ├── extractor.cr
│ │ ├── gif.cr
│ │ ├── ico.cr
│ │ ├── ii_tiff.cr
│ │ ├── jpeg.cr
│ │ ├── mm_tiff.cr
│ │ ├── mng.cr
│ │ ├── png.cr
│ │ ├── psd.cr
│ │ ├── svg.cr
│ │ ├── swf.cr
│ │ └── webp.cr
│ ├── image.cr
│ └── image_format.cr
├── mercure/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── authorization_spec.cr
│ │ ├── discovery_spec.cr
│ │ ├── hub/
│ │ │ ├── hub_spec.cr
│ │ │ └── registry_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── token_factory/
│ │ │ └── jwt_spec.cr
│ │ └── token_provider/
│ │ ├── callable_spec.cr
│ │ ├── factory_spec.cr
│ │ └── static_spec.cr
│ └── src/
│ ├── athena-mercure.cr
│ ├── authorization.cr
│ ├── discovery.cr
│ ├── exception/
│ │ ├── invalid_argument.cr
│ │ └── runtime.cr
│ ├── hub/
│ │ ├── hub.cr
│ │ ├── interface.cr
│ │ └── registry.cr
│ ├── spec.cr
│ ├── token_factory/
│ │ ├── interface.cr
│ │ └── jwt.cr
│ ├── token_provider/
│ │ ├── callable.cr
│ │ ├── factory.cr
│ │ ├── interface.cr
│ │ └── static.cr
│ └── update.cr
├── mercure-bundle/
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ └── mkdocs.yml
├── mime/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── abstract_types_guesser_test_case.cr
│ │ ├── address_spec.cr
│ │ ├── draft_email_spec.cr
│ │ ├── email_spec.cr
│ │ ├── encoder/
│ │ │ ├── base64_content_spec.cr
│ │ │ ├── eight_bit_content_spec.cr
│ │ │ ├── idn_address_spec.cr
│ │ │ ├── quoted_printable_content_spec.cr
│ │ │ ├── quoted_printable_mime_header_spec.cr
│ │ │ └── rfc2231_spec.cr
│ │ ├── fixtures/
│ │ │ ├── content.txt
│ │ │ ├── mimetypes/
│ │ │ │ ├── -test
│ │ │ │ ├── .unknownextension
│ │ │ │ ├── abc.csv
│ │ │ │ ├── directory/
│ │ │ │ │ └── .empty
│ │ │ │ ├── other-file.example
│ │ │ │ └── test
│ │ │ ├── samples/
│ │ │ │ └── charsets/
│ │ │ │ ├── iso-2022-jp/
│ │ │ │ │ └── one.txt
│ │ │ │ ├── iso-8859-1/
│ │ │ │ │ └── one.txt
│ │ │ │ └── utf-8/
│ │ │ │ ├── one.txt
│ │ │ │ ├── three.txt
│ │ │ │ └── two.txt
│ │ │ └── test.docx
│ │ ├── header/
│ │ │ ├── collection_spec.cr
│ │ │ ├── date_spec.cr
│ │ │ ├── identification_spec.cr
│ │ │ ├── mailbox_list_spec.cr
│ │ │ ├── mailbox_spec.cr
│ │ │ ├── parameterized_spec.cr
│ │ │ ├── path_spec.cr
│ │ │ └── unstructured_spec.cr
│ │ ├── magic_types_guesser_spec.cr
│ │ ├── message_converter_spec.cr
│ │ ├── message_spec.cr
│ │ ├── native_types_guessuer_spec.cr
│ │ ├── part/
│ │ │ ├── data_spec.cr
│ │ │ ├── file_spec.cr
│ │ │ ├── message_spec.cr
│ │ │ ├── multipart/
│ │ │ │ ├── alternative_spec.cr
│ │ │ │ ├── digest_spec.cr
│ │ │ │ ├── form_spec.cr
│ │ │ │ ├── mixed_spec.cr
│ │ │ │ └── related_spec.cr
│ │ │ └── text_spec.cr
│ │ ├── spec_helper.cr
│ │ └── types_spec.cr
│ └── src/
│ ├── address.cr
│ ├── athena-mime.cr
│ ├── draft_email.cr
│ ├── email.cr
│ ├── encoder/
│ │ ├── address_encoder_interface.cr
│ │ ├── base64_content.cr
│ │ ├── content_encoder_interface.cr
│ │ ├── eight_bit_content.cr
│ │ ├── encoder_interface.cr
│ │ ├── idn_address.cr
│ │ ├── mime_header_encoder_interface.cr
│ │ ├── quoted_printable_content.cr
│ │ ├── quoted_printable_mime_header.cr
│ │ └── rfc2231.cr
│ ├── exception/
│ │ ├── header_not_found.cr
│ │ ├── invalid_argument.cr
│ │ ├── logic.cr
│ │ └── runtime.cr
│ ├── header/
│ │ ├── abstract.cr
│ │ ├── collection.cr
│ │ ├── date.cr
│ │ ├── identification.cr
│ │ ├── interface.cr
│ │ ├── mailbox.cr
│ │ ├── mailbox_list.cr
│ │ ├── parameterized.cr
│ │ ├── path.cr
│ │ └── unstructured.cr
│ ├── magic_types_guesser.cr
│ ├── message.cr
│ ├── message_converter.cr
│ ├── native_types_guesser.cr
│ ├── part/
│ │ ├── abstract.cr
│ │ ├── abstract_multipart.cr
│ │ ├── data.cr
│ │ ├── file.cr
│ │ ├── message.cr
│ │ ├── multipart/
│ │ │ ├── alternative.cr
│ │ │ ├── digest.cr
│ │ │ ├── form.cr
│ │ │ ├── mixed.cr
│ │ │ └── related.cr
│ │ └── text.cr
│ ├── types/
│ │ └── data.cr
│ ├── types.cr
│ ├── types_guesser_interface.cr
│ └── types_interface.cr
├── negotiation/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── accept_language_spec.cr
│ │ ├── accept_match_spec.cr
│ │ ├── accept_spec.cr
│ │ ├── base_accept_spec.cr
│ │ ├── charset_negotiator_spec.cr
│ │ ├── encoding_negotiator_spec.cr
│ │ ├── language_negotiator_spec.cr
│ │ ├── negotiator_spec.cr
│ │ ├── negotiator_test_case.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── abstract_negotiator.cr
│ ├── accept.cr
│ ├── accept_charset.cr
│ ├── accept_encoding.cr
│ ├── accept_language.cr
│ ├── accept_match.cr
│ ├── athena-negotiation.cr
│ ├── base_accept.cr
│ ├── charset_negotiator.cr
│ ├── encoding_negotiator.cr
│ ├── exception/
│ │ ├── invalid_argument.cr
│ │ ├── invalid_language.cr
│ │ └── invalid_media_type.cr
│ ├── language_negotiator.cr
│ └── negotiator.cr
├── routing/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── fixtures/
│ │ │ └── route_provider/
│ │ │ ├── route_collection0.cr
│ │ │ ├── route_collection1.cr
│ │ │ ├── route_collection10.cr
│ │ │ ├── route_collection11.cr
│ │ │ ├── route_collection12.cr
│ │ │ ├── route_collection2.cr
│ │ │ ├── route_collection3.cr
│ │ │ ├── route_collection4.cr
│ │ │ ├── route_collection5.cr
│ │ │ ├── route_collection6.cr
│ │ │ ├── route_collection7.cr
│ │ │ ├── route_collection8.cr
│ │ │ └── route_collection9.cr
│ │ ├── generator/
│ │ │ └── url_generator_spec.cr
│ │ ├── matcher/
│ │ │ ├── abstract_url_matcher_test_case.cr
│ │ │ ├── redirectable_url_matcher_spec.cr
│ │ │ ├── traceable_url_matcher_spec.cr
│ │ │ └── url_matcher_spec.cr
│ │ ├── parameters_spec.cr
│ │ ├── request_context_spec.cr
│ │ ├── requirement/
│ │ │ ├── enum_spec.cr
│ │ │ └── requirement_spec.cr
│ │ ├── route_collection_spec.cr
│ │ ├── route_compiler_spec.cr
│ │ ├── route_provider_spec.cr
│ │ ├── route_spec.cr
│ │ ├── router_spec.cr
│ │ ├── routing_handler_spec.cr
│ │ ├── spec_helper.cr
│ │ └── static_prefix_collection_spec.cr
│ └── src/
│ ├── annotations.cr
│ ├── athena-routing.cr
│ ├── compiled_route.cr
│ ├── exception/
│ │ ├── invalid_argument.cr
│ │ ├── invalid_parameter.cr
│ │ ├── method_not_allowed.cr
│ │ ├── missing_required_parameters.cr
│ │ ├── no_configuration.cr
│ │ ├── resource_not_found.cr
│ │ └── route_not_found.cr
│ ├── ext/
│ │ └── regex.cr
│ ├── generator/
│ │ ├── configurable_requirements_interface.cr
│ │ ├── interface.cr
│ │ ├── reference_type.cr
│ │ └── url_generator.cr
│ ├── matcher/
│ │ ├── redirectable_url_matcher_interface.cr
│ │ ├── request_matcher_interface.cr
│ │ ├── traceable_url_matcher.cr
│ │ ├── url_matcher.cr
│ │ └── url_matcher_interface.cr
│ ├── parameters.cr
│ ├── request_context.cr
│ ├── request_context_aware_interface.cr
│ ├── requirement/
│ │ ├── enum.cr
│ │ └── requirement.cr
│ ├── route.cr
│ ├── route_collection.cr
│ ├── route_compiler.cr
│ ├── route_provider.cr
│ ├── router.cr
│ ├── router_interface.cr
│ ├── routing_handler.cr
│ └── static_prefix_collection.cr
├── serializer/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-serializer_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── exclusion_strategies/
│ │ │ ├── custom_strategy_spec.cr
│ │ │ ├── group_spec.cr
│ │ │ └── version_spec.cr
│ │ ├── models/
│ │ │ ├── accessor.cr
│ │ │ ├── accessor_order.cr
│ │ │ ├── basic.cr
│ │ │ ├── discriminator.cr
│ │ │ ├── emit_null.cr
│ │ │ ├── empty.cr
│ │ │ ├── exclude.cr
│ │ │ ├── expose.cr
│ │ │ ├── groups.cr
│ │ │ ├── ignore_on_deserialize.cr
│ │ │ ├── ignore_on_serialize.cr
│ │ │ ├── name.cr
│ │ │ ├── nested.cr
│ │ │ ├── post_deserialize.cr
│ │ │ ├── post_serialize.cr
│ │ │ ├── pre_serialize.cr
│ │ │ ├── read_only.cr
│ │ │ ├── skip.cr
│ │ │ ├── skip_when_empty.cr
│ │ │ └── virtual_property.cr
│ │ ├── navigators/
│ │ │ ├── deserialization_navigator_spec.cr
│ │ │ └── serialization_navigator_spec.cr
│ │ ├── serialization_context_spec.cr
│ │ ├── serializer_spec.cr
│ │ ├── spec_helper.cr
│ │ └── visitors/
│ │ ├── json_deserialization_visitor_spec.cr
│ │ ├── json_serialization_visitor_spec.cr
│ │ ├── yaml_deserialization_visitor_spec.cr
│ │ └── yaml_serialization_visitor_spec.cr
│ └── src/
│ ├── annotations.cr
│ ├── any.cr
│ ├── athena-serializer.cr
│ ├── construction/
│ │ ├── instantiate_object_constructor.cr
│ │ └── object_constructor_interface.cr
│ ├── context.cr
│ ├── deserialization_context.cr
│ ├── exception/
│ │ ├── deserialization_exception.cr
│ │ ├── logic.cr
│ │ ├── missing_required_property.cr
│ │ ├── nil_required_property.cr
│ │ ├── property_exception.cr
│ │ └── serialization_exception.cr
│ ├── exclusion_strategies/
│ │ ├── disjunct.cr
│ │ ├── exclusion_strategy_interface.cr
│ │ ├── groups.cr
│ │ └── version.cr
│ ├── navigators/
│ │ ├── deserialization_navigator.cr
│ │ ├── navigator_factory.cr
│ │ └── serialization_navigator.cr
│ ├── property_metadata.cr
│ ├── serializable.cr
│ ├── serialization_context.cr
│ ├── serializer.cr
│ ├── serializer_interface.cr
│ └── visitors/
│ ├── deserialization_visitor.cr
│ ├── deserialization_visitor_interface.cr
│ ├── json_deserialization_visitor.cr
│ ├── json_serialization_visitor.cr
│ ├── serialization_visitor_interface.cr
│ ├── yaml_deserialization_visitor.cr
│ └── yaml_serialization_visitor.cr
├── spec/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-spec_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── methods_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-spec.cr
│ ├── methods.cr
│ └── test_case.cr
└── validator/
├── .editorconfig
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── UPGRADING.md
├── docs/
│ └── README.md
├── mkdocs.yml
├── shard.yml
├── spec/
│ ├── athena-validator_spec.cr
│ ├── constraint_spec.cr
│ ├── constraints/
│ │ ├── all_validator_spec.cr
│ │ ├── at_least_one_of_validator_spec.cr
│ │ ├── blank_validator_spec.cr
│ │ ├── callback_validator_spec.cr
│ │ ├── choice_validator_spec.cr
│ │ ├── collection_spec.cr
│ │ ├── collection_validator_test_case.cr
│ │ ├── composite_spec.cr
│ │ ├── compound_validator_spec.cr
│ │ ├── count_validator_spec.cr
│ │ ├── email_validator_spec.cr
│ │ ├── equal_to_validator_spec.cr
│ │ ├── file_spec.cr
│ │ ├── file_validator_ath_file_spec.cr
│ │ ├── file_validator_path_spec.cr
│ │ ├── file_validator_std_file_spec.cr
│ │ ├── file_validator_test_case.cr
│ │ ├── fixtures/
│ │ │ └── file-big.txt
│ │ ├── greater_than_or_equal_validator_spec.cr
│ │ ├── greater_than_validator_spec.cr
│ │ ├── hash_collection_validator_spec.cr
│ │ ├── hash_like_object_collection_validator_spec.cr
│ │ ├── image_validator_spec.cr
│ │ ├── ip_validator_spec.cr
│ │ ├── is_false_validator_spec.cr
│ │ ├── is_nil_validator_spec.cr
│ │ ├── is_true_validator_spec.cr
│ │ ├── isbn_validator_spec.cr
│ │ ├── isin_validator_spec.cr
│ │ ├── issn_validator_spec.cr
│ │ ├── length_validator_spec.cr
│ │ ├── less_than_or_equal_validator_spec.cr
│ │ ├── less_than_validator_spec.cr
│ │ ├── luhn_validator_spec.cr
│ │ ├── negative_or_zero_validator_spec.cr
│ │ ├── negative_validator_spec.cr
│ │ ├── not_blank_validator_spec.cr
│ │ ├── not_equal_to_validator_spec.cr
│ │ ├── not_nil_validator_spec.cr
│ │ ├── positive_or_zero_validator_spec.cr
│ │ ├── positive_validator_spec.cr
│ │ ├── range_validator_spec.cr
│ │ ├── regex_validator_spec.cr
│ │ ├── sequentially_validator_spec.cr
│ │ ├── unique_validator_spec.cr
│ │ ├── url_validator_spec.cr
│ │ └── valid_validator_spec.cr
│ ├── metadata/
│ │ └── class_metadata_spec.cr
│ ├── property_path_spec.cr
│ ├── spec/
│ │ └── compound_constraint_test_case_spec.cr
│ ├── spec_helper.cr
│ ├── validatable_spec.cr
│ ├── validator/
│ │ └── recursive_validator_spec.cr
│ └── violation/
│ ├── constraint_violation_list_spec.cr
│ └── constraint_violation_spec.cr
└── src/
├── athena-validator.cr
├── constraint.cr
├── constraint_validator.cr
├── constraint_validator_factory.cr
├── constraint_validator_factory_interface.cr
├── constraint_validator_interface.cr
├── constraints/
│ ├── abstract_comparison.cr
│ ├── abstract_comparison_validator.cr
│ ├── all.cr
│ ├── at_least_one_of.cr
│ ├── blank.cr
│ ├── callback.cr
│ ├── choice.cr
│ ├── collection.cr
│ ├── composite.cr
│ ├── compound.cr
│ ├── count.cr
│ ├── email.cr
│ ├── equal_to.cr
│ ├── existence.cr
│ ├── file.cr
│ ├── greater_than.cr
│ ├── greater_than_or_equal.cr
│ ├── group_sequence.cr
│ ├── image.cr
│ ├── ip.cr
│ ├── is_false.cr
│ ├── is_nil.cr
│ ├── is_true.cr
│ ├── isbn.cr
│ ├── isin.cr
│ ├── issn.cr
│ ├── length.cr
│ ├── less_than.cr
│ ├── less_than_or_equal.cr
│ ├── luhn.cr
│ ├── negative.cr
│ ├── negative_or_zero.cr
│ ├── not_blank.cr
│ ├── not_equal_to.cr
│ ├── not_nil.cr
│ ├── optional.cr
│ ├── positive.cr
│ ├── positive_or_zero.cr
│ ├── range.cr
│ ├── regex.cr
│ ├── required.cr
│ ├── sequentially.cr
│ ├── unique.cr
│ ├── url.cr
│ └── valid.cr
├── exception/
│ ├── invalid_argument.cr
│ ├── logic.cr
│ └── unexpected_value_error.cr
├── execution_context.cr
├── execution_context_interface.cr
├── metadata/
│ ├── cascading_strategy.cr
│ ├── class_metadata.cr
│ ├── generic_metadata.cr
│ ├── getter_metadata.cr
│ ├── metadata.cr
│ ├── metadata_factory.cr
│ ├── metadata_factory_interface.cr
│ ├── metadata_interface.cr
│ ├── property_metadata.cr
│ └── property_metadata_interface.cr
├── property_path.cr
├── spec/
│ ├── abstract_validator_test_case.cr
│ ├── compound_constraint_test_case.cr
│ ├── constraint_validator_test_case.cr
│ └── validator_test_case.cr
├── spec.cr
├── validatable.cr
├── validator/
│ ├── contextual_validator_interface.cr
│ ├── recursive_contextual_validator.cr
│ ├── recursive_validator.cr
│ └── validator_interface.cr
└── violation/
├── constraint_violation.cr
├── constraint_violation_builder.cr
├── constraint_violation_builder_interface.cr
├── constraint_violation_interface.cr
├── constraint_violation_list.cr
└── constraint_violation_list_interface.cr
================================================
FILE CONTENTS
================================================
================================================
FILE: .ameba.yml
================================================
Documentation/DocumentationAdmonition:
Enabled: false
Lint/ComparisonToBoolean:
Enabled: false # TODO: Enable once https://github.com/crystal-ameba/ameba/issues/432 is resolved
Lint/Formatting:
Enabled: false # This has its own dedicated CI job
Lint/NotNil:
Enabled: false
Lint/Typos:
Enabled: false # This has its own dedicated CI job
Lint/UselessAssign:
ExcludeTypeDeclarations: true # TODO: Disable this once https://github.com/crystal-ameba/ameba/issues/447 is resolved
Naming/AccessorMethodName:
Enabled: false
Naming/BlockParameterName:
Enabled: false
Naming/QueryBoolMethods:
Enabled: false
Style/LargeNumbers:
Enabled: true
================================================
FILE: .changes/clock/v0.2.0.md
================================================
## [0.2.0] - 2025-01-26
### Changed
- **Breaking:** Remove `Athena::Clock::Interface#sleep(Number)` overload ([#449]) (George Dietrich)
### Fixed
- Fix type error when trying to use `ACLK::Aware#now` ([#498]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/clock/releases/tag/v0.2.0
[#449]: https://github.com/athena-framework/athena/pull/449
[#498]: https://github.com/athena-framework/athena/pull/498
## [0.1.2] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
### Fixed
- Fix that `Athena::Clock::Aware` was not required by default ([#365]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/clock/releases/tag/v0.1.2
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.1.1] - 2023-10-09
_Administrative release, no functional changes_
[0.1.1]: https://github.com/athena-framework/clock/releases/tag/v0.1.1
## [0.1.0] - 2023-09-16
_Initial release._
[0.1.0]: https://github.com/athena-framework/clock/releases/tag/v0.1.0
================================================
FILE: .changes/clock/v0.3.0.md
================================================
## [0.3.0] - 2026-04-19
### Removed
- Remove `ACLK::Monotonic` ([#667]) (George Dietrich) <!-- blacksmoke16 -->
[0.3.0]: https://github.com/athena-framework/clock/releases/tag/v0.3.0
[#667]: https://github.com/athena-framework/athena/pull/667
================================================
FILE: .changes/console/v0.4.1.md
================================================
## [0.4.1] - 2025-02-08
### Fixed
- Fix incorrectly aligned block ([#519]) (Zohir Tamda)
[0.4.1]: https://github.com/athena-framework/console/releases/tag/v0.4.1
[#519]: https://github.com/athena-framework/athena/pull/519
## [0.4.0] - 2025-01-26
### Changed
- **Breaking:** Normalize exception types ([#428]) (George Dietrich)
### Added
- **Breaking:** Add `ACON::Output::Verbosity::SILENT` verbosity level ([#489]) (George Dietrich)
- **Breaking:** Rename `ACON::Completion::Input#must_suggest_values_for?` to `#must_suggest_option_values_for?` ([#498]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.13.0` ([#498]) (George Dietrich)
- Add `#assert_command_is_not_successful` spec expectation method ([#498]) (George Dietrich)
- Add support for [`FORCE_COLOR`](https://force-color.org/) and improve color support logic ([#488]) (George Dietrich)
### Fixed
- Fix unexpected completion value when given an array of options ([#498]) (George Dietrich)
- Fix error when trying to set `ACON::Helper::Table::Style#padding_char` ([#498]) (George Dietrich)
[0.4.0]: https://github.com/athena-framework/console/releases/tag/v0.4.0
[#428]: https://github.com/athena-framework/athena/pull/428
[#488]: https://github.com/athena-framework/athena/pull/488
[#489]: https://github.com/athena-framework/athena/pull/489
[#498]: https://github.com/athena-framework/athena/pull/498
## [0.3.6] - 2024-07-31
### Changed
- **Breaking:** `ACON::Application#getter` and constructor argument must now be a `String` instead of `SemanticVersion` ([#419]) (George Dietrich)
- Changed the default `ACON::Application` version to `UNKNOWN` from `0.1.0` ([#419]) (George Dietrich)
- List commands in a namespace when using it as the command name ([#427]) (George Dietrich)
- Use single quotes in text descriptor to quote values in the output ([#427]) (George Dietrich)
[0.3.6]: https://github.com/athena-framework/console/releases/tag/v0.3.6
[#419]: https://github.com/athena-framework/athena/pull/419
[#427]: https://github.com/athena-framework/athena/pull/427
## [0.3.5] - 2024-04-09
### Changed
- Update minimum `crystal` version to `~> 1.11.0` ([#270]) (George Dietrich)
- Integrate website into monorepo ([#365]) (George Dietrich)
### Added
- Support for Windows OS ([#270]) (George Dietrich)
### Fixed
- Fix incorrect column/width `ACON::Terminal` values on Windows ([#361]) (George Dietrich)
[0.3.5]: https://github.com/athena-framework/console/releases/tag/v0.3.5
[#270]: https://github.com/athena-framework/athena/pull/270
[#365]: https://github.com/athena-framework/athena/pull/365
[#361]: https://github.com/athena-framework/athena/pull/361
## [0.3.4] - 2023-10-10
### Added
- Add support for tab completion to the `bash` shell when binary is in the `bin/` directory and referenced with `./` ([#323]) (George Dietrich)
[0.3.4]: https://github.com/athena-framework/console/releases/tag/v0.3.4
[#323]: https://github.com/athena-framework/athena/pull/323
## [0.3.3] - 2023-10-09
### Changed
- Update minimum `crystal` version to `~> 1.8.0` ([#282]) (George Dietrich)
### Added
- **Breaking:** Add `ACON::Helper::ProgressBar` to enable rendering progress bars ([#304]) (George Dietrich)
- Add native shell tab completion support for `bash`, `zsh`, and `fish` for both built-in and custom commands ([#294], [#296], [#297], [#299]) (George Dietrich)
- Add `ACON::Helper::ProgressIndicator` to enable rendering spinners ([#314]) (George Dietrich)
- Add support for defining a max height for an `ACON::Output::Section` ([#303]) (George Dietrich)
- Add `ACON::Helper.format_time` to format a duration as a human readable string ([#304]) (George Dietrich)
- Add `#assert_command_is_successful` helper method to `ACON::Spec::CommandTester` and `ACON::Spec::ApplicationTester` ([#294]) (George Dietrich)
### Fixed
- Ensure long lines with URLs are not cut when wrapped ([#314]) (George Dietrich)
- Do not emit erroneous newline from `ACON::Style::Athena` when it's the first thing being written ([#314]) (George Dietrich)
- Fix misalignment when word wrapping a hyperlink ([#305]) (George Dietrich)
- Do not emit erroneous extra newlines from an `ACON::Output::Section` ([#303]) (George Dietrich)
- Fix misalignment within a vertical table with multi-line cell ([#300]) (George Dietrich)
[0.3.3]: https://github.com/athena-framework/console/releases/tag/v0.3.3
[#282]: https://github.com/athena-framework/athena/pull/282
[#294]: https://github.com/athena-framework/athena/pull/294
[#296]: https://github.com/athena-framework/athena/pull/296
[#297]: https://github.com/athena-framework/athena/pull/297
[#299]: https://github.com/athena-framework/athena/pull/299
[#300]: https://github.com/athena-framework/athena/pull/300
[#303]: https://github.com/athena-framework/athena/pull/303
[#304]: https://github.com/athena-framework/athena/pull/304
[#305]: https://github.com/athena-framework/athena/pull/305
[#314]: https://github.com/athena-framework/athena/pull/314
## [0.3.2] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
### Fixed
- Fix formatting issue in Crystal `1.8-dev` ([#258]) (George Dietrich)
[0.3.2]: https://github.com/athena-framework/console/releases/tag/v0.3.2
[#261]: https://github.com/athena-framework/athena/pull/261
[#258]: https://github.com/athena-framework/athena/pull/258
## [0.3.1] - 2023-02-04
### Added
- Add better integration between `Athena::Console` and `Athena::DependencyInjection` ([#259]) (George Dietrich)
[0.3.1]: https://github.com/athena-framework/console/releases/tag/v0.3.1
[#259]: https://github.com/athena-framework/athena/pull/259
## [0.3.0] - 2023-01-07
### Changed
- **Breaking:** deprecate command default name/description class variables in favor of the new `ACONA::AsCommand` annotation ([#214]) (George Dietrich)
- **Breaking:** refactor `ACON::Command#application=` to no longer have a `nil` default value ([#217]) (George Dietrich)
- **Breaking:** refactor `ACON::Command#process_title=` no longer accept `nil` ([#217]) (George Dietrich)
- **Breaking:** rename `ACON::Command#process_title=` to `ACON::Command#process_title` ([#217]) (George Dietrich)
### Added
- **Breaking:** add `#table` method to `ACON::Style::Interface` ([#220]) (George Dietrich)
- Add `ACONA::AsCommand` annotation to configure a command's name, description, aliases, and if it should be hidden ([#214]) (George Dietrich)
- Add support for generating tables ([#220]) (George Dietrich)
### Fixed
- Fix issue with using `ACON::Formatter::Output#format_and_wrap` with `nil` input and an edge case when wrapping a string with a space at the limit ([#220]) (George Dietrich)
- Fix `ACON::Formatter::NullStyle#*_option` method using incorrect `ACON::Formatter::Mode` type restriction ([#220]) (George Dietrich)
- Fix some flakiness when testing commands with input ([#224]) (George Dietrich)
- Fix compiler error when trying to use `ACON::Style::Athena#error_style` ([#240]) (George Dietrich)
[0.3.0]: https://github.com/athena-framework/console/releases/tag/v0.3.0
[#214]: https://github.com/athena-framework/athena/pull/214
[#217]: https://github.com/athena-framework/athena/pull/217
[#220]: https://github.com/athena-framework/athena/pull/220
[#224]: https://github.com/athena-framework/athena/pull/224
[#240]: https://github.com/athena-framework/athena/pull/240
## [0.2.1] - 2022-09-05
### Changed
- **Breaking:** ensure parameter names defined on interfaces match the implementation ([#188]) (George Dietrich)
### Added
- Add an `ACON::Input::Interface` based on a command line string ([#186], [#187]) (George Dietrich)
[0.2.1]: https://github.com/athena-framework/console/releases/tag/v0.2.1
[#186]: https://github.com/athena-framework/athena/pull/186
[#187]: https://github.com/athena-framework/athena/pull/187
[#188]: https://github.com/athena-framework/athena/pull/188
## [0.2.0] - 2022-05-14
_First release a part of the monorepo._
### Changed
- **Breaking:** remove `ACON::Formatter::Mode` in favor of `Colorize::Mode`. Breaking only if not using symbol autocasting. ([#170]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Added
- Add `VERSION` constant to `Athena::Console` namespace ([#166]) (George Dietrich)
- Add getting started documentation to API docs ([#172]) (George Dietrich)
### Fixed
- Disallow multi char option shortcuts made up of diff chars ([#164]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/console/releases/tag/v0.2.0
[#164]: https://github.com/athena-framework/athena/pull/164
[#166]: https://github.com/athena-framework/athena/pull/166
[#169]: https://github.com/athena-framework/athena/pull/169
[#170]: https://github.com/athena-framework/athena/pull/170
[#172]: https://github.com/athena-framework/athena/pull/172
## [0.1.1] - 2021-12-01
### Fixed
- **Breaking:** fix typo in parameter name of `ACON::Command#option` method ([#3]) (George Dietrich)
- Fix recursive struct error ([#4]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/console/releases/tag/v0.1.1
[#3]: https://github.com/athena-framework/console/pull/3
[#4]: https://github.com/athena-framework/console/pull/4
## [0.1.0] - 2021-10-30
_Initial release._
[0.1.0]: https://github.com/athena-framework/console/releases/tag/v0.1.0
================================================
FILE: .changes/console/v0.4.2.md
================================================
## [0.4.2] - 2025-09-04
### Added
- Add ability to customize the finished state of an `ACON::Helper::ProgressIndicator` ([#535]) (George Dietrich) <!-- blacksmoke16 -->
- Add `markdown` `ACON::Helper::Table` style ([#536]) (George Dietrich) <!-- blacksmoke16 -->
- Add support for nested style tags ([#568]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Fix `ACON::Helper::ProgressBar` messing up output in console section with EOL ([#537]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.2]: https://github.com/athena-framework/console/releases/tag/v0.4.2
[#535]: https://github.com/athena-framework/athena/pull/535
[#536]: https://github.com/athena-framework/athena/pull/536
[#568]: https://github.com/athena-framework/athena/pull/568
[#537]: https://github.com/athena-framework/athena/pull/537
================================================
FILE: .changes/console/v0.4.3.md
================================================
## [0.4.3] - 2026-04-19
### Added
- Add opt-in support for deriving the command name from `PROGRAM_NAME` when a CLI binary is invoked via a symlink ([#645]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.3]: https://github.com/athena-framework/console/releases/tag/v0.4.3
[#645]: https://github.com/athena-framework/athena/pull/645
================================================
FILE: .changes/contracts/v0.1.0.md
================================================
## [0.1.0] - 2025-08-02
_Initial release._
[0.1.0]: https://github.com/athena-framework/contracts/releases/tag/v0.1.0
================================================
FILE: .changes/dependency-injection/v0.4.3.md
================================================
## [0.4.3] - 2025-02-08
### Changed
- **Breaking:** prevent auto registering of already registered services ([#520]) (George Dietrich)
### Fixed
- Ensure all array values have proper `#of` type ([#508]) (George Dietrich)
[0.4.3]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.4.3
[#508]: https://github.com/athena-framework/athena/pull/508
[#520]: https://github.com/athena-framework/athena/pull/520
## [0.4.2] - 2025-01-26
_Administrative release, no functional changes_
[0.4.2]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.4.2
## [0.4.1] - 2024-07-31
### Changed
- **Breaking:** single implementation aliases are now explicit ([#408]) (George Dietrich)
### Fixed
- Fix default/nil values related to `object_of` and `array_of` being unavailable in bundle extensions ([#432]) (George Dietrich)
[0.4.1]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.4.1
[#408]: https://github.com/athena-framework/athena/pull/408
[#432]: https://github.com/athena-framework/athena/pull/432
## [0.4.0] - 2024-04-09
### Changed
- **Breaking:** remove `Clock`, `Console`, and `EventDispatcher` built-in integrations ([#337]) (George Dietrich)
- **Breaking:** major internal refactor ([#337], [#378]) (George Dietrich)
- **Breaking:** replace `ADI.auto_configure` with [ADI::Autoconfigure](https://athenaframework.org/DependencyInjection/Autoconfigure/) ([#387]) (George Dietrich)
- **Breaking:** replace `alias` `ADI::Register` field with [ADI::AsAlias](https://athenaframework.org/DependencyInjection/AsAlias/) ([#389]) (George Dietrich)
- Integrate website into monorepo ([#365]) (George Dietrich)
### Added
- Add ability to easily extend/customize the container ([#337], [#348], [#371], [#372], [#373], [#374], [#377], [#379], [#382], [#383]) (George Dietrich)
- Add ability to define method calls that should be made during service instantiation ([#384]) (George Dietrich)
- Add new [ADI::AutoconfigureTag](https://athenaframework.org/DependencyInjection/AutoconfigureTag/) and [ADI::TaggedIterator](https://athenaframework.org/DependencyInjection/TaggedIterator/) to make working with tagged services easier ([#387]) (George Dietrich)
- Add `ADI.configuration_annotation` to `Athena::DependencyInjection` from `Athena::Config` ([#392]) (George Dietrich)
[0.4.0]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.4.0
[#337]: https://github.com/athena-framework/athena/pull/337
[#348]: https://github.com/athena-framework/athena/pull/348
[#365]: https://github.com/athena-framework/athena/pull/365
[#371]: https://github.com/athena-framework/athena/pull/371
[#372]: https://github.com/athena-framework/athena/pull/372
[#373]: https://github.com/athena-framework/athena/pull/373
[#374]: https://github.com/athena-framework/athena/pull/374
[#377]: https://github.com/athena-framework/athena/pull/377
[#378]: https://github.com/athena-framework/athena/pull/378
[#379]: https://github.com/athena-framework/athena/pull/379
[#382]: https://github.com/athena-framework/athena/pull/382
[#383]: https://github.com/athena-framework/athena/pull/383
[#384]: https://github.com/athena-framework/athena/pull/384
[#387]: https://github.com/athena-framework/athena/pull/387
[#389]: https://github.com/athena-framework/athena/pull/389
[#392]: https://github.com/athena-framework/athena/pull/392
## [0.3.8] - 2023-12-16
### Fixed
- Avoid depending directly on Crystal macro types ([#335]) (George Dietrich)
[0.3.8]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.8
[#335]: https://github.com/athena-framework/athena/pull/335
## [0.3.7] - 2023-10-09
### Added
- Add integration between `Athena::DependencyInjection` and the `Athena::Clock` component ([#318]) (George Dietrich)
[0.3.7]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.7
[#318]: https://github.com/athena-framework/athena/pull/318
## [0.3.6] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
[0.3.6]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.6
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.3.5] - 2023-02-04
### Added
- Add better integration between `Athena::DependencyInjection` and the `Athena::Console` and `Athena::EventDispatcher` components ([#259]) (George Dietrich)
[0.3.5]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.5
[#259]: https://github.com/athena-framework/athena/pull/259
## [0.3.4] - 2023-01-07
### Changed
- Refactor various internal logic (George Dietrich)
[0.3.4]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.4
## [0.3.3] - 2022-05-14
_First release a part of the monorepo._
### Changed
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Added
- Add getting started documentation to API docs ([#172]) (George Dietrich)
[0.3.3]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.3
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
## [0.3.2] - 2021-10-30
### Changed
- Unused services are now excluded from the container ([#30]) (George Dietrich)
[0.3.2]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.2
[#30]: https://github.com/athena-framework/dependency-injection/pull/30
## [0.3.1] - 2021-03-28
### Fixed
- Fix error with untyped parameters with default values injecting ([#28]) (George Dietrich)
[0.3.1]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.1
[#28]: https://github.com/athena-framework/dependency-injection/pull/28
## [0.3.0] - 2021-03-20
### Added
- Allow injecting [configuration](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--configuration) into services ([#27]) (George Dietrich)
[0.3.0]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.3.0
[#27]: https://github.com/athena-framework/dependency-injection/pull/27
## [0.2.6] - 2021-03-15
### Added
- Allow using the `ADI::Inject` annotation on class methods to create [factories](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--factories) ([#25]) (George Dietrich)
[0.2.6]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.6
[#25]: https://github.com/athena-framework/dependency-injection/pull/25
## [0.2.5] - 2021-01-30
### Changed
- Migrate documentation to [MkDocs](https://mkdocstrings.github.io/crystal/) ([#23], [#24]) (George Dietrich)
[0.2.5]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.5
[#23]: https://github.com/athena-framework/dependency-injection/pull/23
[#24]: https://github.com/athena-framework/dependency-injection/pull/24
## [0.2.4] - 2021-01-29
### Added
- Add dependency on `athena-framework/config` ([#20]) (George Dietrich)
- Add support for injecting [parameters](https://athenaframework.org/architecture/config/#parameters) into a service ([#20]) (George Dietrich)
- Add support for [service proxies](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--service-proxies) ([#21]) (George Dietrich)
### Removed
- Remove the `lazy` `ADI::Register` field. All services are lazy by default now ([#21]) (George Dietrich)
### Fixed
- Fix issue building documentation ([#22]) (George Dietrich)
[0.2.4]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.4
[#20]: https://github.com/athena-framework/dependency-injection/pull/20
[#21]: https://github.com/athena-framework/dependency-injection/pull/21
[#22]: https://github.com/athena-framework/dependency-injection/pull/22
## [0.2.3] - 2020-12-24
### Fixed
- Fix error when a parameter has a default value after an array parameter ([#19]) (George Dietrich)
[0.2.3]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.3
[#19]: https://github.com/athena-framework/dependency-injection/pull/19
## [0.2.2] - 2020-12-03
### Changed
- Update `crystal` version to allow version greater than `1.0.0` ([#18]) (George Dietrich)
[0.2.2]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.2
[#18]: https://github.com/athena-framework/dependency-injection/pull/18
## [0.2.1] - 2020-11-14
### Added
- Add a mock container instance to allow mocking services ([#15]) (George Dietrich)
- Add ability to customize the type of a service within the container ([#15]) (George Dietrich)
- Add support for [factory pattern](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--factories) constructors ([#16]) (George Dietrich)
[0.2.1]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.1
[#15]: https://github.com/athena-framework/dependency-injection/pull/15
[#16]: https://github.com/athena-framework/dependency-injection/pull/16
## [0.2.0] - 2020-06-09
_Major refactor of the component._
### Added
- Add concept of [aliasing services](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--aliasing-services) ([#10]) (George Dietrich)
- Add concept of [binding values](https://athenaframework.org/DependencyInjection/#Athena::DependencyInjection:bind(key,value)) ([#10]) (George Dietrich)
- Add concept of [auto configuration](https://athenaframework.org/DependencyInjection/#Athena::DependencyInjection:auto_configure(type,options)) ([#10]) (George Dietrich)
- Add [ADI::Inject](https://athenaframework.org/DependencyInjection/Inject/) annotation ([#10]) (George Dietrich)
- Add support for [generic services](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--generic-services) ([#10]) (George Dietrich)
### Changed
- **Breaking:** manually provided arguments now need to be prefixed with a `_` ([#10]) (George Dietrich)
- **Breaking:** service names are now based on the `FQN` of the type, downcase underscored by default ([#10]) (George Dietrich)
- Updated [optional services](https://athenaframework.org/DependencyInjection/Register/#Athena::DependencyInjection::Register--optional-services) to now be based on the type/default value of the parameter ([#10]) (George Dietrich)
- Service dependencies are now resolved automatically, removes need to manually provide them ([#10]) (George Dietrich)
### Removed
- **Breaking:** remove the `ADI::Service` module ([#10]) (George Dietrich)
- **Breaking:** remove the `ADI::Injectable` module ([#10]) (George Dietrich)
- **Breaking:** remove the `@?` syntax ([#10]) (George Dietrich)
- **Breaking:** remove the `#get`, `#has`, `#resolve`, `#tagged`, and `#tags` methods from `ADI::ServiceContainer` ([#10]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.2.0
[#10]: https://github.com/athena-framework/dependency-injection/pull/10
## [0.1.3] - 2020-04-06
### Fixed
- Fix an edge case by checking includers via `<=` ([#7]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.1.3
[#7]: https://github.com/athena-framework/dependency-injection/pull/7
## [0.1.2] - 2020-02-22
### Changed
- Change type resolution logic to operate at compile time instead of runtime ([#6]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.1.2
[#6]: https://github.com/athena-framework/dependency-injection/pull/6
## [0.1.1] - 2020-02-06
### Added
- Add the ability to redefine services ([#4]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.1.1
[#4]: https://github.com/athena-framework/dependency-injection/pull/4
## [0.1.0] - 2020-01-31
_Initial release._
[0.1.0]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.1.0
================================================
FILE: .changes/dependency-injection/v0.4.4.md
================================================
## [0.4.4] - 2025-09-04
### Changed
- Relax DI argument validation for string parameters ([#548]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.4]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.4.4
[#548]: https://github.com/athena-framework/athena/pull/548
================================================
FILE: .changes/dependency-injection/v0.4.5.md
================================================
## [0.4.5] - 2026-04-19
### Changed
- Improve compile time error messages ([#646]) (George Dietrich) <!-- blacksmoke16 -->
- Reduce the amount of ivars within `ADI::ServiceContainer` ([#649]) (George Dietrich) <!-- blacksmoke16 -->
### Added
- Add ability to define schema configuration maps; with arbitrary keys, but structured values ([#641]) (George Dietrich) <!-- blacksmoke16 -->
- Add ability to define re-usable schema object types ([#641]) (George Dietrich) <!-- blacksmoke16 -->
- Add ability for aliases to take constructor parameter names into account ([#660]) (George Dietrich) <!-- blacksmoke16 -->
- Add support for nested `>>` doc markup when using `object_schema` ([#684]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Fix global extension schema `Enum` types not retaining their `::` prefix ([#639]) (George Dietrich) <!-- blacksmoke16 -->
- Fix falsey binding values not resolving ([#647]) (George Dietrich) <!-- blacksmoke16 -->
- Fix issue with using multiple extensions when one has a nested schema ([#658]) (George Dietrich) <!-- blacksmoke16 -->
- Fix service argument validation errors overriding schema validation errors ([#659]) (George Dietrich) <!-- blacksmoke16 -->
- Fix enum typed `object_schema` types not allowing symbol/number values ([#661]) (George Dietrich) <!-- blacksmoke16 -->
- Fix compile time error when inadvertently using a type name that conflicts with an internal component type ([#678]) (George Dietrich) <!-- blacksmoke16 -->
- Fix being unable to link to non top-level types within a nested properties' `>>` doc markup ([#684]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.5]: https://github.com/athena-framework/dependency-injection/releases/tag/v0.4.5
[#646]: https://github.com/athena-framework/athena/pull/646
[#649]: https://github.com/athena-framework/athena/pull/649
[#641]: https://github.com/athena-framework/athena/pull/641
[#660]: https://github.com/athena-framework/athena/pull/660
[#684]: https://github.com/athena-framework/athena/pull/684
[#639]: https://github.com/athena-framework/athena/pull/639
[#647]: https://github.com/athena-framework/athena/pull/647
[#658]: https://github.com/athena-framework/athena/pull/658
[#659]: https://github.com/athena-framework/athena/pull/659
[#661]: https://github.com/athena-framework/athena/pull/661
[#678]: https://github.com/athena-framework/athena/pull/678
================================================
FILE: .changes/dotenv/v0.2.0.md
================================================
## [0.2.0] - 2025-01-26
### Changed
- **Breaking:** Normalize exception types ([#428]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/dotenv/releases/tag/v0.2.0
[#428]: https://github.com/athena-framework/athena/pull/428
## [0.1.3] - 2024-07-31
### Changed
- Update minimum `crystal` version to `~> 1.13.0` ([#433]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/dotenv/releases/tag/v0.1.3
[#433]: https://github.com/athena-framework/athena/pull/433
## [0.1.2] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
### Added
- Add helper `Athena::Dotenv.load` method to create and load `.env` files in one call ([#363]) (George Dietrich)
### Fixed
- Fixed error parsing ENV vars starting with `_` ([#346]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/dotenv/releases/tag/v0.1.2
[#346]: https://github.com/athena-framework/athena/pull/346
[#363]: https://github.com/athena-framework/athena/pull/363
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.1.1] - 2023-10-09
_Administrative release, no functional changes_
[0.1.1]: https://github.com/athena-framework/dotenv/releases/tag/v0.1.1
## [0.1.0] - 2023-04-23
_Initial release._
[0.1.0]: https://github.com/athena-framework/dotenv/releases/tag/v0.1.0
================================================
FILE: .changes/dotenv/v0.2.1.md
================================================
## [0.2.1] - 2025-11-09
### Fixed
- Fix being unable to call `Athena::Dotenv.load` with a single file ([#609]) (George Dietrich) <!-- blacksmoke16 -->
[0.2.1]: https://github.com/athena-framework/dotenv/releases/tag/v0.2.1
[#609]: https://github.com/athena-framework/athena/pull/609
================================================
FILE: .changes/event-dispatcher/v0.3.1.md
================================================
## [0.3.1] - 2025-01-26
_Administrative release, no functional changes_
[0.3.1]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.3.1
## [0.3.0] - 2024-04-09
### Changed
- **Breaking:** remove `AED::EventListenerInterface` ([#391]) (George Dietrich)
- Integrate website into monorepo ([#365]) (George Dietrich)
[0.3.0]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.3.0
[#365]: https://github.com/athena-framework/athena/pull/365
[#391]: https://github.com/athena-framework/athena/pull/391
## [0.2.3] - 2023-10-09
_Administrative release, no functional changes_
[0.2.3]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.2.3
## [0.2.2] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
[0.2.2]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.2.2
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.2.1] - 2023-02-04
### Added
- Add better integration between `Athena::EventDispatcher` and `Athena::DependencyInjection` ([#259]) (George Dietrich)
[0.2.1]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.2.1
[#259]: https://github.com/athena-framework/athena/pull/259
## [0.2.0] - 2023-01-07
### Changed
- **Breaking:** refactor how listeners are registered to use the new `AEDA::AsEventListener` annotation on the method instead of the `self.subscribed_events` class method ([#236]) (George Dietrich)
- **Breaking:** refactor and rename the majority of `AED::EventDispatcherInterface` API ([#236]) (George Dietrich)
- **Breaking:** change the representation of a listener when returned from a dispatcher to be an `AED::Callable` instance ([#236]) (George Dietrich)
- **Breaking:** refactor `AED::Event` to now be `abstract` ([#236]) (George Dietrich)
### Added
- Add `AED::GenericEvent` that can be used for convenience within simple use cases ([#236]) (George Dietrich)
- Add the ability to use a listener method without the `AED::EventDispatcherInterface` parameter ([#236]) (George Dietrich)
### Removed
- **Breaking:** remove ability for listeners to automatically be registered with the dispatcher ([#236]) (George Dietrich)
- **Breaking:** remove the `AED::EventDispatcher.new` constructor that accepts an `Array(AED::EventListenerInterface)` ([#236]) (George Dietrich)
- **Breaking:** remove the `AED::EventListenerType` alias ([#236]) (George Dietrich)
- **Breaking:** remove the `AED::SubscribedEvents` alias ([#236]) (George Dietrich)
- **Breaking:** remove the `AED::EventListener` struct ([#236]) (George Dietrich)
- **Breaking:** remove the `AED.create_listener` method ([#236]) (George Dietrich)
- Remove the requirement that listeners methods need to be called `call` ([#236]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.2.0
[#236]: https://github.com/athena-framework/athena/pull/236
## [0.1.4] - 2022-05-14
_First release a part of the monorepo._
### Added
- Add getting started documentation to API docs ([#172]) (George Dietrich)
### Changed
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Fixed
- Fix the `VERSION` constant's value ([#166]) (George Dietrich)
[0.1.4]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.1.4
[#166]: https://github.com/athena-framework/athena/pull/166
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
## [0.1.3] - 2021-01-29
### Changed
- Migrate documentation to [MkDocs](https://mkdocstrings.github.io/crystal/) ([#14]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.1.3
[#14]: https://github.com/athena-framework/event-dispatcher/pull/14
## [0.1.2] - 2020-12-03
### Changed
- Update `crystal` version to allow version greater than `1.0.0` ([#13]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.1.2
[#13]: https://github.com/athena-framework/event-dispatcher/pull/13
## [0.1.1] - 2020-11-12
### Added
- Add the [AED::Spec](https://athenaframework.org/EventDispatcher/Spec/) module to provide helpful testing utilities ([#11]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.1.1
[#11]: https://github.com/athena-framework/event-dispatcher/pull/11
## [0.1.0] - 2020-01-11
_Initial release._
[0.1.0]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.1.0
================================================
FILE: .changes/event-dispatcher/v0.4.0.md
================================================
## [0.4.0] - 2025-09-04
### Changed
- **Breaking:** Changed interface of `AED::EventDispatcherInterface#dispatch` to accept an `ACTR::EventDispatcher::Event` vs `AED::Event` ([#544]) (George Dietrich) <!-- blacksmoke16 -->
### Removed
- Removed `AED::StoppableEvent` in favor of `ACTR::EventDispatcher::StoppableEvent` ([#544]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.0]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.4.0
[#544]: https://github.com/athena-framework/athena/pull/544
================================================
FILE: .changes/event-dispatcher/v0.4.1.md
================================================
## [0.4.1] - 2026-04-19
### Changed
- Improve compile time error messages ([#646]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Fix compatibility with `ACTR::EventDispatcher::Event` based event types ([#656]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.1]: https://github.com/athena-framework/event-dispatcher/releases/tag/v0.4.1
[#646]: https://github.com/athena-framework/athena/pull/646
[#656]: https://github.com/athena-framework/athena/pull/656
================================================
FILE: .changes/framework/v0.20.1.md
================================================
## [0.20.1] - 2025-02-08
### Fixed
- Fix `ATH::ViewHandler` bundle configuration values not being correctly set ([#520]) (George Dietrich)
[0.20.1]: https://github.com/athena-framework/framework/releases/tag/v0.20.1
[#520]: https://github.com/athena-framework/athena/pull/520
## [0.20.0] - 2025-01-26
### Changed
- **Breaking:** Normalize exception types ([#428]) (George Dietrich)
- **Breaking:** The `ATHR::Interface.configuration` macro is no longer scoped to the resolver namespace ([#425]) (George Dietrich)
- **Breaking:** Rename `ATHR::RequestBody::Extract` to `ATHA::MapRequestBody` ([#425]) (George Dietrich)
- **Breaking:** Rename `ATHR::Time::Format` to `ATHA::MapTime` ([#425]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.14.0` ([#433]) (George Dietrich)
- Refactor auto redirection logic to be more robust ([#436], [#480]) (George Dietrich)
- Refactor `ATHR::RequestBody` to raise more accurate deserialization errors ([#490]) (George Dietrich)
### Added
- Add support for [Proxies & Load Balancers](https://athenaframework.org/guides/proxies/) ([#440], [#444]) (George Dietrich)
- Add new `trusted_host` bundle scheme property to allow setting trusted hostnames ([#474]) (George Dietrich)
- Add support for deserializing `application/x-www-form-urlencoded` bodies via `ATHA::MapRequestBody` ([#477]) (George Dietrich)
- Add `ATHA::MapQueryString` to map a request's query string into a DTO type ([#477]) (George Dietrich)
- Add `ATH::Exception.from_status` helper method ([#426]) (George Dietrich)
- Add `ATHA::MapQueryParameter` for handling query parameters ([#426]) (George Dietrich)
- Add `#validation_groups` and `#accept_formats` annotation properties to `ATHA::MapRequestBody` ([#486]) (George Dietrich)
- Add `#validation_groups` annotation property to `ATHA::MapQueryString` ([#486]) (George Dietrich)
- Add `ATH::Request#port` and `ATH::Response#redirect?` methods ([#436]) (George Dietrich)
- Add `#host`, `#scheme`, `#secure?`, and `#from_trusted_proxy?` methods to `ATH::Request` ([#440]) (George Dietrich)
- Add `ATH::Request#content_type_format` to return the request format's name from its `content-type` header ([#477]) (George Dietrich)
- Add `ATH::IPUtils` module ([#440]) (George Dietrich)
- Add `.unquote`, `.split`, and `.combine` methods `ATH::HeaderUtils` ([#440]) (George Dietrich)
- Add request matchers for headers and query parameters ([#491]) (George Dietrich)
### Removed
- **Breaking:** Remove `ATHA::QueryParam` ([#426]) (George Dietrich)
- **Breaking:** Remove `ATHA::RequestParam` ([#426]) (George Dietrich)
- **Breaking:** Remove `ATH::Exception::InvalidParameter` ([#426]) (George Dietrich)
- **Breaking:** Remove everything within `ATH::Params` namespace ([#426]) (George Dietrich)
- **Breaking:** Remove `ATH::Action#params` ([#426]) (George Dietrich)
- **Breaking:** Remove `ATH::Listeners::ParamFetcher` ([#426]) (George Dietrich)
### Fixed
- Fix query parameters being dropped when redirecting to a trailing/non-trailing slash endpoint ([#436]) (George Dietrich)
- Fix auto redirection with non-standard ports ([#480]) (George Dietrich)
- Fix `multipart/form-data` not being mapped to the `form` format ([#441]) (George Dietrich)
- Fix being unable to provide the path of an `ARTA::Route` annotation on a class as a positional argument ([#482]) (George Dietrich)
- Fix error when attempting to use `ATH::Controller#redirect_view` and `ATH::Controller#route_redirect_view` ([#498]) (George Dietrich)
- Fix error when attempting to use `ATH::Spec::APITestCase#unlink` ([#498]) (George Dietrich)
[0.20.0]: https://github.com/athena-framework/framework/releases/tag/v0.20.0
[#425]: https://github.com/athena-framework/athena/pull/425
[#426]: https://github.com/athena-framework/athena/pull/426
[#428]: https://github.com/athena-framework/athena/pull/428
[#433]: https://github.com/athena-framework/athena/pull/433
[#436]: https://github.com/athena-framework/athena/pull/436
[#440]: https://github.com/athena-framework/athena/pull/440
[#441]: https://github.com/athena-framework/athena/pull/441
[#444]: https://github.com/athena-framework/athena/pull/444
[#474]: https://github.com/athena-framework/athena/pull/474
[#477]: https://github.com/athena-framework/athena/pull/477
[#480]: https://github.com/athena-framework/athena/pull/480
[#482]: https://github.com/athena-framework/athena/pull/482
[#486]: https://github.com/athena-framework/athena/pull/486
[#490]: https://github.com/athena-framework/athena/pull/490
[#491]: https://github.com/athena-framework/athena/pull/491
[#498]: https://github.com/athena-framework/athena/pull/498
## [0.19.2] - 2024-07-31
### Added
- Add `ATH.run_console` as an easier entrypoint into the console application ([#413]) (George Dietrich)
- Add support for additional boolean conversion values from request attributes ([#422]) (George Dietrich)
### Changed
- **Breaking:** `ATH::RequestMatcher::Method` now requires an `Array(String)` as opposed to any `Enumerable(String)` ([#431]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.13.0` ([#433]) (George Dietrich)
- Updates usages of `UTF-8` in response headers to `utf-8` as preferred by the RFC ([#417]) (George Dietrich)
### Fixed
- Fix the content negotiation implementation not working ([#431]) (George Dietrich)
[0.19.2]: https://github.com/athena-framework/framework/releases/tag/v0.19.2
[#413]: https://github.com/athena-framework/athena/pull/413
[#417]: https://github.com/athena-framework/athena/pull/417
[#422]: https://github.com/athena-framework/athena/pull/422
[#431]: https://github.com/athena-framework/athena/pull/431
[#433]: https://github.com/athena-framework/athena/pull/433
## [0.19.1] - 2024-04-27
### Fixed
- Fix `framework` component docs landing on an empty page ([#399]) (George Dietrich)
- Fix `Athena::Clock` not being aliased to the interface correctly ([#400]) (George Dietrich)
- Fix `ATHA::View` annotation being defined in incorrect namespace ([#403]) (George Dietrich)
- Fix `ATH::ErrorRenderer` not being aliased to the interface correctly ([#404]) (George Dietrich)
[0.19.1]: https://github.com/athena-framework/framework/releases/tag/v0.19.1
[#399]: https://github.com/athena-framework/athena/pull/399
[#400]: https://github.com/athena-framework/athena/pull/400
[#403]: https://github.com/athena-framework/athena/pull/403
[#404]: https://github.com/athena-framework/athena/pull/404
## [0.19.0] - 2024-04-09
### Changed
- **Breaking:** change how framework features are configured ([#337], [#374], [#383]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.11.0` ([#270]) (George Dietrich)
- Integrate website into monorepo ([#365]) (George Dietrich)
### Added
- Support for Windows OS ([#270]) (George Dietrich)
- Add `ATH::RequestMatcher` as a generic way of matching an `ATH::Request` given a set of rules ([#338]) (George Dietrich)
- Raise an exception if a controller's return value fails to serialize instead of just returning `nil` ([#357]) (George Dietrich)
- Add support for new Crystal 1.12 `Process.on_terminate` method ([#394]) (George Dietrich)
### Fixed
- Fix macro splat deprecation ([#330]) (George Dietrich)
- Normalize `ATH::Request#method` to always be uppercase ([#338]) (George Dietrich)
- Fixed not being able to use top level configuration annotations on controller action parameters ([#356]) (George Dietrich)
[0.19.0]: https://github.com/athena-framework/framework/releases/tag/v0.19.0
[#270]: https://github.com/athena-framework/athena/pull/270
[#330]: https://github.com/athena-framework/athena/pull/330
[#337]: https://github.com/athena-framework/athena/pull/337
[#338]: https://github.com/athena-framework/athena/pull/338
[#356]: https://github.com/athena-framework/athena/pull/356
[#357]: https://github.com/athena-framework/athena/pull/357
[#365]: https://github.com/athena-framework/athena/pull/365
[#374]: https://github.com/athena-framework/athena/pull/374
[#383]: https://github.com/athena-framework/athena/pull/383
[#394]: https://github.com/athena-framework/athena/pull/394
## [0.18.2] - 2023-10-09
### Changed
- Change routing logic to redirect `GET` and `HEAD` requests with a trailing slash to the route without one if it exists, and vice versa ([#307]) (George Dietrich)
### Added
- Add native tab completion support to the built-in `ATH::Commands` ([#296]) (George Dietrich)
- Add support for defining multiple route annotations on a single controller action method ([#315]) (George Dietrich)
- Require the new `Athena::Clock` component ([#318]) (George Dietrich)
- Add additional `ATH::Spec::APITestCase` request helper methods ([#312], [#313]) (George Dietrich)
### Fixed
- Fix incorrectly generated route paths with a controller level prefix and no action level `/` prefix ([#308]) (George Dietrich)
[0.18.2]: https://github.com/athena-framework/framework/releases/tag/v0.18.2
[#296]: https://github.com/athena-framework/athena/pull/296
[#307]: https://github.com/athena-framework/athena/pull/307
[#308]: https://github.com/athena-framework/athena/pull/308
[#312]: https://github.com/athena-framework/athena/pull/312
[#313]: https://github.com/athena-framework/athena/pull/313
[#315]: https://github.com/athena-framework/athena/pull/315
[#318]: https://github.com/athena-framework/athena/pull/318
## [0.18.1] - 2023-05-29
### Added
- Add support for serializing arbitrarily nested controller action return types ([#273]) (George Dietrich)
- Allow using constants for controller action's `path` ([#279]) (George Dietrich)
### Fixed
- Fix incorrect `content-length` header value when returning multi-byte strings ([#288]) (George Dietrich)
[0.18.1]: https://github.com/athena-framework/framework/releases/tag/v0.18.1
[#273]: https://github.com/athena-framework/athena/pull/273
[#279]: https://github.com/athena-framework/athena/pull/279
[#288]: https://github.com/athena-framework/athena/pull/288
## [0.18.0] - 2023-02-20
### Changed
- **Breaking:** upgrade [Athena::EventDispatcher](https://athenaframework.org/EventDispatcher/) to [0.2.x](https://github.com/athena-framework/event-dispatcher/blob/master/CHANGELOG.md#020---2023-01-07) ([#205]) (George Dietrich)
- **Breaking:** deprecate the `ATH::ParamConverter` concept in favor of [Value Resolvers](https://athenaframework.org/Framework/Controller/ValueResolvers/Interface) ([#243]) (George Dietrich)
- **Breaking:** rename various types/methods to better adhere to https://github.com/crystal-lang/crystal/issues/10374 ([#243]) (George Dietrich)
- **Breaking:** Change `ATH::Spec::AbstractBrowser` to be a `class` ([#249]) (George Dietrich)
- **Breaking:** upgrade [Athena::Validator](https://athenaframework.org/Validator/) to [0.3.x](https://github.com/athena-framework/validator/blob/master/CHANGELOG.md#030---2023-01-07) ([#250]) (George Dietrich)
- Improve service `ATH::Controller`s to not need the `public: true` `ADI::Register` field ([#213]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.6.0` ([#205]) (George Dietrich)
### Added
- Add trace logging to `ATH::Listeners::CORS` to aid in debugging ([#265]) (George Dietrich)
- Introduce new `framework.debug` parameter that is `true` if the binary was _not_ built with the `--release` flag ([#249]) (George Dietrich)
- Add built-in [HTTP Expectation](https://athenaframework.org/Framework/Spec/Expectations/HTTP) methods to `ATH::Spec::WebTestCase` ([#249]) (George Dietrich)
- Add `#response` and `#request` methods to `ATH::Spec::AbstractBrowser` types ([#249]) (George Dietrich)
- Add [ATHR](https://athenaframework.org/Framework/aliases/#ATHR) alias to make using value resolver annotations easier ([#243]) (George Dietrich)
- Add [ATH::Commands::Commands::DebugEventDispatcher](https://athenaframework.org/Framework/Commands/DebugEventDispatcher) framework CLI command to aid in debugging the event dispatcher ([#241]) (George Dietrich)
- Add [ATH::Commands::Commands::DebugRouter](https://athenaframework.org/Framework/Commands/DebugRouter) and [ATH::Commands::Commands::DebugRouterMatch](https://athenaframework.org/Framework/Commands/DebugRouterMatch) framework CLI commands to aid in debugging the router ([#224]) (George Dietrich)
- Add integration for the [Athena::Console](https://athenaframework.org/Console/) component ([#218]) (George Dietrich)
### Fixed
- Correctly populate `content-length` based on the response content's size ([#267]) (George Dietrich)
- Prevent wildcard CORS `expose_headers` value when `allow_credentials` is `true` ([#264]) (George Dietrich)
- Correctly handle `JSON::Serializable` values within `Hash`/`NamedTuple` controller action return types ([#253]) (George Dietrich)
- Fix [ATH::ParameterBag#get?](https://athenaframework.org/Framework/ParameterBag/#Athena::Framework::ParameterBag#get?(name,_type)) not returning `nil` if it could not convert the value to the desired type ([#243]) (George Dietrich)
[0.18.0]: https://github.com/athena-framework/framework/releases/tag/v0.18.0
[#205]: https://github.com/athena-framework/athena/pull/205
[#213]: https://github.com/athena-framework/athena/pull/213
[#218]: https://github.com/athena-framework/athena/pull/218
[#224]: https://github.com/athena-framework/athena/pull/224
[#241]: https://github.com/athena-framework/athena/pull/241
[#243]: https://github.com/athena-framework/athena/pull/243
[#249]: https://github.com/athena-framework/athena/pull/249
[#250]: https://github.com/athena-framework/athena/pull/250
[#253]: https://github.com/athena-framework/athena/pull/253
[#264]: https://github.com/athena-framework/athena/pull/264
[#265]: https://github.com/athena-framework/athena/pull/265
[#267]: https://github.com/athena-framework/athena/pull/267
## [0.17.1] - 2022-09-05
### Changed
- **Breaking:** ensure parameter names defined on interfaces match the implementation ([#188]) (George Dietrich)
[0.17.1]: https://github.com/athena-framework/framework/releases/tag/v0.17.1
[#188]: https://github.com/athena-framework/athena/pull/188
## [0.17.0] - 2022-05-14
_Checkout [this](https://forum.crystal-lang.org/t/athena-0-17-0/4624) forum thread for an overview of changes within the ecosystem._
### Added
- Add `pcre2` library dependency to `shard.yml` ([#159]) (George Dietrich)
- Add [ATH::Arguments::Resolvers::Enum](https://athenaframework.org/Framework/Arguments/Resolvers/Enum/) to allow resolving `Enum` members directly to controller actions ([#173]) (George Dietrich)
- Add [ATH::Arguments::Resolvers::UUID](https://athenaframework.org/Framework/Arguments/Resolvers/UUID/) to allow resolving `UUID`s directly to controller actions by ([#176]) (George Dietrich)
- Add [ATH::ParameterBag#has(name, type)](https://athenaframework.org/Framework/ParameterBag/#Athena::Framework::ParameterBag#has?(name,type)) that checks if a parameter with the provided name exists, and that is of the provided type ([#176]) (George Dietrich)
- Add [ATH::Arguments::Resolvers::DefaultValue](https://athenaframework.org/Framework/Arguments/Resolvers/DefaultValue/) to allow resolving an action parameter's default value if no other value was provided ([#177]) (George Dietrich)
### Changed
- **Breaking:** rename `ATH::Arguments::Resolvers::ArgumentValueResolverInterface` to `ATH::Arguments::Resolvers::Interface` ([#176]) (George Dietrich)
- **Breaking:** bump `athena-framework/serializer` to `~> 0.3.0` ([#181]) (George Dietrich)
- **Breaking:** bump `athena-framework/validator` to `~> 0.2.0` ([#181]) (George Dietrich)
- Expose the default value of an [ATH::Arguments::ArgumentMetadata](https://athenaframework.org/Framework/Arguments/ArgumentMetadata/) ([#176]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Fixed
- Fix error when two controller share a common action name ([#146]) (George Dietrich)
- Fix release badge to use correct repo ([#161]) (George Dietrich)
- Fix query/request param docs to use new error responses ([#167]) (George Dietrich)
- Fix incorrect `Athena::Framework` `Log` name ([#175]) (George Dietrich)
[0.17.0]: https://github.com/athena-framework/framework/releases/tag/v0.17.0
[#146]: https://github.com/athena-framework/athena/pull/146
[#159]: https://github.com/athena-framework/athena/pull/159
[#161]: https://github.com/athena-framework/athena/pull/161
[#167]: https://github.com/athena-framework/athena/pull/167
[#169]: https://github.com/athena-framework/athena/pull/169
[#173]: https://github.com/athena-framework/athena/pull/173
[#175]: https://github.com/athena-framework/athena/pull/175
[#176]: https://github.com/athena-framework/athena/pull/176
[#177]: https://github.com/athena-framework/athena/pull/177
[#181]: https://github.com/athena-framework/athena/pull/181
## [0.16.0] - 2022-01-22
_First release in the [athena-framework/framework](https://github.com/athena-framework/framework) repo, post monorepo._
### Added
- Add dependency on `athena-framework/routing` ([#141]) (George Dietrich)
- Allow prepending [HTTP::Handlers](https://crystal-lang.org/api/HTTP/Handler.html) to the Athena server ([#133]) (George Dietrich)
- Add common HTTP methods (get, post, put, delete) to [ATH::Spec::APITestCase](https://athenaframework.org//Framework/Spec/APITestCase/#Athena::Framework::Spec::APITestCase-methods) ([#134]) (George Dietrich)
- Add overload of [ATH::Spec::APITestCase#request](https://athenaframework.org/Framework/Spec/APITestCase/#Athena::Framework::Spec::APITestCase#request(method,path,body,headers)) that accepts an [ATH::Request](https://athenaframework.org/Framework/Request/) or [HTTP::Request](https://crystal-lang.org/api/HTTP/Request.html) ([#134]) (George Dietrich)
- Allow running an HTTPS server via passing an [OpenSSL::SSL::Context::Server](https://crystal-lang.org/api/OpenSSL/SSL/Context/Server.html) to `ATH.run` ([#135], [#136]) (George Dietrich)
- Add [ATH::ParameterBag#set(hash)](https://athenaframework.org/Framework/ParameterBag/#Athena::Framework::ParameterBag#set(name,value,type)) that allows setting a hash of key/value pairs ([#141]) (George Dietrich)
### Changed
- **Breaking:** integrate the [Athena::Routing](https://athenaframework.org/Routing/) component ([#141]) (George Dietrich)
### Removed
- **Breaking:** remove dependency on [amberframework/amber-router](https://github.com/amberframework/amber-router) ([#141]) (George Dietrich)
[0.16.0]: https://github.com/athena-framework/framework/releases/tag/v0.16.0
[#133]: https://github.com/athena-framework/athena/pull/133
[#134]: https://github.com/athena-framework/athena/pull/134
[#135]: https://github.com/athena-framework/athena/pull/135
[#136]: https://github.com/athena-framework/athena/pull/136
[#141]: https://github.com/athena-framework/athena/pull/141
## [0.15.1] - 2021-12-13
### Changed
- Include error list in `ATH::Exception::InvalidParameter` ([#124]) (George Dietrich)
- Set the base path of parameter errors to the name of the parameter ([#124]) (George Dietrich)
[0.15.1]: https://github.com/athena-framework/athena/releases/tag/v0.15.1
[#124]: https://github.com/athena-framework/athena/pull/124
## [0.15.0] - 2021-10-30
_Last release in the [athena-framework/athena](https://github.com/athena-framework/athena) repo, pre monorepo._
### Added
- Expose the raw [HTTP::Request](https://crystal-lang.org/api/HTTP/Request.html) method from an `ATH::Request` ([#115]) (George Dietrich)
- Add built in [ATH::RequestBodyConverter](https://athenaframework.org/Framework/RequestBodyConverter) param converter ([#116]) (George Dietrich)
- Add `VERSION` constant to `Athena::Framework` namespace ([#120]) (George Dietrich)
### Changed
- **Breaking:** rename base param converter type to `ATH::ParamConverter` and make it a class ([#116]) (George Dietrich)
- **Breaking:** rename the component from `Athena::Routing` to `Athena::Framework` ([#120]) (George Dietrich)
### Fixed
- Fix incorrect parameter type restriction on `ATH::ParameterBag#set` ([#116]) (George Dietrich)
- Fix incorrect ivar type on `AVD::Exception::Exceptions::ValidationFailed#violations` ([#116]) (George Dietrich)
- Correctly reject requests with whitespace when converting numeric inputs ([#117]) (George Dietrich)
[0.15.0]: https://github.com/athena-framework/athena/releases/tag/v0.15.0
[#115]: https://github.com/athena-framework/athena/pull/115
[#116]: https://github.com/athena-framework/athena/pull/116
[#117]: https://github.com/athena-framework/athena/pull/117
[#120]: https://github.com/athena-framework/athena/pull/120
================================================
FILE: .changes/framework/v0.21.0.md
================================================
## [0.21.0] - 2025-09-04
### Changed
- **Breaking:** Leverage `ATH::AbstractFile` within `ATH::BinaryFileResponse` ([#563]) (George Dietrich) <!-- blacksmoke16 -->
- Leverage `mime` component within `ATH::BinaryFileResponse` ([#545]) (George Dietrich) <!-- blacksmoke16 -->
- Setter methods on `ATH::Response` and subclasses now return `self` to better support method chaining ([#563]) (George Dietrich) <!-- blacksmoke16 -->
### Added
- Add support for Athena Contract component types ([#544]) (George Dietrich) <!-- blacksmoke16 -->
- Add native file upload support ([#559]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Correctly apply `emit_nil` value from `ATHA::View` ([#526]) (George Dietrich) <!-- blacksmoke16 -->
[0.21.0]: https://github.com/athena-framework/framework/releases/tag/v0.21.0
[#545]: https://github.com/athena-framework/athena/pull/545
[#563]: https://github.com/athena-framework/athena/pull/563
[#544]: https://github.com/athena-framework/athena/pull/544
[#559]: https://github.com/athena-framework/athena/pull/559
[#526]: https://github.com/athena-framework/athena/pull/526
================================================
FILE: .changes/framework/v0.21.1.md
================================================
## [0.21.1] - 2025-10-04
### Fixed
- Fix improper handling of optional file uploads ([#595]) (George Dietrich) <!-- blacksmoke16 -->
[0.21.1]: https://github.com/athena-framework/framework/releases/tag/v0.21.1
[#595]: https://github.com/athena-framework/athena/pull/595
================================================
FILE: .changes/framework/v0.22.0.md
================================================
## [0.22.0] - 2026-04-19
### Changed
- **Breaking:** Store `ATH::Action` within `ATH::Request#attributes` instead of within an ivar ([#636]) (George Dietrich) <!-- blacksmoke16 -->
- **Breaking:** Extract out HTTP related `framework` types into the new `http` component ([#640]) (George Dietrich) <!-- blacksmoke16 -->
- **Breaking:** Extract out Request/Response handling related `framework` types into the new `http_kernel` component ([#657]) (George Dietrich) <!-- blacksmoke16 -->
- **Breaking:** Refactor how annotations are fetched off an action/parameter ([#655]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Fix CORS error when using HTTP/2 but providing uppercase header names ([#670]) (George Dietrich) <!-- blackmsoke16 -->
- Fix compile time error when inadvertently using a type name that conflicts with an internal component type ([#678]) (George Dietrich) <!-- blacksmoke16 -->
[0.22.0]: https://github.com/athena-framework/framework/releases/tag/v0.22.0
[#636]: https://github.com/athena-framework/athena/pull/636
[#640]: https://github.com/athena-framework/athena/pull/640
[#657]: https://github.com/athena-framework/athena/pull/657
[#655]: https://github.com/athena-framework/athena/pull/655
[#670]: https://github.com/athena-framework/athena/pull/670
[#678]: https://github.com/athena-framework/athena/pull/678
================================================
FILE: .changes/header.tpl.md
================================================
# Changelog
================================================
FILE: .changes/http/v0.1.0.md
================================================
## [0.1.0] - 2026-04-19
_Initial release._
[0.1.0]: https://github.com/athena-framework/http/releases/tag/v0.1.0
================================================
FILE: .changes/http-kernel/v0.1.0.md
================================================
## [0.1.0] - 2026-04-19
_Initial release._
[0.1.0]: https://github.com/athena-framework/http-kernel/releases/tag/v0.1.0
================================================
FILE: .changes/image-size/v0.1.4.md
================================================
## [0.1.4] - 2025-01-26
_Administrative release, no functional changes_
[0.1.4]: https://github.com/athena-framework/image-size/releases/tag/v0.1.4
## [0.1.3] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/image-size/releases/tag/v0.1.3
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.1.2] - 2023-10-09
_Administrative release, no functional changes_
[0.1.2]: https://github.com/athena-framework/image-size/releases/tag/v0.1.2
## [0.1.1] - 2022-05-14
_First release a part of the monorepo._
### Added
- Add getting started documentation to API docs ([#172]) (George Dietrich)
### Changed
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Fixed
- Fix incorrect `description` key in `shard.yml` ([#171]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/image-size/releases/tag/v0.1.1
[#169]: https://github.com/athena-framework/athena/pull/169
[#171]: https://github.com/athena-framework/athena/pull/171
[#172]: https://github.com/athena-framework/athena/pull/172
## [0.1.0] - 2022-02-21
_Initial release._
[0.1.0]: https://github.com/athena-framework/image-size/releases/tag/v0.1.0
================================================
FILE: .changes/mercure/v0.1.0.md
================================================
## [0.1.0] - 2026-04-19
_Initial release._
[0.1.0]: https://github.com/athena-framework/mercure/releases/tag/v0.1.0
================================================
FILE: .changes/mercure-bundle/v0.1.0.md
================================================
## [0.1.0] - 2026-04-19
_Initial release._
[0.1.0]: https://github.com/athena-framework/mercure-bundle/releases/tag/v0.1.0
================================================
FILE: .changes/mime/v0.2.0.md
================================================
## [0.2.0] - 2025-05-14
### Added
- **Breaking:** Add `AMIME::Types` to more robustly handles MIME type/file extension/guessing ([#534]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/mime/releases/tag/v0.2.0
[#534]: https://github.com/athena-framework/athena/pull/534
## [0.1.0] - 2025-01-26
_Initial release._
[0.1.0]: https://github.com/athena-framework/mime/releases/tag/v0.1.0
================================================
FILE: .changes/mime/v0.2.1.md
================================================
## [0.2.1] - 2025-09-04
### Added
- Add fallback MIME types guesser based on stdlib `MIME` module ([#546]) (George Dietrich) <!-- blacksmoke16 -->
[0.2.1]: https://github.com/athena-framework/mime/releases/tag/v0.2.1
[#546]: https://github.com/athena-framework/athena/pull/546
================================================
FILE: .changes/negotiation/v0.2.0.md
================================================
## [0.2.0] - 2025-01-26
### Changed
- **Breaking:** Normalize exception types ([#428]) (George Dietrich)
- Use lowercase `utf-8` within header values ([#417]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.13.0` ([#428]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/negotiation/releases/tag/v0.2.0
[#417]: https://github.com/athena-framework/athena/pull/417
[#428]: https://github.com/athena-framework/athena/pull/428
## [0.1.5] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
[0.1.5]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.5
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.1.4] - 2023-10-09
_Administrative release, no functional changes_
[0.1.4]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.4
## [0.1.3] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.3
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.1.2] - 2022-05-14
_First release a part of the monorepo._
### Added
- Add `VERSION` constant to `Athena::Negotiation` namespace ([#166]) (George Dietrich)
- Add getting started documentation to API docs ([#172]) (George Dietrich)
### Changed
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Fixed
- Correct the shard version in `README.md` ([#6]) (syeopite)
[0.1.2]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.2
[#6]: https://github.com/athena-framework/negotiation/pull/6
[#166]: https://github.com/athena-framework/athena/pull/166
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
## [0.1.1] - 2021-02-04
### Changed
- Migrate documentation to [MkDocs](https://mkdocstrings.github.io/crystal/) ([#4]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.1
[#4]: https://github.com/athena-framework/negotiation/pull/4
## [0.1.0] - 2020-12-24
_Initial release._
[0.1.0]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.0
================================================
FILE: .changes/routing/v0.1.10.md
================================================
## [0.1.10] - 2025-01-26
### Changed
- Allow having multiple independent compiled route collections ([#468]) (George Dietrich)
- Log unhandled `ART::RoutingHandler` exceptions ([#470]) (George Dietrich)
### Fixed
- Make `ART::RequestContext.from_uri` more robust ([#498]) (George Dietrich)
[0.1.10]: https://github.com/athena-framework/routing/releases/tag/v0.1.10
[#468]: https://github.com/athena-framework/athena/pull/468
[#470]: https://github.com/athena-framework/athena/pull/470
[#498]: https://github.com/athena-framework/athena/pull/498
## [0.1.9] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
### Added
- **Breaking:** add kwargs overload to `ART::Generator::Interface#generate` ([#375]) (George Dietrich)
### Fixed
- Fix compatibility with PCRE2 10.43 ([#362]) (George Dietrich)
- Fix error when PCRE2 JIT mode is unavailable ([#381]) (George Dietrich)
[0.1.9]: https://github.com/athena-framework/routing/releases/tag/v0.1.9
[#362]: https://github.com/athena-framework/athena/pull/362
[#365]: https://github.com/athena-framework/athena/pull/365
[#375]: https://github.com/athena-framework/athena/pull/375
[#381]: https://github.com/athena-framework/athena/pull/381
## [0.1.8] - 2023-10-09
### Added
- Internal support for redirecting within an `ART::Matcher::*` ([#307]) (George Dietrich)
[0.1.8]: https://github.com/athena-framework/routing/releases/tag/v0.1.8
[#307]: https://github.com/athena-framework/athena/pull/307
## [0.1.7] - 2023-05-29
### Changed
- **Breaking:** Update minimum `crystal` version to `~> 1.8.0`. Drop support for `PCRE1`. ([#281]) (George Dietrich)
[0.1.7]: https://github.com/athena-framework/routing/releases/tag/v0.1.7
[#281]: https://github.com/athena-framework/athena/pull/281
## [0.1.6] - 2023-03-26
### Fixed
- Fix compatibility with Crystal `1.8.0-dev` ([#272]) (George Dietrich)
[0.1.6]: https://github.com/athena-framework/routing/releases/tag/v0.1.6
[#272]: https://github.com/athena-framework/athena/pull/272
## [0.1.5] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
### Added
- Add additional `ART::Requirement` constants ([#257]) (George Dietrich)
### Fixed
- Fix formatting issue in Crystal `1.8-dev` ([#258]) (George Dietrich)
[0.1.5]: https://github.com/athena-framework/routing/releases/tag/v0.1.5
[#257]: https://github.com/athena-framework/athena/pull/257
[#258]: https://github.com/athena-framework/athena/pull/258
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.1.4] - 2023-01-07
### Changed
- Change route compilation to be eager ([#207]) (George Dietrich)
### Added
- Add ability to bubble up exceptions from `ART::RoutingHandler` ([#206]) (George Dietrich)
- Add `ART::Matcher::TraceableURLMatcher` to help with debugging route matches ([#224]) (George Dietrich)
- Add `ART::Route#has_scheme?` ([#224]) (George Dietrich)
[0.1.4]: https://github.com/athena-framework/routing/releases/tag/v0.1.4
[#207]: https://github.com/athena-framework/athena/pull/207
[#206]: https://github.com/athena-framework/athena/pull/206
[#224]: https://github.com/athena-framework/athena/pull/224
## [0.1.3] - 2022-09-05
### Changed
- **Breaking:** ensure parameter names defined on interfaces match the implementation ([#188]) (George Dietrich)
### Added
- Add an `HTTP::Handler` to add basic routing support to a `HTTP::Server` ([#189]) (George Dietrich)
### Fixed
- Fixed slash characters being double escaped in generated URL query params ([#180]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/routing/releases/tag/v0.1.3
[#180]: https://github.com/athena-framework/athena/pull/180
[#188]: https://github.com/athena-framework/athena/pull/188
[#189]: https://github.com/athena-framework/athena/pull/189
## [0.1.2] - 2022-05-14
### Changed
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Added
- Add getting started documentation to API docs ([#172]) (George Dietrich)
- Add common route requirement constants to the [ART::Requirement](https://athenaframework.org/Routing/Requirement/) namespace ([#173]) (George Dietrich)
- Add [ART::Requirement::Enum](https://athenaframework.org/Routing/Requirement/Enum/) to make creating [Enum](https://crystal-lang.org/api/Enum.html) based route requirements easier ([#173]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/routing/releases/tag/v0.1.2
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
[#173]: https://github.com/athena-framework/athena/pull/173
## [0.1.1] - 2022-02-05
_First release a part of the monorepo._
### Fixed
- Fix erroneous mutating of matched route data ([#144]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/routing/releases/tag/v0.1.1
[#144]: https://github.com/athena-framework/athena/pull/144
## [0.1.0] - 2022-01-10
_Initial release._
[0.1.0]: https://github.com/athena-framework/routing/releases/tag/v0.1.0
================================================
FILE: .changes/routing/v0.1.11.md
================================================
## [0.1.11] - 2025-09-04
### Fixed
- Fix linker warning due to duplicate `pcre2-8` linkage ([#560]) (George Dietrich) <!-- blacksmoke16 -->
[0.1.11]: https://github.com/athena-framework/routing/releases/tag/v0.1.11
[#560]: https://github.com/athena-framework/athena/pull/560
================================================
FILE: .changes/routing/v0.1.12.md
================================================
## [0.1.12] - 2025-11-01
### Fixed
- Fix Crystal `1.19` incompatibility ([#600]) (George Dietrich) <!-- blacksmoke16 -->
[0.1.12]: https://github.com/athena-framework/routing/releases/tag/v0.1.12
[#600]: https://github.com/athena-framework/athena/pull/600
================================================
FILE: .changes/routing/v0.2.0.md
================================================
## [0.2.0] - 2026-04-19
### Changed
- **Breaking:** Change `ART::Route#defaults` and matched route parameters return type to `ART::Parameters` ([#652]) (George Dietrich) <!-- blacksmoke16 -->
- **Breaking:** Loosen the type restriction for the `params` parameter of `ART::Generator::Interface#generate` ([#669]) (George Dietrich) <!-- blacksmoke16 -->
### Added
- Allow query-specific parameters within `ART::Generator::URLGenerator` via special `_query` parameter ([#669]) (George Dietrich) <!-- blacksmoke16 -->
[0.2.0]: https://github.com/athena-framework/routing/releases/tag/v0.2.0
[#652]: https://github.com/athena-framework/athena/pull/652
[#669]: https://github.com/athena-framework/athena/pull/669
================================================
FILE: .changes/serializer/v0.4.1.md
================================================
## [0.4.1] - 2025-02-08
### Fixed
- Fix serialization of value when its type is different type than the ivar ([#514]) (George Dietrich)
[0.4.1]: https://github.com/athena-framework/serializer/releases/tag/v0.4.1
[#514]: https://github.com/athena-framework/athena/pull/514
## [0.4.0] - 2025-01-26
### Changed
- **Breaking:** Normalize exception types ([#428]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.13.0` ([#428]) (George Dietrich)
[0.4.0]: https://github.com/athena-framework/serializer/releases/tag/v0.4.0
[#428]: https://github.com/athena-framework/athena/pull/428
## [0.3.6] - 2024-04-27
### Fixed
- Fix misnamed modules being defined in incorrect namespace ([#402]) (George Dietrich)
[0.3.6]: https://github.com/athena-framework/serializer/releases/tag/v0.3.6
[#402]: https://github.com/athena-framework/athena/pull/402
## [0.3.5] - 2024-04-09
### Changed
- Change `Config` dependency to `DependencyInjection` for the custom annotation feature ([#392]) (George Dietrich)
- Integrate website into monorepo ([#365]) (George Dietrich)
[0.3.5]: https://github.com/athena-framework/serializer/releases/tag/v0.3.5
[#392]: https://github.com/athena-framework/athena/pull/392
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.3.4] - 2023-10-09
_Administrative release, no functional changes_
[0.3.4]: https://github.com/athena-framework/serializer/releases/tag/v0.3.4
## [0.3.3] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
[0.3.3]: https://github.com/athena-framework/serializer/releases/tag/v0.3.3
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.3.2] - 2023-01-07
### Fixed
- Fix deserializing `JSON::Any` and `YAML::Any` ([#215]) (George Dietrich)
[0.3.2]: https://github.com/athena-framework/serializer/releases/tag/v0.3.2
[#215]: https://github.com/athena-framework/athena/pull/215
## [0.3.1] - 2022-09-05
### Changed
- **Breaking:** ensure parameter names defined on interfaces match the implementation ([#188]) (George Dietrich)
[0.3.1]: https://github.com/athena-framework/serializer/releases/tag/v0.3.1
[#188]: https://github.com/athena-framework/athena/pull/188
## [0.3.0] - 2022-05-14
_First release a part of the monorepo._
### Added
- Add getting started documentation to API docs ([#172]) (George Dietrich)
### Changed
- **Breaking:** change serialization of [Enums](https://crystal-lang.org/api/Enum.html) to underscored strings by default ([#173]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Fixed
- Fix compiler error when trying to deserialize a `Hash` ([#165]) (George Dietrich)
[0.3.0]: https://github.com/athena-framework/serializer/releases/tag/v0.3.0
[#165]: https://github.com/athena-framework/athena/pull/165
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
[#173]: https://github.com/athena-framework/athena/pull/173
## [0.2.10] - 2021-11-12
### Fixed
- Fix issue with empty YAML input ([#22]) (George Dietrich)
[0.2.10]: https://github.com/athena-framework/serializer/releases/tag/v0.2.10
[#22]: https://github.com/athena-framework/serializer/pull/22
## [0.2.9] - 2021-10-30
### Added
- Add `VERSION` constant to `Athena::Serializer` namespace ([#20]) (George Dietrich)
### Fixed
- Fix broken type link ([#19]) (George Dietrich)
[0.2.9]: https://github.com/athena-framework/serializer/releases/tag/v0.2.9
[#19]: https://github.com/athena-framework/serializer/pull/19
[#20]: https://github.com/athena-framework/serializer/pull/20
## [0.2.8] - 2021-05-17
### Fixed
- Fixes incorrect `nil` check in macro logic ([#17]) (George Dietrich)
[0.2.8]: https://github.com/athena-framework/serializer/releases/tag/v0.2.8
[#17]: https://github.com/athena-framework/serializer/pull/17
## [0.2.7] - 2021-04-09
### Added
- Add some more specialized exception types ([#16]) (George Dietrich)
[0.2.7]: https://github.com/athena-framework/serializer/releases/tag/v0.2.7
[#16]: https://github.com/athena-framework/serializer/pull/16
## [0.2.6] - 2021-03-16
### Added
- Expose a setter for `ASR::Context#version=` ([#15]) (George Dietrich)
### Changed
- Change `athena-framework/config` version constraint to `>= 2.0.0` ([#15]) (George Dietrich)
[0.2.6]: https://github.com/athena-framework/serializer/releases/tag/v0.2.6
[#15]: https://github.com/athena-framework/serializer/pull/15
## [0.2.5] - 2021-01-29
### Changed
- Migrate documentation to [MkDocs](https://mkdocstrings.github.io/crystal/) ([#14]) (George Dietrich)
[0.2.5]: https://github.com/athena-framework/serializer/releases/tag/v0.2.5
[#14]: https://github.com/athena-framework/serializer/pull/14
## [0.2.4] - 2021-01-29
### Changed
- Bump min `athena-framework/config` version to `~> 2.0.0` ([#13]) (George Dietrich)
[0.2.4]: https://github.com/athena-framework/serializer/releases/tag/v0.2.4
[#13]: https://github.com/athena-framework/serializer/pull/13
## [0.2.3] - 2021-01-20
### Fixed
- Fix since/until and group annotations not working for virtual properties ([#12]) (George Dietrich)
[0.2.3]: https://github.com/athena-framework/serializer/releases/tag/v0.2.3
[#12]: https://github.com/athena-framework/serializer/pull/12
## [0.2.2] - 2020-12-03
### Changed
- Update `crystal` version to allow version greater than `1.0.0` ([#11]) (George Dietrich)
[0.2.2]: https://github.com/athena-framework/serializer/releases/tag/v0.2.2
[#11]: https://github.com/athena-framework/serializer/pull/11
## [0.2.1] - 2020-11-08
### Added
- Add deserialization support to `ASRA::Name` ([#9]) (Joakim Repomaa)
[0.2.1]: https://github.com/athena-framework/serializer/releases/tag/v0.2.1
[#9]: https://github.com/athena-framework/serializer/pull/9
## [0.2.0] - 2020-07-08
### Added
- Add dependency on `athena-framework/config` ([#8]) (George Dietrich)
- Add ability to use custom annotations within [exclusion strategies](https://athenaframework.org/Serializer/ExclusionStrategies/ExclusionStrategyInterface/#Athena::Serializer::ExclusionStrategies::ExclusionStrategyInterface--annotation-configurations) ([#8]) (George Dietrich)
- Add [ASR::Context#direction](https://athenaframework.org/Serializer/Context/#Athena::Serializer::Context#direction) to represent which direction the context object represents ([#8]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/serializer/releases/tag/v0.2.0
[#8]: https://github.com/athena-framework/serializer/pull/8
## [0.1.3] - 2020-07-08
### Fixed
- Fix overflow error when deserializing `Int64` values ([#7]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/serializer/releases/tag/v0.1.3
[#7]: https://github.com/athena-framework/serializer/pull/7
## [0.1.2] - 2020-07-05
### Added
- Add improved documentation to various types ([#6]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/serializer/releases/tag/v0.1.2
[#6]: https://github.com/athena-framework/serializer/pull/6
## [0.1.1] - 2020-06-27
### Added
- Add [naming strategies](https://athenaframework.org/Serializer/Annotations/Name/#Athena::Serializer::Annotations::Name--naming-strategies) to `ASRA::Name` ([#5]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/serializer/releases/tag/v0.1.1
[#5]: https://github.com/athena-framework/serializer/pull/5
## [0.1.0] - 2020-06-23
_Initial release._
[0.1.0]: https://github.com/athena-framework/serializer/releases/tag/v0.1.0
================================================
FILE: .changes/serializer/v0.4.2.md
================================================
## [0.4.2] - 2025-08-12
### Fixed
- Fix nightly type incompatibility with `ASR::Any` ([#562]) (George Dietrich)
[0.4.2]: https://github.com/athena-framework/serializer/releases/tag/v0.4.2
[#562]: https://github.com/athena-framework/athena/pull/562
================================================
FILE: .changes/serializer/v0.4.3.md
================================================
## [0.4.3] - 2026-04-19
### Changed
- Improve compile time error messages ([#646]) (George Dietrich) <!-- blacksmoke16 -->
### Removed
- Remove `ASR::PropertyMetadata#class` method and generic variable ([#672]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.3]: https://github.com/athena-framework/serializer/releases/tag/v0.4.3
[#646]: https://github.com/athena-framework/athena/pull/646
[#672]: https://github.com/athena-framework/athena/pull/672
================================================
FILE: .changes/spec/v0.3.11.md
================================================
## [0.3.11] - 2025-05-19
### Fixed
- Fix duplicate test case runs with abstract generic parent test case ([#538]) (George Dietrich)
[0.3.11]: https://github.com/athena-framework/spec/releases/tag/v0.3.11
[#538]: https://github.com/athena-framework/athena/pull/538
## [0.3.10] - 2025-02-08
### Changed
- **Breaking:** prevent defining `ASPEC::TestCase#initialize` methods that accepts arguments/blocks ([#516]) (George Dietrich)
[0.3.10]: https://github.com/athena-framework/spec/releases/tag/v0.3.10
[#516]: https://github.com/athena-framework/athena/pull/516
## [0.3.9] - 2025-01-26
_Administrative release, no functional changes_
[0.3.9]: https://github.com/athena-framework/spec/releases/tag/v0.3.9
## [0.3.8] - 2024-07-31
### Added
- Add support for using the `CRYSTAL` ENV var to customize binary used for `ASPEC::Methods.assert_error` and `ASPEC::Methods.assert_success` ([#424]) (George Dietrich)
[0.3.8]: https://github.com/athena-framework/spec/releases/tag/v0.3.8
[#424]: https://github.com/athena-framework/athena/pull/424
## [0.3.7] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
[0.3.7]: https://github.com/athena-framework/spec/releases/tag/v0.3.7
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.3.6] - 2023-10-09
_Administrative release, no functional changes_
[0.3.6]: https://github.com/athena-framework/spec/releases/tag/v0.3.6
## [0.3.5] - 2023-04-26
### Fixed
- Ensure `#before_all` runs exactly once, and before `#initialize` ([#285]) (George Dietrich)
[0.3.5]: https://github.com/athena-framework/spec/releases/tag/v0.3.5
[#285]: https://github.com/athena-framework/athena/pull/285
## [0.3.4] - 2023-03-19
### Fixed
- Fix exceptions not being counted as errors when raised within the `initialize` method of a test case ([#276]) (George Dietrich)
- Fix a documentation typo in the `TestWith` example ([#269]) (George Dietrich)
[0.3.4]: https://github.com/athena-framework/spec/releases/tag/v0.3.4
[#269]: https://github.com/athena-framework/athena/pull/269
[#276]: https://github.com/athena-framework/athena/pull/276
## [0.3.3] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
[0.3.3]: https://github.com/athena-framework/spec/releases/tag/v0.3.3
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.3.2] - 2023-01-16
### Added
- Add `ASPEC::TestCase::TestWith` that works similar to the `ASPEC::TestCase::DataProvider` but without needing to create a dedicated method ([#254]) (George Dietrich)
[0.3.2]: https://github.com/athena-framework/spec/releases/tag/v0.3.2
[#254]: https://github.com/athena-framework/athena/pull/254
## [0.3.1] - 2023-01-07
### Changed
- Update the docs to clarify the component needs to be manually installed ([#247]) (George Dietrich)
### Added
- Add support for *codegen* for the `ASPEC.assert_error` and `ASPEC.assert_success` methods ([#219]) (George Dietrich)
- Add ability to skip running all examples within a test case via the `ASPEC::TestCase::Skip` annotation ([#248]) (George Dietrich)
[0.3.1]: https://github.com/athena-framework/spec/releases/tag/v0.3.1
[#219]: https://github.com/athena-framework/athena/pull/219
[#247]: https://github.com/athena-framework/athena/pull/247
[#248]: https://github.com/athena-framework/athena/pull/248
## [0.3.0] - 2022-05-14
_First release a part of the monorepo._
### Changed
- **Breaking:** change the `assert_error` to no longer be file based. Code should now be provided as a HEREDOC argument to the method ([#173]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Added
- Add `VERSION` constant to `Athena::Spec` namespace ([#166]) (George Dietrich)
- Add getting started documentation to API docs ([#172]) (George Dietrich)
- Add [ASPEC::Methods.assert_success](https://athenaframework.org/Spec/Methods/#Athena::Spec::Methods#assert_success(code,*,line,file)) ([#173]) (George Dietrich)
[0.3.0]: https://github.com/athena-framework/spec/releases/tag/v0.3.0
[#166]: https://github.com/athena-framework/athena/pull/166
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
[#173]: https://github.com/athena-framework/athena/pull/173
## [0.2.6] - 2021-11-03
### Fixed
- Fix `test` helper macro generating invalid method names by replacing all non alphanumeric chars with `_` ([#12]) (George Dietrich)
[0.2.6]: https://github.com/athena-framework/spec/releases/tag/v0.2.6
[#12]: https://github.com/athena-framework/spec/pull/12
## [0.2.5] - 2021-11-03
### Fixed
- Fix `test` helper macro not actually calling `yield` ([#11]) (George Dietrich)
[0.2.5]: https://github.com/athena-framework/spec/releases/tag/v0.2.5
[#11]: https://github.com/athena-framework/spec/pull/11
## [0.2.4] - 2021-01-29
### Changed
- Finish migration to [MkDocs](https://mkdocstrings.github.io/crystal/) ([#9]) (George Dietrich)
[0.2.4]: https://github.com/athena-framework/spec/releases/tag/v0.2.4
[#9]: https://github.com/athena-framework/spec/pull/9
## [0.2.3] - 2020-12-03
### Changed
- Update `crystal` version to allow version greater than `1.0.0` ([#7]) (George Dietrich)
[0.2.3]: https://github.com/athena-framework/spec/releases/tag/v0.2.3
[#7]: https://github.com/athena-framework/spec/pull/7
## [0.2.2] - 2020-10-02
### Added
- Add support for data providers defined in parent types ([#6]) (George Dietrich)
[0.2.2]: https://github.com/athena-framework/spec/releases/tag/v0.2.2
[#6]: https://github.com/athena-framework/spec/pull/6
## [0.2.1] - 2020-09-25
### Changed
- Changed data provider generated `it` blocks have proper file names and line numbers ([#4]) (George Dietrich)
[0.2.1]: https://github.com/athena-framework/spec/releases/tag/v0.2.1
[#4]: https://github.com/athena-framework/spec/pull/4
## [0.2.0] - 2020-08-08
### Changed
- **Breaking:** require [data providers](https://athenaframework.org/Spec/TestCase/DataProvider/) methods to declare a return type of `Hash`, `NamedTuple`, `Tuple`, or `Array` ([#3]) (George Dietrich)
- Changed data provider generated `it` blocks to include the key/index ([#2]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/spec/releases/tag/v0.2.0
[#2]: https://github.com/athena-framework/spec/pull/2
[#3]: https://github.com/athena-framework/spec/pull/3
## [0.1.0] - 2020-08-06
_Initial release._
[0.1.0]: https://github.com/athena-framework/spec/releases/tag/v0.1.0
================================================
FILE: .changes/spec/v0.4.0.md
================================================
## [0.4.0] - 2025-09-04
### Added
- Add support for generating macro code coverage reports for `.assert_error` and `.assert_compiles` methods ([#551]) (George Dietrich) <!-- blacksmoke16 -->
### Removed
- Remove `codegen` parameter from `ASPEC::Methods.assert_error` and `ASPEC::Methods.assert_success` ([#551]) (George Dietrich) <!-- blacksmoke16 -->
- Remove `ASPEC::Methods.assert_error` in favor of `ASPEC::Methods.assert_compile_time_error` and `ASPEC::Methods.assert_runtime_error` ([#551]) (George Dietrich) <!-- blacksmoke16 -->
- Remove `ASPEC::Methods.assert_success` in favor of `ASPEC::Methods.assert_compiles` and `ASPEC::Methods.assert_executes` ([#551]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.0]: https://github.com/athena-framework/spec/releases/tag/v0.4.0
[#551]: https://github.com/athena-framework/athena/pull/551
================================================
FILE: .changes/spec/v0.4.1.md
================================================
## [0.4.1] - 2025-11-12
### Fixed
- Fix segfault when interacting with a test case ivar object's ivar that was left uninitialized due to an exception in its initializer, within the `tear_down` method ([#613]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.1]: https://github.com/athena-framework/spec/releases/tag/v0.4.1
[#613]: https://github.com/athena-framework/athena/pull/613
================================================
FILE: .changes/spec/v0.4.2.md
================================================
## [0.4.2] - 2026-04-19
### Added
- Generate macro code coverage report for `ASPEC::Methods.assert_compiles` ([#642]) (George Dietrich) <!-- blacksmoke16 -->
- Add `ASPEC.compile_time_assert` helper function for use with `assert_compiles` ([#686]) (George Dietrich) <!-- blacksmoke16 -->
- Add ability to add code before/after the actual code of `ASPEC::Methods.assert_compiles` and `ASPEC::Methods.assert_compile_time_error` ([#687]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Fix compile time error when inadvertently using a type name that conflicts with an internal component type ([#678]) (George Dietrich) <!-- blacksmoke16 -->
- Fix incorrect macro code coverage line numbers ([#686]) (George Dietrich) <!-- blacksmoke16 -->
- Fix macro code coverage output file writing on windows ([#696]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.2]: https://github.com/athena-framework/spec/releases/tag/v0.4.2
[#642]: https://github.com/athena-framework/athena/pull/642
[#686]: https://github.com/athena-framework/athena/pull/686
[#687]: https://github.com/athena-framework/athena/pull/687
[#678]: https://github.com/athena-framework/athena/pull/678
[#696]: https://github.com/athena-framework/athena/pull/696
================================================
FILE: .changes/unreleased/.gitkeep
================================================
================================================
FILE: .changes/unreleased/event-dispatcher-Changed-20260502-225424.yaml
================================================
project: event-dispatcher
kind: Changed
body: Event listeners with a generic type parameter now match all subclasses and module includers of the parameter's type
time: 2026-05-02T22:54:24.690293195-04:00
custom:
Author: George Dietrich
Breaking: "No"
PR: "703"
Username: blacksmoke16
================================================
FILE: .changes/validator/v0.4.0.md
================================================
## [0.4.0] - 2025-01-26
### Changed
- **Breaking:** Normalize exception types ([#428]) (George Dietrich)
### Added
- **Breaking:** Add and make `require_tld: true` the default for `AVD::Constraints::URL` ([#492]) (George Dietrich)
- Add example usages to `AVD::Constraints::*` docs ([#483], [#493]) (Zohir Tamda, George Dietrich)
[0.4.0]: https://github.com/athena-framework/validator/releases/tag/v0.4.0
[#428]: https://github.com/athena-framework/athena/pull/428
[#483]: https://github.com/athena-framework/athena/pull/483
[#492]: https://github.com/athena-framework/athena/pull/492
[#493]: https://github.com/athena-framework/athena/pull/493
## [0.3.4] - 2024-07-31
### Changed
- Update minimum `crystal` version to `~> 1.13.0` ([#433]) (George Dietrich)
[0.3.4]: https://github.com/athena-framework/validator/releases/tag/v0.3.4
[#433]: https://github.com/athena-framework/athena/pull/433
## [0.3.3] - 2024-04-09
### Changed
- Integrate website into monorepo ([#365]) (George Dietrich)
[0.3.3]: https://github.com/athena-framework/validator/releases/tag/v0.3.3
[#365]: https://github.com/athena-framework/athena/pull/365
## [0.3.2] - 2023-10-09
### Fixed
- Fix compiler error when using a composite constraint with a single member and no `of AVD::Constraint` ([#292]) (George Dietrich)
[0.3.2]: https://github.com/athena-framework/validator/releases/tag/v0.3.2
[#292]: https://github.com/athena-framework/athena/pull/292
## [0.3.1] - 2023-02-18
### Changed
- Update some links in preparation for Athena Framework `0.18.0` ([#261]) (George Dietrich)
### Fixed
- Fix issue when using `AVD::Metadata::GetterMetadata` with methods that have parameters ([#252]) (George Dietrich)
[0.3.1]: https://github.com/athena-framework/validator/releases/tag/v0.3.1
[#252]: https://github.com/athena-framework/athena/pull/252
[#261]: https://github.com/athena-framework/athena/pull/261
## [0.3.0] - 2023-01-07
### Changed
- **Breaking:** update default `AVD::Constraints::Email::Mode` to be `:html5` ([#230]) (George Dietrich)
- Refactor `AVD::Constraints::IP` to use new dedicated `Socket::IPAddress` methods ([#205]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.6` ([#205]) (George Dietrich)
### Added
- Add `AVD::Constraints::Collection` ([#229]) (George Dietrich)
- Add `AVD::Constraints::Existence`, `AVD::Constraints::Required`, and `AVD::Constraints::Optional` for use with the collection constraint ([#229]) (George Dietrich)
- Add `AVD::Spec::ConstraintValidatorTestCase#expect_validate_value_at` to more easily handle validation of nested constraints ([#229]) (George Dietrich)
- Add `AVD::Constraints::Email::Mode::HTML5_ALLOW_NO_TLD` that allows matching `HTML` input field validation exactly ([#231]) (George Dietrich)
### Removed
- **Breaking:** remove `AVD::Constraints::Email::Mode::Loose` ([#230]) (George Dietrich)
### Fixed
- **Breaking:** fix spelling of `AVD::Constraints::ISSN#require_hyphen` parameter ([#222]) (George Dietrich)
- Fix property path display issue with `Enumerable` objects ([#229]) (George Dietrich)
- Fix `AVD::Constraints::Valid` constraints incorrectly being allowed within `AVD::Constraints::Composite` ([#229]) (George Dietrich)
[0.3.0]: https://github.com/athena-framework/validator/releases/tag/v0.3.0
[#205]: https://github.com/athena-framework/athena/pull/205
[#222]: https://github.com/athena-framework/athena/pull/222
[#229]: https://github.com/athena-framework/athena/pull/229
[#230]: https://github.com/athena-framework/athena/pull/230
[#231]: https://github.com/athena-framework/athena/pull/231
## [0.2.1] - 2022-09-05
### Added
- Add support for exclusive end support to `AVD::Constraints::Range` ([#184]) (George Dietrich)
### Changed
- Include allowed MIME types within `AVD::Constraints::Image` if they were customized ([#183]) (George Dietrich)
- **Breaking:** ensure parameter names defined on interfaces match the implementation ([#188]) (George Dietrich)
### Fixed
- Fix some file size factorization edge cases in `AVD::Constraints::File` ([#182]) (George Dietrich)
- Fix duplicating constraints due to Crystal generics bug ([#192]) (George Dietrich)
[0.2.1]: https://github.com/athena-framework/validator/releases/tag/v0.2.1
[#182]: https://github.com/athena-framework/athena/pull/182
[#183]: https://github.com/athena-framework/athena/pull/183
[#184]: https://github.com/athena-framework/athena/pull/184
[#188]: https://github.com/athena-framework/athena/pull/188
[#192]: https://github.com/athena-framework/athena/pull/192
## [0.2.0] - 2022-05-14
### Added
- Add the [AVD::Constraints::File](https://athenaframework.org/Validator/Constraints/File/) constraint ([#153]) (George Dietrich)
- Allow `AVD::Spec::MockValidator` to dynamically configure returned violations ([#155], [#157]) (George Dietrich)
- Add the [AVD::Constraints::Image](https://athenaframework.org/Validator/Constraints/Image/) constraint ([#153]) (George Dietrich)
- Add getting started documentation to API docs ([#172]) (George Dietrich)
### Changed
- **Breaking:** make `AVD::ConstraintValidator` classes ([#154]) (George Dietrich)
- **Breaking:** `AVD::ExecutionContext` is no longer a generic type ([#156]) (George Dietrich)
- Update `assert_violation` to use a clearer failure message if no violations were found ([#153]) (George Dietrich)
- Update `AVD::Constraints::ISIN` to use the validator off the context versus an ivar ([#155]) (George Dietrich)
- Update minimum `crystal` version to `~> 1.4.0` ([#169]) (George Dietrich)
### Removed
- **Breaking:** removed `AVD::Spec::MockValidator#violations=` ([#155]) (George Dietrich)
### Fixed
- Fix `AVD::Violation::ConstraintViolation` not comparing correctly ([#153]) (George Dietrich)
- Ensure only `Indexable` types can be used with `AVD::Constraints::Unique` ([#168]) (George Dietrich)
[0.2.0]: https://github.com/athena-framework/validator/releases/tag/v0.2.0
[#153]: https://github.com/athena-framework/athena/pull/153
[#154]: https://github.com/athena-framework/athena/pull/154
[#155]: https://github.com/athena-framework/athena/pull/155
[#156]: https://github.com/athena-framework/athena/pull/156
[#157]: https://github.com/athena-framework/athena/pull/157
[#168]: https://github.com/athena-framework/athena/pull/168
[#169]: https://github.com/athena-framework/athena/pull/169
[#172]: https://github.com/athena-framework/athena/pull/172
## [0.1.7] - 2021-12-27
_First release a part of the monorepo._
### Fixed
- Fix callback constraint methods being incorrectly added as getters ([#132]) (George Dietrich)
[0.1.7]: https://github.com/athena-framework/validator/releases/tag/v0.1.7
[#132]: https://github.com/athena-framework/athena/pull/132
## [0.1.6] - 2021-12-13
### Fixed
- Fix `AVD::Validatable` not working when included into parent types ([#16]) (George Dietrich)
[0.1.6]: https://github.com/athena-framework/validator/releases/tag/v0.1.6
[#16]: https://github.com/athena-framework/validator/pull/16
## [0.1.5] - 2021-10-30
### Added
- Add `VERSION` constant to `Athena::Validator` namespace ([#12]) (George Dietrich)
### Fixed
- Fix incorrect type restriction on validator factory ([#12]) (George Dietrich)
- Fix incorrect link within the docs ([#14]) (George Dietrich)
[0.1.5]: https://github.com/athena-framework/validator/releases/tag/v0.1.5
[#12]: https://github.com/athena-framework/validator/pull/12
[#14]: https://github.com/athena-framework/validator/pull/14
## [0.1.4] - 2021-01-30
### Changed
- Finish migration to [MkDocs](https://mkdocstrings.github.io/crystal/) ([#10], [#11]) (George Dietrich)
[0.1.4]: https://github.com/athena-framework/validator/releases/tag/v0.1.4
[#10]: https://github.com/athena-framework/validator/pull/10
[#11]: https://github.com/athena-framework/validator/pull/11
## [0.1.3] - 2020-12-07
### Changed
- Update `crystal` version to allow version greater than `1.0.0` ([#9]) (George Dietrich)
[0.1.3]: https://github.com/athena-framework/validator/releases/tag/v0.1.3
[#9]: https://github.com/athena-framework/validator/pull/9
## [0.1.2] - 2020-11-25
### Added
- Add the [AVD::Constraints::Choice](https://athenaframework.org/Validator/Constraints/Choice/) constraint ([#7]) (George Dietrich)
### Changed
- Allow setting violations directly on mock validators ([#7]) (George Dietrich)
[0.1.2]: https://github.com/athena-framework/validator/releases/tag/v0.1.2
[#7]: https://github.com/athena-framework/validator/pull/7
## [0.1.1] - 2020-11-08
### Fixed
- Fix compiler error due to less strict `abstract def` implementations ([#6]) (George Dietrich)
[0.1.1]: https://github.com/athena-framework/validator/releases/tag/v0.1.1
[#6]: https://github.com/athena-framework/validator/pull/6
## [0.1.0] - 2020-10-17
_Initial release._
[0.1.0]: https://github.com/athena-framework/validator/releases/tag/v0.1.0
================================================
FILE: .changes/validator/v0.4.1.md
================================================
## [0.4.1] - 2025-09-04
### Changed
- Leverage `mime` component for more robust `AVD::Constraints::File` MIME type validation ([#545]) (George Dietrich) <!-- blacksmoke16 -->
### Added
- Add `AVD::Spec::CompoundConstraintTestCase` to make testing `AVD::Constraints::Compound` easier ([#540]) (George Dietrich) <!-- blacksmoke16 -->
- Add support for `ATH::UploadedFile` to `AVD::Constraints::File` and `AVD::Constraints::Image` ([#559]) (George Dietrich) <!-- blacksmoke16 -->
### Fixed
- Fix equality between `AVD::Constraint` instances ([#540]) (George Dietrich) <!-- blacksmoke16 -->
[0.4.1]: https://github.com/athena-framework/validator/releases/tag/v0.4.1
[#545]: https://github.com/athena-framework/athena/pull/545
[#540]: https://github.com/athena-framework/athena/pull/540
[#559]: https://github.com/athena-framework/athena/pull/559
================================================
FILE: .changes/validator/v0.5.0.md
================================================
## [0.5.0] - 2026-04-19
### Changed
- **Breaking:** Split `AVD::Constraints::Size` into `Count` and `Length` constraints ([#611]) (George Dietrich) <!-- blacksmoke16 -->
- Make identifying constraint violation inequality easier within spec failures ([#610]) (George Dietrich) <!-- blacksmoke16 -->
### Added
- Allow picking the unit used for `AVD::Constraints::Length` validations ([#612]) (George Dietrich) <!-- blacksmoke16 -->
[0.5.0]: https://github.com/athena-framework/validator/releases/tag/v0.5.0
[#610]: https://github.com/athena-framework/athena/pull/610
[#611]: https://github.com/athena-framework/athena/pull/611
[#612]: https://github.com/athena-framework/athena/pull/612
================================================
FILE: .changie.yaml
================================================
changesDir: .changes
unreleasedDir: unreleased
headerPath: header.tpl.md
changelogPath: CHANGELOG.md
versionExt: md
versionFormat: '## [{{.VersionNoPrefix}}] - {{.Time.Format "2006-01-02"}}'
kindFormat: '### {{.Kind}}'
changeFormat: '- {{.Body}} ([#{{.Custom.PR}}]) ({{.Custom.Author}}) <!-- {{.Custom.Username}} -->'
footerFormat: |
[{{.VersionNoPrefix}}]: https://github.com/athena-framework/{{ kebabcase (index .Changes 0).Project }}/releases/tag/{{.Version}}
{{- range (customs .Changes "PR" | uniq) }}
[#{{.}}]: https://github.com/athena-framework/athena/pull/{{.}}
{{- end}}
projectsVersionSeparator: '/'
projects:
# Bundles
- label: Mercure Bundle
key: mercure-bundle
changelog: src/bundles/mercure/CHANGELOG.md
replacements:
- { path: src/bundles/mercure/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/bundles/mercure/src/athena-mercure_bundle.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/mercure-bundle/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
# Components
- label: Clock
key: clock
changelog: src/components/clock/CHANGELOG.md
replacements:
- { path: src/components/clock/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/clock/src/athena-clock.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/clock/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Console
key: console
changelog: src/components/console/CHANGELOG.md
replacements:
- { path: src/components/console/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/console/src/athena-console.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/console/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Contracts
key: contracts
changelog: src/components/contracts/CHANGELOG.md
replacements:
- { path: src/components/contracts/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/contracts/src/athena-contracts.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/contracts/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Dependency Injection
key: dependency-injection
changelog: src/components/dependency_injection/CHANGELOG.md
replacements:
- { path: src/components/dependency_injection/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/dependency_injection/src/athena-dependency_injection.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/dependency_injection/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Dotenv
key: dotenv
changelog: src/components/dotenv/CHANGELOG.md
replacements:
- { path: src/components/dotenv/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/dotenv/src/athena-dotenv.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/dotenv/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Event Dispatcher
key: event-dispatcher
changelog: src/components/event_dispatcher/CHANGELOG.md
replacements:
- { path: src/components/event_dispatcher/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/event_dispatcher/src/athena-event_dispatcher.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/event_dispatcher/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Framework
key: framework
changelog: src/components/framework/CHANGELOG.md
replacements:
- { path: src/components/framework/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/framework/src/athena.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: docs/getting_started/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: HTTP
key: http
changelog: src/components/http/CHANGELOG.md
replacements:
- { path: src/components/http/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/http/src/athena-http.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/http/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: HTTP Kernel
key: http-kernel
changelog: src/components/http_kernel/CHANGELOG.md
replacements:
- { path: src/components/http_kernel/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/http_kernel/src/athena-http_kernel.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/http_kernel/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Image Size
key: image-size
changelog: src/components/image_size/CHANGELOG.md
replacements:
- { path: src/components/image_size/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/image_size/src/athena-image_size.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/image_size/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Mercue
key: mercure
changelog: src/components/mercure/CHANGELOG.md
replacements:
- { path: src/components/mercure/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/mercure/src/athena-mercure.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/mercure/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: MIME
key: mime
changelog: src/components/mime/CHANGELOG.md
replacements:
- { path: src/components/mime/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/mime/src/athena-mime.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/mime/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Negotiation
key: negotiation
changelog: src/components/negotiation/CHANGELOG.md
replacements:
- { path: src/components/negotiation/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/negotiation/src/athena-negotiation.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/negotiation/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Routing
key: routing
changelog: src/components/routing/CHANGELOG.md
replacements:
- { path: src/components/routing/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/routing/src/athena-routing.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/routing/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Serializer
key: serializer
changelog: src/components/serializer/CHANGELOG.md
replacements:
- { path: src/components/serializer/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/serializer/src/athena-serializer.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/serializer/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Spec
key: spec
changelog: src/components/spec/CHANGELOG.md
replacements:
- { path: src/components/spec/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/spec/src/athena-spec.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/spec/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
- label: Validator
key: validator
changelog: src/components/validator/CHANGELOG.md
replacements:
- { path: src/components/validator/shard.yml, find: '^version: .*', replace: 'version: {{.VersionNoPrefix}}' }
- { path: src/components/validator/src/athena-validator.cr, find: ' VERSION = ".*"', replace: ' VERSION = "{{.VersionNoPrefix}}"' }
- { path: src/components/validator/docs/README.md, find: ' version: ~> .*', replace: ' version: ~> {{.Major}}.{{.Minor}}.0' }
kinds:
- label: Changed
changeFormat: '- {{if eq .Custom.Breaking "Yes"}}**Breaking:** {{end}}{{.Body}} ([#{{.Custom.PR}}]) ({{.Custom.Author}}) <!-- {{.Custom.Username}} -->'
additionalChoices:
- type: enum
key: Breaking
label: Is it a breaking change?
enumOptions:
- Yes
- No
- label: Added
- label: Removed
- label: Fixed
custom:
- key: PR
type: int
minInt: 0
optional: false
- key: Author
type: string
optional: false
- key: Username
label: Github Username
type: string
optional: false
newlines:
beforeChangelogVersion: 1
afterKind: 1
beforeKind: 1
beforeFooterTemplate: 1
envPrefix: CHANGIE_
================================================
FILE: .editorconfig
================================================
root = true
[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
================================================
FILE: .gitattributes
================================================
*.cr text eol=lf
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
# Updates tend to be released at the beginning of the month, but not exactly on the first.
# Add some delay to be able to catpure them
schedule:
interval: 'cron'
cronjob: '0 0 7 * *'
labels:
- 'kind:infrastructure'
- 'kind:maintenance'
- package-ecosystem: 'uv'
directory: '/'
schedule:
interval: 'monthly'
labels:
- 'kind:documentation'
- 'kind:maintenance'
groups:
documentation:
patterns:
- '*'
================================================
FILE: .github/pull_request_template.md
================================================
## Context
<!-- Take a minute to add some context for posterity as to what this PR is doing and why -->
<!-- Can be skipped in favor of an issue reference (`Resolves #xxx`) if the related issue has all the context -->
## Changelog
<!-- List out the high level changes this PR makes, especially those that are breaking -->
---
_Before merging, remember to add the `athena-framework/athena` prefix to the PR number in the PR title_
================================================
FILE: .github/workflows/build_and_publish_docs.yml
================================================
on:
workflow_dispatch:
inputs:
branch:
description: 'Which Cloudflare Pages branch (master | dev) to deploy the docs to'
type: string
required: true
workflow_call:
inputs:
branch:
description: 'Which Cloudflare Pages branch (master | dev) to deploy the docs to'
type: string
required: true
jobs:
build-and-publish-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0
- name: Install Crystal
uses: crystal-lang/install-crystal@d8ef131ecec0352ce0e39b81b0a6d95def58fe2f # v1.9.2
- name: Install Crystal Shard Dependencies
run: shards install --without-development
env:
SHARDS_OVERRIDE: ${{ inputs.branch == 'dev' && 'shard.dev.yml' || 'shard.prod.yml' }}
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.0.1
- name: Build Docs
run: just build-docs
- name: Publish to Cloudflare Pages
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3.15.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy ./site --project-name=athenaframework --branch=${{ inputs.branch }}
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
merge_group:
push:
branches:
- master # Allows codecov to receive current HEAD information for each commit merged into master
pull_request:
branches:
- master
schedule:
- cron: '15 1 * * *' # Nightly at 01:15
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check_spelling:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check Spelling
uses: crate-ci/typos@bbaefadf97b0ec5fdc942684b647f1a6ab250274 # v1.46.0
check_format:
strategy:
fail-fast: false
matrix:
crystal:
- latest
- nightly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0
- name: Install Crystal
uses: crystal-lang/install-crystal@d8ef131ecec0352ce0e39b81b0a6d95def58fe2f # v1.9.2
with:
crystal: ${{ matrix.crystal }}
- name: Check Format
run: just format
coding_standards:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0
- name: Install Crystal
uses: crystal-lang/install-crystal@d8ef131ecec0352ce0e39b81b0a6d95def58fe2f # v1.9.2
- name: Install Dependencies
run: shards install
env:
SHARDS_OVERRIDE: shard.dev.yml
- name: Ameba
run: just ameba
test_compiled:
strategy:
fail-fast: false
matrix:
crystal:
- latest
- nightly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0
- name: Install kcov
if: ${{ matrix.crystal == vars.COVERAGE_CHANNEL }}
run: |
sudo apt-get update &&
sudo apt-get install binutils-dev libssl-dev libcurl4-openssl-dev libelf-dev libstdc++-12-dev zlib1g-dev libdw-dev libiberty-dev
curl -L -o ./kcov.tar.gz https://github.com/SimonKagstrom/kcov/archive/refs/tags/v43.tar.gz &&
mkdir kcov-source &&
tar xzf kcov.tar.gz -C kcov-source --strip-components=1 &&
cd kcov-source &&
mkdir build &&
cd build &&
cmake .. &&
make -j$(nproc) &&
sudo make install
- name: Install Crystal
uses: crystal-lang/install-crystal@d8ef131ecec0352ce0e39b81b0a6d95def58fe2f # v1.9.2
with:
crystal: ${{ matrix.crystal }}
- name: Install System Dependencies
run: sudo apt-get update && sudo apt install -y libmagic-dev
- name: Install Dependencies
run: shards install --skip-postinstall --skip-executables
env:
SHARDS_OVERRIDE: shard.dev.yml
- name: Compiled Specs
run: just test-compiled
shell: bash
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
if: ${{ matrix.crystal == vars.COVERAGE_CHANNEL && github.event_name != 'schedule' }} # Only want to upload coverage report once in the matrix
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
directory: coverage
files: '**/cov.xml,**/macro_coverage.*.codecov.json' # There is no `unreachable.codecov.json` file when running _only_ compiled specs
verbose: true
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
if: ${{ matrix.crystal == vars.COVERAGE_CHANNEL && github.event_name != 'schedule' }} # Only want to upload coverage report once in the matrix
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
directory: coverage
files: '**/junit.xml'
verbose: true
report_type: test_results
test_unit:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- ubuntu-24.04-arm
- macos-latest
- windows-latest
crystal:
- latest
- nightly
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
if: github.event_name != 'pull_request'
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
if: github.event_name == 'pull_request'
with:
fetch-depth: 0
- uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0
- name: Install kcov
if: ${{ matrix.os == 'ubuntu-latest' && matrix.crystal == vars.COVERAGE_CHANNEL }}
run: |
sudo apt-get update &&
sudo apt-get install binutils-dev libssl-dev libcurl4-openssl-dev libelf-dev libstdc++-12-dev zlib1g-dev libdw-dev libiberty-dev
curl -L -o ./kcov.tar.gz https://github.com/SimonKagstrom/kcov/archive/refs/tags/v43.tar.gz &&
mkdir kcov-source &&
tar xzf kcov.tar.gz -C kcov-source --strip-components=1 &&
cd kcov-source &&
mkdir build &&
cd build &&
cmake .. &&
make -j$(nproc) &&
sudo make install
- name: Install Crystal
uses: crystal-lang/install-crystal@d8ef131ecec0352ce0e39b81b0a6d95def58fe2f # v1.9.2
with:
crystal: ${{ matrix.crystal }}
- name: Install System Dependencies
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
run: sudo apt-get update && sudo apt install -y libmagic-dev
- name: Install System Dependencies
if: matrix.os == 'macos-latest'
run: brew install libmagic
- name: Install Dependencies
run: shards install --skip-postinstall --skip-executables
env:
SHARDS_OVERRIDE: shard.dev.yml
- name: Specs
run: just test-unit
shell: bash
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
if: ${{ matrix.os == 'ubuntu-latest' && matrix.crystal == vars.COVERAGE_CHANNEL && github.event_name != 'schedule' }} # Only want to upload coverage report once in the matrix
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
directory: coverage
files: '**/cov.xml,**/unreachable.codecov.json'
verbose: true
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
if: ${{ matrix.os == 'ubuntu-latest' && matrix.crystal == vars.COVERAGE_CHANNEL && github.event_name != 'schedule' }} # Only want to upload coverage report once in the matrix
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
directory: coverage
files: '**/junit.xml'
verbose: true
report_type: test_results
================================================
FILE: .github/workflows/sync.yml
================================================
name: Sync
on:
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false # ensure back-to-back merges don't cancel an in-progress sync
jobs:
# Sync changes in the merged PR to the read-only mirror repos.
sync:
runs-on: ubuntu-latest
outputs:
updated_shards: ${{ steps.subtree-sync.outputs.updated_shards }}
kind_docs: ${{ steps.subtree-sync.outputs.kind_docs }}
kind_release: ${{ steps.subtree-sync.outputs.kind_release }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0 # Required so `git subtree` can find its split commit
ssh-key: ${{ secrets.ATHENA_BOT_SSH_PRIV_KEY }}
# Workaround for git 2.52.0 regression breaking subtree split with --squash
# See: https://lore.kernel.org/git/176677910605.6.2281395015810449820.1087545551@dietrich.pub/T/
- name: Cache Git installation
id: cache-git
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/git-custom
key: git-2.51.1-subtree-${{ runner.os }}-${{ runner.arch }}
- name: Build Git with subtree support
if: steps.cache-git.outputs.cache-hit != 'true'
run: |
set -euo pipefail
GIT_VERSION="2.51.1"
INSTALL_DIR="$HOME/git-custom"
# Install build dependencies
sudo apt-get update
sudo apt-get install -y libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
# Download and extract
wget -q "https://mirrors.edge.kernel.org/pub/software/scm/git/git-${GIT_VERSION}.tar.gz"
tar -xzf "git-${GIT_VERSION}.tar.gz"
cd "git-${GIT_VERSION}"
# Build and install git
make -j$(nproc) prefix="$INSTALL_DIR" all
make prefix="$INSTALL_DIR" install
# Build and install contrib/subtree
cd contrib/subtree
make prefix="$INSTALL_DIR" install
- name: Add custom Git to PATH
run: echo "$HOME/git-custom/bin" >> "$GITHUB_PATH"
- name: Sync Shards
id: subtree-sync
env:
GH_TOKEN: ${{ github.token }}
BEFORE_SHA: ${{ github.event.before }}
AFTER_SHA: ${{ github.event.after }}
run: |
set -euo pipefail
UPDATED_SHARDS=()
for shardDir in $(find src/ -maxdepth 3 -type f -name shard.yml | xargs -I{} dirname {} | sort); do
# The git repos uses hyphens instead of underscores.
REPO_NAME=$(basename "$shardDir" | tr '_' '-')
if [[ "$shardDir" == src/bundles/* ]]; then
REPO_NAME="${REPO_NAME}-bundle"
fi
REPO_URL="git@github.com:athena-framework/$REPO_NAME.git"
if ! $(git diff --quiet --exit-code $BEFORE_SHA $AFTER_SHA -- "$shardDir"); then
echo "Syncing: $REPO_NAME"
git remote add "$REPO_NAME" "$REPO_URL" &> /dev/null || true
git fetch --quiet "$REPO_NAME"
git subtree push --prefix="$shardDir" "$REPO_NAME" "master"
UPDATED_SHARDS+=("\"$REPO_NAME\"")
fi
done
JSON_ARR=$(IFS=,; echo "[${UPDATED_SHARDS[*]}]")
echo "Changed shards: $JSON_ARR"
echo "updated_shards=$JSON_ARR" >> "$GITHUB_OUTPUT"
PR_LABELS=$(gh api --jq '.[0].labels | map(.name)' /repos/athena-framework/athena/commits/${{ github.event.after }}/pulls)
IS_KIND_DOCUMENTATION=$([ "null" != "$(jq 'index("kind:documentation")' <<< $PR_LABELS)" ] && echo "true" || echo "false")
IS_KIND_RELEASE=$([ "null" != "$(jq 'index("kind:release")' <<< $PR_LABELS)" ] && echo "true" || echo "false")
echo "kind_docs=$IS_KIND_DOCUMENTATION" >> "$GITHUB_OUTPUT"
echo "kind_release=$IS_KIND_RELEASE" >> "$GITHUB_OUTPUT"
release:
needs:
- sync
if: needs.sync.outputs.updated_shards != '[]' && needs.sync.outputs.kind_release == 'true'
strategy:
fail-fast: false
matrix:
shard: ${{ fromJson(needs.sync.outputs.updated_shards) }}
uses: ./.github/workflows/tag_and_create_release.yml
secrets: inherit
with:
shard: ${{ matrix.shard }}
# Trigger a re-build of the docs after all shards were released.
build-docs:
needs:
- release
uses: ./.github/workflows/build_and_publish_docs.yml
secrets: inherit
with:
branch: master
# Cherry picks changes into `docs` branch of each changed shard(s) for PRs with the `kind:documentation` label
pick-docs:
runs-on: ubuntu-latest
needs:
- sync
if: needs.sync.outputs.updated_shards != '[]' && needs.sync.outputs.kind_docs == 'true'
strategy:
fail-fast: false
matrix:
shard: ${{ fromJson(needs.sync.outputs.updated_shards) }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: athena-framework/${{ matrix.shard }}
ref: docs
ssh-key: ${{ secrets.ATHENA_BOT_SSH_PRIV_KEY }}
- name: Cherry pick commit
run: |
set -euo pipefail
git config user.name "${{ vars.BOT_USER_NAME }}"
git config user.email "${{ vars.BOT_USER_EMAIL }}"
NEW_COMMIT=$(git ls-remote "git@github.com:athena-framework/${{ matrix.shard }}.git" HEAD | awk '{ print $1}')
git fetch origin $NEW_COMMIT
git cherry-pick $NEW_COMMIT
git push origin docs
# If there were shard related doc updates, trigger a re-build after all shards were synced.
build-shard-docs:
needs:
- sync # This needs to also depend on `sync` so we cn use its outputs even if it's redundant because of the dependency on `pick-docs`.
- pick-docs
if: needs.sync.outputs.kind_docs == 'true' && needs.sync.outputs.updated_shards != '[]'
uses: ./.github/workflows/build_and_publish_docs.yml
secrets: inherit
with:
branch: master
# If there were no shard related doc updates, simply trigger a re-build after `sync` job to handle updates to the root docs.
build-root-docs:
needs:
- sync
if: needs.sync.outputs.kind_docs == 'true' && needs.sync.outputs.updated_shards == '[]'
uses: ./.github/workflows/build_and_publish_docs.yml
secrets: inherit
with:
branch: master
# Build and deploy a development version of the docs to allow viewing docs for all un-released changes in `master`
build-dev-docs:
uses: ./.github/workflows/build_and_publish_docs.yml
secrets: inherit
with:
branch: dev
================================================
FILE: .github/workflows/tag_and_create_release.yml
================================================
on:
workflow_dispatch:
inputs:
shard:
description: 'Name of the shard to release'
type: string
required: true
workflow_call:
inputs:
shard:
description: 'Name of the shard to release'
type: string
required: true
jobs:
tag-and-create-release:
runs-on: ubuntu-latest
steps:
- uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
with:
ssh-private-key: ${{ secrets.ATHENA_BOT_SSH_PRIV_KEY }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: athena-framework/${{ inputs.shard }}
ssh-key: ${{ secrets.ATHENA_BOT_SSH_PRIV_KEY }}
path: shard
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: root
- name: Setup Changie
uses: miniscruff/changie-action@11bcad388e7973948cbcecb10863baf024d5f607 # v3.0.0
with:
version: v1.22.1
- name: Get the latest version
working-directory: root
id: current-release
run: |
TAG=$(changie latest --project "${{ inputs.shard }}" | cut -d'/' -f2)
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Tag release
working-directory: shard
run: |
set -euo pipefail
git config gpg.format ssh
git config user.signingkey "${{ vars.BOT_USER_SIGNING_KEY }}"
git config user.name "${{ vars.BOT_USER_NAME }}"
git config user.email "${{ vars.BOT_USER_EMAIL }}"
# Convert from GH repo name format into human readable format
SHARD_NAME=$(echo "${{ inputs.shard }}" | tr '-' ' ' | sed -e 's/\b./\u\0/g')
MESSAGE="Athena $SHARD_NAME ${{ steps.current-release.outputs.tag }}"
git tag -asm "$MESSAGE" ${{ steps.current-release.outputs.tag }}
git push --quiet origin ${{ steps.current-release.outputs.tag }}
# Be sure to reset `docs` branch back to current state of `master` as a release assumes the previously cherry-picked commits are now inherently included
git branch --quiet --force docs master
git push --quiet origin docs --force
- name: Create Release
working-directory: root
env:
GH_TOKEN: ${{ secrets.SYNC_TOKEN }}
run: |
# Cuts off first 2 lines since version number and date are redundant in this context.
# Then replace Author names with GH usernames for use in the GH release notes.
# See https://github.com/miniscruff/changie/discussions/610#discussioncomment-13917611 for reference.
tail -n+3 ".changes/${{ inputs.shard }}/${{ steps.current-release.outputs.tag }}.md" | \
perl -0777 -pe 's{\([^)]+\)\s*<!--\s*([^>]*\S)\s*-->}{ "(" . join(", ", map { s/^\s+|\s+$//g; s/^@?/@/; $_ } grep { length } split(/\s*,\s*/, $1)) . ")" }ge' | \
gh release create ${{ steps.current-release.outputs.tag }} \
--repo athena-framework/${{ inputs.shard }} \
--verify-tag \
--title ${{ steps.current-release.outputs.tag }} \
--notes-file -
================================================
FILE: .gitignore
================================================
.DS_Store
*.dwarf
/.shards/
/bin/
/lib/
/logs/
# Libraries don't need dependency lock
# Dependencies will be locked in application that uses them
/shard.lock
__pycache__
.cache
.venv
site/
coverage/
# Ignore `lib` symlink related to building docs
# This includes bundle docs, due to mkdocs-material project plugin limitations
/src/components/**/lib
/src/bundles/**/lib
================================================
FILE: .python-version
================================================
3.13
================================================
FILE: .typos.toml
================================================
[default]
extend-ignore-re = [
"(?Rm)^.*#\\s*spellchecker:disable-line$", # Allow disabling specific lines
"=[0-9A-F \n\r]{2}", # Disable checking Quoted-Printable encoded strings
]
[default.extend-words]
referer = "referrer"
ASPEC = "ASPEC"
[files]
extend-exclude = [
"src/components/routing/spec/fixtures/route_provider/*",
"src/components/mime/src/types/data.cr",
"src/components/mime/spec/fixtures/*",
]
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
First off, thank you for taking the time to contribute! Athena, and many other open source projects, would not be the same without you!
The following is intended to be a living document describing the guidelines for contributing to Athena, and its shards.
Athena makes use of the monorepo pattern, with each shard having its own read only repository.
As such, all contributions should directed towards this repository.
## Start Here
Find something that isn't working as expected? Have an idea for a new feature/enhancement? Want to improve the documentation?
If you answer "Yes" to any of these, you've come to the right place!
The first step is to search through the current [issues](https://github.com/athena-framework/athena/issues) and [pull requests](https://github.com/athena-framework/athena/pulls) to see if it has already been reported and/or resolved.
If your search comes up empty then feel free to create an issue, or if you're still not sure if you should make one, stop by the [Discord](https://discord.gg/TmDVPb3dmr) server to ask just to be sure; even if the answer is most likely always going to be yes.
## Issue Tracker
The [issue tracker](https://github.com/athena-framework/athena/issues) is the heart of the Athena. Use it for bugs, questions, proposals, and feature requests.
Please always **open a new issue before sending a pull request** if you want to add a new feature to Athena, unless it is a minor obvious fix, or is in relation to an already open & approved issue.
This reduces the likelihood of wasted effort, and ensures the end result is robust by being able to work out implementation details _before_ the work is started.
## Local Development
Before staring any local development, be sure to [fork](https://github.com/athena-framework/athena/fork) the repo, then create a branch to use for the related approved issue you're working on.
Due to Athena's usage of a monorepo, the same single repo can be used to contribute code to all shards.
In addition to Crystal itself, Athena makes use of [just](https://just.systems/man/en/introduction.html) as its command runner.
`just` provides a simple way of executing common commands needed for development.
Once you have it installed, and have cloned the monorepo, first install all the shard dependencies by running:
```sh
just install
```
And that's it, you are now ready to start coding!
From here there are some additional optional tools that will come in handy:
1. [typos](https://github.com/crate-ci/typos) - Source code spell checker, used as part of the `spellcheck` recipe.
1. [watchexec](https://github.com/watchexec/watchexec) - Executes commands in response to file modifications, used as part of the `watch` and `watch-spec` recipes.
1. [kcov](https://github.com/SimonKagstrom/kcov) - Code coverage tool, used to generate coverage reports/files as part of the `test` recipes.
1. [changie](https://changie.dev/) - Changelog management tool, used as part of the `change` recipe.
1. [uv](https://docs.astral.sh/uv/) - Python package manager, used for the `docs` related recipes.
**TIP:** Running `just` will provide a summary of available recipes.
### Development
Because of Athena's usage of a monorepo some interactions may be different than a normal shard.
Most things can be done from the root of the repo; no need to `cd` to whatever shard you're working on; can just go through `just`.
For exploratory work, the suggested workflow is to have your code in the related shard's entry point file.
E.g. `src/components/clock/src/athena-clock.cr` for the `clock` shard.
From here you can run `just watch clock` and that will re-run the file when changes are made.
This makes it simple to play around with early implementations before there is proper test coverage.
#### Testing
Similar to development itself, running the specs are also done through `just`: `just test clock` would run the spec suite for that shard, and generate coverage information if you have `kcov` installed.
The `watch-test` recipe can come in handy to provide quicker feedback while the tests are under development.
##### Athena Spec
Many Athena shards make use of [Athena Spec](https://athenaframework.org/Spec/) for their unit/integration tests.
This library provides an alternate DSL that is 100% compatible with the standard library's `Spec` module.
I.e. they can be used together seamlessly, using whatever DSL is more appropriate for what is being tested.
Being familiar with the base [ASPEC::TestCase](https://athenaframework.org/Spec/TestCase/) type will not only make reading the specs easier, but writing them as well.
It comes with various features to make the tests simpler, reusable, and extensible.
You may even want to use it in your own projects :wink:.
### Linting
Beyond testing, Athena makes use of various forms of linting, including:
* [ameba](https://github.com/crystal-ameba/ameba) for static code analysis
* [typos](https://github.com/crate-ci/typos) for spell checking
* The Crystal [formatter](https://crystal-lang.org/reference/guides/writing_shards.html#coding-style) for code formatting
All of these can be executed at once via the `just lint` recipe, but may also be ran individual as needed via their related `just` recipe.
### Documentation
Athena's [documentation](https://athenaframework.org/) site may be built locally via the `just build-docs` recipe.
Alternatively, a live-updating server may be started via the `just serve-docs` recipe.
## Opening a PR
At this point the code on your branch should have a passing test suite, including linting/spellchecking, and updated documentation if applicable. From here the only step left is to open a PR.
> **NOTE:** Once the PR is opened, please avoid force-pushing to that branch.
Athena comes with a PR template that should be filled out; being sure to reference the issue number in the context section. E.g. `Resolves #xxx`.
The changelog section should include all changes, both internal and external being sure to highlight breaking changes by prefixing the line with `**Breaking:**`.
Additionally, changes that affect end users should also have a `changie` change file. These can most easily be created by following along the prompts of `just change`.
Project maintainers can add the file(s) themselves if needed to move things along; just being sure to give proper attribution in the change file.
> **NOTE:** As of now you'll need to open the PR _before_ creating the change file in order to know what the PR number is.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2021 George Dietrich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# Athena
[](https://github.com/athena-framework/athena/actions/workflows/ci.yml)
[](https://github.com/athena-framework/framework/releases)
A monorepo representing the ecosystem of reusable, independent shards provided by Athena.
See [Athena Framework](https://github.com/athena-framework/framework) for the web framework created using these shards.
## Documentation
Checkout the [External Docs](https://athenaframework.org).
## Contributing
Read the general [Contributing Guide](./CONTRIBUTING.md) for information on how to get started.
## Contributors
- [George Dietrich](https://github.com/blacksmoke16) - creator and maintainer
================================================
FILE: UPGRADING.md
================================================
# Upgrading
The Athena ecosystem generally follows the [semver](https://semver.org/) pattern, but with some nuances due to its pre-1.0 status.
Minor releases are reserved for major breaking changes, usually along side new large features/refactors.
Patch releases contain backwards compatible features, bug fixes, and small breaking changes that are unlikely to affect the average user.
In either case, the changes you need to make in order to upgrade will be documented in files like this one; located within each component's [repository](https://github.com/orgs/athena-framework/repositories?q=topic%3Acomponent&type=source&language=crystal&sort=name) as applicable.
================================================
FILE: codecov.yml
================================================
comment:
layout: 'condensed_header, components, condensed_files, condensed_footer'
coverage:
status:
project:
default:
target: auto # Prevent total coverage from decreasing due to a PR
patch:
default:
target: 100% # Enforce all _new_ code is fully tested
component_management:
individual_components:
# Bundles
- component_id: mercure_bundle
name: Mercure Bundle
paths:
- src/bundles/mercure/**
# Components
- component_id: clock_component
name: Clock Component
paths:
- src/components/clock/**
- component_id: console_component
name: Console Component
paths:
- src/components/console/**
- component_id: dependency_injection_component
name: Dependency Injection Component
paths:
- src/components/dependency_injection/**
- component_id: dotenv_component
name: Dotenv Component
paths:
- src/components/dotenv/**
- component_id: event_dispatcher_component
name: Event Dispatcher Component
paths:
- src/components/event_dispatcher/**
- component_id: framework_component
name: Framework Component
paths:
- src/components/framework/**
- component_id: http_component
name: HTTP Component
paths:
- src/components/http/**
- component_id: http_kernel_component
name: HTTP Kernel Component
paths:
- src/components/http_kernel/**
- component_id: image_size_component
name: Image Size Component
paths:
- src/components/image_size/**
- component_id: mercure_component
name: Mercure Component
paths:
- src/components/mercure/**
- component_id: mime_component
name: MIME Component
paths:
- src/components/mime/**
- component_id: negotiation_component
name: Negotiation Component
paths:
- src/components/negotiation/**
- component_id: routing_component
name: Routing Component
paths:
- src/components/routing/**
- component_id: serializer_component
name: Serializer Component
paths:
- src/components/serializer/**
- component_id: spec_component
name: Spec Component
paths:
- src/components/spec/**
- component_id: validator_component
name: Validator Component
paths:
- src/components/validator/**
================================================
FILE: docs/README.md
================================================
## Athena
Athena is a collection of general-purpose, robust, independent, and reusable components with the goal of powering a software ecosystem.
These include:
* [Clock](/Clock/) (`ACLK`) - Decouples applications from the system clock
* [Console](/Console/) (`ACON`) - Allows the creation of CLI based commands
* [Contracts](/Contracts/) (`ACTR`) - A set of abstractions extracted out of the Athena components
* [DependencyInjection](/DependencyInjection/) (`ADI`) - Robust dependency injection service container framework
* [Dotenv](/Dotenv/) - Registers environment variables from a `.env` file
* [EventDispatcher](/EventDispatcher/) (`AED`) - A Mediator and Observer pattern event library
* [Framework](/Framework/) (`ATH`) - Integrates the components into a single cohesive, flexible, and modular framework
* [HTTP](/HTTP/) (`AHTTP`) - Shared common HTTP abstractions/utilities
* [HTTPKernel](/HTTPKernel/) (`AHK`) - Provides a structured process for converting a Request into a Response
* [ImageSize](/ImageSize/) (`AIS`) - Measures the size of various image formats
* [Mercure](/Mercure/) (`AMC`) - Allows easily pushing updates to web browsers and other HTTP clients using the Mercure protocol
* [MIME](/MIME/) (`AMIME`) - Allows manipulating `MIME` messages
* [Negotiation](/Negotiation/) (`ANG`) - Framework agnostic content negotiation library
* [Routing](/Routing/) (`ART`) - A performant and robust HTTP based routing library/framework
* [Serializer](/Serializer/) (`ASR`) - Object (de)serialization library
* [Spec](/Spec/) (`ASPEC`) - Common/helpful [Spec](https://crystal-lang.org/api/Spec.html) compliant testing utilities
* [Validator](/Validator/) (`AVD`) - Object/value validation library
These components may be used on their own to aid in existing projects or integrated into existing (or new) frameworks.
Additionally, some bundles are also provided that provide opt-in integrations of select components:
* [MercureBundle](/MercureBundle/) (`ABM`) - Integrations the Mercure component into the framework.
TIP: Each component may also define additional shortcut aliases. Check the `Aliases` page of each component in the [API Reference](./api_reference.md) for more information.
## Athena Framework
Athena also provides the [Framework](./getting_started/README.md) component that integrates select components into a single cohesive, flexible, and modular framework.
It is designed in such a way to be non-intrusive and not require a strict organizational convention in regards to how a project is setup;
this allows it to use a minimal amount of setup boilerplate while not preventing it for more complex projects.
Not every component needs to be used or understood to start using the framework, only those which are required for the task at hand.
### Feature Highlights
Athena Framework has quite a few unique features that set it a part from other Crystal frameworks:
* Follows the SOLID principles to encourage good software design
* Architected in such a way to allow maximum flexibility without needing to fight against the framework
* Uses annotations as a means of extension/customization
* Built-in testing utilities
TIP: The [demo](https://github.com/athena-framework/demo) application serves as a good example of what an application using the framework could look like.
## Resources
* [Discord Server](https://discord.gg/TmDVPb3dmr)
* [GitHub Repository](https://github.com/athena-framework/athena)
================================================
FILE: docs/api_reference.md
================================================
Links to the API docs of each component may be found in this section.
These can be a good reference for more in-dept, component specific information, or how when using the component outside of the framework.
================================================
FILE: docs/bundle_reference.md
================================================
Bundles integrate components into the framework by registering services, configuring defaults, and wiring up dependencies via the [dependency injection](/DependencyInjection) component.
Each bundle defines a schema that describes its available configuration options, allowing behavior to be customized without modifying code.
See [Getting Started > Configuration](./getting_started/configuration.md#bundles) for more information on the role bundles play in Athena.
The built-in [Framework Bundle](/Framework/Bundle/) schema is documented in the framework API docs.
Third-party and standalone bundles, such as the [Mercure Bundle](/MercureBundle), are documented in this section.
================================================
FILE: docs/css/index.css
================================================
/* https://mkdocstrings.github.io/crystal/styling.html#recommended-styles */
/* Indentation of sub-items */
div.doc-contents:not(.first) {
padding-left: 15px;
border-left: 4px solid rgba(230, 230, 230);
}
/* Additional Customizations */
body {
font-size: 1rem;
}
/* Increased spacing between macro & instance methods. */
.doc-macro + .doc-macro,
.doc-instance_method + .doc-instance_method {
margin-top: 3em;
}
.md-typeset pre>code::-webkit-scrollbar {
height: 0.4em;
}
/* Slightly more compact headings */
h1.doc-heading {
font-size: 1.3rem;
}
h3.doc-heading {
font-size: 0.85rem;
background: var(--md-code-bg-color);
border-radius: 2px;
}
h3.schema-heading {
margin: 0;
}
.schema-default, .schema-type {
display: inline;
}
.schema-type p {
display: inline;
}
.schema-default p {
display: inline;
}
/* Make content grid a bit wider (but never wider than the screen) */
.md-grid {
max-width: min(80rem, 100vw);
}
/* Make sure page content doesn't get too wide on big 4k monitors */
.md-content {
max-width: 85ch;
}
================================================
FILE: docs/css/monorepo.css
================================================
/* Hide latest release since the monorepo is the old framework repo and still has tags */
.md-source__fact--version {
display: none;
}
================================================
FILE: docs/getting_started/README.md
================================================
Athena Framework does not have any other dependencies outside of Crystal and Shards.
It is designed in such a way to be non-intrusive and not require a strict organizational convention in regards to how a project is setup;
this allows it to use a minimal amount of setup boilerplate while not preventing it for more complex projects.
## Install Athena Framework
Add the framework component to your `shard.yml`:
```yaml
dependencies:
athena:
github: athena-framework/framework
version: ~> 0.22.0
```
Then run `shards install`.
This will install the framework component and its required component dependencies.
Finally require it via `require "athena"`, then are all set to starting using the framework, starting with [Routing & HTTP](./routing.md).
TIP: Check out the [skeleton](https://github.com/athena-framework/skeleton) template repository to get up and running quickly.
================================================
FILE: docs/getting_started/commands.md
================================================
The Athena Framework comes with a built-in integration with the [Athena::Console](/Console) component.
This integration can be a way to define alternate entry points into your business logic,
such as for use with scheduled jobs (Cron, Airflow, etc), or one-off internal/administrative things (running migrations, creating users, etc)
all the while sharing the same dependencies due to it also leveraging [dependency injection](../why_athena.md#dependency-injection).
## Basic Usage
Similar to [event listeners](./middleware.md#event-listeners), console commands can simply be registered as a service to be automatically registered.
If using the preferred [ACONA::AsCommand](/Console/Annotations/AsCommand) annotation, they are registered in a lazy fashion, meaning only the command(s) you execute will actually be instantiated.
```crystal
@[ADI::Register]
@[ACONA::AsCommand("admin:create-user", description: "Creates a new internal user")]
class AdminCreateUser < ACON::Command
# A constructor can be defined to leverage existing services if applicable
#def initialize(
# @some_service : MyService
#)
# # Just be sure to call `super()`!
# super()
#end
# Configure the command by adding arguments, options, aliases, etc.
protected def configure : Nil
self
.argument("id", :required, "The employee's ID")
.argument("name", :required, "The user's name")
.argument("email", :optional, "The user's email. Assumed to be first.last if not provided")
.option("admin", nil, :none, "If the user should be created as an internal admin")
end
protected def execute(input : ACON::Input::Interface, output : ACON::Output::Interface) : ACON::Command::Status
# Provides a standardized format for how to display text in the terminal
style = ACON::Style::Athena.new input, output
input.argument "id", Int32 # => 12
name = input.argument "name" # => "George Dietrich"
input.argument "email" # => nil
input.option "admin", Bool # => true
# Implement your business logic
style.success "Successfully created a user for #{name}!"
# Note the command executed successfully
Status::SUCCESS
end
end
```
From here, if the application was created using the [skeleton](https://github.com/athena-framework/skeleton), commands can be executed via `shards run console -- admin:create-user 12 "George Dietrich" --admin`.
Otherwise [ATH.run_console](/Framework/#Athena::Framework.run_console) can be used for your [entrypoint](/Console/#entrypoint) file.
NOTE: During *development* the console binary needs to re-build the application in order to have access to the changes made since last execution.
When deploying a *production* console binary, or if not doing any new console command dev, build it with the `--release` flag for increased performance.
## Built-in Commands
The framework also comes with some helpful built-in commands to either help with debugging, or provide framework specific features.
See each command within the [ATH::Commands](/Framework/Commands) namespace for more information.
================================================
FILE: docs/getting_started/configuration.md
================================================
Some features need to be configured;
either to enable/control how they work, or to customize the default functionality.
The [ATH.configure](/Framework/top_level/#Athena::Framework:configure(config)) macro is the primary entrypoint for configuring Athena Framework applications.
It is used in conjunction with the related [bundle schema](/Framework/Bundle/Schema/Cors/Defaults/) that defines the possible configuration properties:
```crystal
ATH.configure({
framework: {
cors: {
enabled: true,
defaults: {
allow_credentials: true,
allow_origin: ["https://app.example.com"],
expose_headers: ["X-Transaction-ID X-Some-Custom-Header"],
},
},
},
})
```
In this example we enable the [CORS Listener](/Framework/Listeners/CORS), as well as configure it to function as we desire.
However you may be wondering "how do I know what configuration properties are available?" or "what is that 'bundle schema' thing mentioned earlier?".
For that we need to introduce the concept of a `Bundle`.
## Bundles
It should be well known by now that the components that make up Athena's ecosystem are independent and usable outside of the Athena Framework itself.
However because they are made with the assumption that the entire framework will not be available, there has to be something that provides the tighter integration into the rest of the framework that makes it all work together so nicely.
Bundles in the Athena Framework provide the mechanism by which external code can be integrated into the rest of the framework.
This primarily involves wiring everything up via the [Athena::DependencyInjection](/DependencyInjection) component.
But it also ties back into the configuration theme by allowing the user to control _how_ things are wired up and/or function at runtime.
What makes the bundle concept so powerful and flexible is that it operates at the compile time level.
E.g. if feature(s) are disabled in the configuration, then the types related to those feature(s) will not be included in the resulting binary at all.
Similarly, the configuration values can be accessed/used as constructor arguments to the various services, something a runtime approach would not allow.
TODO: Expand upon bundle internals and how to create custom bundles.
### Schemas
Each bundle is responsible for defining a "schema" that represents the possible configuration properties that relate to the services provided by that bundle.
Each bundle also has a name that is used to namespace the configuration passed to `ATH.configure`.
From there, the keys maps to the downcase snakecased of the types found within the bundle's schema.
For example, the [Framework Bundle](/Framework/Bundle) used in the previous example, exposes `cors` and `format_listener` among others as part of its schema.
NOTE: Bundles and schemas are not something the average end user is going to need to define/manage themselves other than register/configure to fit their needs.
#### Validation
The compile time nature of bundles also extends to how their schemas are validated.
Bundles will raise a compile time error if the provided configuration values are invalid according to its schema.
For example:
```crystal
10 | allow_credentials: 10,
^
Error: Expected configuration value 'framework.cors.defaults.allow_credentials' to be a 'Bool', but got 'Int32'.
```
This also works for nested values:
```crystal
10 | allow_origin: [10, "https://app.example.com"] of String,
^
Error: Expected configuration value 'framework.cors.defaults.allow_origin[0]' to be a 'String', but got 'Int32'.
```
Or if the schema defines a value that is not nilable nor has a default:
```crystal
10 | property some_property : String
^------------
Error: Required configuration property 'framework.some_property : String' must be provided.
```
It can also call out unexpected keys:
```crystal
10 | foo: "bar",
^
Error: Encountered unexpected property 'framework.cors.foo' with value '"bar"'.
```
Hash configuration values are unchecked so are best used for unstructured data.
If you have a fixed set of related configuration, consider using [object_of](/DependencyInjection/Extension/Schema/#Athena::DependencyInjection::Extension::Schema:object_of(name,*)).
#### Multi-Environment
In most cases, the configuration for each bundle is likely going to vary one environment to another.
Values that change machine to machine should ideally be leveraging environmental variables.
However, there are also cases where the underlying configuration should be different.
E.g. locally use an in-memory cache while using redis in other environments.
To handle this, `ATH.configure` may be called multiple times, with the last call taking priority.
The configuration is deep merged together as well, so only the configuration you wish to alter needs to be defined.
However hash/array/namedTuple values are not.
Normal compile time logic may be used to make these conditional as well.
E.g. basing things off `--release` or `--debug` flags vs the environment.
```crystal
ATH.configure({
framework: {
cors: {
defaults: {
allow_credentials: true,
allow_origin: ["https://app.example.com"],
expose_headers: ["X-Transaction-ID", "X-Debug-Header"],
},
},
},
})
# Exclude the debug header in prod, but retain the other two configuration property values
{% if env(Athena::ENV_NAME) == "prod" %}
ATH.configure({
framework: {
cors: {
defaults: {
expose_headers: ["X-Transaction-ID"],
},
},
},
})
{% end %}
# Do this other thing if in a non-release build
{% unless flag? "release" %}
ATH.configure({...})
{% end %}
```
TIP: Consider abstracting the additional `ATH.configure` calls to their own files, and `require` them.
This way things stay pretty organized, without needing large conditional logic blocks.
## Parameters
Sometimes the same configuration value is used in several places within `ATH.configure`.
Instead of repeating it, you can define it as a "parameter", which represents reusable configuration values.
Parameters are intended for values that do not change between machines, and control the application's behavior, e.g. the sender of notification emails, what features are enabled, or other high level application level values.
Parameters should _NOT_ be used for values that rarely change, such as the max amount of items to return per page.
These types of values are better suited to being a [constant](https://crystal-lang.org/reference/syntax_and_semantics/constants.html) within the related type.
Similarly, infrastructure related values that change from one machine to another, e.g. development machine to production server, should be defined using environmental variables.
Parameters can be defined using the special top level `parameters` key within `ATH.configure`.
```crystal
ATH.configure({
parameters: {
# The parameter name is an arbitrary string,
# but is suggested to use some sort of prefix to differentiate your parameters
# from the built-in framework parameters, as well as other bundles.
"app.admin_email": "admin@example.com",
# Boolean param
"app.enable_v2_protocol": true,
# Collection param
"app.supported_locales": ["en", "es", "de"],
},
})
```
The parameter value may be any primitive type, including strings, bools, hashes, arrays, etc.
From here they can be used when configuring a bundle via enclosing the name of the parameter within `%`.
For example:
```crystal
ATH.configure({
some_bundle: {
email: "%app.admin_email%",
},
})
```
TIP: Parameters may also be [injected](/DependencyInjection/Register/#Athena::DependencyInjection::Register--parameters) directly into services via their constructor.
## Custom Annotations
Athena integrates the [Athena::DependencyInjection](/DependencyInjection) component's ability to define custom annotation configurations.
This feature allows developers to define custom annotations, and the data that should be read off of them, then apply/access the annotations on [ATH::Controller](/Framework/Controller) and/or [AHK::Action](/HTTPKernel/Action)s.
This is a powerful feature that allows for almost limitless flexibility/customization.
Some ideas include: storing some value in the request attributes and raise an exception or invoke some external service; all based on the presence/absence of it, a value read off of it, or either/both of those in-conjunction with an external service.
For example:
```crystal
require "athena"
# Define our configuration annotation with an optional `name` argument.
# A default value can also be provided, or made not nilable to be considered required.
ADI.configuration_annotation MyAnnotation, name : String? = nil
# Define and register our listener that will do something based on our annotation.
@[ADI::Register]
class MyAnnotationListener
def initialize(
@annotation_resolver : ATH::AnnotationResolver,
); end
@[AEDA::AsEventListener]
def on_view(event : AHK::Events::View) : Nil
# Represents all custom annotations applied to the current AHK::Action + controller class.
ann_configs = @annotation_resolver.action_annotations(event.request)
# Check if this action has the annotation
unless ann_configs.has? MyAnnotation
# Do something based on presence/absence of it.
# Would be executed for `ExampleController#one` since it does not have the annotation applied.
end
my_ann = ann_configs[MyAnnotation]
# Access data off the annotation.
if my_ann.name == "Fred"
# Do something if the provided name is/is not some value.
# Would be executed for `ExampleController#two` since it has the annotation applied, and name value equal to "Fred".
end
end
end
class ExampleController < ATH::Controller
@[ARTA::Get("one")]
def one : Int32
1
end
@[ARTA::Get("two")]
@[MyAnnotation(name: "Fred")]
def two : Int32
2
end
end
ATH.run
```
### Pagination
<!-- TODO: Move this to a cookbook/how-to type of page/section? -->
A good example use case for custom annotations is the creation of a `Paginated` annotation that can be applied to controller actions to have them be paginated via the listener. Generic pagination can be implemented via listening on the [view](./middleware.md#4-view-event) event which exposes the value returned via the related controller action.
```crystal
# Define our configuration annotation with the default pagination values.
# These values can be overridden on a per endpoint basis.
ADI.configuration_annotation Paginated, page : Int32 = 1, per_page : Int32 = 100, max_per_page : Int32 = 1000
# Define and register our listener that will handle paginating the response.
@[ADI::Register]
struct PaginationListener
private PAGE_QUERY_PARAM = "page"
private PER_PAGE_QUERY_PARAM = "per_page"
def initialize(
@annotation_resolver : ATH::AnnotationResolver,
); end
# Use a high priority to ensure future listeners are working with the paginated data
@[AEDA::AsEventListener(priority: 255)]
def on_view(event : AHK::Events::View) : Nil
# Return if the endpoint is not paginated.
return unless (pagination = @annotation_resolver.action_annotations(event.request)[Paginated]?)
# Return if the action result is not able to be paginated.
return unless (action_result = event.action_result).is_a? Indexable
request = event.request
# Determine pagination values; first checking the request's query parameters,
# using the default values in the `Paginated` object if not provided.
page = request.query_params[PAGE_QUERY_PARAM]?.try &.to_i || pagination.page
per_page = request.query_params[PER_PAGE_QUERY_PARAM]?.try &.to_i || pagination.per_page
# Raise an exception if `per_page` is higher than the max.
raise AHK::Exception::BadRequest.new "Query param 'per_page' should be '#{pagination.max_per_page}' or less." if per_page > pagination.max_per_page
# Paginate the resulting data.
# In the future a more robust pagination service could be injected
# that could handle types other than `Indexable`, such as
# ORM `Collection` objects.
end_index = page * per_page
start_index = end_index - per_page
# Paginate and set the action's result.
event.action_result = action_result[start_index...end_index]
end
end
class ExampleController < ATH::Controller
@[ARTA::Get("values")]
@[Paginated(per_page: 2)]
def get_values : Array(Int32)
(1..10).to_a
end
end
ATH.run
# GET /values # => [1, 2]
# GET /values?page=2 # => [3, 4]
# GET /values?per_page=3 # => [1, 2, 3]
# GET /values?per_page=3&page=2 # => [4, 5, 6]
```
================================================
FILE: docs/getting_started/error_handling.md
================================================
## HTTP Exceptions
Exception handling in the Athena Framework is similar to exception handling in any Crystal program, with the addition of a new unique exception type, [AHK::Exception::HTTPException](/HTTPKernel/Exception/HTTPException).
Custom `HTTP` errors can also be defined by inheriting from `AHK::Exception::HTTPException` or a child type.
A use case for this could be allowing additional data/context to be included within the exception.
Non `AHK::Exception::HTTPException` exceptions are represented as a `500 Internal Server Error`.
When an exception is raised, the framework emits the [Exception](./middleware.md#8-exception-handling) event to allow an opportunity for it to be handled.
By default these exceptions will return a `JSON` serialized version of the exception, via [AHK::ErrorRenderer](/HTTPKernel/ErrorRenderer), that includes the message and code; with the proper response status set.
If the exception goes unhandled, i.e. no listener sets an [AHTTP::Response](/HTTP/Response) on the event, then the request is finished and the exception is re-raised.
```crystal
require "athena"
class ExampleController < ATH::Controller
@[ARTA::Get("/divide/{num1}/{num2}")]
def divide(num1 : Int32, num2 : Int32) : Int32
num1 // num2
end
@[ARTA::Get("/divide_rescued/{num1}/{num2}")]
def divide_rescued(num1 : Int32, num2 : Int32) : Int32
num1 // num2
# Rescue a non `AHK::Exception::HTTPException`
rescue ex : DivisionByZeroError
# in order to raise an `AHK::Exception::HTTPException` to provide a better error message to the client.
raise AHK::Exception::BadRequest.new "Invalid num2: Cannot divide by zero"
end
end
ATH.run
# GET /divide/10/0 # => {"code":500,"message":"Internal Server Error"}
# GET /divide_rescued/10/0 # => {"code":400,"message":"Invalid num2: Cannot divide by zero"}
# GET /divide_rescued/10/10 # => 1
```
## Logging
Logging is handled via Crystal's [Log](https://crystal-lang.org/api/Log.html) module. Athena Framework logs when a request matches a controller action, as well as any exception. This of course can be augmented with additional application specific messages.
```bash
2022-01-08T20:44:18.134423Z INFO - athena.routing: Server has started and is listening at http://0.0.0.0:3000
2022-01-08T20:44:19.773376Z INFO - athena.routing: Matched route 'example_controller_divide' -- route: "example_controller_divide", route_parameters: {"_route" => "example_controller_divide", "_controller" => "ExampleController#divide", "num1" => "10", "num2" => "0"}, request_uri: "/divide/10/0", method: "GET"
2022-01-08T20:44:19.892748Z ERROR - athena.routing: Uncaught exception #<DivisionByZeroError:Division by 0> at /usr/lib/crystal/int.cr:141:7 in 'check_div_argument'
Division by 0 (DivisionByZeroError)
from /usr/lib/crystal/int.cr:141:7 in 'check_div_argument'
from /usr/lib/crystal/int.cr:105:5 in '//'
from src/components/framework/src/athena.cr:206:5 in 'divide'
from src/components/framework/src/ext/routing/annotation_route_loader.cr:8:5 in '->'
from /usr/lib/crystal/primitives.cr:266:3 in 'execute'
from src/components/framework/src/route_handler.cr:76:16 in 'handle_raw'
from src/components/framework/src/route_handler.cr:19:5 in 'handle'
from src/components/framework/src/athena.cr:161:27 in '->'
from /usr/lib/crystal/primitives.cr:266:3 in 'process'
from /usr/lib/crystal/http/server.cr:515:5 in 'handle_client'
from /usr/lib/crystal/http/server.cr:468:13 in '->'
from /usr/lib/crystal/primitives.cr:266:3 in 'run'
from /usr/lib/crystal/fiber.cr:98:34 in '->'
from ???
2022-01-08T20:45:10.803001Z INFO - athena.routing: Matched route 'example_controller_divide_rescued' -- route: "example_controller_divide_rescued", route_parameters: {"_route" => "example_controller_divide_rescued", "_controller" => "ExampleController#divide_rescued", "num1" => "10", "num2" => "0"}, request_uri: "/divide_rescued/10/0", method: "GET"
2022-01-08T20:45:10.923945Z WARN - athena.routing: Uncaught exception #<Athena::Framework::Exception::BadRequest:Invalid num2: Cannot divide by zero> at src/components/framework/src/athena.cr:215:5 in 'divide_rescued'
Invalid num2: Cannot divide by zero (Athena::Framework::Exception::BadRequest)
from src/components/framework/src/athena.cr:215:5 in 'divide_rescued'
from src/components/framework/src/ext/routing/annotation_route_loader.cr:8:5 in '->'
from /usr/lib/crystal/primitives.cr:266:3 in 'execute'
from src/components/framework/src/route_handler.cr:76:16 in 'handle_raw'
from src/components/framework/src/route_handler.cr:19:5 in 'handle'
from src/components/framework/src/athena.cr:161:27 in '->'
from /usr/lib/crystal/primitives.cr:266:3 in 'process'
from /usr/lib/crystal/http/server.cr:515:5 in 'handle_client'
from /usr/lib/crystal/http/server.cr:468:13 in '->'
from /usr/lib/crystal/primitives.cr:266:3 in 'run'
from /usr/lib/crystal/fiber.cr:98:34 in '->'
from ???
2022-01-08T20:45:14.132652Z INFO - athena.routing: Matched route 'example_controller_divide_rescued' -- route: "example_controller_divide_rescued", route_parameters: {"_route" => "example_controller_divide_rescued", "_controller" => "ExampleController#divide_rescued", "num1" => "10", "num2" => "10"}, request_uri: "/divide_rescued/10/10", method: "GET"
```
#### Customization
By default the Athena Framework utilizes the default [Log::Formatter](https://crystal-lang.org/api/Log/Formatter.html) and [Log::Backend](https://crystal-lang.org/api/Log/Backend.html)s Crystal defines. This of course can be customized via interacting with Crystal's [Log](https://crystal-lang.org/api/Log.html) module. It is also possible to control what exceptions, and with what severity, will be logged by redefining the `log_exception` method within [AHK::Listeners::Error](/HTTPKernel/Listeners/Error).
TIP: Since `AHK::Listeners::Error` logs already include the error message and first line of the trace, consider defining a custom [Log Formatter](https://crystal-lang.org/api/Log/Formatter.html) that excludes the `exception` to have shorter, single line error logs in console:
```crystal
Log.define_formatter SingleLineFormatter, "#{timestamp} #{severity} - #{source(after: ": ")}#{message}" \
"#{data(before: " -- ")}#{context(before: " -- ")}"
# 2024-03-04T05:30:29.329041Z INFO - athena.framework: Server has started and is listening at http://0.0.0.0:3000
# 2024-03-04T05:30:37.568264Z INFO - athena.framework: Matched route 'view_controller_bar' -- route: "view_controller_bar", route_parameters: {"_route" => "view_controller_bar", # "_controller" => "ViewController#bar"}, request_uri: "/bar", method: "GET"
# 2024-03-04T05:30:40.280070Z INFO - athena.framework: Matched route 'view_controller_foo' -- route: "view_controller_foo", route_parameters: {"_route" => "view_controller_foo", # "_controller" => "ViewController#foo"}, request_uri: "/foo", method: "GET"
# 2024-03-04T05:30:40.351541Z ERROR - athena.framework: Uncaught exception #<Athena::Framework::Exception::Logic:Failed to serialize response body. Did you forget to include # either `JSON::Serializable` or `ASR::Serializable`?> at src/components/framework/src/view/view_handler.cr:166:21 in 'init_response'
# 2024-03-04T05:30:41.281275Z INFO - athena.framework: Matched route 'view_controller_foo' -- route: "view_controller_foo", route_parameters: {"_route" => "view_controller_foo", # "_controller" => "ViewController#foo"}, request_uri: "/foo", method: "GET"
# 2024-03-04T05:30:41.282632Z ERROR - athena.framework: Uncaught exception #<Athena::Framework::Exception::Logic:Failed to serialize response body. Did you forget to include # either `JSON::Serializable` or `ASR::Serializable`?> at src/components/framework/src/view/view_handler.cr:166:21 in 'init_response'
# 2024-03-04T05:30:43.886367Z INFO - athena.framework: Matched route 'view_controller_bar' -- route: "view_controller_bar", route_parameters: {"_route" => "view_controller_bar", # "_controller" => "ViewController#bar"}, request_uri: "/bar", method: "GET"
```
================================================
FILE: docs/getting_started/middleware.md
================================================
At a high level the Athena Framework's job is *to interpret a request and create the appropriate response based on your application logic*.
Conceptually this could be broken down into three steps:
1. Consume the request
2. Apply application logic to determine what the response should be
3. Return the response
Steps 1 and 3 are handled via Crystal's [HTTP::Server](https://crystal-lang.org/api/HTTP/Server.html), while step 2 is where the framework fits in.
## Events
Athena Framework is an event based framework, meaning it emits various events via the [Event Dispatcher](/EventDispatcher) component during the life-cycle of a request.
These events are listened on internally in order to handle each request; custom listeners on these events can also be registered.
The flow of a request, and the related events that are dispatched, is depicted below in a visual format:

### 1. Request Event
The very first event that is dispatched is the [AHK::Events::Request](/HTTPKernel/Events/Request) event and can have a variety of listeners. The primary purpose of this event is to create an [AHTTP::Response](/HTTP/Response/) directly, or to add information to the requests' attributes; a simple key/value store tied to request instance accessible via [AHTTP::Request#attributes](/HTTP/Request/#Athena::HTTP::Request#attributes).
In some cases the listener may have enough information to return an [AHTTP::Response](/HTTP/Response/) immediately. An example of this would be the [ATH::Listeners::CORS](/Framework/Listeners/CORS/) listener. If enabled it is able to return a `CORS` preflight response even before routing is invoked.
WARNING: If an [AHTTP::Response](/HTTP/Response/) is returned at this stage, the flow of the request skips directly to the [response](#5-response-event) event. Future `Request` event listeners will not be invoked either.
Another use case for this event is populating additional data into the request's attributes; such as the locale or format of the request.
!!! example "Request event in the Athena Framework"
This is the event that [AHK::Listeners::Routing](/HTTPKernel/Listeners/Routing/) listens on to determine which [ATH::Controller](/Framework/Controller/)/[AHK::Action](/HTTPKernel/Action/) pair should handle the request.
See [ATH::Controller](/Framework/Controller/) for more details on routing.
### 2. Action Event
The next event to be dispatched is the [AHK::Events::Action](/HTTPKernel/Events/Action/) event, assuming a response was not already returned within the [request](#1-request-event) event.
This event is dispatched after the related controller/action pair is determined, but before it is executed.
This event is intended to be used when a listener requires information from the related [AHK::Action](/HTTPKernel/Action/); such as reading [custom annotations](./configuration.md#custom-annotations).
### 3. Invoke the Controller Action
This next step is not an event, but a important concept within the Athena Framework nonetheless; executing the controller action related to the current request.
#### Argument Resolution
Before the controller action can be invoked, the arguments, if any, to pass to it need to be determined.
This is achieved via an [AHK::Controller::ArgumentResolverInterface](/HTTPKernel/Controller/ArgumentResolverInterface/) that facilitates gathering all the arguments.
One or more [ATHR::Interface](/Framework/Controller/ValueResolvers/Interface/) will then be used to resolve each specific argument's value.
Checkout [ATH::Controller::ValueResolvers](/Framework/Controller/ValueResolvers/) for a summary of the built-in resolvers, and the order in which they are invoked.
Custom value resolves may be created & registered to extend this functionality.
TODO: An additional event could possibly be added after the arguments have been resolved, but before invoking the controller action.
#### Execute the Controller Action
The job of a controller action is to apply business/application logic to build a response for the related request; such as an HTML page, a JSON string, or anything else. How/what exactly this should be is up to the developer creating the application.
#### Handle the Response
The type of the value returned from the controller action determines what happens next. If the value is an [AHTTP::Response](/HTTP/Response/), then it is used as is, skipping directly to the [response](#5-response-event) event. However, if the value is _NOT_ an `AHTTP::Response`, then the [view](#4-view-event) is dispatched (since the framework _needs_ an `AHTTP::Response` in order to have something to send back to the client).
### 4. View Event
The [AHK::Events::View](/HTTPKernel/Events/View/) event is only dispatched when the controller action does _NOT_ return an [AHTTP::Response](/HTTP/Response/). The purpose of this event is to turn the controller action's return value into an `AHTTP::Response`.
An [ATH::View](/Framework/View/) may be used to customize the response, e.g. setting a custom response status and/or adding additional headers; while keeping the controller action response data intact.
This event is intended to be used as a "View" layer; allowing scalar values/objects to be returned while listeners convert that value to the expected format (e.g. JSON, HTML, etc.). See the [negotiation](./routing.md#content-negotiation) component for more information on this feature.
!!! example "View event in the Athena Framework"
By default the framework will JSON serialize any non [AHTTP::Response](/HTTP/Response/) values.
### 5. Response Event
The end goal of the Athena Framework is to return an [AHTTP::Response](/HTTP/Response/) back to the client; which might be created within the [request](#1-request-event) event, returned from the related controller action, or set within the [view](#4-view-event) event. Regardless of how the response was created, the [AHK::Events::Response](/HTTPKernel/Events/Response/) event is dispatched directly after.
The intended use case for this event is to allow for modifying the response object in some manner. Common examples include: add/edit headers, add cookies, change/compress the response body.
### 6. Return the Response
The raw [HTTP::Server::Response](https://crystal-lang.org/api/HTTP/Server/Response.html) object is never directly exposed. The reasoning for this is to allow listeners to mutate the response before it is returned as mentioned in the [response](#5-response-event) event section. If the raw response object was exposed, whenever any data is written to it it'll immediately be sent to the client and the status/headers will be locked; as mentioned in the Crystal API docs:
> The response `#status` and `#headers` must be configured before writing the response body. Once response output is written, changing the `#status` and `#headers` properties has no effect.
Each [AHTTP::Response](/HTTP/Response/) has a [AHTTP::Response::Writer](/HTTP/Response/Writer/) instance that determines _how_ the response should be written to the raw response's IO. By default it is written directly, but can be customized via the [response](#5-response-event) event, such as for compression.
### 7. Terminate Event
The final event to be dispatched is the [AHK::Events::Terminate](/HTTPKernel/Events/Terminate/) event. This is event is dispatched _after_ the response has been sent to the user.
The intended use case for this event is to perform some "heavy" action after the user has received the response; as to not affect the response time of the request. E.x. queuing up emails or logs to be sent/written after a successful request.
### 8. Exception Handling
If an exception is raised at anytime while a request is being handled, the [AHK::Events::Exception](/HTTPKernel/Events/Exception/) is dispatched. The purpose of this event is to convert the exception into an [AHTTP::Response](/HTTP/Response/). This is globally handled via an [AHK::ErrorRendererInterface](/HTTPKernel/ErrorRendererInterface/), with the default being to JSON serialize the exception.
It is also possible to handle specific error states differently by registering multiple exception listeners to handle each case. An example of this could be to invoke some special logic only if the exception is of a specific type.
## Event Listeners
Unlike other frameworks, Athena Framework leverages event based middleware instead of a pipeline based approach.
The primary use case for event listeners is to tap into the life-cycle of the request, such as adding common headers, setting state extracted from the request, or whatever else the application requires.
These can be created by creating a type annotated with [ADI::Register](/DependencyInjection/Register), then annotating one or more methods with [AEDA::AsEventListener](/EventDispatcher/Annotations/AsEventListener).
```crystal
require "athena"
@[ADI::Register]
class CustomListener
@[AEDA::AsEventListener]
def on_response(event : AHK::Events::Response) : Nil
event.response.headers["FOO"] = "BAR"
end
end
class ExampleController < ATH::Controller
get "/" do
"Hello World"
end
end
ATH.run
# GET / # => Hello World (with `FOO => BAR` header)
```
Similarly, the framework itself is implemented using the same features available to the users.
Thus it is very easy to run specific listeners before/after the built-in ones if so desired.
TIP: Check out the `debug:event-dispatcher` [command](./commands.md) for an easy way to see all the listeners and the order in which they are executed.
TIP: A single event listener may listen on multiple events. Instance variables can be used to share state between the events.
WARNING: The "type" of the listener has an effect on its behavior!
When a `struct` service is retrieved or injected into a type, it will be a copy of the one in the SC (passed by value).
This means that changes made to it in one type, will *NOT* be reflected in other types.
A `class` service on the other hand will be a reference to the one in the SC. This allows it to share state between services.
## Custom Events
Using events can be a helpful design pattern to allow for code that is easily extensible.
An event represents something _has happened_ where nobody may be interested in it, or in other words there may be zero or more listeners listening on a given event.
A more concrete example is an event could be dispatched after some core piece of application logic.
From here it would be easy to tap into when this logic is executed to perform some other follow up action, without increasing the complexity of the type that performs the core action.
This also adheres to the [single responsibility](../why_athena.md#single-responsibility) principle.
```crystal
require "athena"
# Define a custom event
class MyEvent < AED::Event
property value : Int32
def initialize(@value : Int32); end
end
# Define a listener that listens our the custom event.
@[ADI::Register]
class CustomEventListener
@[AEDA::AsEventListener]
def call(event : MyEvent) : Nil
event.value *= 10
end
end
# Register a controller as a service,
# injecting the event dispatcher to handle processing our value.
@[ADI::Register]
class ExampleController < ATH::Controller
def initialize(@event_dispatcher : AED::EventDispatcherInterface); end
@[ARTA::Get("/{value}")]
def get_value(value : Int32) : Int32
event = MyEvent.new value
@event_dispatcher.dispatch event
event.value
end
end
ATH.run
# GET /10 # => 100
```
================================================
FILE: docs/getting_started/routing.md
================================================
## Controllers
The Athena Framework is a MVC based framework, as such, the logic to handle a given route is defined within an [ATH::Controller](/Framework/Controller).
Athena Framework takes an annotation based approach to routing.
An annotation, such as `ARTA::Get` is applied to an instance method of a controller class, which will be executed when that endpoint receives a request.
### Creating a Route
In Athena Framework, controllers are simply classes and route actions are simply methods.
This means they can be documented/tested as you would any Crystal class/method.
However see the [testing](./testing.md#testing-controllers) section for how to best test a controller.
```crystal
require "athena"
# Define a controller
class ExampleController < ATH::Controller
# Define an action to handle the related route
@[ARTA::Get("/")]
def index : String
"Hello World"
end
# The macro DSL can also be used
get "/" do
"Hello World"
end
end
# Run the server
ATH.run
# GET / # => Hello World
```
Routing is handled via the [Athena::Routing](/Routing) component.
It provides a flexible and robust foundation for handling determining which route should match a given request.
TIP: Check out the `debug:router` [command](./commands.md) to view all of the routes the framework is aware of within your application.
### Raw Response
An [AHTTP::Response](/HTTP/Response) can be used to fully customize the response; such as returning a specific status code, or adding some one-off headers.
```crystal
require "athena"
require "mime"
class ExampleController < ATH::Controller
# A GET endpoint returning an `AHTTP::Response`.
# Can be used to return raw data, such as HTML or CSS etc, in a one-off manner.
@[ARTA::Get("/index")]
def index : AHTTP::Response
AHTTP::Response.new(
"<h1>Welcome to my website!</h1>",
headers: HTTP::Headers{"content-type" => MIME.from_extension(".html")}
)
end
end
ATH.run
# GET /index # => "<h1>Welcome to my website!</h1>"
```
A [View](./middleware.md#4-view-event) event is emitted if the returned value is _NOT_ an [AHTTP::Response](/HTTP/Response).
By default, non `AHTTP::Response`s are JSON serialized.
However, this event can be listened on to customize how the value is serialized.
More on this in the [Content Negotiation](#content-negotiation) section.
### Route Parameters
Arguments are converted to their expected types if possible, otherwise an error response is automatically returned.
The values are provided directly as method arguments, thus preventing the need for `env.params.url["name"]` and any boilerplate related to it.
Just like normal method arguments, default values can be defined.
The method's return type adds some type safety to ensure the expected value is being returned.
```crystal
require "athena"
class ExampleController < ATH::Controller
@[ARTA::Get("/add/{value1}/{value2}")]
def add(value1 : Int32, value2 : Int32) : Int32
value1 + value2
end
end
ATH.run
# GET /add/2/3 # => 5
# GET /add/foo/12 # => {"code":400,"message":"Required parameter 'value1' with value 'foo' could not be converted into a valid 'Int32'"}
```
TIP: For more complex conversions, consider creating a [Value Resolver](/Framework/Controller/ValueResolvers/Interface) to encapsulate the logic.
#### Query Parameters
[ATHA::MapQueryParameter](/Framework/Annotations/MapQueryParameter) can be used to map a query parameter directly to a controller action parameter.
```crystal
require "athena"
class ExampleController < ATH::Controller
@[ARTA::Get("/")]
def index(@[ATHA::MapQueryParameter] page : Int32) : Int32
page
end
end
ATH.run
# GET / # => {"code":404,"message":"Missing query parameter: 'page'."}
# GET /?page=10 # => 10
# GET /?page=bar # => {"code":404,"message":"Invalid query parameter: 'page'."}
```
This works well enough for one-off parameters.
However [ATHA::MapQueryString](/Framework/Annotations/MapQueryString) can be used to the request's query string into a DTO type, much like how `JSON::Serializable` works for example.
In addition to making it easier to reuse, it also allows for enhanced validation of the query parameters via the [`Athena::Validator`](/Validator/#validating-objects) component.
### Raw Request
Restricting an action argument to [AHTTP::Request](/HTTP/Request) will provide the raw request object.
This can be useful to access data directly off the request object, such as consuming the request's body.
This approach is fine for simple or one-off endpoints.
TIP: Check out [ATHR::RequestBody](/Framework/Controller/ValueResolvers/RequestBody) for a better way to handle this.
```crystal
require "athena"
class ExampleController < ATH::Controller
@[ARTA::Post("/data")]
def data(request : AHTTP::Request) : String
raise AHK::Exception::BadRequest.new "Request body is empty." unless body = request.body
JSON.parse(body).as_h["name"].as_s
end
end
ATH.run
# POST /data body: {"id":1,"name":"Jim"} # => Jim
```
### Streaming Response
By default `AHTTP::Response` content is written all at once to the response's `IO`.
However in some cases the content may be too large to fit into memory. In this case an [AHTTP::StreamedResponse](/HTTP/StreamedResponse) may be used to stream the content back to the client.
```crystal
require "athena"
require "mime"
class ExampleController < ATH::Controller
@[ARTA::Get(path: "/users")]
def users : AHTTP::Response
AHTTP::StreamedResponse.new headers: HTTP::Headers{"content-type" => "application/json; charset=UTF-8"} do |io|
User.all.to_json io
end
end
end
ATH.run
# GET /athena/users" # => [{"id":1,...},...]
```
## File Uploads
Athena supports the [opt-in](/Framework/Bundle/Schema/FileUploads/) feature of populating [AHTTP::Request#files](/HTTP/Request/#Athena::HTTP::Request#files)
based on the files included in a `multipart/form-data` file upload request.
A [HTTP::FormData::Part](https://crystal-lang.org/api/HTTP/FormData/Part.html) without a *filename* is considered to be just a normal textual field and will be added to [AHTTP::Request#attributes](/HTTP/Request/#Athena::HTTP::Request#attributes).
These values can be provided to the controller action in the same way route parameters can.
```crystal
require "athena"
class ExampleController < ATH::Controller
@[ARTA::Post(path: "/avatar")]
def avatar(request : AHTTP::Request) : String
request.files["profile_picture"][0].client_original_name
end
end
ATH.configure({
framework: {
file_uploads: {
enabled: true,
},
},
})
ATH.run
# POST /avatar" (multipart/form-data request with `profile_picture` key pointing to the `pic.png` file) # => pic.png
```
TIP: Check out [ATHA::MapUploadedFile](/Framework/Annotations/MapUploadedFile/) for a better way to handle this.
## File Response
An [AHTTP::BinaryFileResponse](/HTTP/BinaryFileResponse) may be used to return static files/content.
This response type handles caching, partial requests, and setting the relevant headers.
The Athena Framework also supports downloading of dynamically generated content by using an [AHTTP::Response](/HTTP/Response) with the [content-disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header.
[AHTTP::HeaderUtils.make_disposition](/HTTP/HeaderUtils/#Athena::HTTP::HeaderUtils.make_disposition(disposition,filename,fallback_filename)) can be used to easily build the header.
```crystal
require "athena"
require "mime"
class ExampleController < ATH::Controller
@[ARTA::Get(path: "/data/export")]
def data_export : AHTTP::Response
content = # ...
AHTTP::Response.new(
content,
headers: HTTP::Headers{
"content-disposition" => ATH::HeaderUtils.make_disposition(:attachment, "data.csv"),
"content-type" => MIME.from_extension(".csv")
}
)
end
end
ATH.run
```
### Static Files
Static files can also be served from an Athena application.
This can be achieved by combining an [AHTTP::BinaryFileResponse](/HTTP/BinaryFileResponse) with the [request](./middleware.md#1-request-event) event;
checking if the request's path represents a file/directory within the application's public directory and returning the file if so.
```crystal
# Register a request event listener to handle returning static files.
@[ADI::Register]
struct StaticFileListener
# This could be parameter if the directory changes between environments.
private PUBLIC_DIR = Path.new("public").expand
# Run this listener with a very high priority so it is invoked before any application logic.
@[AEDA::AsEventListener(priority: 256)]
def on_request(event : AHK::Events::Request) : Nil
# Fallback if the request method isn't intended for files.
# Alternatively, a 405 could be thrown if the server is dedicated to serving files.
return unless event.request.method.in? "GET", "HEAD"
original_path = event.request.path
request_path = URI.decode original_path
# File path cannot contains '\0' (NUL).
if request_path.includes? '\0'
raise AHK::Exception::BadRequest.new "File path cannot contain NUL bytes."
end
request_path = Path.posix request_path
expanded_path = request_path.expand "/"
file_path = PUBLIC_DIR.join expanded_path.to_kind Path::Kind.native
is_dir = Dir.exists? file_path
is_dir_path = original_path.ends_with? '/'
event.response = if request_path != expanded_path || is_dir && !is_dir_path
redirect_path = expanded_path
if is_dir && !is_dir_path
redirec
gitextract_igikmbi3/
├── .ameba.yml
├── .changes/
│ ├── clock/
│ │ ├── v0.2.0.md
│ │ └── v0.3.0.md
│ ├── console/
│ │ ├── v0.4.1.md
│ │ ├── v0.4.2.md
│ │ └── v0.4.3.md
│ ├── contracts/
│ │ └── v0.1.0.md
│ ├── dependency-injection/
│ │ ├── v0.4.3.md
│ │ ├── v0.4.4.md
│ │ └── v0.4.5.md
│ ├── dotenv/
│ │ ├── v0.2.0.md
│ │ └── v0.2.1.md
│ ├── event-dispatcher/
│ │ ├── v0.3.1.md
│ │ ├── v0.4.0.md
│ │ └── v0.4.1.md
│ ├── framework/
│ │ ├── v0.20.1.md
│ │ ├── v0.21.0.md
│ │ ├── v0.21.1.md
│ │ └── v0.22.0.md
│ ├── header.tpl.md
│ ├── http/
│ │ └── v0.1.0.md
│ ├── http-kernel/
│ │ └── v0.1.0.md
│ ├── image-size/
│ │ └── v0.1.4.md
│ ├── mercure/
│ │ └── v0.1.0.md
│ ├── mercure-bundle/
│ │ └── v0.1.0.md
│ ├── mime/
│ │ ├── v0.2.0.md
│ │ └── v0.2.1.md
│ ├── negotiation/
│ │ └── v0.2.0.md
│ ├── routing/
│ │ ├── v0.1.10.md
│ │ ├── v0.1.11.md
│ │ ├── v0.1.12.md
│ │ └── v0.2.0.md
│ ├── serializer/
│ │ ├── v0.4.1.md
│ │ ├── v0.4.2.md
│ │ └── v0.4.3.md
│ ├── spec/
│ │ ├── v0.3.11.md
│ │ ├── v0.4.0.md
│ │ ├── v0.4.1.md
│ │ └── v0.4.2.md
│ ├── unreleased/
│ │ ├── .gitkeep
│ │ └── event-dispatcher-Changed-20260502-225424.yaml
│ └── validator/
│ ├── v0.4.0.md
│ ├── v0.4.1.md
│ └── v0.5.0.md
├── .changie.yaml
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build_and_publish_docs.yml
│ ├── ci.yml
│ ├── sync.yml
│ └── tag_and_create_release.yml
├── .gitignore
├── .python-version
├── .typos.toml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── UPGRADING.md
├── codecov.yml
├── docs/
│ ├── README.md
│ ├── api_reference.md
│ ├── bundle_reference.md
│ ├── css/
│ │ ├── index.css
│ │ └── monorepo.css
│ ├── getting_started/
│ │ ├── README.md
│ │ ├── commands.md
│ │ ├── configuration.md
│ │ ├── error_handling.md
│ │ ├── middleware.md
│ │ ├── routing.md
│ │ ├── testing.md
│ │ └── validation.md
│ ├── guides/
│ │ ├── README.md
│ │ └── proxies.md
│ ├── index.cr
│ ├── templates/
│ │ └── crystal/
│ │ └── material/
│ │ ├── schema.html
│ │ └── type.html
│ └── why_athena.md
├── gen_doc_stubs.py
├── justfile
├── mkdocs-common.yml
├── mkdocs.yml
├── pyproject.toml
├── scripts/
│ └── test.sh
├── shard.dev.yml
├── shard.prod.yml
├── shard.yml
└── src/
├── bundles/
│ └── mercure/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── shard.yml
│ ├── spec/
│ │ ├── authorization_spec.cr
│ │ ├── bundle_spec.cr
│ │ ├── discovery_spec.cr
│ │ ├── listeners/
│ │ │ ├── add_link_header_spec.cr
│ │ │ └── set_cookie_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-mercure_bundle.cr
│ ├── authorization.cr
│ ├── discovery.cr
│ └── listeners/
│ ├── add_link_header.cr
│ └── set_cookie.cr
└── components/
├── clock/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-clock_spec.cr
│ │ ├── aware_spec.cr
│ │ ├── mock_clock_spec.cr
│ │ ├── native_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-clock.cr
│ ├── aware.cr
│ ├── interface.cr
│ ├── native.cr
│ └── spec.cr
├── console/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── application_spec.cr
│ │ ├── application_tester_spec.cr
│ │ ├── command_spec.cr
│ │ ├── command_tester_spec.cr
│ │ ├── commands/
│ │ │ ├── complete_spec.cr
│ │ │ ├── dump_completion_spec.cr
│ │ │ ├── help_spec.cr
│ │ │ ├── lazy_spec.cr
│ │ │ └── list_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── completion/
│ │ │ ├── input_spec.cr
│ │ │ └── output/
│ │ │ ├── bash_spec.cr
│ │ │ ├── completion_output_test_case.cr
│ │ │ ├── fish_spec.cr
│ │ │ └── zsh_spec.cr
│ │ ├── cursor_spec.cr
│ │ ├── descriptor/
│ │ │ ├── abstract_descriptor_test_case.cr
│ │ │ ├── application_spec.cr
│ │ │ ├── object_provider.cr
│ │ │ └── text_spec.cr
│ │ ├── fixtures/
│ │ │ ├── applications/
│ │ │ │ ├── descriptor1.cr
│ │ │ │ └── descriptor2.cr
│ │ │ ├── commands/
│ │ │ │ ├── annotation_configured.cr
│ │ │ │ ├── annotation_configured_aliases.cr
│ │ │ │ ├── annotation_configured_hidden.cr
│ │ │ │ ├── annotation_configured_hidden_field.cr
│ │ │ │ ├── bar_buc.cr
│ │ │ │ ├── descriptor1.cr
│ │ │ │ ├── descriptor2.cr
│ │ │ │ ├── descriptor3.cr
│ │ │ │ ├── descriptor4.cr
│ │ │ │ ├── foo.cr
│ │ │ │ ├── foo1.cr
│ │ │ │ ├── foo2.cr
│ │ │ │ ├── foo3.cr
│ │ │ │ ├── foo4.cr
│ │ │ │ ├── foo6.cr
│ │ │ │ ├── foo_bar.cr
│ │ │ │ ├── foo_hidden.cr
│ │ │ │ ├── foo_opt.cr
│ │ │ │ ├── foo_same_case_lowercase.cr
│ │ │ │ ├── foo_same_case_uppercase.cr
│ │ │ │ ├── foo_subnamespaced1.cr
│ │ │ │ ├── foo_subnamespaced2.cr
│ │ │ │ ├── foo_without_alias.cr
│ │ │ │ ├── io.cr
│ │ │ │ ├── test.cr
│ │ │ │ ├── test_ambiguous_command_registering1.cr
│ │ │ │ └── test_ambiguous_command_registering2.cr
│ │ │ ├── helper/
│ │ │ │ └── table/
│ │ │ │ ├── borderless.txt
│ │ │ │ ├── borderless_vertical.txt
│ │ │ │ ├── box.txt
│ │ │ │ ├── compact.txt
│ │ │ │ ├── compact_vertical.txt
│ │ │ │ ├── default.txt
│ │ │ │ ├── default_cells_with_colspan.txt
│ │ │ │ ├── default_cells_with_formatting_tags.txt
│ │ │ │ ├── default_cells_with_non_formatting_tags.txt
│ │ │ │ ├── default_cells_with_rowspan.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_alignment.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_custom_format.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_fgbg.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_and_line_breaks.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_no_separators.txt
│ │ │ │ ├── default_cells_with_rowspan_and_colspan_separator_in_rowspan.txt
│ │ │ │ ├── default_colspan_and_table_cell_with_comment_style.txt
│ │ │ │ ├── default_formatted_row_with_line_breaks.txt
│ │ │ │ ├── default_headerless.txt
│ │ │ │ ├── default_line_break_after_colspan_cell.txt
│ │ │ │ ├── default_line_breaks_after_colspan_cell.txt
│ │ │ │ ├── default_missing_cell_values.txt
│ │ │ │ ├── default_multiline_cells.txt
│ │ │ │ ├── default_multiple_header_lines.txt
│ │ │ │ ├── default_no_rows.txt
│ │ │ │ ├── default_row_with_multiple_cells.txt
│ │ │ │ ├── double_box_separator.txt
│ │ │ │ ├── markdown.txt
│ │ │ │ └── suggested_vertical.txt
│ │ │ ├── style/
│ │ │ │ ├── backslashes.txt
│ │ │ │ ├── block.txt
│ │ │ │ ├── block_line_endings.txt
│ │ │ │ ├── block_no_prefix_type.txt
│ │ │ │ ├── block_padding.txt
│ │ │ │ ├── block_prefix_no_type.txt
│ │ │ │ ├── blocks.txt
│ │ │ │ ├── closing_tag.txt
│ │ │ │ ├── definition_list.txt
│ │ │ │ ├── emojis.txt
│ │ │ │ ├── empty_buffer.txt
│ │ │ │ ├── horizontal_table.txt
│ │ │ │ ├── long_line_block.txt
│ │ │ │ ├── long_line_block_wrapping.txt
│ │ │ │ ├── long_line_comment.txt
│ │ │ │ ├── long_line_comment_decorated.txt
│ │ │ │ ├── multi_line_block.txt
│ │ │ │ ├── nested_tag_prefix.txt
│ │ │ │ ├── non_interactive_question.txt
│ │ │ │ ├── table.txt
│ │ │ │ ├── table_horizontal.txt
│ │ │ │ ├── table_vertical.txt
│ │ │ │ ├── text_block_blank_line.txt
│ │ │ │ ├── title_block.txt
│ │ │ │ ├── titles.txt
│ │ │ │ └── titles_text.txt
│ │ │ └── text/
│ │ │ ├── application_1.txt
│ │ │ ├── application_2.txt
│ │ │ ├── application_alternative_namespace.txt
│ │ │ ├── application_filtered_namespace.txt
│ │ │ ├── application_renderexception1.txt
│ │ │ ├── application_renderexception2.txt
│ │ │ ├── application_renderexception3.txt
│ │ │ ├── application_renderexception3_decorated.txt
│ │ │ ├── application_renderexception4.txt
│ │ │ ├── application_renderexception_doublewidth1.txt
│ │ │ ├── application_renderexception_escapeslines.txt
│ │ │ ├── application_renderexception_linebreaks.txt
│ │ │ ├── application_renderexception_synopsis_escapeslines.txt
│ │ │ ├── application_run1.txt
│ │ │ ├── application_run2.txt
│ │ │ ├── application_run3.txt
│ │ │ ├── application_run4.txt
│ │ │ ├── application_run5.txt
│ │ │ ├── command_1.txt
│ │ │ ├── command_2.txt
│ │ │ ├── input_argument_1.txt
│ │ │ ├── input_argument_2.txt
│ │ │ ├── input_argument_3.txt
│ │ │ ├── input_argument_4.txt
│ │ │ ├── input_argument_with_style.txt
│ │ │ ├── input_definition_1.txt
│ │ │ ├── input_definition_2.txt
│ │ │ ├── input_definition_3.txt
│ │ │ ├── input_definition_4.txt
│ │ │ ├── input_option_1.txt
│ │ │ ├── input_option_2.txt
│ │ │ ├── input_option_3.txt
│ │ │ ├── input_option_4.txt
│ │ │ ├── input_option_5.txt
│ │ │ ├── input_option_6.txt
│ │ │ ├── input_option_with_style.txt
│ │ │ └── input_option_with_style_array.txt
│ │ ├── formatter/
│ │ │ ├── null_spec.cr
│ │ │ ├── null_style_spec.cr
│ │ │ ├── output_formatter_spec.cr
│ │ │ ├── output_formatter_style_spec.cr
│ │ │ └── output_formatter_style_stack_spec.cr
│ │ ├── helper/
│ │ │ ├── abstract_question_helper_test_case.cr
│ │ │ ├── athena_question_spec.cr
│ │ │ ├── formatter_spec.cr
│ │ │ ├── helper_set_spec.cr
│ │ │ ├── helper_spec.cr
│ │ │ ├── output_wrapper_spec.cr
│ │ │ ├── progress_bar_spec.cr
│ │ │ ├── progress_indicator_spec.cr
│ │ │ ├── question_spec.cr
│ │ │ ├── table_spec.cr
│ │ │ └── table_style_spec.cr
│ │ ├── input/
│ │ │ ├── argument_spec.cr
│ │ │ ├── argv_spec.cr
│ │ │ ├── definition_spec.cr
│ │ │ ├── hash_spec.cr
│ │ │ ├── input_spec.cr
│ │ │ ├── option_spec.cr
│ │ │ ├── string_line_spec.cr
│ │ │ └── value/
│ │ │ ├── array_spec.cr
│ │ │ ├── bool_spec.cr
│ │ │ ├── nil_spec.cr
│ │ │ ├── number_spec.cr
│ │ │ └── string_spec.cr
│ │ ├── output/
│ │ │ ├── console_section_output_spec.cr
│ │ │ ├── io_spec.cr
│ │ │ ├── null_spec.cr
│ │ │ └── output_spec.cr
│ │ ├── question/
│ │ │ ├── choice_spec.cr
│ │ │ ├── confirmation_spec.cr
│ │ │ ├── multiple_choice_spec.cr
│ │ │ └── question_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── style/
│ │ │ └── athena_style_spec.cr
│ │ └── terminal_spec.cr
│ └── src/
│ ├── annotations.cr
│ ├── application.cr
│ ├── athena-console.cr
│ ├── command.cr
│ ├── commands/
│ │ ├── complete.cr
│ │ ├── dump_completion.cr
│ │ ├── generic.cr
│ │ ├── help.cr
│ │ ├── lazy.cr
│ │ └── list.cr
│ ├── completion/
│ │ ├── input.cr
│ │ ├── output/
│ │ │ ├── bash.cr
│ │ │ ├── completion.bash
│ │ │ ├── completion.fish
│ │ │ ├── completion.zsh
│ │ │ ├── fish.cr
│ │ │ ├── interface.cr
│ │ │ └── zsh.cr
│ │ └── suggestions.cr
│ ├── cursor.cr
│ ├── descriptor/
│ │ ├── application.cr
│ │ ├── context.cr
│ │ ├── descriptor.cr
│ │ ├── interface.cr
│ │ └── text.cr
│ ├── exception/
│ │ ├── command_not_found.cr
│ │ ├── invalid_argument.cr
│ │ ├── invalid_option.cr
│ │ ├── logic.cr
│ │ ├── missing_input.cr
│ │ ├── namespace_not_found.cr
│ │ └── runtime.cr
│ ├── ext/
│ │ └── terminal.cr
│ ├── formatter/
│ │ ├── interface.cr
│ │ ├── null.cr
│ │ ├── null_style.cr
│ │ ├── output.cr
│ │ ├── output_formatter_style_stack.cr
│ │ ├── output_style.cr
│ │ ├── output_style_interface.cr
│ │ └── wrappable_interface.cr
│ ├── helper/
│ │ ├── athena_question.cr
│ │ ├── descriptor_helper.cr
│ │ ├── formatter.cr
│ │ ├── helper.cr
│ │ ├── helper_set.cr
│ │ ├── interface.cr
│ │ ├── output_wrapper.cr
│ │ ├── progress_bar.cr
│ │ ├── progress_indicator.cr
│ │ ├── question.cr
│ │ ├── table.cr
│ │ ├── table_cell_style.cr
│ │ └── table_style.cr
│ ├── input/
│ │ ├── argument.cr
│ │ ├── argv.cr
│ │ ├── definition.cr
│ │ ├── hash.cr
│ │ ├── input.cr
│ │ ├── interface.cr
│ │ ├── option.cr
│ │ ├── streamable.cr
│ │ ├── string_line.cr
│ │ └── value/
│ │ ├── array.cr
│ │ ├── bool.cr
│ │ ├── nil.cr
│ │ ├── number.cr
│ │ ├── string.cr
│ │ └── value.cr
│ ├── loader/
│ │ ├── factory.cr
│ │ └── interface.cr
│ ├── output/
│ │ ├── console_output.cr
│ │ ├── console_output_interface.cr
│ │ ├── interface.cr
│ │ ├── io.cr
│ │ ├── null.cr
│ │ ├── output.cr
│ │ ├── section.cr
│ │ ├── sized_buffer.cr
│ │ ├── type.cr
│ │ └── verbosity.cr
│ ├── question/
│ │ ├── abstract_choice.cr
│ │ ├── base.cr
│ │ ├── choice.cr
│ │ ├── confirmation.cr
│ │ ├── multiple_choice.cr
│ │ └── question.cr
│ ├── spec/
│ │ └── expectations/
│ │ └── command_is_successful.cr
│ ├── spec.cr
│ ├── style/
│ │ ├── athena.cr
│ │ ├── interface.cr
│ │ └── output.cr
│ └── terminal.cr
├── contracts/
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ └── .gitkeep
│ └── src/
│ ├── alias.cr
│ ├── athena-contracts.cr
│ ├── contracts/
│ │ └── event_dispatcher/
│ │ ├── event.cr
│ │ ├── interface.cr
│ │ └── stoppable_event.cr
│ └── event_dispatcher.cr
├── dependency_injection/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── abstract_bundle_spec.cr
│ │ ├── athena-dependency_injection_spec.cr
│ │ ├── compiler_passes/
│ │ │ ├── auto_wire_spec.cr
│ │ │ ├── define_getters_spec.cr
│ │ │ ├── inline_service_definitions_spec.cr
│ │ │ ├── merge_configs_spec.cr
│ │ │ ├── merge_extension_config_spec.cr
│ │ │ ├── namespaced_spec.cr
│ │ │ ├── normalize_definitions_spec.cr
│ │ │ ├── optional_services_spec.cr
│ │ │ ├── parameters_spec.cr
│ │ │ ├── process_aliases_spec.cr
│ │ │ ├── process_auto_configurations_spec.cr
│ │ │ ├── process_bindings_spec.cr
│ │ │ ├── process_parameters_spec.cr
│ │ │ ├── proxy_spec.cr
│ │ │ ├── register_services_spec.cr
│ │ │ ├── remove_unused_services_spec.cr
│ │ │ ├── resolve_parameter_placeholders_spec.cr
│ │ │ ├── resolve_values_spec.cr
│ │ │ ├── untyped_with_default_spec.cr
│ │ │ ├── validate_arguments_spec.cr
│ │ │ └── validate_generics_spec.cr
│ │ ├── extension_spec.cr
│ │ ├── spec_helper.cr
│ │ └── spec_spec.cr
│ └── src/
│ ├── abstract_bundle.cr
│ ├── annotation_configurations.cr
│ ├── annotations.cr
│ ├── athena-dependency_injection.cr
│ ├── compiler_passes/
│ │ ├── analyze_service_references.cr
│ │ ├── auto_wire.cr
│ │ ├── define_getters.cr
│ │ ├── inline_service_definitions.cr
│ │ ├── merge_configs.cr
│ │ ├── merge_extension_config.cr
│ │ ├── normalize_definitions.cr
│ │ ├── process_aliases.cr
│ │ ├── process_annotation_bindings.cr
│ │ ├── process_autoconfigure_annotations.cr
│ │ ├── process_bindings.cr
│ │ ├── process_parameters.cr
│ │ ├── process_tags.cr
│ │ ├── register_services.cr
│ │ ├── remove_unused_services.cr
│ │ ├── resolve_parameter_placeholders.cr
│ │ ├── resolve_tagged_iterators.cr
│ │ ├── resolve_values.cr
│ │ ├── validate_arguments.cr
│ │ └── validate_generics.cr
│ ├── extension.cr
│ ├── proxy.cr
│ ├── service_container.cr
│ └── spec.cr
├── dotenv/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-dotenv_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-dotenv.cr
│ └── exception/
│ ├── format.cr
│ ├── logic.cr
│ └── path.cr
├── event_dispatcher/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── callable_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── event_dispatcher_spec.cr
│ │ ├── generic_event_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── annotations.cr
│ ├── athena-event_dispatcher.cr
│ ├── callable.cr
│ ├── event.cr
│ ├── event_dispatcher.cr
│ ├── event_dispatcher_interface.cr
│ ├── generic_event.cr
│ └── spec.cr
├── framework/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── .gitkeep
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── argument_resolver_controller_spec.cr
│ │ ├── assets/
│ │ │ ├── file-big.txt
│ │ │ ├── file-small.txt
│ │ │ ├── foo.txt
│ │ │ ├── greeting.ecr
│ │ │ ├── layout.ecr
│ │ │ └── openssl/
│ │ │ ├── openssl.crt
│ │ │ └── openssl.key
│ │ ├── athena_spec.cr
│ │ ├── bundle_spec.cr
│ │ ├── commands/
│ │ │ ├── debug_event_dispatcher_spec.cr
│ │ │ ├── debug_router_match_spec.cr
│ │ │ └── debug_router_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── controller/
│ │ │ ├── redirect_spec.cr
│ │ │ └── value_resolvers/
│ │ │ ├── enum_spec.cr
│ │ │ ├── query_parameter_spec.cr
│ │ │ ├── request_body_spec.cr
│ │ │ ├── time_spec.cr
│ │ │ └── uuid_spec.cr
│ │ ├── controller_spec.cr
│ │ ├── controllers/
│ │ │ ├── argument_resolver_controller.cr
│ │ │ ├── custom_annotation_controller.cr
│ │ │ ├── file_upload_controller.cr
│ │ │ ├── prefix_controller.cr
│ │ │ ├── routing_controller.cr
│ │ │ └── view_controller.cr
│ │ ├── custom_annotation_spec.cr
│ │ ├── ext/
│ │ │ ├── console/
│ │ │ │ └── register_commands_spec.cr
│ │ │ └── routing/
│ │ │ └── annotation_route_loader_spec.cr
│ │ ├── file_parser_spec.cr
│ │ ├── file_upload_controller_spec.cr
│ │ ├── listeners/
│ │ │ ├── cors_spec.cr
│ │ │ ├── file_spec.cr
│ │ │ ├── format_spec.cr
│ │ │ └── view_spec.cr
│ │ ├── prefix_spec.cr
│ │ ├── routing_spec.cr
│ │ ├── spec/
│ │ │ ├── expectations/
│ │ │ │ ├── request/
│ │ │ │ │ └── attribute_equals_spec.cr
│ │ │ │ └── response/
│ │ │ │ ├── cookie_value_equals_spec.cr
│ │ │ │ ├── format_equals_spec.cr
│ │ │ │ ├── has_cookie_spec.cr
│ │ │ │ ├── has_header_spec.cr
│ │ │ │ ├── has_status_spec.cr
│ │ │ │ ├── header_equals_spec.cr
│ │ │ │ ├── is_redirected_spec.cr
│ │ │ │ ├── is_successful_spec.cr
│ │ │ │ └── is_unprocessable_spec.cr
│ │ │ └── web_test_case_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── view/
│ │ │ ├── context_spec.cr
│ │ │ ├── format_negotiator_spec.cr
│ │ │ ├── view_handler_spec.cr
│ │ │ └── view_spec.cr
│ │ └── view_controller_spec.cr
│ └── src/
│ ├── annotation_resolver.cr
│ ├── annotations.cr
│ ├── athena.cr
│ ├── bundle.cr
│ ├── commands/
│ │ ├── debug_event_dispatcher.cr
│ │ ├── debug_router.cr
│ │ └── debug_router_match.cr
│ ├── compiler_passes/
│ │ └── expose_controller_services.cr
│ ├── controller/
│ │ ├── redirect.cr
│ │ └── value_resolvers/
│ │ ├── enum.cr
│ │ ├── interface.cr
│ │ ├── query_parameter.cr
│ │ ├── request_body.cr
│ │ ├── time.cr
│ │ └── uuid.cr
│ ├── controller.cr
│ ├── ext/
│ │ ├── clock.cr
│ │ ├── console/
│ │ │ ├── application.cr
│ │ │ ├── compiler_passes/
│ │ │ │ └── register_commands.cr
│ │ │ ├── container_command_loader.cr
│ │ │ ├── descriptor/
│ │ │ │ ├── descriptor.cr
│ │ │ │ └── text.cr
│ │ │ └── helper/
│ │ │ └── descriptor_helper.cr
│ │ ├── console.cr
│ │ ├── event_dispatcher.cr
│ │ ├── http.cr
│ │ ├── http_kernel.cr
│ │ ├── routing/
│ │ │ ├── annotation_route_loader.cr
│ │ │ ├── redirectable_url_matcher.cr
│ │ │ └── router.cr
│ │ ├── routing.cr
│ │ ├── serializer.cr
│ │ ├── validator/
│ │ │ └── validation_failed_exception.cr
│ │ └── validator.cr
│ ├── file_parser.cr
│ ├── listeners/
│ │ ├── cors.cr
│ │ ├── file.cr
│ │ ├── format.cr
│ │ └── view.cr
│ ├── logging.cr
│ ├── spec/
│ │ ├── abstract_browser.cr
│ │ ├── api_test_case.cr
│ │ ├── expectations/
│ │ │ ├── http.cr
│ │ │ ├── request/
│ │ │ │ └── attribute_equals.cr
│ │ │ └── response/
│ │ │ ├── base.cr
│ │ │ ├── cookie_value_equals.cr
│ │ │ ├── format_equals.cr
│ │ │ ├── has_cookie.cr
│ │ │ ├── has_header.cr
│ │ │ ├── has_status.cr
│ │ │ ├── header_equals.cr
│ │ │ ├── is_redirected.cr
│ │ │ ├── is_successful.cr
│ │ │ └── is_unprocessable.cr
│ │ ├── http_browser.cr
│ │ └── web_test_case.cr
│ ├── spec.cr
│ └── view/
│ ├── configurable_view_handler_interface.cr
│ ├── context.cr
│ ├── format_handler_interface.cr
│ ├── format_negotiator.cr
│ ├── view.cr
│ ├── view_handler.cr
│ └── view_handler_interface.cr
├── http/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── assets/
│ │ │ ├── .unknownextension
│ │ │ ├── case-sensitive-mime-type.xlsm
│ │ │ ├── directory/
│ │ │ │ └── .empty
│ │ │ ├── file-big.txt
│ │ │ ├── file-small.txt
│ │ │ ├── foo.txt
│ │ │ ├── fööö.html
│ │ │ ├── test
│ │ │ └── webkitdirectory/
│ │ │ ├── nested/
│ │ │ │ └── test.txt
│ │ │ └── test.txt
│ │ ├── binary_file_response_spec.cr
│ │ ├── ext/
│ │ │ └── conversion_types_spec.cr
│ │ ├── file_spec.cr
│ │ ├── header_utils_spec.cr
│ │ ├── ip_utils_spec.cr
│ │ ├── parameter_bag_spec.cr
│ │ ├── redirect_response_spec.cr
│ │ ├── request_matcher/
│ │ │ ├── attributes_spec.cr
│ │ │ ├── header_spec.cr
│ │ │ ├── hostname_spec.cr
│ │ │ ├── method_spec.cr
│ │ │ ├── path_spec.cr
│ │ │ └── query_parameter_spec.cr
│ │ ├── request_matcher_spec.cr
│ │ ├── request_spec.cr
│ │ ├── response_headers_spec.cr
│ │ ├── response_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── streamed_response_spec.cr
│ │ └── uploaded_file_spec.cr
│ └── src/
│ ├── abstract_file.cr
│ ├── athena-http.cr
│ ├── binary_file_response.cr
│ ├── exception/
│ │ ├── conflicting_headers.cr
│ │ ├── file.cr
│ │ ├── file_not_found.cr
│ │ ├── file_size_limit_exceeded.cr
│ │ ├── logic.cr
│ │ ├── request_exception_interface.cr
│ │ └── suspicious_operation.cr
│ ├── ext/
│ │ └── conversion_types.cr
│ ├── file.cr
│ ├── header_utils.cr
│ ├── ip_utils.cr
│ ├── parameter_bag.cr
│ ├── redirect_response.cr
│ ├── request.cr
│ ├── request_matcher/
│ │ ├── attributes.cr
│ │ ├── header.cr
│ │ ├── hostname.cr
│ │ ├── method.cr
│ │ ├── path.cr
│ │ └── query_parameter.cr
│ ├── request_matcher.cr
│ ├── request_store.cr
│ ├── response.cr
│ ├── response_headers.cr
│ ├── streamed_response.cr
│ └── uploaded_file.cr
├── http_kernel/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── controller/
│ │ │ ├── argument_resolver_spec.cr
│ │ │ ├── parameter_metadata_spec.cr
│ │ │ └── value_resolvers/
│ │ │ ├── default_value_spec.cr
│ │ │ ├── request_attribute_spec.cr
│ │ │ └── request_spec.cr
│ │ ├── error_renderer_spec.cr
│ │ ├── exception/
│ │ │ ├── bad_gateway_spec.cr
│ │ │ ├── http_exception_spec.cr
│ │ │ ├── service_unavailable_spec.cr
│ │ │ ├── too_many_requests_spec.cr
│ │ │ └── unauthorized_spec.cr
│ │ ├── http_kernel_spec.cr
│ │ ├── listeners/
│ │ │ └── error_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── action.cr
│ ├── action_resolver.cr
│ ├── action_resolver_interface.cr
│ ├── athena-http_kernel.cr
│ ├── controller/
│ │ ├── argument_resolver.cr
│ │ ├── argument_resolver_interface.cr
│ │ ├── parameter_metadata.cr
│ │ └── value_resolvers/
│ │ ├── default_value.cr
│ │ ├── interface.cr
│ │ ├── request.cr
│ │ └── request_attribute.cr
│ ├── error_renderer.cr
│ ├── error_renderer_interface.cr
│ ├── events/
│ │ ├── action_event.cr
│ │ ├── exception_event.cr
│ │ ├── request_aware.cr
│ │ ├── request_event.cr
│ │ ├── response_event.cr
│ │ ├── settable_response.cr
│ │ ├── terminate_event.cr
│ │ └── view_event.cr
│ ├── exception/
│ │ ├── bad_gateway.cr
│ │ ├── bad_request.cr
│ │ ├── conflict.cr
│ │ ├── forbidden.cr
│ │ ├── gone.cr
│ │ ├── http_exception.cr
│ │ ├── length_required.cr
│ │ ├── logic.cr
│ │ ├── method_not_allowed.cr
│ │ ├── not_acceptable.cr
│ │ ├── not_found.cr
│ │ ├── not_implemented.cr
│ │ ├── precondition_failed.cr
│ │ ├── service_unavailable.cr
│ │ ├── stop_format_listener.cr
│ │ ├── too_many_requests.cr
│ │ ├── unauthorized.cr
│ │ ├── unprocessable_entity.cr
│ │ └── unsupported_media_type.cr
│ ├── http_kernel.cr
│ └── listeners/
│ ├── error.cr
│ └── routing.cr
├── image_size/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-image_size_spec.cr
│ │ ├── images/
│ │ │ ├── cur/
│ │ │ │ └── 32x256_8_0.cur
│ │ │ ├── mng/
│ │ │ │ └── 61x42_0_0.mng
│ │ │ ├── png/
│ │ │ │ └── 192x110_8_0.apng
│ │ │ ├── psd/
│ │ │ │ └── 16x20_8_3.psd
│ │ │ ├── swf/
│ │ │ │ └── 450x200_0_0.swf
│ │ │ └── tiff/
│ │ │ ├── big-endian.68x49_8_1.tiff
│ │ │ └── little-endian.40x68_8_1.tiff
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-image_size.cr
│ ├── extractors/
│ │ ├── abstract_ico.cr
│ │ ├── abstract_png.cr
│ │ ├── abstract_tiff.cr
│ │ ├── apng.cr
│ │ ├── bmp.cr
│ │ ├── cur.cr
│ │ ├── extractor.cr
│ │ ├── gif.cr
│ │ ├── ico.cr
│ │ ├── ii_tiff.cr
│ │ ├── jpeg.cr
│ │ ├── mm_tiff.cr
│ │ ├── mng.cr
│ │ ├── png.cr
│ │ ├── psd.cr
│ │ ├── svg.cr
│ │ ├── swf.cr
│ │ └── webp.cr
│ ├── image.cr
│ └── image_format.cr
├── mercure/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── authorization_spec.cr
│ │ ├── discovery_spec.cr
│ │ ├── hub/
│ │ │ ├── hub_spec.cr
│ │ │ └── registry_spec.cr
│ │ ├── spec_helper.cr
│ │ ├── token_factory/
│ │ │ └── jwt_spec.cr
│ │ └── token_provider/
│ │ ├── callable_spec.cr
│ │ ├── factory_spec.cr
│ │ └── static_spec.cr
│ └── src/
│ ├── athena-mercure.cr
│ ├── authorization.cr
│ ├── discovery.cr
│ ├── exception/
│ │ ├── invalid_argument.cr
│ │ └── runtime.cr
│ ├── hub/
│ │ ├── hub.cr
│ │ ├── interface.cr
│ │ └── registry.cr
│ ├── spec.cr
│ ├── token_factory/
│ │ ├── interface.cr
│ │ └── jwt.cr
│ ├── token_provider/
│ │ ├── callable.cr
│ │ ├── factory.cr
│ │ ├── interface.cr
│ │ └── static.cr
│ └── update.cr
├── mercure-bundle/
│ ├── README.md
│ ├── docs/
│ │ └── README.md
│ └── mkdocs.yml
├── mime/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── abstract_types_guesser_test_case.cr
│ │ ├── address_spec.cr
│ │ ├── draft_email_spec.cr
│ │ ├── email_spec.cr
│ │ ├── encoder/
│ │ │ ├── base64_content_spec.cr
│ │ │ ├── eight_bit_content_spec.cr
│ │ │ ├── idn_address_spec.cr
│ │ │ ├── quoted_printable_content_spec.cr
│ │ │ ├── quoted_printable_mime_header_spec.cr
│ │ │ └── rfc2231_spec.cr
│ │ ├── fixtures/
│ │ │ ├── content.txt
│ │ │ ├── mimetypes/
│ │ │ │ ├── -test
│ │ │ │ ├── .unknownextension
│ │ │ │ ├── abc.csv
│ │ │ │ ├── directory/
│ │ │ │ │ └── .empty
│ │ │ │ ├── other-file.example
│ │ │ │ └── test
│ │ │ ├── samples/
│ │ │ │ └── charsets/
│ │ │ │ ├── iso-2022-jp/
│ │ │ │ │ └── one.txt
│ │ │ │ ├── iso-8859-1/
│ │ │ │ │ └── one.txt
│ │ │ │ └── utf-8/
│ │ │ │ ├── one.txt
│ │ │ │ ├── three.txt
│ │ │ │ └── two.txt
│ │ │ └── test.docx
│ │ ├── header/
│ │ │ ├── collection_spec.cr
│ │ │ ├── date_spec.cr
│ │ │ ├── identification_spec.cr
│ │ │ ├── mailbox_list_spec.cr
│ │ │ ├── mailbox_spec.cr
│ │ │ ├── parameterized_spec.cr
│ │ │ ├── path_spec.cr
│ │ │ └── unstructured_spec.cr
│ │ ├── magic_types_guesser_spec.cr
│ │ ├── message_converter_spec.cr
│ │ ├── message_spec.cr
│ │ ├── native_types_guessuer_spec.cr
│ │ ├── part/
│ │ │ ├── data_spec.cr
│ │ │ ├── file_spec.cr
│ │ │ ├── message_spec.cr
│ │ │ ├── multipart/
│ │ │ │ ├── alternative_spec.cr
│ │ │ │ ├── digest_spec.cr
│ │ │ │ ├── form_spec.cr
│ │ │ │ ├── mixed_spec.cr
│ │ │ │ └── related_spec.cr
│ │ │ └── text_spec.cr
│ │ ├── spec_helper.cr
│ │ └── types_spec.cr
│ └── src/
│ ├── address.cr
│ ├── athena-mime.cr
│ ├── draft_email.cr
│ ├── email.cr
│ ├── encoder/
│ │ ├── address_encoder_interface.cr
│ │ ├── base64_content.cr
│ │ ├── content_encoder_interface.cr
│ │ ├── eight_bit_content.cr
│ │ ├── encoder_interface.cr
│ │ ├── idn_address.cr
│ │ ├── mime_header_encoder_interface.cr
│ │ ├── quoted_printable_content.cr
│ │ ├── quoted_printable_mime_header.cr
│ │ └── rfc2231.cr
│ ├── exception/
│ │ ├── header_not_found.cr
│ │ ├── invalid_argument.cr
│ │ ├── logic.cr
│ │ └── runtime.cr
│ ├── header/
│ │ ├── abstract.cr
│ │ ├── collection.cr
│ │ ├── date.cr
│ │ ├── identification.cr
│ │ ├── interface.cr
│ │ ├── mailbox.cr
│ │ ├── mailbox_list.cr
│ │ ├── parameterized.cr
│ │ ├── path.cr
│ │ └── unstructured.cr
│ ├── magic_types_guesser.cr
│ ├── message.cr
│ ├── message_converter.cr
│ ├── native_types_guesser.cr
│ ├── part/
│ │ ├── abstract.cr
│ │ ├── abstract_multipart.cr
│ │ ├── data.cr
│ │ ├── file.cr
│ │ ├── message.cr
│ │ ├── multipart/
│ │ │ ├── alternative.cr
│ │ │ ├── digest.cr
│ │ │ ├── form.cr
│ │ │ ├── mixed.cr
│ │ │ └── related.cr
│ │ └── text.cr
│ ├── types/
│ │ └── data.cr
│ ├── types.cr
│ ├── types_guesser_interface.cr
│ └── types_interface.cr
├── negotiation/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── accept_language_spec.cr
│ │ ├── accept_match_spec.cr
│ │ ├── accept_spec.cr
│ │ ├── base_accept_spec.cr
│ │ ├── charset_negotiator_spec.cr
│ │ ├── encoding_negotiator_spec.cr
│ │ ├── language_negotiator_spec.cr
│ │ ├── negotiator_spec.cr
│ │ ├── negotiator_test_case.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── abstract_negotiator.cr
│ ├── accept.cr
│ ├── accept_charset.cr
│ ├── accept_encoding.cr
│ ├── accept_language.cr
│ ├── accept_match.cr
│ ├── athena-negotiation.cr
│ ├── base_accept.cr
│ ├── charset_negotiator.cr
│ ├── encoding_negotiator.cr
│ ├── exception/
│ │ ├── invalid_argument.cr
│ │ ├── invalid_language.cr
│ │ └── invalid_media_type.cr
│ ├── language_negotiator.cr
│ └── negotiator.cr
├── routing/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── fixtures/
│ │ │ └── route_provider/
│ │ │ ├── route_collection0.cr
│ │ │ ├── route_collection1.cr
│ │ │ ├── route_collection10.cr
│ │ │ ├── route_collection11.cr
│ │ │ ├── route_collection12.cr
│ │ │ ├── route_collection2.cr
│ │ │ ├── route_collection3.cr
│ │ │ ├── route_collection4.cr
│ │ │ ├── route_collection5.cr
│ │ │ ├── route_collection6.cr
│ │ │ ├── route_collection7.cr
│ │ │ ├── route_collection8.cr
│ │ │ └── route_collection9.cr
│ │ ├── generator/
│ │ │ └── url_generator_spec.cr
│ │ ├── matcher/
│ │ │ ├── abstract_url_matcher_test_case.cr
│ │ │ ├── redirectable_url_matcher_spec.cr
│ │ │ ├── traceable_url_matcher_spec.cr
│ │ │ └── url_matcher_spec.cr
│ │ ├── parameters_spec.cr
│ │ ├── request_context_spec.cr
│ │ ├── requirement/
│ │ │ ├── enum_spec.cr
│ │ │ └── requirement_spec.cr
│ │ ├── route_collection_spec.cr
│ │ ├── route_compiler_spec.cr
│ │ ├── route_provider_spec.cr
│ │ ├── route_spec.cr
│ │ ├── router_spec.cr
│ │ ├── routing_handler_spec.cr
│ │ ├── spec_helper.cr
│ │ └── static_prefix_collection_spec.cr
│ └── src/
│ ├── annotations.cr
│ ├── athena-routing.cr
│ ├── compiled_route.cr
│ ├── exception/
│ │ ├── invalid_argument.cr
│ │ ├── invalid_parameter.cr
│ │ ├── method_not_allowed.cr
│ │ ├── missing_required_parameters.cr
│ │ ├── no_configuration.cr
│ │ ├── resource_not_found.cr
│ │ └── route_not_found.cr
│ ├── ext/
│ │ └── regex.cr
│ ├── generator/
│ │ ├── configurable_requirements_interface.cr
│ │ ├── interface.cr
│ │ ├── reference_type.cr
│ │ └── url_generator.cr
│ ├── matcher/
│ │ ├── redirectable_url_matcher_interface.cr
│ │ ├── request_matcher_interface.cr
│ │ ├── traceable_url_matcher.cr
│ │ ├── url_matcher.cr
│ │ └── url_matcher_interface.cr
│ ├── parameters.cr
│ ├── request_context.cr
│ ├── request_context_aware_interface.cr
│ ├── requirement/
│ │ ├── enum.cr
│ │ └── requirement.cr
│ ├── route.cr
│ ├── route_collection.cr
│ ├── route_compiler.cr
│ ├── route_provider.cr
│ ├── router.cr
│ ├── router_interface.cr
│ ├── routing_handler.cr
│ └── static_prefix_collection.cr
├── serializer/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-serializer_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── exclusion_strategies/
│ │ │ ├── custom_strategy_spec.cr
│ │ │ ├── group_spec.cr
│ │ │ └── version_spec.cr
│ │ ├── models/
│ │ │ ├── accessor.cr
│ │ │ ├── accessor_order.cr
│ │ │ ├── basic.cr
│ │ │ ├── discriminator.cr
│ │ │ ├── emit_null.cr
│ │ │ ├── empty.cr
│ │ │ ├── exclude.cr
│ │ │ ├── expose.cr
│ │ │ ├── groups.cr
│ │ │ ├── ignore_on_deserialize.cr
│ │ │ ├── ignore_on_serialize.cr
│ │ │ ├── name.cr
│ │ │ ├── nested.cr
│ │ │ ├── post_deserialize.cr
│ │ │ ├── post_serialize.cr
│ │ │ ├── pre_serialize.cr
│ │ │ ├── read_only.cr
│ │ │ ├── skip.cr
│ │ │ ├── skip_when_empty.cr
│ │ │ └── virtual_property.cr
│ │ ├── navigators/
│ │ │ ├── deserialization_navigator_spec.cr
│ │ │ └── serialization_navigator_spec.cr
│ │ ├── serialization_context_spec.cr
│ │ ├── serializer_spec.cr
│ │ ├── spec_helper.cr
│ │ └── visitors/
│ │ ├── json_deserialization_visitor_spec.cr
│ │ ├── json_serialization_visitor_spec.cr
│ │ ├── yaml_deserialization_visitor_spec.cr
│ │ └── yaml_serialization_visitor_spec.cr
│ └── src/
│ ├── annotations.cr
│ ├── any.cr
│ ├── athena-serializer.cr
│ ├── construction/
│ │ ├── instantiate_object_constructor.cr
│ │ └── object_constructor_interface.cr
│ ├── context.cr
│ ├── deserialization_context.cr
│ ├── exception/
│ │ ├── deserialization_exception.cr
│ │ ├── logic.cr
│ │ ├── missing_required_property.cr
│ │ ├── nil_required_property.cr
│ │ ├── property_exception.cr
│ │ └── serialization_exception.cr
│ ├── exclusion_strategies/
│ │ ├── disjunct.cr
│ │ ├── exclusion_strategy_interface.cr
│ │ ├── groups.cr
│ │ └── version.cr
│ ├── navigators/
│ │ ├── deserialization_navigator.cr
│ │ ├── navigator_factory.cr
│ │ └── serialization_navigator.cr
│ ├── property_metadata.cr
│ ├── serializable.cr
│ ├── serialization_context.cr
│ ├── serializer.cr
│ ├── serializer_interface.cr
│ └── visitors/
│ ├── deserialization_visitor.cr
│ ├── deserialization_visitor_interface.cr
│ ├── json_deserialization_visitor.cr
│ ├── json_serialization_visitor.cr
│ ├── serialization_visitor_interface.cr
│ ├── yaml_deserialization_visitor.cr
│ └── yaml_serialization_visitor.cr
├── spec/
│ ├── .editorconfig
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── LICENSE
│ ├── README.md
│ ├── UPGRADING.md
│ ├── docs/
│ │ └── README.md
│ ├── mkdocs.yml
│ ├── shard.yml
│ ├── spec/
│ │ ├── athena-spec_spec.cr
│ │ ├── compiler_spec.cr
│ │ ├── methods_spec.cr
│ │ └── spec_helper.cr
│ └── src/
│ ├── athena-spec.cr
│ ├── methods.cr
│ └── test_case.cr
└── validator/
├── .editorconfig
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── UPGRADING.md
├── docs/
│ └── README.md
├── mkdocs.yml
├── shard.yml
├── spec/
│ ├── athena-validator_spec.cr
│ ├── constraint_spec.cr
│ ├── constraints/
│ │ ├── all_validator_spec.cr
│ │ ├── at_least_one_of_validator_spec.cr
│ │ ├── blank_validator_spec.cr
│ │ ├── callback_validator_spec.cr
│ │ ├── choice_validator_spec.cr
│ │ ├── collection_spec.cr
│ │ ├── collection_validator_test_case.cr
│ │ ├── composite_spec.cr
│ │ ├── compound_validator_spec.cr
│ │ ├── count_validator_spec.cr
│ │ ├── email_validator_spec.cr
│ │ ├── equal_to_validator_spec.cr
│ │ ├── file_spec.cr
│ │ ├── file_validator_ath_file_spec.cr
│ │ ├── file_validator_path_spec.cr
│ │ ├── file_validator_std_file_spec.cr
│ │ ├── file_validator_test_case.cr
│ │ ├── fixtures/
│ │ │ └── file-big.txt
│ │ ├── greater_than_or_equal_validator_spec.cr
│ │ ├── greater_than_validator_spec.cr
│ │ ├── hash_collection_validator_spec.cr
│ │ ├── hash_like_object_collection_validator_spec.cr
│ │ ├── image_validator_spec.cr
│ │ ├── ip_validator_spec.cr
│ │ ├── is_false_validator_spec.cr
│ │ ├── is_nil_validator_spec.cr
│ │ ├── is_true_validator_spec.cr
│ │ ├── isbn_validator_spec.cr
│ │ ├── isin_validator_spec.cr
│ │ ├── issn_validator_spec.cr
│ │ ├── length_validator_spec.cr
│ │ ├── less_than_or_equal_validator_spec.cr
│ │ ├── less_than_validator_spec.cr
│ │ ├── luhn_validator_spec.cr
│ │ ├── negative_or_zero_validator_spec.cr
│ │ ├── negative_validator_spec.cr
│ │ ├── not_blank_validator_spec.cr
│ │ ├── not_equal_to_validator_spec.cr
│ │ ├── not_nil_validator_spec.cr
│ │ ├── positive_or_zero_validator_spec.cr
│ │ ├── positive_validator_spec.cr
│ │ ├── range_validator_spec.cr
│ │ ├── regex_validator_spec.cr
│ │ ├── sequentially_validator_spec.cr
│ │ ├── unique_validator_spec.cr
│ │ ├── url_validator_spec.cr
│ │ └── valid_validator_spec.cr
│ ├── metadata/
│ │ └── class_metadata_spec.cr
│ ├── property_path_spec.cr
│ ├── spec/
│ │ └── compound_constraint_test_case_spec.cr
│ ├── spec_helper.cr
│ ├── validatable_spec.cr
│ ├── validator/
│ │ └── recursive_validator_spec.cr
│ └── violation/
│ ├── constraint_violation_list_spec.cr
│ └── constraint_violation_spec.cr
└── src/
├── athena-validator.cr
├── constraint.cr
├── constraint_validator.cr
├── constraint_validator_factory.cr
├── constraint_validator_factory_interface.cr
├── constraint_validator_interface.cr
├── constraints/
│ ├── abstract_comparison.cr
│ ├── abstract_comparison_validator.cr
│ ├── all.cr
│ ├── at_least_one_of.cr
│ ├── blank.cr
│ ├── callback.cr
│ ├── choice.cr
│ ├── collection.cr
│ ├── composite.cr
│ ├── compound.cr
│ ├── count.cr
│ ├── email.cr
│ ├── equal_to.cr
│ ├── existence.cr
│ ├── file.cr
│ ├── greater_than.cr
│ ├── greater_than_or_equal.cr
│ ├── group_sequence.cr
│ ├── image.cr
│ ├── ip.cr
│ ├── is_false.cr
│ ├── is_nil.cr
│ ├── is_true.cr
│ ├── isbn.cr
│ ├── isin.cr
│ ├── issn.cr
│ ├── length.cr
│ ├── less_than.cr
│ ├── less_than_or_equal.cr
│ ├── luhn.cr
│ ├── negative.cr
│ ├── negative_or_zero.cr
│ ├── not_blank.cr
│ ├── not_equal_to.cr
│ ├── not_nil.cr
│ ├── optional.cr
│ ├── positive.cr
│ ├── positive_or_zero.cr
│ ├── range.cr
│ ├── regex.cr
│ ├── required.cr
│ ├── sequentially.cr
│ ├── unique.cr
│ ├── url.cr
│ └── valid.cr
├── exception/
│ ├── invalid_argument.cr
│ ├── logic.cr
│ └── unexpected_value_error.cr
├── execution_context.cr
├── execution_context_interface.cr
├── metadata/
│ ├── cascading_strategy.cr
│ ├── class_metadata.cr
│ ├── generic_metadata.cr
│ ├── getter_metadata.cr
│ ├── metadata.cr
│ ├── metadata_factory.cr
│ ├── metadata_factory_interface.cr
│ ├── metadata_interface.cr
│ ├── property_metadata.cr
│ └── property_metadata_interface.cr
├── property_path.cr
├── spec/
│ ├── abstract_validator_test_case.cr
│ ├── compound_constraint_test_case.cr
│ ├── constraint_validator_test_case.cr
│ └── validator_test_case.cr
├── spec.cr
├── validatable.cr
├── validator/
│ ├── contextual_validator_interface.cr
│ ├── recursive_contextual_validator.cr
│ ├── recursive_validator.cr
│ └── validator_interface.cr
└── violation/
├── constraint_violation.cr
├── constraint_violation_builder.cr
├── constraint_violation_builder_interface.cr
├── constraint_violation_interface.cr
├── constraint_violation_list.cr
└── constraint_violation_list_interface.cr
SYMBOL INDEX (1 symbols across 1 files) FILE: gen_doc_stubs.py function patched_update_env (line 17) | def patched_update_env(config: dict[str, Any]) -> None:
Condensed preview — 1321 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,330K chars).
[
{
"path": ".ameba.yml",
"chars": 652,
"preview": "Documentation/DocumentationAdmonition:\n Enabled: false\nLint/ComparisonToBoolean:\n Enabled: false # TODO: Enable once h"
},
{
"path": ".changes/clock/v0.2.0.md",
"chars": 1019,
"preview": "## [0.2.0] - 2025-01-26\n\n### Changed\n\n- **Breaking:** Remove `Athena::Clock::Interface#sleep(Number)` overload ([#449]) "
},
{
"path": ".changes/clock/v0.3.0.md",
"chars": 246,
"preview": "## [0.3.0] - 2026-04-19\n\n### Removed\n\n- Remove `ACLK::Monotonic` ([#667]) (George Dietrich) <!-- blacksmoke16 -->\n\n[0.3."
},
{
"path": ".changes/console/v0.4.1.md",
"chars": 9355,
"preview": "## [0.4.1] - 2025-02-08\n\n### Fixed\n\n- Fix incorrectly aligned block ([#519]) (Zohir Tamda)\n\n[0.4.1]: https://github.com/"
},
{
"path": ".changes/console/v0.4.2.md",
"chars": 805,
"preview": "## [0.4.2] - 2025-09-04\n\n### Added\n\n- Add ability to customize the finished state of an `ACON::Helper::ProgressIndicator"
},
{
"path": ".changes/console/v0.4.3.md",
"chars": 333,
"preview": "## [0.4.3] - 2026-04-19\n\n### Added\n\n- Add opt-in support for deriving the command name from `PROGRAM_NAME` when a CLI bi"
},
{
"path": ".changes/contracts/v0.1.0.md",
"chars": 120,
"preview": "## [0.1.0] - 2025-08-02\n\n_Initial release._\n\n[0.1.0]: https://github.com/athena-framework/contracts/releases/tag/v0.1.0\n"
},
{
"path": ".changes/dependency-injection/v0.4.3.md",
"chars": 12032,
"preview": "## [0.4.3] - 2025-02-08\n\n### Changed\n\n- **Breaking:** prevent auto registering of already registered services ([#520]) ("
},
{
"path": ".changes/dependency-injection/v0.4.4.md",
"chars": 287,
"preview": "## [0.4.4] - 2025-09-04\n\n### Changed\n\n- Relax DI argument validation for string parameters ([#548]) (George Dietrich) <!"
},
{
"path": ".changes/dependency-injection/v0.4.5.md",
"chars": 2377,
"preview": "## [0.4.5] - 2026-04-19\n\n### Changed\n\n- Improve compile time error messages ([#646]) (George Dietrich) <!-- blacksmoke16"
},
{
"path": ".changes/dotenv/v0.2.0.md",
"chars": 1320,
"preview": "## [0.2.0] - 2025-01-26\n\n### Changed\n\n- **Breaking:** Normalize exception types ([#428]) (George Dietrich)\n\n[0.2.0]: htt"
},
{
"path": ".changes/dotenv/v0.2.1.md",
"chars": 286,
"preview": "## [0.2.1] - 2025-11-09\n\n### Fixed\n\n- Fix being unable to call `Athena::Dotenv.load` with a single file ([#609]) (George"
},
{
"path": ".changes/event-dispatcher/v0.3.1.md",
"chars": 4578,
"preview": "## [0.3.1] - 2025-01-26\n\n_Administrative release, no functional changes_\n\n[0.3.1]: https://github.com/athena-framework/e"
},
{
"path": ".changes/event-dispatcher/v0.4.0.md",
"chars": 515,
"preview": "## [0.4.0] - 2025-09-04\n\n### Changed\n\n- **Breaking:** Changed interface of `AED::EventDispatcherInterface#dispatch` to a"
},
{
"path": ".changes/event-dispatcher/v0.4.1.md",
"chars": 463,
"preview": "## [0.4.1] - 2026-04-19\n\n### Changed\n\n- Improve compile time error messages ([#646]) (George Dietrich) <!-- blacksmoke16"
},
{
"path": ".changes/framework/v0.20.1.md",
"chars": 20460,
"preview": "## [0.20.1] - 2025-02-08\n\n### Fixed\n\n- Fix `ATH::ViewHandler` bundle configuration values not being correctly set ([#520"
},
{
"path": ".changes/framework/v0.21.0.md",
"chars": 1113,
"preview": "## [0.21.0] - 2025-09-04\n\n### Changed\n\n- **Breaking:** Leverage `ATH::AbstractFile` within `ATH::BinaryFileResponse` ([#"
},
{
"path": ".changes/framework/v0.21.1.md",
"chars": 273,
"preview": "## [0.21.1] - 2025-10-04\n\n### Fixed\n\n- Fix improper handling of optional file uploads ([#595]) (George Dietrich) <!-- bl"
},
{
"path": ".changes/framework/v0.22.0.md",
"chars": 1342,
"preview": "## [0.22.0] - 2026-04-19\n\n### Changed\n\n- **Breaking:** Store `ATH::Action` within `ATH::Request#attributes` instead of w"
},
{
"path": ".changes/header.tpl.md",
"chars": 12,
"preview": "# Changelog\n"
},
{
"path": ".changes/http/v0.1.0.md",
"chars": 115,
"preview": "## [0.1.0] - 2026-04-19\n\n_Initial release._\n\n[0.1.0]: https://github.com/athena-framework/http/releases/tag/v0.1.0\n"
},
{
"path": ".changes/http-kernel/v0.1.0.md",
"chars": 122,
"preview": "## [0.1.0] - 2026-04-19\n\n_Initial release._\n\n[0.1.0]: https://github.com/athena-framework/http-kernel/releases/tag/v0.1."
},
{
"path": ".changes/image-size/v0.1.4.md",
"chars": 1248,
"preview": "## [0.1.4] - 2025-01-26\n\n_Administrative release, no functional changes_\n\n[0.1.4]: https://github.com/athena-framework/i"
},
{
"path": ".changes/mercure/v0.1.0.md",
"chars": 118,
"preview": "## [0.1.0] - 2026-04-19\n\n_Initial release._\n\n[0.1.0]: https://github.com/athena-framework/mercure/releases/tag/v0.1.0\n"
},
{
"path": ".changes/mercure-bundle/v0.1.0.md",
"chars": 125,
"preview": "## [0.1.0] - 2026-04-19\n\n_Initial release._\n\n[0.1.0]: https://github.com/athena-framework/mercure-bundle/releases/tag/v0"
},
{
"path": ".changes/mime/v0.2.0.md",
"chars": 404,
"preview": "## [0.2.0] - 2025-05-14\n\n### Added\n\n- **Breaking:** Add `AMIME::Types` to more robustly handles MIME type/file extension"
},
{
"path": ".changes/mime/v0.2.1.md",
"chars": 280,
"preview": "## [0.2.1] - 2025-09-04\n\n### Added\n\n- Add fallback MIME types guesser based on stdlib `MIME` module ([#546]) (George Die"
},
{
"path": ".changes/negotiation/v0.2.0.md",
"chars": 2238,
"preview": "## [0.2.0] - 2025-01-26\n\n### Changed\n\n- **Breaking:** Normalize exception types ([#428]) (George Dietrich)\n- Use lowerca"
},
{
"path": ".changes/routing/v0.1.10.md",
"chars": 5061,
"preview": "## [0.1.10] - 2025-01-26\n\n### Changed\n\n- Allow having multiple independent compiled route collections ([#468]) (George D"
},
{
"path": ".changes/routing/v0.1.11.md",
"chars": 278,
"preview": "## [0.1.11] - 2025-09-04\n\n### Fixed\n\n- Fix linker warning due to duplicate `pcre2-8` linkage ([#560]) (George Dietrich) "
},
{
"path": ".changes/routing/v0.1.12.md",
"chars": 259,
"preview": "## [0.1.12] - 2025-11-01\n\n### Fixed\n\n- Fix Crystal `1.19` incompatibility ([#600]) (George Dietrich) <!-- blacksmoke16 -"
},
{
"path": ".changes/routing/v0.2.0.md",
"chars": 712,
"preview": "## [0.2.0] - 2026-04-19\n\n### Changed\n\n- **Breaking:** Change `ART::Route#defaults` and matched route parameters return t"
},
{
"path": ".changes/serializer/v0.4.1.md",
"chars": 7540,
"preview": "## [0.4.1] - 2025-02-08\n\n### Fixed\n\n- Fix serialization of value when its type is different type than the ivar ([#514]) "
},
{
"path": ".changes/serializer/v0.4.2.md",
"chars": 251,
"preview": "## [0.4.2] - 2025-08-12\n\n### Fixed\n\n- Fix nightly type incompatibility with `ASR::Any` ([#562]) (George Dietrich)\n\n[0.4."
},
{
"path": ".changes/serializer/v0.4.3.md",
"chars": 452,
"preview": "## [0.4.3] - 2026-04-19\n\n### Changed\n\n- Improve compile time error messages ([#646]) (George Dietrich) <!-- blacksmoke16"
},
{
"path": ".changes/spec/v0.3.11.md",
"chars": 6543,
"preview": "## [0.3.11] - 2025-05-19\n\n### Fixed\n\n- Fix duplicate test case runs with abstract generic parent test case ([#538]) (Geo"
},
{
"path": ".changes/spec/v0.4.0.md",
"chars": 844,
"preview": "## [0.4.0] - 2025-09-04\n\n### Added\n\n- Add support for generating macro code coverage reports for `.assert_error` and `.a"
},
{
"path": ".changes/spec/v0.4.1.md",
"chars": 382,
"preview": "## [0.4.1] - 2025-11-12\n\n### Fixed\n\n- Fix segfault when interacting with a test case ivar object's ivar that was left un"
},
{
"path": ".changes/spec/v0.4.2.md",
"chars": 1222,
"preview": "## [0.4.2] - 2026-04-19\n\n### Added\n\n- Generate macro code coverage report for `ASPEC::Methods.assert_compiles` ([#642]) "
},
{
"path": ".changes/unreleased/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": ".changes/unreleased/event-dispatcher-Changed-20260502-225424.yaml",
"chars": 300,
"preview": "project: event-dispatcher\nkind: Changed\nbody: Event listeners with a generic type parameter now match all subclasses and"
},
{
"path": ".changes/validator/v0.4.0.md",
"chars": 8836,
"preview": "## [0.4.0] - 2025-01-26\n\n### Changed\n\n- **Breaking:** Normalize exception types ([#428]) (George Dietrich)\n\n### Added\n\n-"
},
{
"path": ".changes/validator/v0.4.1.md",
"chars": 849,
"preview": "## [0.4.1] - 2025-09-04\n\n### Changed\n\n- Leverage `mime` component for more robust `AVD::Constraints::File` MIME type val"
},
{
"path": ".changes/validator/v0.5.0.md",
"chars": 690,
"preview": "## [0.5.0] - 2026-04-19\n\n### Changed\n\n- **Breaking:** Split `AVD::Constraints::Size` into `Count` and `Length` constrain"
},
{
"path": ".changie.yaml",
"chars": 10431,
"preview": "changesDir: .changes\nunreleasedDir: unreleased\nheaderPath: header.tpl.md\nchangelogPath: CHANGELOG.md\nversionExt: md\nvers"
},
{
"path": ".editorconfig",
"chars": 150,
"preview": "root = true\n\n[*.cr]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntr"
},
{
"path": ".gitattributes",
"chars": 17,
"preview": "*.cr text eol=lf\n"
},
{
"path": ".github/dependabot.yml",
"chars": 578,
"preview": "version: 2\n\nupdates:\n - package-ecosystem: 'github-actions'\n directory: '/'\n # Updates tend to be released at the"
},
{
"path": ".github/pull_request_template.md",
"chars": 435,
"preview": "## Context\n\n<!-- Take a minute to add some context for posterity as to what this PR is doing and why -->\n<!-- Can be ski"
},
{
"path": ".github/workflows/build_and_publish_docs.yml",
"chars": 1431,
"preview": "on:\n workflow_dispatch:\n inputs:\n branch:\n description: 'Which Cloudflare Pages branch (master | dev) to"
},
{
"path": ".github/workflows/ci.yml",
"chars": 7130,
"preview": "name: CI\n\non:\n merge_group:\n push:\n branches:\n - master # Allows codecov to receive current HEAD information f"
},
{
"path": ".github/workflows/sync.yml",
"chars": 6673,
"preview": "name: Sync\n\non:\n push:\n branches:\n - master\n\nconcurrency:\n group: ${{ github.workflow }}\n cancel-in-progress:"
},
{
"path": ".github/workflows/tag_and_create_release.yml",
"chars": 3200,
"preview": "on:\n workflow_dispatch:\n inputs:\n shard:\n description: 'Name of the shard to release'\n type: stri"
},
{
"path": ".gitignore",
"chars": 373,
"preview": ".DS_Store\n*.dwarf\n/.shards/\n/bin/\n/lib/\n/logs/\n\n# Libraries don't need dependency lock\n# Dependencies will be locked in "
},
{
"path": ".python-version",
"chars": 5,
"preview": "3.13\n"
},
{
"path": ".typos.toml",
"chars": 420,
"preview": "[default]\nextend-ignore-re = [\n \"(?Rm)^.*#\\\\s*spellchecker:disable-line$\", # Allow disabling specific lines\n \"=[0-9A-F"
},
{
"path": "CONTRIBUTING.md",
"chars": 6518,
"preview": "# Contributing\n\nFirst off, thank you for taking the time to contribute! Athena, and many other open source projects, wou"
},
{
"path": "LICENSE",
"chars": 1082,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2021 George Dietrich\n\nPermission is hereby granted, free of charge, to any person o"
},
{
"path": "README.md",
"chars": 840,
"preview": "# Athena\n\n[ pattern, but with some nuances due"
},
{
"path": "codecov.yml",
"chars": 2440,
"preview": "comment:\n layout: 'condensed_header, components, condensed_files, condensed_footer'\n\ncoverage:\n status:\n project:\n "
},
{
"path": "docs/README.md",
"chars": 3444,
"preview": "## Athena\n\nAthena is a collection of general-purpose, robust, independent, and reusable components with the goal of powe"
},
{
"path": "docs/api_reference.md",
"chars": 208,
"preview": "Links to the API docs of each component may be found in this section.\nThese can be a good reference for more in-dept, co"
},
{
"path": "docs/bundle_reference.md",
"chars": 680,
"preview": "Bundles integrate components into the framework by registering services, configuring defaults, and wiring up dependencie"
},
{
"path": "docs/css/index.css",
"chars": 1060,
"preview": "/* https://mkdocstrings.github.io/crystal/styling.html#recommended-styles */\n\n/* Indentation of sub-items */\ndiv.doc-con"
},
{
"path": "docs/css/monorepo.css",
"chars": 139,
"preview": "/* Hide latest release since the monorepo is the old framework repo and still has tags */\n.md-source__fact--version {\n "
},
{
"path": "docs/getting_started/README.md",
"chars": 890,
"preview": "Athena Framework does not have any other dependencies outside of Crystal and Shards.\nIt is designed in such a way to be "
},
{
"path": "docs/getting_started/commands.md",
"chars": 3087,
"preview": "The Athena Framework comes with a built-in integration with the [Athena::Console](/Console) component.\nThis integration "
},
{
"path": "docs/getting_started/configuration.md",
"chars": 12787,
"preview": "Some features need to be configured;\neither to enable/control how they work, or to customize the default functionality.\n"
},
{
"path": "docs/getting_started/error_handling.md",
"chars": 8119,
"preview": "## HTTP Exceptions\n\nException handling in the Athena Framework is similar to exception handling in any Crystal program, "
},
{
"path": "docs/getting_started/middleware.md",
"chars": 11545,
"preview": "At a high level the Athena Framework's job is *to interpret a request and create the appropriate response based on your "
},
{
"path": "docs/getting_started/routing.md",
"chars": 21616,
"preview": "## Controllers\n\nThe Athena Framework is a MVC based framework, as such, the logic to handle a given route is defined wit"
},
{
"path": "docs/getting_started/testing.md",
"chars": 4797,
"preview": "One of the benefits of using the Athena Framework is testing is considered a first class citizen.\nBoth the framework and"
},
{
"path": "docs/getting_started/validation.md",
"chars": 1198,
"preview": "The [Athena::Validator](/Validator) component adds a robust/flexible validation framework.\nThis component is also mostly"
},
{
"path": "docs/guides/README.md",
"chars": 112,
"preview": "This section of the documentation includes various guides for high level features that don't fit anywhere else.\n"
},
{
"path": "docs/guides/proxies.md",
"chars": 3739,
"preview": "It's usually considered a best practice to run an application behind a reverse proxy or load balancer.\nFor the most part"
},
{
"path": "docs/index.cr",
"chars": 236,
"preview": "# This file is included by each component when building docs.\n# Can be used to create things in the docs that are common"
},
{
"path": "docs/templates/crystal/material/schema.html",
"chars": 3303,
"preview": "{% macro render_members(members, obj, heading_level, parent_id, parent_short_name) %}\n {% for member in members %}\n "
},
{
"path": "docs/templates/crystal/material/type.html",
"chars": 3262,
"preview": "{{ log.debug() }}\n\n<div class=\"doc doc-object doc-type {{ obj.kind }}\">\n{% if \"Athena::DependencyInjection::Extension::S"
},
{
"path": "docs/why_athena.md",
"chars": 17087,
"preview": "## Creating \"good\" Software\n\nWhen creating an application, actually writing the code is often the easiest part. Designi"
},
{
"path": "gen_doc_stubs.py",
"chars": 3102,
"preview": "# Generates virtual doc files for the mkdocs site.\n# You can also run this script directly to actually write out those f"
},
{
"path": "justfile",
"chars": 3189,
"preview": "# Configuration\n\nOUTPUT_DIR := './site'\n\n# Binaries\n# Scoped to the justfile so do not need to be exported\n\nUV := 'uv'\n\n"
},
{
"path": "mkdocs-common.yml",
"chars": 1233,
"preview": "theme:\n name: material\n palette:\n - media: '(prefers-color-scheme: light)'\n scheme: default\n primary: bla"
},
{
"path": "mkdocs.yml",
"chars": 1658,
"preview": "INHERIT: ./mkdocs-common.yml\n\nsite_name: Athena\nsite_url: https://athenaframework.org/\nrepo_url: https://github.com/athe"
},
{
"path": "pyproject.toml",
"chars": 476,
"preview": "[project]\nname = \"athena-docs\"\nversion = \"0.0.0\"\ndescription = \"Documentation build dependencies for the Athena ecosyste"
},
{
"path": "scripts/test.sh",
"chars": 3702,
"preview": "#!/usr/bin/env bash\n\n# $1 shard name\n# $2 shard type\nfunction runSpecs() (\n set -e\n $CRYSTAL spec \"${DEFAULT_BUILD_OPT"
},
{
"path": "shard.dev.yml",
"chars": 1070,
"preview": "# $ SHARDS_OVERRIDE=shard.dev.yml shards update\ndependencies:\n athena:\n path: ./src/components/framework\n athena-cl"
},
{
"path": "shard.prod.yml",
"chars": 1516,
"preview": "# Used for prod builds of the docs to ensure API docs can be updated w/o a dedicated release\n# $ SHARDS_OVERRIDE=shard.p"
},
{
"path": "shard.yml",
"chars": 1589,
"preview": "name: athena-ecosystem\nversion: 0.0.0\n\n# Min Crystal version required to run the test suite/develop on Athena.\ncrystal: "
},
{
"path": "src/bundles/mercure/.editorconfig",
"chars": 150,
"preview": "root = true\n\n[*.cr]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntr"
},
{
"path": "src/bundles/mercure/.gitignore",
"chars": 142,
"preview": "/lib/\n/bin/\n/.shards/\n*.dwarf\n\n# Libraries don't need dependency lock\n# Dependencies will be locked in applications that"
},
{
"path": "src/bundles/mercure/CHANGELOG.md",
"chars": 138,
"preview": "# Changelog\n\n## [0.1.0] - 2026-04-19\n\n_Initial release._\n\n[0.1.0]: https://github.com/athena-framework/mercure-bundle/re"
},
{
"path": "src/bundles/mercure/CONTRIBUTING.md",
"chars": 199,
"preview": "# Contributing\n\nThis repository is a read-only mirror. Please refer the [main Athena repository](https://github.com/athe"
},
{
"path": "src/bundles/mercure/LICENSE",
"chars": 1082,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2026 George Dietrich\n\nPermission is hereby granted, free of charge, to any person o"
},
{
"path": "src/bundles/mercure/README.md",
"chars": 678,
"preview": "# MercureBundle\n\n[](https://common-changelog.org)\n[ : Nil\n ASPEC::Methods.a"
},
{
"path": "src/bundles/mercure/spec/discovery_spec.cr",
"chars": 2031,
"preview": "require \"./spec_helper\"\n\nstruct DiscoveryTest < ASPEC::TestCase\n def test_add_link : Nil\n discovery = ABM::Discovery"
},
{
"path": "src/bundles/mercure/spec/listeners/add_link_header_spec.cr",
"chars": 1855,
"preview": "require \"../spec_helper\"\n\nstruct AddLinkHeaderListenerTest < ASPEC::TestCase\n def test_no_links_attribute : Nil\n eve"
},
{
"path": "src/bundles/mercure/spec/listeners/set_cookie_spec.cr",
"chars": 1933,
"preview": "require \"../spec_helper\"\n\nstruct SetCookieListenerTest < ASPEC::TestCase\n def test_no_cookies_attribute : Nil\n event"
},
{
"path": "src/bundles/mercure/spec/spec_helper.cr",
"chars": 891,
"preview": "require \"spec\"\n\nrequire \"athena-spec\"\n\nrequire \"../src/athena-mercure_bundle\"\n\nrequire \"athena-mercure/src/spec\"\n\nASPEC."
},
{
"path": "src/bundles/mercure/src/athena-mercure_bundle.cr",
"chars": 8548,
"preview": "require \"athena-dependency_injection\"\nrequire \"athena-http_kernel\"\nrequire \"athena-mercure\"\n\nrequire \"./authorization\"\nr"
},
{
"path": "src/bundles/mercure/src/authorization.cr",
"chars": 2981,
"preview": "struct Athena::MercureBundle < ADI::AbstractBundle; end\n\n# Extension of [AMC::Authorization](/Mercure/Authorization) to "
},
{
"path": "src/bundles/mercure/src/discovery.cr",
"chars": 867,
"preview": "# Extension of [AMC::Discovery](/Mercure/Discovery/) that accepts [AHTTP::Request](/HTTP/Request/)\n# and stores the link"
},
{
"path": "src/bundles/mercure/src/listeners/add_link_header.cr",
"chars": 495,
"preview": "# Adds Mercure hub `Link` headers that was stored in the request attributes via `ABM::Discovery`.\n@[ADI::Register]\nstruc"
},
{
"path": "src/bundles/mercure/src/listeners/set_cookie.cr",
"chars": 576,
"preview": "# Adds `mercureAuthorization` cookies that were stored in the request attributes via `ABM::Authorization`.\n@[ADI::Regist"
},
{
"path": "src/components/clock/.editorconfig",
"chars": 150,
"preview": "root = true\n\n[*.cr]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\ntr"
},
{
"path": "src/components/clock/.gitignore",
"chars": 142,
"preview": "/lib/\n/bin/\n/.shards/\n*.dwarf\n\n# Libraries don't need dependency lock\n# Dependencies will be locked in applications that"
},
{
"path": "src/components/clock/CHANGELOG.md",
"chars": 1279,
"preview": "# Changelog\n\n## [0.3.0] - 2026-04-19\n\n### Removed\n\n- Remove `ACLK::Monotonic` ([#667]) (George Dietrich) <!-- blacksmoke"
},
{
"path": "src/components/clock/CONTRIBUTING.md",
"chars": 199,
"preview": "# Contributing\n\nThis repository is a read-only mirror. Please refer the [main Athena repository](https://github.com/athe"
},
{
"path": "src/components/clock/LICENSE",
"chars": 1082,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2023 George Dietrich\n\nPermission is hereby granted, free of charge, to any person o"
},
{
"path": "src/components/clock/README.md",
"chars": 671,
"preview": "# Clock\n\n[](https://common-changelog.org)\n[\n\nCopyright (c) 2021 George Dietrich\n\nPermission is hereby granted, free of charge, to any person o"
},
{
"path": "src/components/console/README.md",
"chars": 680,
"preview": "# Console\n\n[](https://common-changelog.org)\n[ : Nil"
},
{
"path": "src/components/console/spec/command_tester_spec.cr",
"chars": 6546,
"preview": "require \"./spec_helper\"\n\nstruct CommandTesterTest < ASPEC::TestCase\n @command : ACON::Command\n @tester : ACON::Spec::C"
},
{
"path": "src/components/console/spec/commands/complete_spec.cr",
"chars": 5241,
"preview": "require \"../spec_helper\"\n\n@[ACONA::AsCommand(\"hello|ahoy\", description: \"Hello test command\")]\nprivate class HelloComman"
},
{
"path": "src/components/console/spec/commands/dump_completion_spec.cr",
"chars": 497,
"preview": "require \"../spec_helper\"\n\nstruct DumpCompletionCommandTest < ASPEC::TestCase\n @[DataProvider(\"complete_provider\")]\n de"
},
{
"path": "src/components/console/spec/commands/help_spec.cr",
"chars": 1826,
"preview": "require \"../spec_helper\"\n\nstruct HelpCommandTest < ASPEC::TestCase\n def test_execute_alias : Nil\n command = ACON::Co"
},
{
"path": "src/components/console/spec/commands/lazy_spec.cr",
"chars": 1785,
"preview": "require \"../spec_helper\"\n\n@[ACONA::AsCommand(\"blahhhh\")]\nprivate class MockCommand < ACON::Command\n protected def execu"
},
{
"path": "src/components/console/spec/commands/list_spec.cr",
"chars": 3519,
"preview": "require \"../spec_helper\"\n\nprivate def normalize(input : String) : String\n input.gsub EOL, \"\\n\"\nend\n\nstruct ListCommandT"
},
{
"path": "src/components/console/spec/compiler_spec.cr",
"chars": 1500,
"preview": "require \"./spec_helper\"\n\ndescribe Athena::Console do\n describe \"compiler errors\", tags: \"compiled\" do\n describe \"whe"
},
{
"path": "src/components/console/spec/completion/input_spec.cr",
"chars": 5764,
"preview": "require \"../spec_helper\"\n\nprivate alias Input = ACON::Completion::Input\n\nstruct CompletionInputTest < ASPEC::TestCase\n "
},
{
"path": "src/components/console/spec/completion/output/bash_spec.cr",
"chars": 368,
"preview": "require \"./completion_output_test_case\"\n\nstruct BashTest < CompletionOutputTestCase\n def completion_output : ACON::Comp"
},
{
"path": "src/components/console/spec/completion/output/completion_output_test_case.cr",
"chars": 1129,
"preview": "require \"../../spec_helper\"\n\nabstract struct CompletionOutputTestCase < ASPEC::TestCase\n abstract def completion_output"
},
{
"path": "src/components/console/spec/completion/output/fish_spec.cr",
"chars": 356,
"preview": "require \"./completion_output_test_case\"\n\nstruct FishTest < CompletionOutputTestCase\n def completion_output : ACON::Comp"
},
{
"path": "src/components/console/spec/completion/output/zsh_spec.cr",
"chars": 459,
"preview": "require \"./completion_output_test_case\"\n\nstruct ZshTest < CompletionOutputTestCase\n def completion_output : ACON::Compl"
},
{
"path": "src/components/console/spec/cursor_spec.cr",
"chars": 2635,
"preview": "require \"./spec_helper\"\n\nstruct CursorTest < ASPEC::TestCase\n @cursor : ACON::Cursor\n @output : ACON::Output::IO\n\n de"
},
{
"path": "src/components/console/spec/descriptor/abstract_descriptor_test_case.cr",
"chars": 2384,
"preview": "require \"../spec_helper\"\nrequire \"./object_provider\"\n\nabstract struct AbstractDescriptorTestCase < ASPEC::TestCase\n @[D"
},
{
"path": "src/components/console/spec/descriptor/application_spec.cr",
"chars": 796,
"preview": "require \"../spec_helper\"\n\nprivate class TestApplication < ACON::Application\n protected def default_commands : Array(ACO"
},
{
"path": "src/components/console/spec/descriptor/object_provider.cr",
"chars": 2849,
"preview": "module ObjectProvider\n def self.input_arguments : Hash(String, ACON::Input::Argument)\n {\n \"input_argument_1\" "
},
{
"path": "src/components/console/spec/descriptor/text_spec.cr",
"chars": 644,
"preview": "require \"../spec_helper\"\nrequire \"./abstract_descriptor_test_case\"\n\nstruct TextDescriptorTest < AbstractDescriptorTestCa"
},
{
"path": "src/components/console/spec/fixtures/applications/descriptor1.cr",
"chars": 53,
"preview": "class DescriptorApplication1 < ACON::Application\nend\n"
},
{
"path": "src/components/console/spec/fixtures/applications/descriptor2.cr",
"chars": 264,
"preview": "class DescriptorApplication2 < ACON::Application\n def initialize\n super \"My Athena application\", \"1.0.0\"\n\n self.a"
},
{
"path": "src/components/console/spec/fixtures/commands/annotation_configured.cr",
"chars": 321,
"preview": "@[ACONA::AsCommand(\"annotation:configured\", description: \"Command configured via annotation\", aliases: [\"ac\"])]\nclass An"
},
{
"path": "src/components/console/spec/fixtures/commands/annotation_configured_aliases.cr",
"chars": 264,
"preview": "@[ACONA::AsCommand(\"annotation:configured|ac\")]\nclass AnnotationConfiguredAliasesCommand < ACON::Command\n protected def"
},
{
"path": "src/components/console/spec/fixtures/commands/annotation_configured_hidden.cr",
"chars": 261,
"preview": "@[ACONA::AsCommand(\"|annotation:configured\")]\nclass AnnotationConfiguredHiddenCommand < ACON::Command\n protected def ex"
},
{
"path": "src/components/console/spec/fixtures/commands/annotation_configured_hidden_field.cr",
"chars": 279,
"preview": "@[ACONA::AsCommand(\"annotation:configured\", hidden: true)]\nclass AnnotationConfiguredHiddenFieldCommand < ACON::Command\n"
},
{
"path": "src/components/console/spec/fixtures/commands/bar_buc.cr",
"chars": 266,
"preview": "class BarBucCommand < ACON::Command\n protected def configure : Nil\n self\n .name(\"bar:buc\")\n end\n\n protected d"
},
{
"path": "src/components/console/spec/fixtures/commands/descriptor1.cr",
"chars": 392,
"preview": "class DescriptorCommand1 < ACON::Command\n protected def configure : Nil\n self\n .name(\"descriptor:command1\")\n "
},
{
"path": "src/components/console/spec/fixtures/commands/descriptor2.cr",
"chars": 523,
"preview": "class DescriptorCommand2 < ACON::Command\n protected def configure : Nil\n self\n .name(\"descriptor:command2\")\n "
},
{
"path": "src/components/console/spec/fixtures/commands/descriptor3.cr",
"chars": 371,
"preview": "class DescriptorCommand3 < ACON::Command\n protected def configure : Nil\n self\n .name(\"descriptor:command3\")\n "
},
{
"path": "src/components/console/spec/fixtures/commands/descriptor4.cr",
"chars": 350,
"preview": "class DescriptorCommand4 < ACON::Command\n protected def configure : Nil\n self\n .name(\"descriptor:command4\")\n "
},
{
"path": "src/components/console/spec/fixtures/commands/foo.cr",
"chars": 474,
"preview": "class FooCommand < IOCommand\n protected def configure : Nil\n self\n .name(\"foo:bar\")\n .description(\"The foo"
},
{
"path": "src/components/console/spec/fixtures/commands/foo1.cr",
"chars": 175,
"preview": "class Foo1Command < IOCommand\n protected def configure : Nil\n self\n .name(\"foo:bar1\")\n .description(\"The f"
},
{
"path": "src/components/console/spec/fixtures/commands/foo2.cr",
"chars": 331,
"preview": "class Foo2Command < IOCommand\n protected def configure : Nil\n self\n .name(\"foo1:bar\")\n .description(\"The f"
},
{
"path": "src/components/console/spec/fixtures/commands/foo3.cr",
"chars": 562,
"preview": "class Foo3Command < ACON::Command\n protected def configure : Nil\n self\n .name(\"foo3:bar\")\n .description(\"T"
},
{
"path": "src/components/console/spec/fixtures/commands/foo4.cr",
"chars": 269,
"preview": "class Foo4Command < ACON::Command\n protected def configure : Nil\n self\n .name(\"foo3:bar:toh\")\n end\n\n protecte"
},
{
"path": "src/components/console/spec/fixtures/commands/foo6.cr",
"chars": 304,
"preview": "class Foo6Command < ACON::Command\n protected def configure : Nil\n self\n .name(\"0foo:bar\")\n .description(\"0"
},
{
"path": "src/components/console/spec/fixtures/commands/foo_bar.cr",
"chars": 154,
"preview": "class FooBarCommand < IOCommand\n protected def configure : Nil\n self\n .name(\"foobar:foo\")\n .description(\"T"
},
{
"path": "src/components/console/spec/fixtures/commands/foo_hidden.cr",
"chars": 321,
"preview": "class FooHiddenCommand < ACON::Command\n protected def configure : Nil\n self\n .name(\"foo:hidden\")\n .aliases"
},
{
"path": "src/components/console/spec/fixtures/commands/foo_opt.cr",
"chars": 486,
"preview": "class FooOptCommand < IOCommand\n protected def configure : Nil\n self\n .name(\"foo:bar\")\n .description(\"The "
},
{
"path": "src/components/console/spec/fixtures/commands/foo_same_case_lowercase.cr",
"chars": 318,
"preview": "class FooSameCaseLowercaseCommand < ACON::Command\n protected def configure : Nil\n self\n .name(\"foo:bar\")\n "
},
{
"path": "src/components/console/spec/fixtures/commands/foo_same_case_uppercase.cr",
"chars": 318,
"preview": "class FooSameCaseUppercaseCommand < ACON::Command\n protected def configure : Nil\n self\n .name(\"foo:BAR\")\n "
},
{
"path": "src/components/console/spec/fixtures/commands/foo_subnamespaced1.cr",
"chars": 195,
"preview": "class FooSubnamespaced1Command < IOCommand\n protected def configure : Nil\n self\n .name(\"foo:bar:baz\")\n .de"
},
{
"path": "src/components/console/spec/fixtures/commands/foo_subnamespaced2.cr",
"chars": 192,
"preview": "class FooSubnamespaced2Command < IOCommand\n protected def configure : Nil\n self\n .name(\"foo:bar:go\")\n .des"
},
{
"path": "src/components/console/spec/fixtures/commands/foo_without_alias.cr",
"chars": 339,
"preview": "class FooWithoutAliasCommand < IOCommand\n protected def configure : Nil\n self\n .name(\"foo\")\n .description("
},
{
"path": "src/components/console/spec/fixtures/commands/io.cr",
"chars": 287,
"preview": "abstract class IOCommand < ACON::Command\n getter! input : ACON::Input::Interface\n getter! output : ACON::Output::Inter"
},
{
"path": "src/components/console/spec/fixtures/commands/test.cr",
"chars": 520,
"preview": "class TestCommand < ACON::Command\n protected def configure : Nil\n self\n .name(\"namespace:name\")\n .descript"
},
{
"path": "src/components/console/spec/fixtures/commands/test_ambiguous_command_registering1.cr",
"chars": 397,
"preview": "class TestAmbiguousCommandRegistering < ACON::Command\n protected def configure : Nil\n self\n .name(\"test-ambiguo"
},
{
"path": "src/components/console/spec/fixtures/commands/test_ambiguous_command_registering2.cr",
"chars": 378,
"preview": "class TestAmbiguousCommandRegistering2 < ACON::Command\n protected def configure : Nil\n self\n .name(\"test-ambigu"
},
{
"path": "src/components/console/spec/fixtures/helper/table/borderless.txt",
"chars": 512,
"preview": " =============== ========================== ================== \n ISBN Title Author "
},
{
"path": "src/components/console/spec/fixtures/helper/table/borderless_vertical.txt",
"chars": 363,
"preview": " ============================== \n ISBN: 99921-58-10-7 \n Title: Divine Comedy \n Author: Dante Aligh"
},
{
"path": "src/components/console/spec/fixtures/helper/table/box.txt",
"chars": 512,
"preview": "┌───────────────┬──────────────────────────┬──────────────────┐\n│ ISBN │ Title │ Author "
},
{
"path": "src/components/console/spec/fixtures/helper/table/compact.txt",
"chars": 285,
"preview": "ISBN Title Author \n99921-58-10-7 Divine Comedy Dante Alighieri \n9971-5"
},
{
"path": "src/components/console/spec/fixtures/helper/table/compact_vertical.txt",
"chars": 241,
"preview": " ISBN: 99921-58-10-7 \n Title: Divine Comedy \nAuthor: Dante Alighieri \n Price: 9.95 \n"
},
{
"path": "src/components/console/spec/fixtures/helper/table/default.txt",
"chars": 512,
"preview": "+---------------+--------------------------+------------------+\n| ISBN | Title | Author "
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_colspan.txt",
"chars": 1344,
"preview": "+-------------------------------+-------------------------------+-----------------------------+\n| ISBN "
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_formatting_tags.txt",
"chars": 354,
"preview": "+---------------+----------------------+-----------------+\n| ISBN | Title | Author |\n+-"
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_non_formatting_tags.txt",
"chars": 468,
"preview": "+----------------------------------+----------------------+-----------------+\n| ISBN | Title"
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan.txt",
"chars": 624,
"preview": "+---------------+---------------+-----------------+\n| ISBN | Title | Author |\n+---------------"
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan.txt",
"chars": 490,
"preview": "+------------------+---------+-----------------+\n| ISBN | Title | Author |\n+------------------+--"
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_alignment.txt",
"chars": 702,
"preview": "+---------------+---------------+-------------------------------------------+\n| ISBN | Title | "
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_custom_format.txt",
"chars": 471,
"preview": "+----------------+---------------+---------------------+\n|\u001b[30;46m ISBN \u001b[39;49m|\u001b[32m Title \u001b[39m|\u001b[3"
},
{
"path": "src/components/console/spec/fixtures/helper/table/default_cells_with_rowspan_and_colspan_and_fgbg.txt",
"chars": 646,
"preview": "+---------------+---------------+-------------------------------------------+\n| \u001b[31m978\u001b[39m | De Monarchia "
}
]
// ... and 1121 more files (download for full content)
About this extraction
This page contains the full source code of the athena-framework/athena GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1321 files (3.8 MB), approximately 1.1M tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.