Full Code of phoenixframework/phoenix for AI

main eca672ec4bdc cached
454 files
3.1 MB
833.7k tokens
2488 symbols
1 requests
Download .txt
Showing preview only (3,324K chars total). Download the full file or copy to clipboard to get everything.
Repository: phoenixframework/phoenix
Branch: main
Commit: eca672ec4bdc
Files: 454
Total size: 3.1 MB

Directory structure:
gitextract_fy0ivb69/

├── .formatter.exs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── config.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── assets.yml
│       ├── ci.yml
│       └── npm-publish.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── RELEASE.md
├── SECURITY.md
├── assets/
│   ├── js/
│   │   └── phoenix/
│   │       ├── ajax.js
│   │       ├── channel.js
│   │       ├── constants.js
│   │       ├── index.js
│   │       ├── longpoll.js
│   │       ├── presence.js
│   │       ├── push.js
│   │       ├── serializer.js
│   │       ├── socket.js
│   │       ├── timer.js
│   │       └── utils.js
│   └── test/
│       ├── channel_test.js
│       ├── longpoll_test.js
│       ├── presence_test.js
│       ├── serializer.js
│       ├── serializer_test.js
│       ├── socket_http_test.js
│       └── socket_test.js
├── babel.config.json
├── config/
│   └── config.exs
├── eslint.config.mjs
├── guides/
│   ├── asset_management.md
│   ├── authn_authz/
│   │   ├── api_authentication.md
│   │   ├── authn_authz.md
│   │   ├── mix_phx_gen_auth.md
│   │   └── scopes.md
│   ├── cheatsheets/
│   │   └── router.cheatmd
│   ├── components.md
│   ├── controllers.md
│   ├── data_modelling/
│   │   ├── contexts.md
│   │   ├── cross_context_boundaries.md
│   │   ├── faq.md
│   │   ├── in_context_relationships.md
│   │   ├── more_examples.md
│   │   └── your_first_context.md
│   ├── deployment/
│   │   ├── deployment.md
│   │   ├── fly.md
│   │   ├── gigalixir.md
│   │   ├── heroku.md
│   │   └── releases.md
│   ├── directory_structure.md
│   ├── ecto.md
│   ├── howto/
│   │   ├── custom_error_pages.md
│   │   ├── file_uploads.md
│   │   ├── swapping_databases.md
│   │   ├── using_ssl.md
│   │   └── writing_a_channels_client.md
│   ├── introduction/
│   │   ├── community.md
│   │   ├── installation.md
│   │   ├── overview.md
│   │   ├── packages_glossary.md
│   │   └── up_and_running.md
│   ├── json_and_apis.md
│   ├── live_view.md
│   ├── plug.md
│   ├── real_time/
│   │   ├── channels.md
│   │   └── presence.md
│   ├── request_lifecycle.md
│   ├── routing.md
│   ├── security.md
│   ├── telemetry.md
│   └── testing/
│       ├── testing.md
│       ├── testing_channels.md
│       ├── testing_contexts.md
│       └── testing_controllers.md
├── installer/
│   ├── .gitignore
│   ├── README.md
│   ├── lib/
│   │   ├── mix/
│   │   │   └── tasks/
│   │   │       ├── local.phx.ex
│   │   │       ├── phx.new.ecto.ex
│   │   │       ├── phx.new.ex
│   │   │       └── phx.new.web.ex
│   │   └── phx_new/
│   │       ├── ecto.ex
│   │       ├── generator.ex
│   │       ├── mailer.ex
│   │       ├── project.ex
│   │       ├── single.ex
│   │       ├── umbrella.ex
│   │       └── web.ex
│   ├── mix.exs
│   ├── recreate_default_css.exs
│   ├── templates/
│   │   ├── phx_assets/
│   │   │   ├── app.css.eex
│   │   │   ├── app.js.eex
│   │   │   ├── daisyui-theme.js.eex
│   │   │   ├── daisyui.js.eex
│   │   │   ├── heroicons.js.eex
│   │   │   ├── logo.svg.eex
│   │   │   ├── topbar.js.eex
│   │   │   └── tsconfig.json.eex
│   │   ├── phx_ecto/
│   │   │   ├── data_case.ex.eex
│   │   │   ├── formatter.exs.eex
│   │   │   ├── repo.ex.eex
│   │   │   └── seeds.exs.eex
│   │   ├── phx_gettext/
│   │   │   ├── en/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── errors.po.eex
│   │   │   ├── errors.pot.eex
│   │   │   └── gettext.ex.eex
│   │   ├── phx_mailer/
│   │   │   └── lib/
│   │   │       └── app_name/
│   │   │           └── mailer.ex.eex
│   │   ├── phx_single/
│   │   │   ├── README.md.eex
│   │   │   ├── config/
│   │   │   │   ├── config.exs.eex
│   │   │   │   ├── dev.exs.eex
│   │   │   │   ├── prod.exs.eex
│   │   │   │   ├── runtime.exs.eex
│   │   │   │   └── test.exs.eex
│   │   │   ├── formatter.exs.eex
│   │   │   ├── gitignore.eex
│   │   │   ├── lib/
│   │   │   │   ├── app_name/
│   │   │   │   │   └── application.ex.eex
│   │   │   │   ├── app_name.ex.eex
│   │   │   │   └── app_name_web.ex.eex
│   │   │   ├── mix.exs.eex
│   │   │   └── test/
│   │   │       └── test_helper.exs.eex
│   │   ├── phx_static/
│   │   │   ├── app.css
│   │   │   ├── app.js
│   │   │   ├── default.css
│   │   │   └── robots.txt
│   │   ├── phx_test/
│   │   │   ├── controllers/
│   │   │   │   ├── error_html_test.exs.eex
│   │   │   │   ├── error_json_test.exs.eex
│   │   │   │   └── page_controller_test.exs.eex
│   │   │   └── support/
│   │   │       └── conn_case.ex.eex
│   │   ├── phx_umbrella/
│   │   │   ├── README.md.eex
│   │   │   ├── apps/
│   │   │   │   ├── app_name/
│   │   │   │   │   ├── README.md.eex
│   │   │   │   │   ├── config/
│   │   │   │   │   │   └── config.exs.eex
│   │   │   │   │   ├── formatter.exs.eex
│   │   │   │   │   ├── gitignore.eex
│   │   │   │   │   ├── lib/
│   │   │   │   │   │   ├── app_name/
│   │   │   │   │   │   │   └── application.ex.eex
│   │   │   │   │   │   └── app_name.ex.eex
│   │   │   │   │   ├── mix.exs.eex
│   │   │   │   │   └── test/
│   │   │   │   │       └── test_helper.exs.eex
│   │   │   │   └── app_name_web/
│   │   │   │       ├── README.md.eex
│   │   │   │       ├── config/
│   │   │   │       │   ├── config.exs.eex
│   │   │   │       │   ├── dev.exs.eex
│   │   │   │       │   ├── prod.exs.eex
│   │   │   │       │   ├── runtime.exs.eex
│   │   │   │       │   └── test.exs.eex
│   │   │   │       ├── formatter.exs.eex
│   │   │   │       ├── gitignore.eex
│   │   │   │       ├── lib/
│   │   │   │       │   ├── app_name/
│   │   │   │       │   │   └── application.ex.eex
│   │   │   │       │   └── app_name.ex.eex
│   │   │   │       ├── mix.exs.eex
│   │   │   │       └── test/
│   │   │   │           └── test_helper.exs.eex
│   │   │   ├── config/
│   │   │   │   ├── config.exs.eex
│   │   │   │   ├── dev.exs.eex
│   │   │   │   ├── extra_config.exs.eex
│   │   │   │   ├── prod.exs.eex
│   │   │   │   ├── runtime.exs.eex
│   │   │   │   └── test.exs.eex
│   │   │   ├── formatter.exs.eex
│   │   │   ├── gitignore.eex
│   │   │   └── mix.exs.eex
│   │   ├── phx_web/
│   │   │   ├── components/
│   │   │   │   ├── core_components.ex.eex
│   │   │   │   ├── layouts/
│   │   │   │   │   └── root.html.heex.eex
│   │   │   │   └── layouts.ex.eex
│   │   │   ├── controllers/
│   │   │   │   ├── error_html.ex.eex
│   │   │   │   ├── error_json.ex.eex
│   │   │   │   ├── page_controller.ex.eex
│   │   │   │   ├── page_html/
│   │   │   │   │   └── home.html.heex.eex
│   │   │   │   └── page_html.ex.eex
│   │   │   ├── endpoint.ex.eex
│   │   │   ├── router.ex.eex
│   │   │   └── telemetry.ex.eex
│   │   └── usage-rules/
│   │       ├── assets.md
│   │       ├── phoenix.md
│   │       └── project.md
│   └── test/
│       ├── mix_helper.exs
│       ├── phx_new_ecto_test.exs
│       ├── phx_new_test.exs
│       ├── phx_new_umbrella_test.exs
│       ├── phx_new_web_test.exs
│       └── test_helper.exs
├── integration_test/
│   ├── README.md
│   ├── config/
│   │   └── config.exs
│   ├── docker-compose.yml
│   ├── docker.sh
│   ├── mix.exs
│   ├── test/
│   │   ├── code_generation/
│   │   │   ├── app_with_defaults_test.exs
│   │   │   ├── app_with_mssql_adapter_test.exs
│   │   │   ├── app_with_mysql_adapter_test.exs
│   │   │   ├── app_with_no_options_test.exs
│   │   │   ├── app_with_scopes_test.exs
│   │   │   ├── app_with_sqlite3_adapter_test.exs
│   │   │   └── umbrella_app_with_defaults_test.exs
│   │   ├── support/
│   │   │   └── code_generator_case.ex
│   │   └── test_helper.exs
│   └── test.sh
├── jest.config.js
├── lib/
│   ├── mix/
│   │   ├── phoenix/
│   │   │   ├── context.ex
│   │   │   ├── schema.ex
│   │   │   └── scope.ex
│   │   ├── phoenix.ex
│   │   └── tasks/
│   │       ├── compile.phoenix.ex
│   │       ├── phx.digest.clean.ex
│   │       ├── phx.digest.ex
│   │       ├── phx.ex
│   │       ├── phx.gen.auth/
│   │       │   ├── hashing_library.ex
│   │       │   ├── injector.ex
│   │       │   └── migration.ex
│   │       ├── phx.gen.auth.ex
│   │       ├── phx.gen.cert.ex
│   │       ├── phx.gen.channel.ex
│   │       ├── phx.gen.context.ex
│   │       ├── phx.gen.embedded.ex
│   │       ├── phx.gen.ex
│   │       ├── phx.gen.html.ex
│   │       ├── phx.gen.json.ex
│   │       ├── phx.gen.live.ex
│   │       ├── phx.gen.notifier.ex
│   │       ├── phx.gen.presence.ex
│   │       ├── phx.gen.release.ex
│   │       ├── phx.gen.schema.ex
│   │       ├── phx.gen.secret.ex
│   │       ├── phx.gen.socket.ex
│   │       ├── phx.routes.ex
│   │       └── phx.server.ex
│   ├── phoenix/
│   │   ├── channel/
│   │   │   └── server.ex
│   │   ├── channel.ex
│   │   ├── code_reloader/
│   │   │   ├── mix_listener.ex
│   │   │   ├── proxy.ex
│   │   │   └── server.ex
│   │   ├── code_reloader.ex
│   │   ├── config.ex
│   │   ├── controller/
│   │   │   └── pipeline.ex
│   │   ├── controller.ex
│   │   ├── debug.ex
│   │   ├── digester/
│   │   │   ├── compressor.ex
│   │   │   └── gzip.ex
│   │   ├── digester.ex
│   │   ├── endpoint/
│   │   │   ├── cowboy2_adapter.ex
│   │   │   ├── render_errors.ex
│   │   │   ├── supervisor.ex
│   │   │   ├── sync_code_reload_plug.ex
│   │   │   └── watcher.ex
│   │   ├── endpoint.ex
│   │   ├── exceptions.ex
│   │   ├── flash.ex
│   │   ├── logger.ex
│   │   ├── naming.ex
│   │   ├── param.ex
│   │   ├── presence.ex
│   │   ├── router/
│   │   │   ├── console_formatter.ex
│   │   │   ├── helpers.ex
│   │   │   ├── resource.ex
│   │   │   ├── route.ex
│   │   │   └── scope.ex
│   │   ├── router.ex
│   │   ├── socket/
│   │   │   ├── message.ex
│   │   │   ├── pool_supervisor.ex
│   │   │   ├── serializer.ex
│   │   │   ├── serializers/
│   │   │   │   ├── v1_json_serializer.ex
│   │   │   │   └── v2_json_serializer.ex
│   │   │   └── transport.ex
│   │   ├── socket.ex
│   │   ├── test/
│   │   │   ├── channel_test.ex
│   │   │   └── conn_test.ex
│   │   ├── token.ex
│   │   ├── transports/
│   │   │   ├── long_poll.ex
│   │   │   ├── long_poll_server.ex
│   │   │   └── websocket.ex
│   │   └── verified_routes.ex
│   └── phoenix.ex
├── mix.exs
├── package.json
├── priv/
│   ├── static/
│   │   ├── phoenix.cjs.js
│   │   ├── phoenix.js
│   │   └── phoenix.mjs
│   └── templates/
│       ├── phx.gen.auth/
│       │   ├── AGENTS.md.eex
│       │   ├── auth.ex.eex
│       │   ├── auth_test.exs.eex
│       │   ├── confirmation_live.ex.eex
│       │   ├── confirmation_live_test.exs.eex
│       │   ├── conn_case.exs.eex
│       │   ├── context_fixtures_functions.ex.eex
│       │   ├── context_functions.ex.eex
│       │   ├── login_live.ex.eex
│       │   ├── login_live_test.exs.eex
│       │   ├── migration.ex.eex
│       │   ├── notifier.ex.eex
│       │   ├── registration_controller.ex.eex
│       │   ├── registration_controller_test.exs.eex
│       │   ├── registration_html.ex.eex
│       │   ├── registration_live.ex.eex
│       │   ├── registration_live_test.exs.eex
│       │   ├── registration_new.html.heex.eex
│       │   ├── routes.ex.eex
│       │   ├── schema.ex.eex
│       │   ├── schema_token.ex.eex
│       │   ├── scope.ex.eex
│       │   ├── session_confirm.html.heex.eex
│       │   ├── session_controller.ex.eex
│       │   ├── session_controller_test.exs.eex
│       │   ├── session_html.ex.eex
│       │   ├── session_new.html.heex.eex
│       │   ├── settings_controller.ex.eex
│       │   ├── settings_controller_test.exs.eex
│       │   ├── settings_edit.html.heex.eex
│       │   ├── settings_html.ex.eex
│       │   ├── settings_live.ex.eex
│       │   ├── settings_live_test.exs.eex
│       │   └── test_cases.exs.eex
│       ├── phx.gen.channel/
│       │   ├── channel.ex.eex
│       │   ├── channel_case.ex.eex
│       │   └── channel_test.exs.eex
│       ├── phx.gen.context/
│       │   ├── access_no_schema.ex.eex
│       │   ├── access_no_schema_scope.ex.eex
│       │   ├── context.ex.eex
│       │   ├── context_test.exs.eex
│       │   ├── fixtures.ex.eex
│       │   ├── fixtures_module.ex.eex
│       │   ├── schema_access.ex.eex
│       │   ├── schema_access_scope.ex.eex
│       │   ├── test_cases.exs.eex
│       │   └── test_cases_scope.exs.eex
│       ├── phx.gen.embedded/
│       │   └── embedded_schema.ex.eex
│       ├── phx.gen.html/
│       │   ├── controller.ex.eex
│       │   ├── controller_test.exs.eex
│       │   ├── edit.html.heex.eex
│       │   ├── html.ex.eex
│       │   ├── index.html.heex.eex
│       │   ├── new.html.heex.eex
│       │   ├── resource_form.html.heex.eex
│       │   └── show.html.heex.eex
│       ├── phx.gen.json/
│       │   ├── changeset_json.ex.eex
│       │   ├── controller.ex.eex
│       │   ├── controller_test.exs.eex
│       │   ├── fallback_controller.ex.eex
│       │   └── json.ex.eex
│       ├── phx.gen.live/
│       │   ├── form.ex.eex
│       │   ├── index.ex.eex
│       │   ├── live_test.exs.eex
│       │   └── show.ex.eex
│       ├── phx.gen.notifier/
│       │   ├── notifier.ex.eex
│       │   └── notifier_test.exs.eex
│       ├── phx.gen.presence/
│       │   └── presence.ex.eex
│       ├── phx.gen.release/
│       │   ├── Dockerfile.eex
│       │   ├── dockerignore.eex
│       │   ├── rel/
│       │   │   ├── migrate.bat.eex
│       │   │   ├── migrate.sh.eex
│       │   │   ├── server.bat.eex
│       │   │   └── server.sh.eex
│       │   └── release.ex.eex
│       ├── phx.gen.schema/
│       │   ├── migration.exs.eex
│       │   └── schema.ex.eex
│       └── phx.gen.socket/
│           ├── socket.ex.eex
│           └── socket.js.eex
├── test/
│   ├── fixtures/
│   │   ├── digest/
│   │   │   ├── cleaner/
│   │   │   │   ├── cache_manifest.json
│   │   │   │   └── latest_not_most_recent_cache_manifest.json
│   │   │   ├── compile/
│   │   │   │   ├── cache_manifest.json
│   │   │   │   └── cache_manifest_upgrade.json
│   │   │   └── priv/
│   │   │       ├── output/
│   │   │       │   ├── foo-288ea8c7954498e65663c817382eeac4.css
│   │   │       │   └── foo-d978852bea6530fcd197b5445ed008fd.css
│   │   │       └── static/
│   │   │           ├── app.js
│   │   │           ├── css/
│   │   │           │   └── app.css
│   │   │           ├── foo.css
│   │   │           ├── manifest.json
│   │   │           └── precompressed.js.br
│   │   ├── hello.txt
│   │   ├── ssl/
│   │   │   ├── cert.pem
│   │   │   └── key.pem
│   │   ├── templates/
│   │   │   ├── custom.foo
│   │   │   ├── layout/
│   │   │   │   ├── app.html.eex
│   │   │   │   └── root.html.eex
│   │   │   ├── no_trim.text.eex
│   │   │   ├── path.html.eex
│   │   │   ├── safe.html.eex
│   │   │   ├── show.html.eex
│   │   │   ├── trim.html.eex
│   │   │   └── user/
│   │   │       ├── index.html.eex
│   │   │       ├── profiles/
│   │   │       │   └── admin.html.eex
│   │   │       ├── render_template.html.eex
│   │   │       └── show.json.exs
│   │   └── views.exs
│   ├── mix/
│   │   ├── phoenix_test.exs
│   │   └── tasks/
│   │       ├── phx.digest.clean_test.exs
│   │       ├── phx.digest_test.exs
│   │       ├── phx.gen.auth/
│   │       │   └── injector_test.exs
│   │       ├── phx.gen.auth_test.exs
│   │       ├── phx.gen.cert_test.exs
│   │       ├── phx.gen.channel_test.exs
│   │       ├── phx.gen.context_test.exs
│   │       ├── phx.gen.embedded_test.exs
│   │       ├── phx.gen.html_test.exs
│   │       ├── phx.gen.json_test.exs
│   │       ├── phx.gen.live_test.exs
│   │       ├── phx.gen.notifier_test.exs
│   │       ├── phx.gen.presence_test.exs
│   │       ├── phx.gen.release_test.exs
│   │       ├── phx.gen.schema_test.exs
│   │       ├── phx.gen.secret_test.exs
│   │       ├── phx.gen.socket_test.exs
│   │       ├── phx.routes_test.exs
│   │       └── phx_test.exs
│   ├── phoenix/
│   │   ├── channel_test.exs
│   │   ├── code_reloader_test.exs
│   │   ├── config_test.exs
│   │   ├── controller/
│   │   │   ├── controller_test.exs
│   │   │   ├── flash_test.exs
│   │   │   ├── pipeline_test.exs
│   │   │   └── render_test.exs
│   │   ├── debug_test.exs
│   │   ├── digester/
│   │   │   └── gzip_test.exs
│   │   ├── digester_test.exs
│   │   ├── endpoint/
│   │   │   ├── endpoint_test.exs
│   │   │   ├── render_errors_test.exs
│   │   │   ├── supervisor_test.exs
│   │   │   └── watcher_test.exs
│   │   ├── integration/
│   │   │   ├── endpoint_test.exs
│   │   │   ├── long_poll_channels_test.exs
│   │   │   ├── long_poll_socket_test.exs
│   │   │   ├── websocket_channels_test.exs
│   │   │   └── websocket_socket_test.exs
│   │   ├── logger_test.exs
│   │   ├── naming_test.exs
│   │   ├── param_test.exs
│   │   ├── presence_test.exs
│   │   ├── router/
│   │   │   ├── console_formatter_test.exs
│   │   │   ├── forward_test.exs
│   │   │   ├── helpers_test.exs
│   │   │   ├── pipeline_test.exs
│   │   │   ├── resource_test.exs
│   │   │   ├── resources_test.exs
│   │   │   ├── route_test.exs
│   │   │   ├── routing_test.exs
│   │   │   └── scope_test.exs
│   │   ├── socket/
│   │   │   ├── message_test.exs
│   │   │   ├── socket_test.exs
│   │   │   ├── transport_test.exs
│   │   │   ├── v1_json_serializer_test.exs
│   │   │   └── v2_json_serializer_test.exs
│   │   ├── test/
│   │   │   ├── channel_test.exs
│   │   │   └── conn_test.exs
│   │   ├── token_test.exs
│   │   └── verified_routes_test.exs
│   ├── support/
│   │   ├── endpoint_helper.exs
│   │   ├── http_client.exs
│   │   ├── router_helper.exs
│   │   └── websocket_client.exs
│   └── test_helper.exs
└── usage-rules/
    ├── ecto.md
    ├── elixir.md
    ├── html.md
    ├── liveview.md
    └── phoenix.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .formatter.exs
================================================
locals_without_parens = [
  # Phoenix.Channel
  intercept: 1,

  # Phoenix.Router
  connect: 3,
  connect: 4,
  delete: 3,
  delete: 4,
  forward: 2,
  forward: 3,
  forward: 4,
  get: 3,
  get: 4,
  head: 3,
  head: 4,
  match: 4,
  match: 5,
  options: 3,
  options: 4,
  patch: 3,
  patch: 4,
  pipeline: 2,
  pipe_through: 1,
  post: 3,
  post: 4,
  put: 3,
  put: 4,
  resources: 2,
  resources: 3,
  resources: 4,
  trace: 4,

  # Phoenix.Controller
  action_fallback: 1,

  # Phoenix.Endpoint
  plug: 1,
  plug: 2,
  socket: 2,
  socket: 3,

  # Phoenix.Socket
  channel: 2,
  channel: 3,

  # Phoenix.ChannelTest
  assert_broadcast: 2,
  assert_broadcast: 3,
  assert_push: 2,
  assert_push: 3,
  assert_reply: 2,
  assert_reply: 3,
  assert_reply: 4,
  refute_broadcast: 2,
  refute_broadcast: 3,
  refute_push: 2,
  refute_push: 3,
  refute_reply: 2,
  refute_reply: 3,
  refute_reply: 4,

  # Phoenix.ConnTest
  assert_error_sent: 2,

  # Phoenix.Live{Dashboard,View}
  attr: 2,
  attr: 3,
  embed_templates: 1,
  embed_templates: 2,
  live: 2,
  live: 3,
  live: 4,
  live_dashboard: 1,
  live_dashboard: 2,
  on_mount: 1,
  slot: 1,
  slot: 2,
  slot: 3,

  # Phoenix.LiveViewTest
  assert_patch: 1,
  assert_patch: 2,
  assert_patch: 3,
  assert_patched: 2,
  assert_push_event: 3,
  assert_push_event: 4,
  assert_redirect: 1,
  assert_redirect: 2,
  assert_redirect: 3,
  assert_redirected: 2,
  assert_reply: 2,
  assert_reply: 3,
  refute_redirected: 1,
  refute_redirected: 2,
  refute_patched: 1,
  refute_patched: 2,
  refute_push_event: 3,
  refute_push_event: 4
]

[
  locals_without_parens: locals_without_parens,
  export: [locals_without_parens: locals_without_parens]
]


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

### Environment

* Elixir version (elixir -v):
* Phoenix version (mix deps):
* Operating system:

### Actual behavior

<!--
Describe the actual behaviour. If you are seeing an error, include the full message and stacktrace.

If possible, also provide a single file app that reproduces the behaviour, you can start here:
https://github.com/wojtekmach/mix_install_examples/blob/main/phoenix.exs
-->

### Expected behavior



================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
---
blank_issues_enabled: true

contact_links:
  - name: Ask questions, support, and general discussions
    url: https://elixirforum.com/c/phoenix-forum
    about: Ask questions, provide support, and more on Elixir Forum

  - name: Propose new features
    url: https://elixirforum.com/c/phoenix-forum
    about: Propose new features on Elixir Forum


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  # Maintain dependencies for GitHub Actions
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"
  # Maintain dependencies for NPM
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "monthly"
    groups:
      minor-and-patch:
        applies-to: version-updates
        update-types:
          - "minor"
          - "patch"


================================================
FILE: .github/workflows/assets.yml
================================================
name: Assets

on:
  push:
    branches:
      - main
      - "v*.*"

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-24.04
    env:
      elixir: 1.18.3
      otp: 27.2
    permissions:
      contents: write # for stefanzweifel/git-auto-commit-action to push code in repo
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Elixir
        uses: erlef/setup-beam@3580539ceec3dc05b0ed51e9e10b08eb7a7c2bb4 # v1.21.0
        with:
          elixir-version: ${{ env.elixir }}
          otp-version: ${{ env.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: |
            deps
            _build
          key: ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }}-dev
          restore-keys: |
            ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-
      - name: Install Dependencies
        run: mix deps.get --only dev

      - name: Set up Node.js 20.x
        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
        with:
          node-version: 20.x

      - name: Restore npm cache
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install npm dependencies
        run: npm ci

      - name: Build assets
        run: mix assets.build

      - name: Push updated assets
        id: push_assets
        uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
        with:
          commit_message: Update assets
          file_pattern: priv/static


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on: [push, pull_request]
permissions:
  contents: read

jobs:
  mix_test:
    name: mix test (OTP ${{matrix.otp}} | Elixir ${{matrix.elixir}})

    env:
      MIX_ENV: test
      PHX_CI: true

    strategy:
      matrix:
        include:
          - elixir: "1.15.8"
            otp: "25.3.2.9"

          - elixir: "1.18.4"
            otp: "27.3"

          - elixir: "1.19.5"
            otp: "28.3.3"
            lint: true
            installer: true

    runs-on: ubuntu-24.04

    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Elixir
        uses: erlef/setup-beam@3580539ceec3dc05b0ed51e9e10b08eb7a7c2bb4 # v1.21.0
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}

      - name: Restore deps and _build cache
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: |
            deps
            _build
          key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}

      - name: Install dependencies
        run: mix deps.get --only test

      - name: Remove compiled application files
        run: mix clean

      - name: Compile & lint dependencies
        run: mix compile --warnings-as-errors
        if: ${{ matrix.lint }}

      - name: Run tests
        run: mix test

      - name: Run installer test
        run: |
          cd installer
          mix deps.get
          mix test
        if: ${{ matrix.installer }}

  npm_test:
    name: npm test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Restore deps and _build cache
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: |
            deps
            _build
          key: deps-${{ runner.os }}-npm-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            deps-${{ runner.os }}-npm

      - name: Set up Node.js 20.x
        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
        with:
          node-version: 20.x

      - name: Restore npm cache
        uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: npm install and test
        run: |
          cd assets
          npm install
          npm test

  integration-test-elixir:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        include:
          # look for correct alpine image here: https://hub.docker.com/r/hexpm/elixir/tags
          - elixir: "1.15.8"
            otp: "25.3.2.9"
            suffix: "alpine-3.20.3"

          - elixir: "1.19.5"
            otp: "28.3.3"
            suffix: "alpine-3.20.9"

    container:
      image: hexpm/elixir:${{ matrix.elixir }}-erlang-${{ matrix.otp }}-${{ matrix.suffix }}
      env:
        ELIXIR_ASSERT_TIMEOUT: 10000
        PHX_CI: true
    services:
      postgres:
        image: postgres
        ports:
          - 5432:5432
        env:
          POSTGRES_PASSWORD: postgres
      mysql:
        image: mysql
        ports:
          - 3306:3306
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      mssql:
        image: mcr.microsoft.com/mssql/server:2019-latest
        env:
          ACCEPT_EULA: Y
          SA_PASSWORD: some!Password
        ports:
          - 1433:1433
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Run test script
        run: ./integration_test/test.sh


================================================
FILE: .github/workflows/npm-publish.yml
================================================
# https://docs.npmjs.com/trusted-publishers
name: NPM Publish

on:
  push:
    tags:
      - "v*"

permissions:
  id-token: write  # Required for OIDC
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "24"
          registry-url: "https://registry.npmjs.org"

      # Ensure npm 11.5.1 or later is installed
      - name: Update npm
        run: npm install -g npm@latest

      - name: Determine npm tag
        id: npm-tag
        run: |
          TAG=${GITHUB_REF#refs/tags/}
          # Update this condition when bumping the major version!
          if [[ $TAG == v1.7.* ]]; then
            echo "tag=old-version" >> $GITHUB_OUTPUT
          elif [[ $TAG == *-rc* ]]; then
            echo "tag=rc" >> $GITHUB_OUTPUT
          else
            echo "tag=latest" >> $GITHUB_OUTPUT
          fi

      - name: Publish to npm
        run: npm publish --tag ${{ steps.npm-tag.outputs.tag }}


================================================
FILE: .gitignore
================================================
/_build/
/deps/
/doc/
/node_modules/
/tmp/
/cover/

/assets/node_modules/

/installer/_build/
/installer/assets/
/installer/deps/
/installer/doc/
/installer/phx_new-*.ez
/installer/tmp/

/integration_test/_build/
/integration_test/deps/

erl_crash.dump
phoenix-*.ez

.DS_Store

/priv/templates/phx.gen.live/core_components.ex.eex


================================================
FILE: CHANGELOG.md
================================================
# Changelog for v1.8

This release requires Erlang/OTP 25+.

## Streamlined generators

  * Extend tailwindcss support in new apps with [daisyUI](https://daisyui.com/) for light/dark/system mode support for entire app, including core components
  * Simplify layout handling for new apps. Now there is only a single `root.html.heex` which wraps the render pipeline. Other dynamic layouts, like `app.html.heex` are called as needed within templates as regular function components
  * Simplify core components and live generators to more closely match basic `phx.gen.html` crud. This serves as a better base for seasoned devs to start with, and lessens the amount of code newcomers need to get up to speed with on the basics
  * Introduce magic links (passwordless auth) and "sudo mode" to `mix phx.gen.auth` while simplifying the generated structure
  * Introduce scopes to Phoenix generators, designed to make secure data access the *default*, not something you remember (or forget) to do later

## `put_secure_browser_headers`

`put_secure_browser_headers` has been updated to the latest security practices. In particular, it sets the `content-security-policy` header to `"base-uri 'self'; frame-ancestors 'self';"` if none is set, restricting embedding of your application and the use of `<base>` element to same origin respectively. If you expect your application to be embedded by third-parties, you want to consult the documentation.

The headers `x-download-options` and `x-frame-options` are no longer set as they have been deprecated by standards.

## Deprecations

This release introduces deprecation warnings for several features that have been soft-deprecated in the past.

  * `use Phoenix.Controller` must now specify the `:formats` option, which may be set to an empty list if the formats are not known upfront
  * The `:namespace` and `:put_default_views` options on `use Phoenix.Controller` are deprecated and emit a warning on use
  * Specifying layouts without modules, such as `put_layout(conn, :print)` or `put_layout(conn, html: :print)` is deprecated
  * The `:trailing_slash` option in `Phoenix.Router` has been deprecated in favor of using `Phoenix.VerifiedRoutes`. The overall usage of helpers will be deprecated in the future

## Potential breaking changes

  * The `config` variable is no longer available in `Phoenix.Endpoint`. In the past, it was possible to read your endpoint configuration at compile-time via an injected variable named `config`, which is no longer supported. Use `Application.compile_env/3` instead, which is tracked by the Elixir compiler and lead to a better developer experience. This may also lead to errors on application boot if you were previously incorrectly setting compile time config at runtime.

## 1.8.5 (2026-03-05)

### JavaScript Client Bug Fixes
- Fix socket connecting on visibility change when never established

### Enhancements
- Fix warnings on Elixir 1.20

## 1.8.4 (2026-02-23)

### JavaScript Client Bug Fixes
- Fix bug reconnecting connections when close was gracefully initiated by server
- Fix LongPoll transport name in sessionStorage and logs

### Enhancements
- Adds guards support in `assert_push`, `assert_broadcast`, and `assert_reply`
- Enable purging in Phoenix code server for Elixir 1.20

## 1.8.3 (2025-12-08)

### Enhancements
  - Add top-level phoenix config: `sort_verified_routes_query_params` to enable sorting query params in verified routes during tests

### Bug fixes
  - Fix endpoint port config in an umbrella application. ([#6549](https://github.com/phoenixframework/phoenix/pull/6549))
  - Drop incoming channel messages with stale join refs

## 1.8.2 (2025-11-26)

### Bug fixes
  - [phoenix.js] fix issue where LongPoll can cause "unmatched topic" errors (observed on iOS only) ([#6538](https://github.com/phoenixframework/phoenix/pull/6538))
  - [phx.gen.live] fix tests when schema and table names are equal ([#6477](https://github.com/phoenixframework/phoenix/pull/6477))
  - [Verified Routes] do not add path prefixes for static routes
  - [Phoenix.Endpoint] fix LongPoll being active by default since 1.8.0 ([#6487](https://github.com/phoenixframework/phoenix/pull/6487))

### Enhancements
  - [phoenix.js] socket now stops reconnection attempts while the page is hidden ([#6534](https://github.com/phoenixframework/phoenix/pull/6534))
  - [phx.new] (re-)add `<.input field={@form[:foo]} type="hidden" />` support in core components
  - [phx.new] set `force_ssl` in `prod.exs` by default ([#6435](https://github.com/phoenixframework/phoenix/pull/6435))
  - [phx.new] change `--docker` base image to debian trixie ([#6521](https://github.com/phoenixframework/phoenix/pull/6521))
  - [Phoenix.Socket.assign/2] allow passing a function as second argument `assign(socket, fn _existing_assigns -> %{this_gets: "merged"} end)` ([#6530](https://github.com/phoenixframework/phoenix/pull/6530))
  - [Phoenix.Controller.assign/2] allow passing a function as second argument ([#6542](https://github.com/phoenixframework/phoenix/pull/6542))
  - [Phoenix.Controller.assign/2] support keyword lists and maps as second argument similar to LiveView ([#6513](https://github.com/phoenixframework/phoenix/pull/6513))
  - [Presence] support custom dispatcher for `presence_diff` broadcast ([#6500](https://github.com/phoenixframework/phoenix/pull/6500))
  - [AGENTS.md] add short test guidelines to usage rules

## 1.8.1 (2025-08-28)

### Bug fixes
  - [phx.new] Fix AGENTS.md failing to include CSS and JavaScript sections

## 1.8.0 (2025-08-05)

### Bug fixes
  - [phx.new] Don't include node_modules override in generated `tsconfig.json`

### Enhancements
  - [phx.gen.live|html|json] - Make context argument optional. Defaults to the plural name.
  - [phx.new] Add `mix precommit` alias
  - [phx.new] Add `AGENTS.md` generation compatible with [`usage_rules`](https://hexdocs.pm/usage_rules/)
  - [phx.new] Add `usage_rules` folder to installer, allowing to sync generic Phoenix rules into new projects
  - [phx.new] Use LiveView 1.1 release in generated code
  - [phx.new] Ensure theme selector and flash closing works without LiveView

## 1.8.0-rc.4 (2025-07-14)

### Bug Fixes
  - Fix phx.gen.presence PubSub server name for umbrella apps
  - Fix `phx.gen.live` subscribing to pubsub in disconnected mounts

### Enhancements
  - [phx.new] Initialize initial git repo when git is installed
  - [phx.new] Opt-in to HEEx `:debug_tags_location` in development
  - [phx.gen.live|html|json|context] Make context name optional and inflect based on schema when missing
  - [phx.gen.*] Use new Ecto 3.13 `Repo.transact/2` in generators
  - [phx.gen.auth] Warn when using `phx.gen.auth` without esbuild as features assume `phoenix_html.js` in bundle
  - Add `security.md` guide for security best practices
  - [phoenix.js] - Add fetch() support to LongPoll when XMLHTTPRequest is not available
  - Optimize parameter scrubbing by precompiling patterns

## 1.8.0-rc.3 (2025-05-07)

### Enhancements
  - [phx.gen.auth] Allow configuring the scope's assign key in phx.gen.auth
  - [phx.new] Do not override theme in root layout if explicitly set

## 1.8.0-rc.2 (2025-04-29)

### Bug Fixes
  - [phx.gen.live] Only subscribe to pubsub if connected
  - [phx.gen.auth] Remove unused current_password field
  - [phx.gen.auth] Use context_app for scopes to fix generated scopes in umbrella apps

## 1.8.0-rc.1 (2025-04-16)

### Enhancements
  - [phx.new] Support PORT in dev
  - [phx.gen.auth] Replace `utc_now/0 + truncate/1` with `utc_now/1`
  - [phx.gen.auth] Make dev mailbox link more obvious

### Bug Fixes
  - [phx.new] Fix Tailwind custom variants for loading classes (#6194)
  - [phx.new] Fix heroicons path for umbrella apps
  - [phx.gen.auth] Fix missing index for scoped resources (#6186)
  - [phx.gen.live] Fix crash when an open :show page gets a PubSub broadcast for items (#6197)

## 1.8.0-rc.0 (2025-04-01) 🚀

- First release candidate!

## v1.7

The CHANGELOG for v1.7 releases can be found in the [v1.7 branch](https://github.com/phoenixframework/phoenix/blob/v1.7/CHANGELOG.md).


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Code of Conduct

As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
* Other unethical or unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.

This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.2.0, available at [https://www.contributor-covenant.org/version/1/2/0/code-of-conduct/](https://www.contributor-covenant.org/version/1/2/0/code-of-conduct/)


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Phoenix

Please take a moment to review this document in order to make the contribution
process easy and effective for everyone involved!
Also make sure you read our [Code of Conduct](CODE_OF_CONDUCT.md) that outlines our commitment towards an open and welcoming environment.

## Using the issue tracker

Use the issues tracker for:

* [Bug reports](#bug-reports)
* [Submitting pull requests](#pull-requests)

For requesting help and discussing new features:

* [The Phoenix subforum on the Elixir Forum](https://elixirforum.com/c/phoenix-forum)
* **[#elixir](irc://irc.libera.chat/elixir)** on [Libera](https://libera.chat/) IRC

We do our best to keep the issue tracker tidy and organized, making it useful
for everyone. For example, we classify open issues per perceived difficulty,
making it easier for developers to [contribute to Phoenix](#pull-requests).

## Bug reports

A bug is either a _demonstrable problem_ that is caused by the code in the repository,
or indicates missing, unclear, or misleading documentation. Good bug reports are extremely
helpful - thank you!

Guidelines for bug reports:

1. **Use the GitHub issue search** &mdash; check if the issue has already been
   reported.

2. **Check if the issue has been fixed** &mdash; try to reproduce it using the
   `main` branch in the repository.

3. **Isolate and report the problem** &mdash; ideally create a reduced test
   case.

Please try to be as detailed as possible in your report. Include information about
your Operating System, as well as your Erlang, Elixir and Phoenix versions. Please provide steps to
reproduce the issue as well as the outcome you were expecting! All these details
will help developers to fix any potential bugs.

Example:

> Short and descriptive example bug report title
>
> A summary of the issue and the environment in which it occurs. If suitable,
> include the steps required to reproduce the bug.
>
> 1. This is the first step
> 2. This is the second step
> 3. Further steps, etc.
>
> `<url>` - a link to the reduced test case (e.g. a GitHub Gist)
>
> Any other information you want to share that is relevant to the issue being
> reported. This might include the lines of code that you have identified as
> causing the bug, and potential solutions (and your opinions on their
> merits).

## Feature requests

Feature requests are welcome and should be discussed on the [Phoenix subforum](https://elixirforum.com/c/phoenix-forum). But take a moment to find
out whether your idea fits with the scope and aims of the project. It's up to *you*
to make a strong case to convince the community of the merits of this feature.
Please provide as much detail and context as possible.

## Contributing Documentation

Code documentation (`@doc`, `@moduledoc`, `@typedoc`) has a special convention:
the first paragraph is considered to be a short summary.

For functions, macros, and callbacks say what it will do. For example write
something like:

```elixir
@doc """
Marks the given value as HTML safe.
"""
def safe({:safe, value}), do: {:safe, value}
```

For modules, protocols, and types say what it is. For example write
something like:

```elixir
defmodule Phoenix.HTML do
  @moduledoc """
  Conveniences for working HTML strings and templates.
  ...
  """
```

Keep in mind that the first paragraph might show up in a summary somewhere, long
texts in the first paragraph create very ugly summaries. As a rule of thumb
anything longer than 80 characters is too long.

Try to keep unnecessary details out of the first paragraph, it's only there to
give a user a quick idea of what the documented "thing" does/is. The rest of the
documentation string can contain the details, for example when a value and when
`nil` is returned.

If possible include examples, preferably in a form that works with doctests.
This makes it easy to test the examples so that they don't go stale and examples
are often a great help in explaining what a function does.

## Pull requests

Good pull requests - patches, improvements, new features - are a fantastic
help. They should remain focused in scope and avoid containing unrelated
commits.

**IMPORTANT**: By submitting a patch, you agree that your work will be
licensed under the license used by the project.

If you have any large pull request in mind (e.g. implementing features,
refactoring code, etc), **please ask first** otherwise you risk spending
a lot of time working on something that the project's developers might
not want to merge into the project.

Please adhere to the coding conventions in the project (indentation,
accurate comments, etc.) and don't forget to add your own tests and
documentation. When working with git, we recommend the following process
in order to craft an excellent pull request:

1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork,
   and configure the remotes:

   ```bash
   # Clone your fork of the repo into the current directory
   git clone https://github.com/<your-username>/phoenix

   # Navigate to the newly cloned directory
   cd phoenix

   # Assign the original repo to a remote called "upstream"
   git remote add upstream https://github.com/phoenixframework/phoenix
   ```

2. If you cloned a while ago, get the latest changes from upstream, and update your fork:

   ```bash
   git checkout main
   git pull upstream main
   git push
   ```

3. Create a new topic branch (off of `main`) to contain your feature, change,
   or fix.

   **IMPORTANT**: Making changes in `main` is discouraged. You should always
   keep your local `main` in sync with upstream `main` and make your
   changes in topic branches.

   ```bash
   git checkout -b <topic-branch-name>
   ```

4. Commit your changes in logical chunks. Keep your commit messages organized,
   with a short description in the first line and more detailed information on
   the following lines. Feel free to use Git's
   [interactive rebase](https://help.github.com/articles/about-git-rebase/)
   feature to tidy up your commits before making them public.

5. Make sure all the tests are still passing.

   ```bash
   mix test
   ```

6. Push your topic branch up to your fork:

   ```bash
   git push origin <topic-branch-name>
   ```

7. [Open a Pull Request](https://help.github.com/articles/about-pull-requests/)
    with a clear title and description.

8. If you haven't updated your pull request for a while, you should consider
   rebasing on main and resolving any conflicts.

   **IMPORTANT**: _Never ever_ merge upstream `main` into your branches. You
   should always `git rebase` on `main` to bring your changes up to date when
   necessary.

   ```bash
   git checkout main
   git pull upstream main
   git checkout <your-topic-branch>
   git rebase main
   ```

Thank you for your contributions!

## Guides

These Guides aim to be inclusive. We use "we" and "our" instead of "you" and
"your" to foster this sense of inclusion.

Ideally there is something for everybody in each guide, from beginner to expert.
This is hard, maybe impossible. When we need to compromise, we do so on behalf
of beginning users because expert users have more tools at their disposal to
help themselves.

The general pattern we use for presenting information is to first introduce a
small, discrete topic, then write a small amount of code to demonstrate the
concept, then verify that the code worked.

In this way, we build from small, easily digestible concepts into more complex
ones. The shorter this cycle is, as long as the information is still clear and
complete, the better.

For formatting the guides:

- We use the `elixir` code fence for all module code.
- We use the `iex` for IEx sessions.
- We use the `console` code fence for shell commands.
- We use the `html` code fence for html templates, even if there is elixir code
  in the template.
- We use backticks for filenames and directory paths.
- We use backticks for module names, function names, and variable names.
- Documentation line length should hard wrapped at around 100 characters if possible.


================================================
FILE: LICENSE.md
================================================
# MIT License

Copyright (c) 2014 Chris McCord

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
================================================
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="./priv/static/phoenix-orange.png" />
  <source media="(prefers-color-scheme: light)" srcset="./priv/static/phoenix.png" />
  <img src="./priv/static/phoenix.png" alt="Phoenix logo" />
</picture>

> Peace of mind from prototype to production.

[![Build Status](https://github.com/phoenixframework/phoenix/workflows/CI/badge.svg)](https://github.com/phoenixframework/phoenix/actions/workflows/ci.yml) [![Hex.pm](https://img.shields.io/hexpm/v/phoenix.svg)](https://hex.pm/packages/phoenix) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/phoenix)

## Getting started

See the official site at <https://www.phoenixframework.org/>.

Install the latest version of Phoenix by following the instructions at <https://hexdocs.pm/phoenix/installation.html#phoenix>.

## Documentation

API documentation is available at <https://hexdocs.pm/phoenix>.

Phoenix.js documentation is available at <https://hexdocs.pm/phoenix/js>.

## Contributing

We appreciate any contribution to Phoenix. Check our [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) and [CONTRIBUTING.md](CONTRIBUTING.md) guides for more information. We usually keep a list of features and bugs in the [issue tracker][4].

### Generating a Phoenix project from unreleased versions

You can create a new project using the latest Phoenix source installer (the `phx.new` Mix task) with the following steps:

1. Remove any previously installed `phx_new` archives so that Mix will pick up the local source code. This can be done with `mix archive.uninstall phx_new` or by simply deleting the file, which is usually in `~/.mix/archives/`.
2. Copy this repo via `git clone https://github.com/phoenixframework/phoenix` or by downloading it
3. Run the `phx.new` Mix task from within the `installer` directory, for example:

```bash
cd phoenix/installer
mix phx.new dev_app --dev
```

The `--dev` flag will configure your new project's `:phoenix` dep as a relative path dependency, pointing to your local Phoenix checkout:

```elixir
defp deps do
  [{:phoenix, path: "../..", override: true},
```

To create projects outside of the `installer/` directory, add the latest archive to your machine by following the instructions in [installer/README.md](https://github.com/phoenixframework/phoenix/blob/main/installer/README.md)

### Building from source

To build the documentation:

```bash
npm install
MIX_ENV=docs mix docs
```

To build Phoenix:

```bash
mix deps.get
mix compile
```

To build the Phoenix installer:

```bash
mix deps.get
mix compile
mix archive.build
```

To build Phoenix.js:

```bash
cd assets
npm install
```

## Important links

* [#elixir][1] on [Libera][2] IRC
* [elixir-lang Slack channel][3]
* [Issues tracker][4]
* [Phoenix Forum (questions and proposals)][5]
* Visit Phoenix's sponsor, DockYard, for expert [Phoenix Consulting](https://dockyard.com/phoenix-consulting)

  [1]: https://web.libera.chat/?channels=#elixir
  [2]: https://libera.chat/
  [3]: https://elixir-lang.slack.com/
  [4]: https://github.com/phoenixframework/phoenix/issues
  [5]: https://elixirforum.com/c/phoenix-forum

## Copyright and License

Copyright (c) 2014, Chris McCord.

Phoenix source code is licensed under the [MIT License](LICENSE.md).


================================================
FILE: RELEASE.md
================================================
# Release Instructions

  1. Check related deps for required version bumps and compatibility (`phoenix_ecto`, `phoenix_html`)
  2. Bump version in related files below
  3. Bump external dependency version in related external files below
  4. Run tests:
      - `mix test` in the root folder
      - `mix test` in the `installer/` folder
  5. Commit, push code
  6. Publish `phx_new` and `phoenix` packages and docs after pruning any extraneous uncommitted files
  7. Test installer by generating a new app, running `mix deps.get`, and compiling
  8. Publish to `npm` with `npm publish`
  9. Update Elixir and Erlang/OTP versions on new.phoenixframework.org
  10. Start -dev version in related files below

## Files with version

  * `CHANGELOG`
  * `mix.exs`
  * `installer/mix.exs`
  * `package.json`
  * `assets/package.json`

## Files with external dependency versions

  * `priv/templates/phx.gen.release/Docker.eex` (debian)
  * `priv/templates/phx.gen.release/Docker.eex` (esbuild)


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported versions

Phoenix applies bug fixes only to the latest minor branch. Security patches are
available for the last 4 minor branches:

Phoenix version | Support
:-------------- | :-----------------------------
1.7             | Bug fixes and security patches
1.6             | Security patches only
1.5             | Security patches only
1.4             | Security patches only

## Announcements

[Security advisories will be published on GitHub](https://github.com/phoenixframework/phoenix/security).

## Reporting a vulnerability

[Please disclose security vulnerabilities privately via GitHub](https://github.com/phoenixframework/phoenix/security).


================================================
FILE: assets/js/phoenix/ajax.js
================================================
import {
  global,
  XHR_STATES
} from "./constants"

export default class Ajax {

  static request(method, endPoint, headers, body, timeout, ontimeout, callback){
    if(global.XDomainRequest){
      let req = new global.XDomainRequest() // IE8, IE9
      return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)
    } else if(global.XMLHttpRequest){
      let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari
      return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)
    } else if(global.fetch && global.AbortController){
      // Fetch with AbortController for modern browsers
      return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)
    } else {
      throw new Error("No suitable XMLHttpRequest implementation found")
    }
  }

  static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){
    let options = {
      method,
      headers,
      body,
    }
    let controller = null
    if(timeout){
      controller = new AbortController()
      const _timeoutId = setTimeout(() => controller.abort(), timeout)
      options.signal = controller.signal
    }
    global.fetch(endPoint, options)
      .then(response => response.text())
      .then(data => this.parseJSON(data))
      .then(data => callback && callback(data))
      .catch(err => {
        if(err.name === "AbortError" && ontimeout){
          ontimeout()
        } else {
          callback && callback(null)
        }
      })
    return controller
  }

  static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){
    req.timeout = timeout
    req.open(method, endPoint)
    req.onload = () => {
      let response = this.parseJSON(req.responseText)
      callback && callback(response)
    }
    if(ontimeout){ req.ontimeout = ontimeout }

    // Work around bug in IE9 that requires an attached onprogress handler
    req.onprogress = () => { }

    req.send(body)
    return req
  }

  static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){
    req.open(method, endPoint, true)
    req.timeout = timeout
    for(let [key, value] of Object.entries(headers)){
      req.setRequestHeader(key, value)
    }
    req.onerror = () => callback && callback(null)
    req.onreadystatechange = () => {
      if(req.readyState === XHR_STATES.complete && callback){
        let response = this.parseJSON(req.responseText)
        callback(response)
      }
    }
    if(ontimeout){ req.ontimeout = ontimeout }

    req.send(body)
    return req
  }

  static parseJSON(resp){
    if(!resp || resp === ""){ return null }

    try {
      return JSON.parse(resp)
    } catch {
      console && console.log("failed to parse JSON response", resp)
      return null
    }
  }

  static serialize(obj, parentKey){
    let queryStr = []
    for(var key in obj){
      if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }
      let paramKey = parentKey ? `${parentKey}[${key}]` : key
      let paramVal = obj[key]
      if(typeof paramVal === "object"){
        queryStr.push(this.serialize(paramVal, paramKey))
      } else {
        queryStr.push(encodeURIComponent(paramKey) + "=" + encodeURIComponent(paramVal))
      }
    }
    return queryStr.join("&")
  }

  static appendParams(url, params){
    if(Object.keys(params).length === 0){ return url }

    let prefix = url.match(/\?/) ? "&" : "?"
    return `${url}${prefix}${this.serialize(params)}`
  }
}


================================================
FILE: assets/js/phoenix/channel.js
================================================
import {closure} from "./utils"
import {
  CHANNEL_EVENTS,
  CHANNEL_STATES,
} from "./constants"

import Push from "./push"
import Timer from "./timer"

/**
 *
 * @param {string} topic
 * @param {(Object|function)} params
 * @param {Socket} socket
 */
export default class Channel {
  constructor(topic, params, socket){
    this.state = CHANNEL_STATES.closed
    this.topic = topic
    this.params = closure(params || {})
    this.socket = socket
    this.bindings = []
    this.bindingRef = 0
    this.timeout = this.socket.timeout
    this.joinedOnce = false
    this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)
    this.pushBuffer = []
    this.stateChangeRefs = []

    this.rejoinTimer = new Timer(() => {
      if(this.socket.isConnected()){ this.rejoin() }
    }, this.socket.rejoinAfterMs)
    this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))
    this.stateChangeRefs.push(this.socket.onOpen(() => {
      this.rejoinTimer.reset()
      if(this.isErrored()){ this.rejoin() }
    })
    )
    this.joinPush.receive("ok", () => {
      this.state = CHANNEL_STATES.joined
      this.rejoinTimer.reset()
      this.pushBuffer.forEach(pushEvent => pushEvent.send())
      this.pushBuffer = []
    })
    this.joinPush.receive("error", () => {
      this.state = CHANNEL_STATES.errored
      if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }
    })
    this.onClose(() => {
      this.rejoinTimer.reset()
      if(this.socket.hasLogger()) this.socket.log("channel", `close ${this.topic} ${this.joinRef()}`)
      this.state = CHANNEL_STATES.closed
      this.socket.remove(this)
    })
    this.onError(reason => {
      if(this.socket.hasLogger()) this.socket.log("channel", `error ${this.topic}`, reason)
      if(this.isJoining()){ this.joinPush.reset() }
      this.state = CHANNEL_STATES.errored
      if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }
    })
    this.joinPush.receive("timeout", () => {
      if(this.socket.hasLogger()) this.socket.log("channel", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)
      let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)
      leavePush.send()
      this.state = CHANNEL_STATES.errored
      this.joinPush.reset()
      if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }
    })
    this.on(CHANNEL_EVENTS.reply, (payload, ref) => {
      this.trigger(this.replyEventName(ref), payload)
    })
  }

  /**
   * Join the channel
   * @param {integer} timeout
   * @returns {Push}
   */
  join(timeout = this.timeout){
    if(this.joinedOnce){
      throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance")
    } else {
      this.timeout = timeout
      this.joinedOnce = true
      this.rejoin()
      return this.joinPush
    }
  }

  /**
   * Hook into channel close
   * @param {Function} callback
   */
  onClose(callback){
    this.on(CHANNEL_EVENTS.close, callback)
  }

  /**
   * Hook into channel errors
   * @param {Function} callback
   */
  onError(callback){
    return this.on(CHANNEL_EVENTS.error, reason => callback(reason))
  }

  /**
   * Subscribes on channel events
   *
   * Subscription returns a ref counter, which can be used later to
   * unsubscribe the exact event listener
   *
   * @example
   * const ref1 = channel.on("event", do_stuff)
   * const ref2 = channel.on("event", do_other_stuff)
   * channel.off("event", ref1)
   * // Since unsubscription, do_stuff won't fire,
   * // while do_other_stuff will keep firing on the "event"
   *
   * @param {string} event
   * @param {Function} callback
   * @returns {integer} ref
   */
  on(event, callback){
    let ref = this.bindingRef++
    this.bindings.push({event, ref, callback})
    return ref
  }

  /**
   * Unsubscribes off of channel events
   *
   * Use the ref returned from a channel.on() to unsubscribe one
   * handler, or pass nothing for the ref to unsubscribe all
   * handlers for the given event.
   *
   * @example
   * // Unsubscribe the do_stuff handler
   * const ref1 = channel.on("event", do_stuff)
   * channel.off("event", ref1)
   *
   * // Unsubscribe all handlers from event
   * channel.off("event")
   *
   * @param {string} event
   * @param {integer} ref
   */
  off(event, ref){
    this.bindings = this.bindings.filter((bind) => {
      return !(bind.event === event && (typeof ref === "undefined" || ref === bind.ref))
    })
  }

  /**
   * @private
   */
  canPush(){ return this.socket.isConnected() && this.isJoined() }

  /**
   * Sends a message `event` to phoenix with the payload `payload`.
   * Phoenix receives this in the `handle_in(event, payload, socket)`
   * function. if phoenix replies or it times out (default 10000ms),
   * then optionally the reply can be received.
   *
   * @example
   * channel.push("event")
   *   .receive("ok", payload => console.log("phoenix replied:", payload))
   *   .receive("error", err => console.log("phoenix errored", err))
   *   .receive("timeout", () => console.log("timed out pushing"))
   * @param {string} event
   * @param {Object} payload
   * @param {number} [timeout]
   * @returns {Push}
   */
  push(event, payload, timeout = this.timeout){
    payload = payload || {}
    if(!this.joinedOnce){
      throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)
    }
    let pushEvent = new Push(this, event, function (){ return payload }, timeout)
    if(this.canPush()){
      pushEvent.send()
    } else {
      pushEvent.startTimeout()
      this.pushBuffer.push(pushEvent)
    }

    return pushEvent
  }

  /** Leaves the channel
   *
   * Unsubscribes from server events, and
   * instructs channel to terminate on server
   *
   * Triggers onClose() hooks
   *
   * To receive leave acknowledgements, use the `receive`
   * hook to bind to the server ack, ie:
   *
   * @example
   * channel.leave().receive("ok", () => alert("left!") )
   *
   * @param {integer} timeout
   * @returns {Push}
   */
  leave(timeout = this.timeout){
    this.rejoinTimer.reset()
    this.joinPush.cancelTimeout()

    this.state = CHANNEL_STATES.leaving
    let onClose = () => {
      if(this.socket.hasLogger()) this.socket.log("channel", `leave ${this.topic}`)
      this.trigger(CHANNEL_EVENTS.close, "leave")
    }
    let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)
    leavePush.receive("ok", () => onClose())
      .receive("timeout", () => onClose())
    leavePush.send()
    if(!this.canPush()){ leavePush.trigger("ok", {}) }

    return leavePush
  }

  /**
   * Overridable message hook
   *
   * Receives all events for specialized message handling
   * before dispatching to the channel callbacks.
   *
   * Must return the payload, modified or unmodified
   * @param {string} event
   * @param {Object} payload
   * @param {integer} ref
   * @returns {Object}
   */
  onMessage(_event, payload, _ref){ return payload }

  /**
   * @private
   */
  isMember(topic, event, payload, joinRef){
    if(this.topic !== topic){ return false }

    if(joinRef && joinRef !== this.joinRef()){
      if(this.socket.hasLogger()) this.socket.log("channel", "dropping outdated message", {topic, event, payload, joinRef})
      return false
    } else {
      return true
    }
  }

  /**
   * @private
   */
  joinRef(){ return this.joinPush.ref }

  /**
   * @private
   */
  rejoin(timeout = this.timeout){
    if(this.isLeaving()){ return }
    this.socket.leaveOpenTopic(this.topic)
    this.state = CHANNEL_STATES.joining
    this.joinPush.resend(timeout)
  }

  /**
   * @private
   */
  trigger(event, payload, ref, joinRef){
    let handledPayload = this.onMessage(event, payload, ref, joinRef)
    if(payload && !handledPayload){ throw new Error("channel onMessage callbacks must return the payload, modified or unmodified") }

    let eventBindings = this.bindings.filter(bind => bind.event === event)

    for(let i = 0; i < eventBindings.length; i++){
      let bind = eventBindings[i]
      bind.callback(handledPayload, ref, joinRef || this.joinRef())
    }
  }

  /**
   * @private
   */
  replyEventName(ref){ return `chan_reply_${ref}` }

  /**
   * @private
   */
  isClosed(){ return this.state === CHANNEL_STATES.closed }

  /**
   * @private
   */
  isErrored(){ return this.state === CHANNEL_STATES.errored }

  /**
   * @private
   */
  isJoined(){ return this.state === CHANNEL_STATES.joined }

  /**
   * @private
   */
  isJoining(){ return this.state === CHANNEL_STATES.joining }

  /**
   * @private
   */
  isLeaving(){ return this.state === CHANNEL_STATES.leaving }
}


================================================
FILE: assets/js/phoenix/constants.js
================================================
export const globalSelf = typeof self !== "undefined" ? self : null
export const phxWindow = typeof window !== "undefined" ? window : null
export const global = globalSelf || phxWindow || globalThis
export const DEFAULT_VSN = "2.0.0"
export const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}
export const DEFAULT_TIMEOUT = 10000
export const WS_CLOSE_NORMAL = 1000
export const CHANNEL_STATES = {
  closed: "closed",
  errored: "errored",
  joined: "joined",
  joining: "joining",
  leaving: "leaving",
}
export const CHANNEL_EVENTS = {
  close: "phx_close",
  error: "phx_error",
  join: "phx_join",
  reply: "phx_reply",
  leave: "phx_leave"
}

export const TRANSPORTS = {
  longpoll: "longpoll",
  websocket: "websocket"
}
export const XHR_STATES = {
  complete: 4
}
export const AUTH_TOKEN_PREFIX = "base64url.bearer.phx."


================================================
FILE: assets/js/phoenix/index.js
================================================
/**
 * Phoenix Channels JavaScript client
 *
 * ## Socket Connection
 *
 * A single connection is established to the server and
 * channels are multiplexed over the connection.
 * Connect to the server using the `Socket` class:
 *
 * ```javascript
 * let socket = new Socket("/socket", {params: {userToken: "123"}})
 * socket.connect()
 * ```
 *
 * The `Socket` constructor takes the mount point of the socket,
 * the authentication params, as well as options that can be found in
 * the Socket docs, such as configuring the `LongPoll` transport, and
 * heartbeat.
 *
 * ## Channels
 *
 * Channels are isolated, concurrent processes on the server that
 * subscribe to topics and broker events between the client and server.
 * To join a channel, you must provide the topic, and channel params for
 * authorization. Here's an example chat room example where `"new_msg"`
 * events are listened for, messages are pushed to the server, and
 * the channel is joined with ok/error/timeout matches:
 *
 * ```
 * let channel = socket.channel("room:123", {token: roomToken})
 * channel.on("new_msg", msg => console.log("Got message", msg) )
 * $input.onEnter( e => {
 *   channel.push("new_msg", {body: e.target.val}, 10000)
 *     .receive("ok", (msg) => console.log("created message", msg) )
 *     .receive("error", (reasons) => console.log("create failed", reasons) )
 *     .receive("timeout", () => console.log("Networking issue...") )
 * })
 *
 * channel.join()
 *   .receive("ok", ({messages}) => console.log("catching up", messages) )
 *   .receive("error", ({reason}) => console.log("failed join", reason) )
 *   .receive("timeout", () => console.log("Networking issue. Still waiting..."))
 *```
 *
 * ## Joining
 *
 * Creating a channel with `socket.channel(topic, params)`, binds the params to
 * `channel.params`, which are sent up on `channel.join()`.
 * Subsequent rejoins will send up the modified params for
 * updating authorization params, or passing up last_message_id information.
 * Successful joins receive an "ok" status, while unsuccessful joins
 * receive "error".
 *
 * With the default serializers and WebSocket transport, JSON text frames are
 * used for pushing a JSON object literal. If an `ArrayBuffer` instance is provided,
 * binary encoding will be used and the message will be sent with the binary
 * opcode.
 *
 * *Note*: binary messages are only supported on the WebSocket transport.
 *
 * ## Duplicate Join Subscriptions
 *
 * While the client may join any number of topics on any number of channels,
 * the client may only hold a single subscription for each unique topic at any
 * given time. When attempting to create a duplicate subscription,
 * the server will close the existing channel, log a warning, and
 * spawn a new channel for the topic. The client will have their
 * `channel.onClose` callbacks fired for the existing channel, and the new
 * channel join will have its receive hooks processed as normal.
 *
 * ## Pushing Messages
 *
 * From the previous example, we can see that pushing messages to the server
 * can be done with `channel.push(eventName, payload)` and we can optionally
 * receive responses from the push. Additionally, we can use
 * `receive("timeout", callback)` to abort waiting for our other `receive` hooks
 *  and take action after some period of waiting. The default timeout is 10000ms.
 *
 *
 * ## Socket Hooks
 *
 * Lifecycle events of the multiplexed connection can be hooked into via
 * `socket.onError()` and `socket.onClose()` events, ie:
 *
 * ```
 * socket.onError( () => console.log("there was an error with the connection!") )
 * socket.onClose( () => console.log("the connection dropped") )
 * ```
 *
 *
 * ## Channel Hooks
 *
 * For each joined channel, you can bind to `onError` and `onClose` events
 * to monitor the channel lifecycle, ie:
 *
 * ```
 * channel.onError( () => console.log("there was an error!") )
 * channel.onClose( () => console.log("the channel has gone away gracefully") )
 * ```
 *
 * ### onError hooks
 *
 * `onError` hooks are invoked if the socket connection drops, or the channel
 * crashes on the server. In either case, a channel rejoin is attempted
 * automatically in an exponential backoff manner.
 *
 * ### onClose hooks
 *
 * `onClose` hooks are invoked only in two cases. 1) the channel explicitly
 * closed on the server, or 2). The client explicitly closed, by calling
 * `channel.leave()`
 *
 *
 * ## Presence
 *
 * The `Presence` object provides features for syncing presence information
 * from the server with the client and handling presences joining and leaving.
 *
 * ### Syncing state from the server
 *
 * To sync presence state from the server, first instantiate an object and
 * pass your channel in to track lifecycle events:
 *
 * ```
 * let channel = socket.channel("some:topic")
 * let presence = new Presence(channel)
 * ```
 *
 * Next, use the `presence.onSync` callback to react to state changes
 * from the server. For example, to render the list of users every time
 * the list changes, you could write:
 *
 * ```
 * presence.onSync(() => {
 *   myRenderUsersFunction(presence.list())
 * })
 * ```
 *
 * ### Listing Presences
 *
 * `presence.list` is used to return a list of presence information
 * based on the local state of metadata. By default, all presence
 * metadata is returned, but a `listBy` function can be supplied to
 * allow the client to select which metadata to use for a given presence.
 * For example, you may have a user online from different devices with
 * a metadata status of "online", but they have set themselves to "away"
 * on another device. In this case, the app may choose to use the "away"
 * status for what appears on the UI. The example below defines a `listBy`
 * function which prioritizes the first metadata which was registered for
 * each user. This could be the first tab they opened, or the first device
 * they came online from:
 *
 * ```
 * let listBy = (id, {metas: [first, ...rest]}) => {
 *   first.count = rest.length + 1 // count of this user's presences
 *   first.id = id
 *   return first
 * }
 * let onlineUsers = presence.list(listBy)
 * ```
 *
 * ### Handling individual presence join and leave events
 *
 * The `presence.onJoin` and `presence.onLeave` callbacks can be used to
 * react to individual presences joining and leaving the app. For example:
 *
 * ```
 * let presence = new Presence(channel)
 *
 * // detect if user has joined for the 1st time or from another tab/device
 * presence.onJoin((id, current, newPres) => {
 *   if(!current){
 *     console.log("user has entered for the first time", newPres)
 *   } else {
 *     console.log("user additional presence", newPres)
 *   }
 * })
 *
 * // detect if user has left from all tabs/devices, or is still present
 * presence.onLeave((id, current, leftPres) => {
 *   if(current.metas.length === 0){
 *     console.log("user has left from all devices", leftPres)
 *   } else {
 *     console.log("user left from a device", leftPres)
 *   }
 * })
 * // receive presence data from server
 * presence.onSync(() => {
 *   displayUsers(presence.list())
 * })
 * ```
 * @module phoenix
 */

import Channel from "./channel"
import LongPoll from "./longpoll"
import Presence from "./presence"
import Serializer from "./serializer"
import Socket from "./socket"

export {
  Channel,
  LongPoll,
  Presence,
  Serializer,
  Socket
}


================================================
FILE: assets/js/phoenix/longpoll.js
================================================
import {
  SOCKET_STATES,
  TRANSPORTS,
  AUTH_TOKEN_PREFIX
} from "./constants"

import Ajax from "./ajax"

let arrayBufferToBase64 = (buffer) => {
  let binary = ""
  let bytes = new Uint8Array(buffer)
  let len = bytes.byteLength
  for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }
  return btoa(binary)
}

export default class LongPoll {

  constructor(endPoint, protocols){
    // we only support subprotocols for authToken
    // ["phoenix", "base64url.bearer.phx.BASE64_ENCODED_TOKEN"]
    if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){
      this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))
    }
    this.endPoint = null
    this.token = null
    this.skipHeartbeat = true
    this.reqs = new Set()
    this.awaitingBatchAck = false
    this.currentBatch = null
    this.currentBatchTimer = null
    this.batchBuffer = []
    this.onopen = function (){ } // noop
    this.onerror = function (){ } // noop
    this.onmessage = function (){ } // noop
    this.onclose = function (){ } // noop
    this.pollEndpoint = this.normalizeEndpoint(endPoint)
    this.readyState = SOCKET_STATES.connecting
    // we must wait for the caller to finish setting up our callbacks and timeout properties
    setTimeout(() => this.poll(), 0)
  }

  normalizeEndpoint(endPoint){
    return (endPoint
      .replace("ws://", "http://")
      .replace("wss://", "https://")
      .replace(new RegExp("(.*)\/" + TRANSPORTS.websocket), "$1/" + TRANSPORTS.longpoll))
  }

  endpointURL(){
    return Ajax.appendParams(this.pollEndpoint, {token: this.token})
  }

  closeAndRetry(code, reason, wasClean){
    this.close(code, reason, wasClean)
    this.readyState = SOCKET_STATES.connecting
  }

  ontimeout(){
    this.onerror("timeout")
    this.closeAndRetry(1005, "timeout", false)
  }

  isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }

  poll(){
    const headers = {"Accept": "application/json"}
    if(this.authToken){
      headers["X-Phoenix-AuthToken"] = this.authToken
    }
    this.ajax("GET", headers, null, () => this.ontimeout(), resp => {
      if(resp){
        var {status, token, messages} = resp
        if(status === 410 && this.token !== null){
          // In case we already have a token, this means that our existing session
          // is gone. We fail so that the client rejoins its channels.
          this.onerror(410)
          this.closeAndRetry(3410, "session_gone", false)
          return
        }
        this.token = token
      } else {
        status = 0
      }

      switch(status){
        case 200:
          messages.forEach(msg => {
            // Tasks are what things like event handlers, setTimeout callbacks,
            // promise resolves and more are run within.
            // In modern browsers, there are two different kinds of tasks,
            // microtasks and macrotasks.
            // Microtasks are mainly used for Promises, while macrotasks are
            // used for everything else.
            // Microtasks always have priority over macrotasks. If the JS engine
            // is looking for a task to run, it will always try to empty the
            // microtask queue before attempting to run anything from the
            // macrotask queue.
            //
            // For the WebSocket transport, messages always arrive in their own
            // event. This means that if any promises are resolved from within,
            // their callbacks will always finish execution by the time the
            // next message event handler is run.
            //
            // In order to emulate this behaviour, we need to make sure each
            // onmessage handler is run within its own macrotask.
            setTimeout(() => this.onmessage({data: msg}), 0)
          })
          this.poll()
          break
        case 204:
          this.poll()
          break
        case 410:
          this.readyState = SOCKET_STATES.open
          this.onopen({})
          this.poll()
          break
        case 403:
          this.onerror(403)
          this.close(1008, "forbidden", false)
          break
        case 0:
        case 500:
          this.onerror(500)
          this.closeAndRetry(1011, "internal server error", 500)
          break
        default: throw new Error(`unhandled poll status ${status}`)
      }
    })
  }

  // we collect all pushes within the current event loop by
  // setTimeout 0, which optimizes back-to-back procedural
  // pushes against an empty buffer

  send(body){
    if(typeof(body) !== "string"){ body = arrayBufferToBase64(body) }
    if(this.currentBatch){
      this.currentBatch.push(body)
    } else if(this.awaitingBatchAck){
      this.batchBuffer.push(body)
    } else {
      this.currentBatch = [body]
      this.currentBatchTimer = setTimeout(() => {
        this.batchSend(this.currentBatch)
        this.currentBatch = null
      }, 0)
    }
  }

  batchSend(messages){
    this.awaitingBatchAck = true
    this.ajax("POST", {"Content-Type": "application/x-ndjson"}, messages.join("\n"), () => this.onerror("timeout"), resp => {
      this.awaitingBatchAck = false
      if(!resp || resp.status !== 200){
        this.onerror(resp && resp.status)
        this.closeAndRetry(1011, "internal server error", false)
      } else if(this.batchBuffer.length > 0){
        this.batchSend(this.batchBuffer)
        this.batchBuffer = []
      }
    })
  }

  close(code, reason, wasClean){
    for(let req of this.reqs){ req.abort() }
    this.readyState = SOCKET_STATES.closed
    let opts = Object.assign({code: 1000, reason: undefined, wasClean: true}, {code, reason, wasClean})
    this.batchBuffer = []
    clearTimeout(this.currentBatchTimer)
    this.currentBatchTimer = null
    if(typeof(CloseEvent) !== "undefined"){
      this.onclose(new CloseEvent("close", opts))
    } else {
      this.onclose(opts)
    }
  }

  ajax(method, headers, body, onCallerTimeout, callback){
    let req
    let ontimeout = () => {
      this.reqs.delete(req)
      onCallerTimeout()
    }
    req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {
      this.reqs.delete(req)
      if(this.isActive()){ callback(resp) }
    })
    this.reqs.add(req)
  }
}


================================================
FILE: assets/js/phoenix/presence.js
================================================
/**
 * Initializes the Presence
 * @param {Channel} channel - The Channel
 * @param {Object} opts - The options,
 *        for example `{events: {state: "state", diff: "diff"}}`
 */
export default class Presence {

  constructor(channel, opts = {}){
    let events = opts.events || {state: "presence_state", diff: "presence_diff"}
    this.state = {}
    this.pendingDiffs = []
    this.channel = channel
    this.joinRef = null
    this.caller = {
      onJoin: function (){ },
      onLeave: function (){ },
      onSync: function (){ }
    }

    this.channel.on(events.state, newState => {
      let {onJoin, onLeave, onSync} = this.caller

      this.joinRef = this.channel.joinRef()
      this.state = Presence.syncState(this.state, newState, onJoin, onLeave)

      this.pendingDiffs.forEach(diff => {
        this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)
      })
      this.pendingDiffs = []
      onSync()
    })

    this.channel.on(events.diff, diff => {
      let {onJoin, onLeave, onSync} = this.caller

      if(this.inPendingSyncState()){
        this.pendingDiffs.push(diff)
      } else {
        this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)
        onSync()
      }
    })
  }

  onJoin(callback){ this.caller.onJoin = callback }

  onLeave(callback){ this.caller.onLeave = callback }

  onSync(callback){ this.caller.onSync = callback }

  list(by){ return Presence.list(this.state, by) }

  inPendingSyncState(){
    return !this.joinRef || (this.joinRef !== this.channel.joinRef())
  }

  // lower-level public static API

  /**
   * Used to sync the list of presences on the server
   * with the client's state. An optional `onJoin` and `onLeave` callback can
   * be provided to react to changes in the client's local presences across
   * disconnects and reconnects with the server.
   *
   * @returns {Presence}
   */
  static syncState(currentState, newState, onJoin, onLeave){
    let state = this.clone(currentState)
    let joins = {}
    let leaves = {}

    this.map(state, (key, presence) => {
      if(!newState[key]){
        leaves[key] = presence
      }
    })
    this.map(newState, (key, newPresence) => {
      let currentPresence = state[key]
      if(currentPresence){
        let newRefs = newPresence.metas.map(m => m.phx_ref)
        let curRefs = currentPresence.metas.map(m => m.phx_ref)
        let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)
        let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)
        if(joinedMetas.length > 0){
          joins[key] = newPresence
          joins[key].metas = joinedMetas
        }
        if(leftMetas.length > 0){
          leaves[key] = this.clone(currentPresence)
          leaves[key].metas = leftMetas
        }
      } else {
        joins[key] = newPresence
      }
    })
    return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)
  }

  /**
   *
   * Used to sync a diff of presence join and leave
   * events from the server, as they happen. Like `syncState`, `syncDiff`
   * accepts optional `onJoin` and `onLeave` callbacks to react to a user
   * joining or leaving from a device.
   *
   * @returns {Presence}
   */
  static syncDiff(state, diff, onJoin, onLeave){
    let {joins, leaves} = this.clone(diff)
    if(!onJoin){ onJoin = function (){ } }
    if(!onLeave){ onLeave = function (){ } }

    this.map(joins, (key, newPresence) => {
      let currentPresence = state[key]
      state[key] = this.clone(newPresence)
      if(currentPresence){
        let joinedRefs = state[key].metas.map(m => m.phx_ref)
        let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)
        state[key].metas.unshift(...curMetas)
      }
      onJoin(key, currentPresence, newPresence)
    })
    this.map(leaves, (key, leftPresence) => {
      let currentPresence = state[key]
      if(!currentPresence){ return }
      let refsToRemove = leftPresence.metas.map(m => m.phx_ref)
      currentPresence.metas = currentPresence.metas.filter(p => {
        return refsToRemove.indexOf(p.phx_ref) < 0
      })
      onLeave(key, currentPresence, leftPresence)
      if(currentPresence.metas.length === 0){
        delete state[key]
      }
    })
    return state
  }

  /**
   * Returns the array of presences, with selected metadata.
   *
   * @param {Object} presences
   * @param {Function} chooser
   *
   * @returns {Presence}
   */
  static list(presences, chooser){
    if(!chooser){ chooser = function (key, pres){ return pres } }

    return this.map(presences, (key, presence) => {
      return chooser(key, presence)
    })
  }

  // private

  static map(obj, func){
    return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))
  }

  static clone(obj){ return JSON.parse(JSON.stringify(obj)) }
}


================================================
FILE: assets/js/phoenix/push.js
================================================
/**
 * Initializes the Push
 * @param {Channel} channel - The Channel
 * @param {string} event - The event, for example `"phx_join"`
 * @param {Object} payload - The payload, for example `{user_id: 123}`
 * @param {number} timeout - The push timeout in milliseconds
 */
export default class Push {
  constructor(channel, event, payload, timeout){
    this.channel = channel
    this.event = event
    this.payload = payload || function (){ return {} }
    this.receivedResp = null
    this.timeout = timeout
    this.timeoutTimer = null
    this.recHooks = []
    this.sent = false
  }

  /**
   *
   * @param {number} timeout
   */
  resend(timeout){
    this.timeout = timeout
    this.reset()
    this.send()
  }

  /**
   *
   */
  send(){
    if(this.hasReceived("timeout")){ return }
    this.startTimeout()
    this.sent = true
    this.channel.socket.push({
      topic: this.channel.topic,
      event: this.event,
      payload: this.payload(),
      ref: this.ref,
      join_ref: this.channel.joinRef()
    })
  }

  /**
   *
   * @param {*} status
   * @param {*} callback
   */
  receive(status, callback){
    if(this.hasReceived(status)){
      callback(this.receivedResp.response)
    }

    this.recHooks.push({status, callback})
    return this
  }

  /**
   * @private
   */
  reset(){
    this.cancelRefEvent()
    this.ref = null
    this.refEvent = null
    this.receivedResp = null
    this.sent = false
  }

  /**
   * @private
   */
  matchReceive({status, response, _ref}){
    this.recHooks.filter(h => h.status === status)
      .forEach(h => h.callback(response))
  }

  /**
   * @private
   */
  cancelRefEvent(){
    if(!this.refEvent){ return }
    this.channel.off(this.refEvent)
  }

  /**
   * @private
   */
  cancelTimeout(){
    clearTimeout(this.timeoutTimer)
    this.timeoutTimer = null
  }

  /**
   * @private
   */
  startTimeout(){
    if(this.timeoutTimer){ this.cancelTimeout() }
    this.ref = this.channel.socket.makeRef()
    this.refEvent = this.channel.replyEventName(this.ref)

    this.channel.on(this.refEvent, payload => {
      this.cancelRefEvent()
      this.cancelTimeout()
      this.receivedResp = payload
      this.matchReceive(payload)
    })

    this.timeoutTimer = setTimeout(() => {
      this.trigger("timeout", {})
    }, this.timeout)
  }

  /**
   * @private
   */
  hasReceived(status){
    return this.receivedResp && this.receivedResp.status === status
  }

  /**
   * @private
   */
  trigger(status, response){
    this.channel.trigger(this.refEvent, {status, response})
  }
}


================================================
FILE: assets/js/phoenix/serializer.js
================================================
/* The default serializer for encoding and decoding messages */
import {
  CHANNEL_EVENTS
} from "./constants"

export default {
  HEADER_LENGTH: 1,
  META_LENGTH: 4,
  KINDS: {push: 0, reply: 1, broadcast: 2},

  encode(msg, callback){
    if(msg.payload.constructor === ArrayBuffer){
      return callback(this.binaryEncode(msg))
    } else {
      let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]
      return callback(JSON.stringify(payload))
    }
  },

  decode(rawPayload, callback){
    if(rawPayload.constructor === ArrayBuffer){
      return callback(this.binaryDecode(rawPayload))
    } else {
      let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)
      return callback({join_ref, ref, topic, event, payload})
    }
  },

  // private

  binaryEncode(message){
    let {join_ref, ref, event, topic, payload} = message
    let metaLength = this.META_LENGTH + join_ref.length + ref.length + topic.length + event.length
    let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)
    let view = new DataView(header)
    let offset = 0

    view.setUint8(offset++, this.KINDS.push) // kind
    view.setUint8(offset++, join_ref.length)
    view.setUint8(offset++, ref.length)
    view.setUint8(offset++, topic.length)
    view.setUint8(offset++, event.length)
    Array.from(join_ref, char => view.setUint8(offset++, char.charCodeAt(0)))
    Array.from(ref, char => view.setUint8(offset++, char.charCodeAt(0)))
    Array.from(topic, char => view.setUint8(offset++, char.charCodeAt(0)))
    Array.from(event, char => view.setUint8(offset++, char.charCodeAt(0)))

    var combined = new Uint8Array(header.byteLength + payload.byteLength)
    combined.set(new Uint8Array(header), 0)
    combined.set(new Uint8Array(payload), header.byteLength)

    return combined.buffer
  },

  binaryDecode(buffer){
    let view = new DataView(buffer)
    let kind = view.getUint8(0)
    let decoder = new TextDecoder()
    switch(kind){
      case this.KINDS.push: return this.decodePush(buffer, view, decoder)
      case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)
      case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)
    }
  },

  decodePush(buffer, view, decoder){
    let joinRefSize = view.getUint8(1)
    let topicSize = view.getUint8(2)
    let eventSize = view.getUint8(3)
    let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref
    let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))
    offset = offset + joinRefSize
    let topic = decoder.decode(buffer.slice(offset, offset + topicSize))
    offset = offset + topicSize
    let event = decoder.decode(buffer.slice(offset, offset + eventSize))
    offset = offset + eventSize
    let data = buffer.slice(offset, buffer.byteLength)
    return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}
  },

  decodeReply(buffer, view, decoder){
    let joinRefSize = view.getUint8(1)
    let refSize = view.getUint8(2)
    let topicSize = view.getUint8(3)
    let eventSize = view.getUint8(4)
    let offset = this.HEADER_LENGTH + this.META_LENGTH
    let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))
    offset = offset + joinRefSize
    let ref = decoder.decode(buffer.slice(offset, offset + refSize))
    offset = offset + refSize
    let topic = decoder.decode(buffer.slice(offset, offset + topicSize))
    offset = offset + topicSize
    let event = decoder.decode(buffer.slice(offset, offset + eventSize))
    offset = offset + eventSize
    let data = buffer.slice(offset, buffer.byteLength)
    let payload = {status: event, response: data}
    return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}
  },

  decodeBroadcast(buffer, view, decoder){
    let topicSize = view.getUint8(1)
    let eventSize = view.getUint8(2)
    let offset = this.HEADER_LENGTH + 2
    let topic = decoder.decode(buffer.slice(offset, offset + topicSize))
    offset = offset + topicSize
    let event = decoder.decode(buffer.slice(offset, offset + eventSize))
    offset = offset + eventSize
    let data = buffer.slice(offset, buffer.byteLength)

    return {join_ref: null, ref: null, topic: topic, event: event, payload: data}
  }
}


================================================
FILE: assets/js/phoenix/socket.js
================================================
import {
  global,
  phxWindow,
  CHANNEL_EVENTS,
  DEFAULT_TIMEOUT,
  DEFAULT_VSN,
  SOCKET_STATES,
  TRANSPORTS,
  WS_CLOSE_NORMAL,
  AUTH_TOKEN_PREFIX
} from "./constants"

import {
  closure
} from "./utils"

import Ajax from "./ajax"
import Channel from "./channel"
import LongPoll from "./longpoll"
import Serializer from "./serializer"
import Timer from "./timer"

/** Initializes the Socket *
 *
 * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)
 *
 * @param {string} endPoint - The string WebSocket endpoint, ie, `"ws://example.com/socket"`,
 *                                               `"wss://example.com"`
 *                                               `"/socket"` (inherited host & protocol)
 * @param {Object} [opts] - Optional configuration
 * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.
 *
 * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.
 * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.
 *
 * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport
 * before falling back to the LongPoll transport. Disabled by default.
 *
 * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.
 *
 * @param {Function} [opts.encode] - The function to encode outgoing messages.
 *
 * Defaults to JSON encoder.
 *
 * @param {Function} [opts.decode] - The function to decode incoming messages.
 *
 * Defaults to JSON:
 *
 * ```javascript
 * (payload, callback) => callback(JSON.parse(payload))
 * ```
 *
 * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.
 *
 * Defaults `DEFAULT_TIMEOUT`
 * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message
 * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the
 * socket reconnect interval, in milliseconds.
 *
 * Defaults to stepped backoff of:
 *
 * ```javascript
 * function(tries){
 *   return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000
 * }
 * ````
 *
 * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec
 * rejoin interval for individual channels.
 *
 * ```javascript
 * function(tries){
 *   return [1000, 2000, 5000][tries - 1] || 10000
 * }
 * ````
 *
 * @param {Function} [opts.logger] - The optional function for specialized logging, ie:
 *
 * ```javascript
 * function(kind, msg, data) {
 *   console.log(`${kind}: ${msg}`, data)
 * }
 * ```
 *
 * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.
 *
 * Defaults to 20s (double the server long poll timer).
 *
 * @param {(Object|function)} [opts.params] - The optional params to pass when connecting
 * @param {string} [opts.authToken] - the optional authentication token to be exposed on the server
 * under the `:auth_token` connect_info key.
 * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.
 *
 * Defaults to "arraybuffer"
 *
 * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.
 *
 * Defaults to DEFAULT_VSN.
 *
 * @param {Object} [opts.sessionStorage] - An optional Storage compatible object
 * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is
 * useful when Phoenix won't have access to `sessionStorage`. For example, This could
 * happen if a site loads a cross-domain channel in an iframe. Example usage:
 *
 *     class InMemoryStorage {
 *       constructor() { this.storage = {} }
 *       getItem(keyName) { return this.storage[keyName] || null }
 *       removeItem(keyName) { delete this.storage[keyName] }
 *       setItem(keyName, keyValue) { this.storage[keyName] = keyValue }
 *     }
 *
*/
export default class Socket {
  constructor(endPoint, opts = {}){
    this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}
    this.channels = []
    this.sendBuffer = []
    this.ref = 0
    this.fallbackRef = null
    this.timeout = opts.timeout || DEFAULT_TIMEOUT
    this.transport = opts.transport || global.WebSocket || LongPoll
    this.primaryPassedHealthCheck = false
    this.longPollFallbackMs = opts.longPollFallbackMs
    this.fallbackTimer = null
    this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)
    this.establishedConnections = 0
    this.defaultEncoder = Serializer.encode.bind(Serializer)
    this.defaultDecoder = Serializer.decode.bind(Serializer)
    // We start with closeWasClean true to avoid the visibility change
    // logic from connecting if the socket was never connected in the first place.
    // transportConnect sets it to false on open.
    this.closeWasClean = true
    this.disconnecting = false
    this.binaryType = opts.binaryType || "arraybuffer"
    this.connectClock = 1
    this.pageHidden = false
    if(this.transport !== LongPoll){
      this.encode = opts.encode || this.defaultEncoder
      this.decode = opts.decode || this.defaultDecoder
    } else {
      this.encode = this.defaultEncoder
      this.decode = this.defaultDecoder
    }
    let awaitingConnectionOnPageShow = null
    if(phxWindow && phxWindow.addEventListener){
      phxWindow.addEventListener("pagehide", _e => {
        if(this.conn){
          this.disconnect()
          awaitingConnectionOnPageShow = this.connectClock
        }
      })
      phxWindow.addEventListener("pageshow", _e => {
        if(awaitingConnectionOnPageShow === this.connectClock){
          awaitingConnectionOnPageShow = null
          this.connect()
        }
      })
      phxWindow.addEventListener("visibilitychange", () => {
        if(document.visibilityState === "hidden"){
          this.pageHidden = true
        } else {
          this.pageHidden = false
          // reconnect immediately
          if(!this.isConnected() && !this.closeWasClean){
            this.teardown(() => this.connect())
          }
        }
      })
    }
    this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000
    this.rejoinAfterMs = (tries) => {
      if(opts.rejoinAfterMs){
        return opts.rejoinAfterMs(tries)
      } else {
        return [1000, 2000, 5000][tries - 1] || 10000
      }
    }
    this.reconnectAfterMs = (tries) => {
      if(opts.reconnectAfterMs){
        return opts.reconnectAfterMs(tries)
      } else {
        return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000
      }
    }
    this.logger = opts.logger || null
    if(!this.logger && opts.debug){
      this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }
    }
    this.longpollerTimeout = opts.longpollerTimeout || 20000
    this.params = closure(opts.params || {})
    this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`
    this.vsn = opts.vsn || DEFAULT_VSN
    this.heartbeatTimeoutTimer = null
    this.heartbeatTimer = null
    this.pendingHeartbeatRef = null
    this.reconnectTimer = new Timer(() => {
      if(this.pageHidden){
        this.log("Not reconnecting as page is hidden!")
        this.teardown()
        return
      }
      this.teardown(() => this.connect())
    }, this.reconnectAfterMs)
    this.authToken = opts.authToken
  }

  /**
   * Returns the LongPoll transport reference
   */
  getLongPollTransport(){ return LongPoll }

  /**
   * Disconnects and replaces the active transport
   *
   * @param {Function} newTransport - The new transport class to instantiate
   *
   */
  replaceTransport(newTransport){
    this.connectClock++
    this.closeWasClean = true
    clearTimeout(this.fallbackTimer)
    this.reconnectTimer.reset()
    if(this.conn){
      this.conn.close()
      this.conn = null
    }
    this.transport = newTransport
  }

  /**
   * Returns the socket protocol
   *
   * @returns {string}
   */
  protocol(){ return location.protocol.match(/^https/) ? "wss" : "ws" }

  /**
   * The fully qualified socket url
   *
   * @returns {string}
   */
  endPointURL(){
    let uri = Ajax.appendParams(
      Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})
    if(uri.charAt(0) !== "/"){ return uri }
    if(uri.charAt(1) === "/"){ return `${this.protocol()}:${uri}` }

    return `${this.protocol()}://${location.host}${uri}`
  }

  /**
   * Disconnects the socket
   *
   * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.
   *
   * @param {Function} callback - Optional callback which is called after socket is disconnected.
   * @param {integer} code - A status code for disconnection (Optional).
   * @param {string} reason - A textual description of the reason to disconnect. (Optional)
   */
  disconnect(callback, code, reason){
    this.connectClock++
    this.disconnecting = true
    this.closeWasClean = true
    clearTimeout(this.fallbackTimer)
    this.reconnectTimer.reset()
    this.teardown(() => {
      this.disconnecting = false
      callback && callback()
    }, code, reason)
  }

  /**
   *
   * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`
   *
   * Passing params to connect is deprecated; pass them in the Socket constructor instead:
   * `new Socket("/socket", {params: {user_id: userToken}})`.
   */
  connect(params){
    if(params){
      console && console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor")
      this.params = closure(params)
    }
    if(this.conn && !this.disconnecting){ return }
    if(this.longPollFallbackMs && this.transport !== LongPoll){
      this.connectWithFallback(LongPoll, this.longPollFallbackMs)
    } else {
      this.transportConnect()
    }
  }

  /**
   * Logs the message. Override `this.logger` for specialized logging. noops by default
   * @param {string} kind
   * @param {string} msg
   * @param {Object} data
   */
  log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }

  /**
   * Returns true if a logger has been set on this socket.
   */
  hasLogger(){ return this.logger !== null }

  /**
   * Registers callbacks for connection open events
   *
   * @example socket.onOpen(function(){ console.info("the socket was opened") })
   *
   * @param {Function} callback
   */
  onOpen(callback){
    let ref = this.makeRef()
    this.stateChangeCallbacks.open.push([ref, callback])
    return ref
  }

  /**
   * Registers callbacks for connection close events
   * @param {Function} callback
   */
  onClose(callback){
    let ref = this.makeRef()
    this.stateChangeCallbacks.close.push([ref, callback])
    return ref
  }

  /**
   * Registers callbacks for connection error events
   *
   * @example socket.onError(function(error){ alert("An error occurred") })
   *
   * @param {Function} callback
   */
  onError(callback){
    let ref = this.makeRef()
    this.stateChangeCallbacks.error.push([ref, callback])
    return ref
  }

  /**
   * Registers callbacks for connection message events
   * @param {Function} callback
   */
  onMessage(callback){
    let ref = this.makeRef()
    this.stateChangeCallbacks.message.push([ref, callback])
    return ref
  }

  /**
   * Pings the server and invokes the callback with the RTT in milliseconds
   * @param {Function} callback
   *
   * Returns true if the ping was pushed or false if unable to be pushed.
   */
  ping(callback){
    if(!this.isConnected()){ return false }
    let ref = this.makeRef()
    let startTime = Date.now()
    this.push({topic: "phoenix", event: "heartbeat", payload: {}, ref: ref})
    let onMsgRef = this.onMessage(msg => {
      if(msg.ref === ref){
        this.off([onMsgRef])
        callback(Date.now() - startTime)
      }
    })
    return true
  }

  /**
   * @private
   *
   * @param {Function}
   */
  transportName(transport){
    // JavaScript minification, enabled by default in production in Phoenix
    // projects, renames symbols to reduce code size.
    // See https://esbuild.github.io/api/#keep-names.
    // This helper ensures we return the correct name for the LongPoll transport
    // even after minification. The other common transport is WebSocket, which
    // is native to browsers and does not need special handling.
    switch(transport){
      case LongPoll: return "LongPoll"
      default: return transport.name
    }
  }

  /**
   * @private
   */
  transportConnect(){
    this.connectClock++
    this.closeWasClean = false
    let protocols = undefined
    // Sec-WebSocket-Protocol based token
    // (longpoll uses Authorization header instead)
    if(this.authToken){
      protocols = ["phoenix", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken).replace(/=/g, "")}`]
    }
    this.conn = new this.transport(this.endPointURL(), protocols)
    this.conn.binaryType = this.binaryType
    this.conn.timeout = this.longpollerTimeout
    this.conn.onopen = () => this.onConnOpen()
    this.conn.onerror = error => this.onConnError(error)
    this.conn.onmessage = event => this.onConnMessage(event)
    this.conn.onclose = event => this.onConnClose(event)
  }

  getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }

  storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }

  connectWithFallback(fallbackTransport, fallbackThreshold = 2500){
    clearTimeout(this.fallbackTimer)
    let established = false
    let primaryTransport = true
    let openRef, errorRef
    let fallbackTransportName = this.transportName(fallbackTransport)
    let fallback = (reason) => {
      this.log("transport", `falling back to ${fallbackTransportName}...`, reason)
      this.off([openRef, errorRef])
      primaryTransport = false
      this.replaceTransport(fallbackTransport)
      this.transportConnect()
    }
    if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback("memorized") }

    this.fallbackTimer = setTimeout(fallback, fallbackThreshold)

    errorRef = this.onError(reason => {
      this.log("transport", "error", reason)
      if(primaryTransport && !established){
        clearTimeout(this.fallbackTimer)
        fallback(reason)
      }
    })
    if(this.fallbackRef){
      this.off([this.fallbackRef])
    }
    this.fallbackRef = this.onOpen(() => {
      established = true
      if(!primaryTransport){
        let fallbackTransportName = this.transportName(fallbackTransport)
        // only memorize LP if we never connected to primary
        if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, "true") }
        return this.log("transport", `established ${fallbackTransportName} fallback`)
      }
      // if we've established primary, give the fallback a new period to attempt ping
      clearTimeout(this.fallbackTimer)
      this.fallbackTimer = setTimeout(fallback, fallbackThreshold)
      this.ping(rtt => {
        this.log("transport", "connected to primary after", rtt)
        this.primaryPassedHealthCheck = true
        clearTimeout(this.fallbackTimer)
      })
    })
    this.transportConnect()
  }

  clearHeartbeats(){
    clearTimeout(this.heartbeatTimer)
    clearTimeout(this.heartbeatTimeoutTimer)
  }

  onConnOpen(){
    if(this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)
    this.closeWasClean = false
    this.disconnecting = false
    this.establishedConnections++
    this.flushSendBuffer()
    this.reconnectTimer.reset()
    this.resetHeartbeat()
    this.stateChangeCallbacks.open.forEach(([, callback]) => callback())
  }

  /**
   * @private
   */

  heartbeatTimeout(){
    if(this.pendingHeartbeatRef){
      this.pendingHeartbeatRef = null
      if(this.hasLogger()){ this.log("transport", "heartbeat timeout. Attempting to re-establish connection") }
      this.triggerChanError()
      this.closeWasClean = false
      this.teardown(() => this.reconnectTimer.scheduleTimeout(), WS_CLOSE_NORMAL, "heartbeat timeout")
    }
  }

  resetHeartbeat(){
    if(this.conn && this.conn.skipHeartbeat){ return }
    this.pendingHeartbeatRef = null
    this.clearHeartbeats()
    this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)
  }

  teardown(callback, code, reason){
    if(!this.conn){
      return callback && callback()
    }

    // If someone calls connect before we finish tearing down,
    // we create a new connection, but we still want to finish tearing down the old one.
    const connToClose = this.conn

    this.waitForBufferDone(connToClose, () => {
      if(code){ connToClose.close(code, reason || "") } else { connToClose.close() }

      this.waitForSocketClosed(connToClose, () => {
        if(this.conn === connToClose){
          this.conn.onopen = function (){ } // noop
          this.conn.onerror = function (){ } // noop
          this.conn.onmessage = function (){ } // noop
          this.conn.onclose = function (){ } // noop
          this.conn = null
        }

        callback && callback()
      })
    })
  }

  waitForBufferDone(conn, callback, tries = 1){
    if(tries === 5 || !conn.bufferedAmount){
      callback()
      return
    }

    setTimeout(() => {
      this.waitForBufferDone(conn, callback, tries + 1)
    }, 150 * tries)
  }

  waitForSocketClosed(conn, callback, tries = 1){
    if(tries === 5 || conn.readyState === SOCKET_STATES.closed){
      callback()
      return
    }

    setTimeout(() => {
      this.waitForSocketClosed(conn, callback, tries + 1)
    }, 150 * tries)
  }

  onConnClose(event){
    if(this.conn) this.conn.onclose = () => {} // noop to prevent recursive calls in teardown
    let closeCode = event && event.code
    if(this.hasLogger()) this.log("transport", "close", event)
    this.triggerChanError()
    this.clearHeartbeats()
    if(!this.closeWasClean && closeCode !== 1000){
      this.reconnectTimer.scheduleTimeout()
    }
    this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))
  }

  /**
   * @private
   */
  onConnError(error){
    if(this.hasLogger()) this.log("transport", error)
    let transportBefore = this.transport
    let establishedBefore = this.establishedConnections
    this.stateChangeCallbacks.error.forEach(([, callback]) => {
      callback(error, transportBefore, establishedBefore)
    })
    if(transportBefore === this.transport || establishedBefore > 0){
      this.triggerChanError()
    }
  }

  /**
   * @private
   */
  triggerChanError(){
    this.channels.forEach(channel => {
      if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){
        channel.trigger(CHANNEL_EVENTS.error)
      }
    })
  }

  /**
   * @returns {string}
   */
  connectionState(){
    switch(this.conn && this.conn.readyState){
      case SOCKET_STATES.connecting: return "connecting"
      case SOCKET_STATES.open: return "open"
      case SOCKET_STATES.closing: return "closing"
      default: return "closed"
    }
  }

  /**
   * @returns {boolean}
   */
  isConnected(){ return this.connectionState() === "open" }

  /**
   * @private
   *
   * @param {Channel}
   */
  remove(channel){
    this.off(channel.stateChangeRefs)
    this.channels = this.channels.filter(c => c !== channel)
  }

  /**
   * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.
   *
   * @param {refs} - list of refs returned by calls to
   *                 `onOpen`, `onClose`, `onError,` and `onMessage`
   */
  off(refs){
    for(let key in this.stateChangeCallbacks){
      this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {
        return refs.indexOf(ref) === -1
      })
    }
  }

  /**
   * Initiates a new channel for the given topic
   *
   * @param {string} topic
   * @param {Object} chanParams - Parameters for the channel
   * @returns {Channel}
   */
  channel(topic, chanParams = {}){
    let chan = new Channel(topic, chanParams, this)
    this.channels.push(chan)
    return chan
  }

  /**
   * @param {Object} data
   */
  push(data){
    if(this.hasLogger()){
      let {topic, event, payload, ref, join_ref} = data
      this.log("push", `${topic} ${event} (${join_ref}, ${ref})`, payload)
    }

    if(this.isConnected()){
      this.encode(data, result => this.conn.send(result))
    } else {
      this.sendBuffer.push(() => this.encode(data, result => this.conn.send(result)))
    }
  }

  /**
   * Return the next message ref, accounting for overflows
   * @returns {string}
   */
  makeRef(){
    let newRef = this.ref + 1
    if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }

    return this.ref.toString()
  }

  sendHeartbeat(){
    if(this.pendingHeartbeatRef && !this.isConnected()){ return }
    this.pendingHeartbeatRef = this.makeRef()
    this.push({topic: "phoenix", event: "heartbeat", payload: {}, ref: this.pendingHeartbeatRef})
    this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)
  }

  flushSendBuffer(){
    if(this.isConnected() && this.sendBuffer.length > 0){
      this.sendBuffer.forEach(callback => callback())
      this.sendBuffer = []
    }
  }

  onConnMessage(rawMessage){
    this.decode(rawMessage.data, msg => {
      let {topic, event, payload, ref, join_ref} = msg
      if(ref && ref === this.pendingHeartbeatRef){
        this.clearHeartbeats()
        this.pendingHeartbeatRef = null
        this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)
      }

      if(this.hasLogger()) this.log("receive", `${payload.status || ""} ${topic} ${event} ${ref && "(" + ref + ")" || ""}`, payload)

      for(let i = 0; i < this.channels.length; i++){
        const channel = this.channels[i]
        if(!channel.isMember(topic, event, payload, join_ref)){ continue }
        channel.trigger(event, payload, ref, join_ref)
      }

      for(let i = 0; i < this.stateChangeCallbacks.message.length; i++){
        let [, callback] = this.stateChangeCallbacks.message[i]
        callback(msg)
      }
    })
  }

  leaveOpenTopic(topic){
    let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))
    if(dupChannel){
      if(this.hasLogger()) this.log("transport", `leaving duplicate topic "${topic}"`)
      dupChannel.leave()
    }
  }
}


================================================
FILE: assets/js/phoenix/timer.js
================================================
/**
 *
 * Creates a timer that accepts a `timerCalc` function to perform
 * calculated timeout retries, such as exponential backoff.
 *
 * @example
 * let reconnectTimer = new Timer(() => this.connect(), function(tries){
 *   return [1000, 5000, 10000][tries - 1] || 10000
 * })
 * reconnectTimer.scheduleTimeout() // fires after 1000
 * reconnectTimer.scheduleTimeout() // fires after 5000
 * reconnectTimer.reset()
 * reconnectTimer.scheduleTimeout() // fires after 1000
 *
 * @param {Function} callback
 * @param {Function} timerCalc
 */
export default class Timer {
  constructor(callback, timerCalc){
    this.callback = callback
    this.timerCalc = timerCalc
    this.timer = null
    this.tries = 0
  }

  reset(){
    this.tries = 0
    clearTimeout(this.timer)
  }

  /**
   * Cancels any previous scheduleTimeout and schedules callback
   */
  scheduleTimeout(){
    clearTimeout(this.timer)

    this.timer = setTimeout(() => {
      this.tries = this.tries + 1
      this.callback()
    }, this.timerCalc(this.tries + 1))
  }
}


================================================
FILE: assets/js/phoenix/utils.js
================================================
// wraps value in closure or returns closure
export let closure = (value) => {
  if(typeof value === "function"){
    return value
  } else {
    let closure = function (){ return value }
    return closure
  }
}


================================================
FILE: assets/test/channel_test.js
================================================
import {jest} from "@jest/globals"
import {Channel, Socket} from "../js/phoenix"

let channel, socket

const defaultRef = 1
const defaultTimeout = 10000

class WSMock {
  constructor(url, protocols){
    this.url = url
    this.protocols = protocols
  }
  close(){}
  send(){}
}

describe("with transport", function (){
  beforeAll(function (){
    global.WebSocket = WSMock
  })

  afterAll(function (){
    global.WebSocket = null
  })

  describe("constructor", function (){
    beforeEach(function (){
      socket = new Socket("/", {timeout: 1234})
    })

    it("sets defaults", function (){
      channel = new Channel("topic", {one: "two"}, socket)

      expect(channel.state).toBe("closed")
      expect(channel.topic).toBe("topic")
      expect(channel.params()).toEqual({one: "two"})
      expect(channel.socket).toBe(socket)
      expect(channel.timeout).toBe(1234)
      expect(channel.joinedOnce).toBe(false)
      expect(channel.joinPush).toBeTruthy()
      expect(channel.pushBuffer).toEqual([])
    })

    it("sets up joinPush object with literal params", function (){
      channel = new Channel("topic", {one: "two"}, socket)
      const joinPush = channel.joinPush

      expect(joinPush.channel).toBe(channel)
      expect(joinPush.payload()).toEqual({one: "two"})
      expect(joinPush.event).toBe("phx_join")
      expect(joinPush.timeout).toBe(1234)
    })

    it("sets up joinPush object with closure params", function (){
      channel = new Channel("topic", () => ({one: "two"}), socket)
      const joinPush = channel.joinPush

      expect(joinPush.channel).toBe(channel)
      expect(joinPush.payload()).toEqual({one: "two"})
      expect(joinPush.event).toBe("phx_join")
      expect(joinPush.timeout).toBe(1234)
    })

    it("sets subprotocols when authToken is provided", function (){
      const authToken = "1234"
      const socket = new Socket("/socket", {authToken})
      
      socket.connect()
      expect(socket.conn.protocols).toEqual(["phoenix", "base64url.bearer.phx.MTIzNA"])
    })
  })

  describe("updating join params", function (){
    it("can update the join params", function (){
      let counter = 0
      let params = () => ({value: counter})
      socket = {timeout: 1234, onError: function (){}, onOpen: function (){}}

      channel = new Channel("topic", params, socket)
      const joinPush = channel.joinPush

      expect(joinPush.channel).toBe(channel)
      expect(joinPush.payload()).toEqual({value: 0})
      expect(joinPush.event).toBe("phx_join")
      expect(joinPush.timeout).toBe(1234)

      counter++

      expect(joinPush.channel).toBe(channel)
      expect(joinPush.payload()).toEqual({value: 1})
      expect(channel.params()).toEqual({value: 1})
      expect(joinPush.event).toBe("phx_join")
      expect(joinPush.timeout).toBe(1234)
    })
  })

  describe("join", function (){
    beforeEach(function (){
      socket = new Socket("/socket", {timeout: defaultTimeout})

      channel = socket.channel("topic", {one: "two"})
    })

    it("sets state to joining", function (){
      channel.join()

      expect(channel.state).toBe("joining")
    })

    it("sets joinedOnce to true", function (){
      expect(channel.joinedOnce).toBe(false)

      channel.join()

      expect(channel.joinedOnce).toBe(true)
    })

    it("throws if attempting to join multiple times", function (){
      channel.join()

      expect(() => channel.join()).toThrow(/^tried to join multiple times/)
    })

    it("triggers socket push with channel params", function (){
      jest.spyOn(socket, "makeRef").mockReturnValue(defaultRef)
      const spy = jest.spyOn(socket, "push")

      channel.join()

      expect(spy).toHaveBeenCalledTimes(1)
      expect(spy).toHaveBeenCalledWith({
        topic: "topic",
        event: "phx_join",
        payload: {one: "two"},
        ref: defaultRef,
        join_ref: channel.joinRef(),
      })
    })

    it("can set timeout on joinPush", function (){
      const newTimeout = 2000
      const joinPush = channel.joinPush

      expect(joinPush.timeout).toBe(defaultTimeout)

      channel.join(newTimeout)

      expect(joinPush.timeout).toBe(newTimeout)
    })

    it("leaves existing duplicate topic on new join", function (done){
      channel.join().receive("ok", () => {
        let newChannel = socket.channel("topic")
        expect(channel.isJoined()).toBe(true)
        newChannel.join()
        expect(channel.isJoined()).toBe(false)
        done()
      })

      channel.joinPush.trigger("ok", {})
    })

    describe("timeout behavior", function (){
      let joinPush

      const helpers = {
        receiveSocketOpen(){
          jest.spyOn(socket, "isConnected").mockReturnValue(true)
          socket.onConnOpen()
        },
      }

      beforeEach(function (){
        jest.useFakeTimers()
        joinPush = channel.joinPush
      })

      afterEach(function (){
        jest.useRealTimers()
      })

      it("succeeds before timeout", function (){
        const spy = jest.spyOn(socket, "push")
        const timeout = joinPush.timeout

        socket.connect()
        helpers.receiveSocketOpen()

        channel.join()
        expect(spy).toHaveBeenCalledTimes(1)

        expect(channel.timeout).toBe(10000)
        jest.advanceTimersByTime(100)

        joinPush.trigger("ok", {})

        expect(channel.state).toBe("joined")

        jest.advanceTimersByTime(timeout)
        expect(spy).toHaveBeenCalledTimes(1)
      })

      it("retries with backoff after timeout", function (){
        const spy = jest.spyOn(socket, "push")
        const timeoutSpy = jest.fn()
        const timeout = joinPush.timeout

        socket.connect()
        helpers.receiveSocketOpen()

        channel.join().receive("timeout", timeoutSpy)

        expect(spy).toHaveBeenCalledTimes(1)
        expect(timeoutSpy).toHaveBeenCalledTimes(0)

        jest.advanceTimersByTime(timeout)
        expect(spy).toHaveBeenCalledTimes(2) // leave pushed to server
        expect(timeoutSpy).toHaveBeenCalledTimes(1)

        jest.advanceTimersByTime(timeout + 1000)
        expect(spy).toHaveBeenCalledTimes(4) // leave + rejoin
        expect(timeoutSpy).toHaveBeenCalledTimes(2)

        jest.advanceTimersByTime(10000)
        joinPush.trigger("ok", {})
        expect(spy).toHaveBeenCalledTimes(6)
        expect(channel.state).toBe("joined")
      })

      it("with socket and join delay", function (){
        const spy = jest.spyOn(socket, "push")
        jest.useFakeTimers()
        const joinPush = channel.joinPush

        channel.join()
        expect(spy).toHaveBeenCalledTimes(1)

        // open socket after delay
        jest.advanceTimersByTime(9000)

        expect(spy).toHaveBeenCalledTimes(1)

        // join request returns between timeouts
        jest.advanceTimersByTime(1000)
        socket.connect()

        expect(channel.state).toBe("errored")

        helpers.receiveSocketOpen()
        joinPush.trigger("ok", {})

        // join request succeeds after delay
        jest.advanceTimersByTime(1000)

        expect(channel.state).toBe("joined")

        expect(spy).toHaveBeenCalledTimes(3) // leave pushed to server
      })

      it("with socket delay only", function (){
        jest.useFakeTimers()
        const joinPush = channel.joinPush

        channel.join()

        expect(channel.state).toBe("joining")

        // connect socket after delay
        jest.advanceTimersByTime(6000)
        socket.connect()

        // open socket after delay
        jest.advanceTimersByTime(5000)
        helpers.receiveSocketOpen()
        joinPush.trigger("ok", {})

        joinPush.trigger("ok", {})
        expect(channel.state).toBe("joined")
      })
    })
  })

  describe("joinPush", function (){
    let joinPush
    let response

    const helpers = {
      receiveOk(){
        jest.advanceTimersByTime(joinPush.timeout / 2) // before timeout
        return joinPush.channel.trigger("phx_reply", {status: "ok", response: response}, joinPush.ref, joinPush.ref)
        // return joinPush.trigger("ok", response)
      },

      receiveTimeout(){
        jest.advanceTimersByTime(joinPush.timeout * 2) // after timeout
      },

      receiveError(){
        jest.advanceTimersByTime(joinPush.timeout / 2) // before timeout
        return joinPush.trigger("error", response)
      },

      getBindings(event){
        return channel.bindings.filter(bind => bind.event === event)
      },
    }

    beforeEach(function (){
      jest.useFakeTimers()

      socket = new Socket("/socket", {timeout: defaultTimeout})
      jest.spyOn(socket, "isConnected").mockReturnValue(true)
      jest.spyOn(socket, "push").mockReturnValue(true)

      channel = socket.channel("topic", {one: "two"})
      joinPush = channel.joinPush

      channel.join()
    })

    afterEach(function (){
      jest.useRealTimers()
    })

    describe("receives 'ok'", function (){
      beforeEach(function (){
        response = {chan: "reply"}
      })

      it("sets channel state to joined", function (){
        expect(channel.state).not.toBe("joined")

        helpers.receiveOk()

        expect(channel.state).toBe("joined")
      })

      it("triggers receive('ok') callback after ok response", function (){
        const spyOk = jest.fn()

        joinPush.receive("ok", spyOk)

        helpers.receiveOk()

        expect(spyOk).toHaveBeenCalledTimes(1)
      })

      it("triggers receive('ok') callback if ok response already received", function (){
        const spyOk = jest.fn()

        helpers.receiveOk()

        joinPush.receive("ok", spyOk)

        expect(spyOk).toHaveBeenCalledTimes(1)
      })

      it("does not trigger other receive callbacks after ok response", function (){
        const spyError = jest.fn()
        const spyTimeout = jest.fn()

        joinPush.receive("error", spyError).receive("timeout", spyTimeout)

        helpers.receiveOk()
        jest.advanceTimersByTime(channel.timeout * 2) // attempt timeout

        expect(spyError).not.toHaveBeenCalled()
        expect(spyTimeout).not.toHaveBeenCalled()
      })

      it("clears timeoutTimer", function (){
        expect(joinPush.timeoutTimer).toBeTruthy()

        helpers.receiveOk()

        expect(joinPush.timeoutTimer).toBeNull()
      })

      it("sets receivedResp", function (){
        expect(joinPush.receivedResp).toBeNull()

        helpers.receiveOk()

        expect(joinPush.receivedResp).toEqual({status: "ok", response})
      })

      it("removes channel bindings", function (){
        let bindings = helpers.getBindings("chan_reply_3")
        expect(bindings.length).toBe(1)

        helpers.receiveOk()

        bindings = helpers.getBindings("chan_reply_3")
        expect(bindings.length).toBe(0)
      })

      it("resets channel rejoinTimer", function (){
        expect(channel.rejoinTimer).toBeTruthy()

        const spy = jest.spyOn(channel.rejoinTimer, "reset")

        helpers.receiveOk()

        expect(spy).toHaveBeenCalledTimes(1)
      })

      it("sends and empties channel's buffered pushEvents", function (done){
        const pushEvent = {send(){}}
        const spy = jest.spyOn(pushEvent, "send")

        channel.pushBuffer.push(pushEvent)

        expect(channel.state).toBe("joining")
        joinPush.receive("ok", () => {
          expect(spy).toHaveBeenCalledTimes(1)
          expect(channel.pushBuffer.length).toBe(0)
          done()
        })
        helpers.receiveOk()
      })
    })

    describe("receives 'timeout'", function (){
      it("sets channel state to errored", function (done){
        joinPush.receive("timeout", () => {
          expect(channel.state).toBe("errored")
          done()
        })

        helpers.receiveTimeout()
      })

      it("triggers receive('timeout') callback after ok response", function (){
        const spyTimeout = jest.fn()

        joinPush.receive("timeout", spyTimeout)

        helpers.receiveTimeout()

        expect(spyTimeout).toHaveBeenCalledTimes(1)
      })

      it("does not trigger other receive callbacks after timeout response", function (done){
        const spyOk = jest.fn()
        const spyError = jest.fn()
        jest.spyOn(channel.rejoinTimer, "scheduleTimeout").mockReturnValue(true)

        channel.test = true
        joinPush.receive("ok", spyOk).receive("error", spyError).receive("timeout", () => {
          expect(spyOk).not.toHaveBeenCalled()
          expect(spyError).not.toHaveBeenCalled()
          done()
        })

        helpers.receiveTimeout()
        helpers.receiveOk()
      })

      it("schedules rejoinTimer timeout", function (){
        expect(channel.rejoinTimer).toBeTruthy()

        const spy = jest.spyOn(channel.rejoinTimer, "scheduleTimeout")

        helpers.receiveTimeout()

        expect(spy).toHaveBeenCalled() // TODO why called multiple times?
      })
    })

    describe("receives 'error'", function (){
      beforeEach(function (){
        response = {chan: "fail"}
      })

      it("triggers receive('error') callback after error response", function (){
        const spyError = jest.fn()

        expect(channel.state).toBe("joining")
        joinPush.receive("error", spyError)

        helpers.receiveError()
        joinPush.trigger("error", {})

        expect(spyError).toHaveBeenCalledTimes(1)
      })

      it("triggers receive('error') callback if error response already received", function (){
        const spyError = jest.fn()

        helpers.receiveError()

        joinPush.receive("error", spyError)

        expect(spyError).toHaveBeenCalledTimes(1)
      })

      it("does not trigger other receive callbacks after error response", function (){
        const spyOk = jest.fn()
        const spyError = jest.fn()
        const spyTimeout = jest.fn()

        joinPush.receive("ok", spyOk).receive("error", () => {
          spyError()
          channel.leave()
        }).receive("timeout", spyTimeout)

        helpers.receiveError()
        jest.advanceTimersByTime(channel.timeout * 2) // attempt timeout

        expect(spyError).toHaveBeenCalledTimes(1)
        expect(spyOk).not.toHaveBeenCalled()
        expect(spyTimeout).not.toHaveBeenCalled()
      })

      it("clears timeoutTimer", function (){
        expect(joinPush.timeoutTimer).toBeTruthy()

        helpers.receiveError()

        expect(joinPush.timeoutTimer).toBeNull()
      })

      it("sets receivedResp with error trigger after binding", function (done){
        expect(joinPush.receivedResp).toBeNull()

        joinPush.receive("error", resp => {
          expect(resp).toEqual(response)
          done()
        })

        helpers.receiveError()
      })

      it("sets receivedResp with error trigger before binding", function (done){
        expect(joinPush.receivedResp).toBeNull()

        helpers.receiveError()
        joinPush.receive("error", resp => {
          expect(resp).toEqual(response)
          done()
        })
      })

      it("does not set channel state to joined", function (){
        helpers.receiveError()

        expect(channel.state).toBe("errored")
      })

      it("does not trigger channel's buffered pushEvents", function (){
        const pushEvent = {send: () => {}}
        const spy = jest.spyOn(pushEvent, "send")

        channel.pushBuffer.push(pushEvent)

        helpers.receiveError()

        expect(spy).not.toHaveBeenCalled()
        expect(channel.pushBuffer.length).toBe(1)
      })
    })
  })

  describe("onError", function (){
    let joinPush

    beforeEach(function (){
      jest.useFakeTimers()

      socket = new Socket("/socket", {timeout: defaultTimeout})
      jest.spyOn(socket, "isConnected").mockReturnValue(true)
      jest.spyOn(socket, "push").mockReturnValue(true)

      channel = socket.channel("topic", {one: "two"})

      joinPush = channel.joinPush

      channel.join()
      joinPush.trigger("ok", {})
    })

    afterEach(function (){
      jest.useRealTimers()
    })

    it("sets state to 'errored'", function (){
      expect(channel.state).not.toBe("errored")

      channel.trigger("phx_error")

      expect(channel.state).toBe("errored")
    })

    it("does not trigger redundant errors during backoff", function (){
      const spy = jest.spyOn(joinPush, "send").mockImplementation(() => {})

      expect(spy).toHaveBeenCalledTimes(0)

      channel.trigger("phx_error")

      jest.advanceTimersByTime(1000)
      expect(spy).toHaveBeenCalledTimes(1)

      joinPush.trigger("error", {})

      jest.advanceTimersByTime(10000)
      expect(spy).toHaveBeenCalledTimes(1)
    })

    it("does not rejoin if channel leaving", function (){
      channel.state = "leaving"

      const spy = jest.spyOn(joinPush, "send")

      socket.onConnError({})

      jest.advanceTimersByTime(1000)
      expect(spy).toHaveBeenCalledTimes(0)

      jest.advanceTimersByTime(2000)
      expect(spy).toHaveBeenCalledTimes(0)

      expect(channel.state).toBe("leaving")
    })

    it("does not rejoin if channel closed", function (){
      channel.state = "closed"

      const spy = jest.spyOn(joinPush, "send")

      socket.onConnError({})

      jest.advanceTimersByTime(1000)
      expect(spy).toHaveBeenCalledTimes(0)

      jest.advanceTimersByTime(2000)
      expect(spy).toHaveBeenCalledTimes(0)

      expect(channel.state).toBe("closed")
    })

    it("triggers additional callbacks after join", function (){
      const spy = jest.fn()
      channel.onError(spy)
      joinPush.trigger("ok", {})

      expect(channel.state).toBe("joined")
      expect(spy).toHaveBeenCalledTimes(0)

      channel.trigger("phx_error")

      expect(spy).toHaveBeenCalledTimes(1)
    })
  })

  describe("onClose", function (){
    let joinPush

    beforeEach(function (){
      jest.useFakeTimers()

      socket = new Socket("/socket", {timeout: defaultTimeout})
      jest.spyOn(socket, "isConnected").mockReturnValue(true)
      jest.spyOn(socket, "push").mockReturnValue(true)

      channel = socket.channel("topic", {one: "two"})

      joinPush = channel.joinPush

      channel.join()
    })

    afterEach(function (){
      jest.useRealTimers()
    })

    it("sets state to 'closed'", function (){
      expect(channel.state).not.toBe("closed")

      channel.trigger("phx_close")

      expect(channel.state).toBe("closed")
    })

    it("does not rejoin", function (){
      const spy = jest.spyOn(joinPush, "send")

      channel.trigger("phx_close")

      jest.advanceTimersByTime(1000)
      expect(spy).toHaveBeenCalledTimes(0)

      jest.advanceTimersByTime(2000)
      expect(spy).toHaveBeenCalledTimes(0)
    })

    it("triggers additional callbacks", function (){
      const spy = jest.fn()
      channel.onClose(spy)

      expect(spy).toHaveBeenCalledTimes(0)

      channel.trigger("phx_close")

      expect(spy).toHaveBeenCalledTimes(1)
    })

    it("removes channel from socket", function (){
      expect(socket.channels.length).toBe(1)
      expect(socket.channels[0]).toBe(channel)

      channel.trigger("phx_close")

      expect(socket.channels.length).toBe(0)
    })
  })

  describe("onMessage", function (){
    it("returns payload by default", function (){
      socket = new Socket("/socket")
      channel = socket.channel("topic", {one: "two"})
      jest.spyOn(socket, "makeRef").mockReturnValue(defaultRef)
      const payload = channel.onMessage("event", {one: "two"}, defaultRef)

      expect(payload).toEqual({one: "two"})
    })
  })

  describe("canPush", function (){
    beforeEach(function (){
      socket = new Socket("/socket")

      channel = socket.channel("topic", {one: "two"})
    })

    it("returns true when socket connected and channel joined", function (){
      jest.spyOn(socket, "isConnected").mockReturnValue(true)
      channel.state = "joined"

      expect(channel.canPush()).toBe(true)
    })

    it("otherwise returns false", function (){
      const isConnectedStub = jest.spyOn(socket, "isConnected")

      isConnectedStub.mockReturnValue(false)
      channel.state = "joined"

      expect(channel.canPush()).toBe(false)

      isConnectedStub.mockReturnValue(true)
      channel.state = "joining"

      expect(channel.canPush()).toBe(false)

      isConnectedStub.mockReturnValue(false)
      channel.state = "joining"

      expect(channel.canPush()).toBe(false)
    })
  })

  describe("on", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
      jest.spyOn(socket, "makeRef").mockReturnValue(defaultRef)

      channel = socket.channel("topic", {one: "two"})
    })

    it("sets up callback for event", function (){
      const spy = jest.fn()

      channel.trigger("event", {}, defaultRef)
      expect(spy).not.toHaveBeenCalled()

      channel.on("event", spy)

      channel.trigger("event", {}, defaultRef)

      expect(spy).toHaveBeenCalled()
    })

    it("other event callbacks are ignored", function (){
      const spy = jest.fn()
      const ignoredSpy = jest.fn()

      channel.trigger("event", {}, defaultRef)

      expect(ignoredSpy).not.toHaveBeenCalled()

      channel.on("event", spy)

      channel.trigger("event", {}, defaultRef)

      expect(ignoredSpy).not.toHaveBeenCalled()
    })

    it("generates unique refs for callbacks", function (){
      const ref1 = channel.on("event1", () => 0)
      const ref2 = channel.on("event2", () => 0)
      expect(ref1 + 1).toBe(ref2)
    })

    it("calls all callbacks for event if they modified during event processing", function (){
      const spy = jest.fn()

      const ref = channel.on("event", () => {
        channel.off("event", ref)
      })
      channel.on("event", spy)

      channel.trigger("event", {}, defaultRef)

      expect(spy).toHaveBeenCalled()
    })
  })

  describe("off", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
      jest.spyOn(socket, "makeRef").mockReturnValue(defaultRef)

      channel = socket.channel("topic", {one: "two"})
    })

    it("removes all callbacks for event", function (){
      const spy1 = jest.fn()
      const spy2 = jest.fn()
      const spy3 = jest.fn()

      channel.on("event", spy1)
      channel.on("event", spy2)
      channel.on("other", spy3)

      channel.off("event")

      channel.trigger("event", {}, defaultRef)
      channel.trigger("other", {}, defaultRef)

      expect(spy1).not.toHaveBeenCalled()
      expect(spy2).not.toHaveBeenCalled()
      expect(spy3).toHaveBeenCalled()
    })

    it("removes callback by its ref", function (){
      const spy1 = jest.fn()
      const spy2 = jest.fn()

      const ref1 = channel.on("event", spy1)
      const _ref2 = channel.on("event", spy2)

      channel.off("event", ref1)
      channel.trigger("event", {}, defaultRef)

      expect(spy1).not.toHaveBeenCalled()
      expect(spy2).toHaveBeenCalled()
    })
  })

  describe("push", function (){
    let joinPush
    let socketSpy

    const pushParams = (channel) => {
      return {
        topic: "topic",
        event: "event",
        payload: {foo: "bar"},
        join_ref: channel.joinRef(),
        ref: defaultRef,
      }
    }

    beforeEach(function (){
      jest.useFakeTimers()

      socket = new Socket("/socket", {timeout: defaultTimeout})
      jest.spyOn(socket, "makeRef").mockReturnValue(defaultRef)
      jest.spyOn(socket, "isConnected").mockReturnValue(true)
      socketSpy = jest.spyOn(socket, "push").mockReturnValue(undefined)

      channel = socket.channel("topic", {one: "two"})
    })

    afterEach(function (){
      jest.useRealTimers()
    })

    it("sends push event when successfully joined", function (){
      channel.join().trigger("ok", {})
      channel.push("event", {foo: "bar"})

      expect(socketSpy).toHaveBeenCalledWith(pushParams(channel))
    })

    it("enqueues push event to be sent once join has succeeded", function (){
      joinPush = channel.join()
      channel.push("event", {foo: "bar"})

      expect(socketSpy).not.toHaveBeenCalledWith(pushParams(channel))

      jest.advanceTimersByTime(channel.timeout / 2)
      joinPush.trigger("ok", {})

      expect(socketSpy).toHaveBeenCalledWith(pushParams(channel))
    })

    it("does not push if channel join times out", function (){
      joinPush = channel.join()
      channel.push("event", {foo: "bar"})

      expect(socketSpy).not.toHaveBeenCalledWith(pushParams(channel))

      jest.advanceTimersByTime(channel.timeout * 2)
      joinPush.trigger("ok", {})

      expect(socketSpy).not.toHaveBeenCalledWith(pushParams(channel))
    })

    it("uses channel timeout by default", function (){
      const timeoutSpy = jest.fn()
      channel.join().trigger("ok", {})

      channel.push("event", {foo: "bar"}).receive("timeout", timeoutSpy)

      jest.advanceTimersByTime(channel.timeout / 2)
      expect(timeoutSpy).not.toHaveBeenCalled()

      jest.advanceTimersByTime(channel.timeout)
      expect(timeoutSpy).toHaveBeenCalled()
    })

    it("accepts timeout arg", function (){
      const timeoutSpy = jest.fn()
      channel.join().trigger("ok", {})

      channel.push("event", {foo: "bar"}, channel.timeout * 2).receive("timeout", timeoutSpy)

      jest.advanceTimersByTime(channel.timeout)
      expect(timeoutSpy).not.toHaveBeenCalled()

      jest.advanceTimersByTime(channel.timeout * 2)
      expect(timeoutSpy).toHaveBeenCalled()
    })

    it("does not time out after receiving 'ok'", function (){
      channel.join().trigger("ok", {})
      const timeoutSpy = jest.fn()
      const push = channel.push("event", {foo: "bar"})
      push.receive("timeout", timeoutSpy)

      jest.advanceTimersByTime(push.timeout / 2)
      expect(timeoutSpy).not.toHaveBeenCalled()

      push.trigger("ok", {})

      jest.advanceTimersByTime(push.timeout)
      expect(timeoutSpy).not.toHaveBeenCalled()
    })

    it("throws if channel has not been joined", function (){
      expect(() => channel.push("event", {})).toThrow(/^tried to push.*before joining/)
    })
  })

  describe("leave", function (){
    let socketSpy

    beforeEach(function (){
      jest.useFakeTimers()

      socket = new Socket("/socket", {timeout: defaultTimeout})
      jest.spyOn(socket, "isConnected").mockReturnValue(true)
      socketSpy = jest.spyOn(socket, "push").mockReturnValue(undefined)

      channel = socket.channel("topic", {one: "two"})
      channel.join().trigger("ok", {})
    })

    afterEach(function (){
      jest.useRealTimers()
    })

    it("unsubscribes from server events", function (){
      jest.spyOn(socket, "makeRef").mockReturnValue(defaultRef)
      const joinRef = channel.joinRef()

      channel.leave()

      expect(socketSpy).toHaveBeenCalledWith({
        topic: "topic",
        event: "phx_leave",
        payload: {},
        ref: defaultRef,
        join_ref: joinRef,
      })
    })

    it("closes channel on 'ok' from server", function (){
      const anotherChannel = socket.channel("another", {three: "four"})
      expect(socket.channels.length).toBe(2)

      channel.leave().trigger("ok", {})

      expect(socket.channels.length).toBe(1)
      expect(socket.channels[0]).toBe(anotherChannel)
    })

    it("sets state to closed on 'ok' event", function (){
      expect(channel.state).not.toBe("closed")

      channel.leave().trigger("ok", {})

      expect(channel.state).toBe("closed")
    })

    // TODO - the following tests are skipped until Channel.leave
    // behavior can be fixed; currently, 'ok' is triggered immediately
    // within Channel.leave so timeout callbacks are never reached
    //
    it.skip("sets state to leaving initially", function (){
      expect(channel.state).not.toBe("leaving")

      channel.leave()

      expect(channel.state).toBe("leaving")
    })

    it.skip("closes channel on 'timeout'", function (){
      channel.leave()

      jest.advanceTimersByTime(channel.timeout)

      expect(channel.state).toBe("closed")
    })

    it.skip("accepts timeout arg", function (){
      channel.leave(channel.timeout * 2)

      jest.advanceTimersByTime(channel.timeout)

      expect(channel.state).toBe("leaving")

      jest.advanceTimersByTime(channel.timeout * 2)

      expect(channel.state).toBe("closed")
    })
  })
})


================================================
FILE: assets/test/longpoll_test.js
================================================
import {jest} from "@jest/globals"
import {LongPoll} from "../js/phoenix"
import {Socket} from "../js/phoenix"
import {AUTH_TOKEN_PREFIX} from "../js/phoenix/constants"
import Ajax from "../js/phoenix/ajax"

describe("LongPoll", () => {
  let originalXHR

  beforeEach(() => {
    originalXHR = global.XMLHttpRequest
    
    // Mock XMLHttpRequest
    const mockOpen = jest.fn()
    const mockSend = jest.fn()
    const mockAbort = jest.fn()
    const mockSetRequestHeader = jest.fn()
    
    global.XMLHttpRequest = jest.fn(() => ({
      open: mockOpen,
      send: mockSend,
      abort: mockAbort,
      setRequestHeader: mockSetRequestHeader,
      readyState: 4,
      status: 200,
      responseText: JSON.stringify({status: 200, token: "token123", messages: []}),
      onreadystatechange: null,
    }))

    // Spy on Ajax.request
    jest.spyOn(Ajax, "request").mockImplementation(() => {
      return {abort: jest.fn()}
    })
  })

  afterEach(() => {
    global.XMLHttpRequest = originalXHR
    jest.restoreAllMocks()
  })

  describe("constructor", () => {
    it("should handle undefined protocols", () => {
      const longpoll = new LongPoll("http://localhost/socket/longpoll", undefined)
      
      // Verify longpoll was initialized correctly without error
      expect(longpoll.pollEndpoint).toBe("http://localhost/socket/longpoll")
      expect(longpoll.authToken).toBeUndefined()
      expect(longpoll.readyState).toBe(0) // connecting
    })

    it("should handle null protocols", () => {
      const longpoll = new LongPoll("http://localhost/socket/longpoll", null)
      
      // Verify longpoll was initialized correctly without error
      expect(longpoll.pollEndpoint).toBe("http://localhost/socket/longpoll")
      expect(longpoll.authToken).toBeUndefined()
      expect(longpoll.readyState).toBe(0) // connecting
    })

    it("should handle empty array protocols", () => {
      const longpoll = new LongPoll("http://localhost/socket/longpoll", [])
      
      // Verify longpoll was initialized correctly without error
      expect(longpoll.pollEndpoint).toBe("http://localhost/socket/longpoll")
      expect(longpoll.authToken).toBeUndefined()
      expect(longpoll.readyState).toBe(0) // connecting
    })

    it("should extract authToken when valid protocols are provided", () => {
      const authToken = "my-auth-token"
      const encodedToken = btoa(authToken)
      const protocols = ["phoenix", `${AUTH_TOKEN_PREFIX}${encodedToken}`]
      
      const longpoll = new LongPoll("http://localhost/socket/longpoll", protocols)
      
      // Verify auth token was extracted correctly
      expect(longpoll.authToken).toBe(authToken)
    })
  })

  describe("poll", () => {
    it("should include auth token in headers when present", () => {
      const authToken = "my-auth-token"
      const encodedToken = btoa(authToken)
      const protocols = ["phoenix", `${AUTH_TOKEN_PREFIX}${encodedToken}`]

      const longpoll = new LongPoll("http://localhost/socket/longpoll", protocols)
      longpoll.timeout = 1000
      longpoll.poll()

      // Verify Ajax.request was called with the correct headers
      expect(Ajax.request).toHaveBeenCalledWith(
        "GET",
        expect.any(String),
        {"Accept": "application/json", "X-Phoenix-AuthToken": authToken},
        null,
        expect.any(Number),
        expect.any(Function),
        expect.any(Function)
      )
    })

    it("should not include auth token in headers when not present", () => {
      const longpoll = new LongPoll("http://localhost/socket/longpoll", undefined)
      longpoll.timeout = 1000
      longpoll.poll()

      // Verify Ajax.request was called without auth token header
      expect(Ajax.request).toHaveBeenCalledWith(
        "GET",
        expect.any(String),
        {"Accept": "application/json"},
        null,
        expect.any(Number),
        expect.any(Function),
        expect.any(Function)
      )
    })

    it("should treat 410 as error when token already exists", () => {
      const longpoll = new LongPoll("http://localhost/socket/longpoll", undefined)
      longpoll.timeout = 1000
      longpoll.token = "existing-token"

      const mockOnerror = jest.fn()
      const mockCloseAndRetry = jest.fn()
      longpoll.onerror = mockOnerror
      longpoll.closeAndRetry = mockCloseAndRetry

      Ajax.request.mockImplementation((method, url, headers, body, timeout, ontimeout, callback) => {
        callback({status: 410, token: "new-token", messages: []})
        return {abort: jest.fn()}
      })

      longpoll.poll()

      expect(mockOnerror).toHaveBeenCalledWith(410)
      expect(mockCloseAndRetry).toHaveBeenCalledWith(3410, "session_gone", false)
    })
  })

  describe("batchSend", () => {
    it("should send with correct content-type header format", () => {
      const longpoll = new LongPoll("http://localhost/socket/longpoll", undefined)
      longpoll.timeout = 1000
      const messages = ["message1", "message2"]
      
      longpoll.batchSend(messages)
      
      // Verify Ajax.request was called with correct headers format
      expect(Ajax.request).toHaveBeenCalledWith(
        "POST",
        expect.any(String),
        {"Content-Type": "application/x-ndjson"},
        "message1\nmessage2",
        expect.any(Number),
        expect.any(Function),
        expect.any(Function)
      )
    })
  })
})

describe("Socket with LongPoll", () => {
  describe("transportConnect", () => {
    it("should initialize with undefined protocols when no auth token", () => {
      const socket = new Socket("/socket", {transport: LongPoll})
      
      // Mock the transport to capture the protocols argument
      socket.transport = jest.fn(() => ({
        onopen: jest.fn(),
        onerror: jest.fn(),
        onmessage: jest.fn(),
        onclose: jest.fn()
      }))
      
      socket.transportConnect()
      
      // Verify that the transport was called with undefined protocols
      expect(socket.transport).toHaveBeenCalledWith(
        expect.any(String),
        undefined
      )
    })
    
    it("should only set protocols array when auth token is present", () => {
      const authToken = "my-auth-token"
      const socket = new Socket("/socket", {
        transport: LongPoll,
        params: {token: authToken}
      })
      
      // Set auth token
      socket.authToken = authToken
      
      // Mock the transport to capture the protocols argument
      socket.transport = jest.fn(() => ({
        onopen: jest.fn(),
        onerror: jest.fn(),
        onmessage: jest.fn(),
        onclose: jest.fn()
      }))
      
      socket.transportConnect()
      
      // Verify that the transport was called with correct protocols array
      expect(socket.transport).toHaveBeenCalledWith(
        expect.any(String),
        ["phoenix", `${AUTH_TOKEN_PREFIX}${btoa(authToken).replace(/=/g, "")}`]
      )
    })
  })
})

describe("Ajax.request", () => {
  let originalXMLHttpRequest, originalFetch, originalAbortController

  beforeEach(() => {
    originalXMLHttpRequest = global.XMLHttpRequest
    originalFetch = global.fetch
    originalAbortController = global.AbortController

    // Mock AbortController
    global.AbortController = jest.fn(() => ({
      abort: jest.fn(),
      signal: {}
    }))

    // Mock XMLHttpRequest
    global.XMLHttpRequest = jest.fn(() => ({
      open: jest.fn(),
      send: jest.fn(),
      setRequestHeader: jest.fn(),
      onreadystatechange: null,
      readyState: 4,
      status: 200,
      responseText: JSON.stringify({success: true})
    }))

    // Mock fetch
    global.fetch = jest.fn(() =>
      Promise.resolve({
        text: () => Promise.resolve(JSON.stringify({success: true}))
      })
    )
  })

  afterEach(() => {
    global.XMLHttpRequest = originalXMLHttpRequest
    global.fetch = originalFetch
    global.AbortController = originalAbortController
    jest.restoreAllMocks()
  })

  it("should use XMLHttpRequest by default", () => {
    Ajax.request("GET", "/test-endpoint", {}, null, 0, null, (response) => {
      expect(response).toEqual({success: true})
    })

    expect(global.XMLHttpRequest).toHaveBeenCalled()
  })

  it("should use fetch when XMLHttpRequest is not available", () => {
    global.XMLHttpRequest = undefined // Simulate it being unavailable
    Ajax.request("GET", "/test-endpoint", {}, null, 0, null, (response) => {
      expect(response).toEqual({success: true})
    })

    expect(global.fetch).toHaveBeenCalledWith(
      "/test-endpoint",
      expect.objectContaining({
        method: "GET",
      })
    )
  })
})



================================================
FILE: assets/test/presence_test.js
================================================
import {Presence} from "../js/phoenix"

const clone = (obj) => {
  let cloned = JSON.parse(JSON.stringify(obj))
  Object.entries(obj).forEach(([key, val]) => {
    if(val === undefined){
      cloned[key] = undefined
    }
  })
  return cloned
}

const fixtures = {
  joins(){
    return {u1: {metas: [{id: 1, phx_ref: "1.2"}]}}
  },
  leaves(){
    return {u2: {metas: [{id: 2, phx_ref: "2"}]}}
  },
  state(){
    return {
      u1: {metas: [{id: 1, phx_ref: "1"}]},
      u2: {metas: [{id: 2, phx_ref: "2"}]},
      u3: {metas: [{id: 3, phx_ref: "3"}]},
    }
  },
}

const channelStub = {
  ref: 1,
  events: {},

  on(event, callback){
    this.events[event] = callback
  },

  trigger(event, data){
    this.events[event](data)
  },

  joinRef(){
    return `${this.ref}`
  },

  simulateDisconnectAndReconnect(){
    this.ref++
  },
}

const listByFirst = (id, {metas: [first, ..._rest]}) => first

describe("syncState", () => {
  it("syncs empty state", () => {
    let newState = {u1: {metas: [{id: 1, phx_ref: "1"}]}}
    let state = {}
    let stateBefore = clone(state)
    Presence.syncState(state, newState)
    expect(state).toEqual(stateBefore)

    state = Presence.syncState(state, newState)
    expect(state).toEqual(newState)
  })

  it("onJoins new presences and onLeave's left presences", () => {
    let newState = fixtures.state()
    let state = {u4: {metas: [{id: 4, phx_ref: "4"}]}}
    let joined = {}
    let left = {}
    const onJoin = (key, current, newPres) => {
      joined[key] = {current, newPres}
    }
    const onLeave = (key, current, leftPres) => {
      left[key] = {current, leftPres}
    }

    state = Presence.syncState(state, newState, onJoin, onLeave)
    expect(state).toEqual(newState)
    expect(joined).toEqual({
      u1: {current: undefined, newPres: {metas: [{id: 1, phx_ref: "1"}]}},
      u2: {current: undefined, newPres: {metas: [{id: 2, phx_ref: "2"}]}},
      u3: {current: undefined, newPres: {metas: [{id: 3, phx_ref: "3"}]}},
    })
    expect(left).toEqual({
      u4: {current: {metas: []}, leftPres: {metas: [{id: 4, phx_ref: "4"}]}},
    })
  })

  it("onJoins only newly added metas", () => {
    let newState = {u3: {metas: [{id: 3, phx_ref: "3"}, {id: 3, phx_ref: "3.new"}]}}
    let state = {u3: {metas: [{id: 3, phx_ref: "3"}]}}
    let joined = []
    let left = []
    const onJoin = (key, current, newPres) => {
      joined.push([key, clone({current, newPres})])
    }
    const onLeave = (key, current, leftPres) => {
      left.push([key, clone({current, leftPres})])
    }
    state = Presence.syncState(state, clone(newState), onJoin, onLeave)
    expect(state).toEqual(newState)
    expect(joined).toEqual([
      ["u3", {current: {metas: [{id: 3, phx_ref: "3"}]}, newPres: {metas: [{id: 3, phx_ref: "3.new"}]}}],
    ])
    expect(left).toEqual([])
  })
})

describe("syncDiff", () => {
  it("syncs empty state", () => {
    let joins = {u1: {metas: [{id: 1, phx_ref: "1"}]}}
    let state = Presence.syncDiff({}, {joins, leaves: {}})
    expect(state).toEqual(joins)
  })

  it("removes presence when meta is empty and adds additional meta", () => {
    let state = fixtures.state()
    state = Presence.syncDiff(state, {joins: fixtures.joins(), leaves: fixtures.leaves()})

    expect(state).toEqual({
      u1: {metas: [{id: 1, phx_ref: "1"}, {id: 1, phx_ref: "1.2"}]},
      u3: {metas: [{id: 3, phx_ref: "3"}]},
    })
  })

  it("removes meta while leaving key if other metas exist", () => {
    let state = {u1: {metas: [{id: 1, phx_ref: "1"}, {id: 1, phx_ref: "1.2"}]}}
    state = Presence.syncDiff(state, {joins: {}, leaves: {u1: {metas: [{id: 1, phx_ref: "1"}]}}})

    expect(state).toEqual({
      u1: {metas: [{id: 1, phx_ref: "1.2"}]},
    })
  })
})

describe("list", () => {
  it("lists full presence by default", () => {
    let state = fixtures.state()
    expect(Presence.list(state)).toEqual([
      {metas: [{id: 1, phx_ref: "1"}]},
      {metas: [{id: 2, phx_ref: "2"}]},
      {metas: [{id: 3, phx_ref: "3"}]},
    ])
  })

  it("lists with custom function", () => {
    let state = {u1: {metas: [{id: 1, phx_ref: "1.first"}, {id: 1, phx_ref: "1.second"}]}}

    const listBy = (key, {metas: [first, ..._rest]}) => first

    expect(Presence.list(state, listBy)).toEqual([{id: 1, phx_ref: "1.first"}])
  })
})

describe("instance", () => {
  it("syncs state and diffs", () => {
    let presence = new Presence(channelStub)
    let user1 = {metas: [{id: 1, phx_ref: "1"}]}
    let user2 = {metas: [{id: 2, phx_ref: "2"}]}
    let newState = {u1: user1, u2: user2}

    channelStub.trigger("presence_state", newState)
    expect(presence.list(listByFirst)).toEqual([{id: 1, phx_ref: "1"}, {id: 2, phx_ref: "2"}])

    channelStub.trigger("presence_diff", {joins: {}, leaves: {u1: user1}})
    expect(presence.list(listByFirst)).toEqual([{id: 2, phx_ref: "2"}])
  })

  it("applies pending diff if state is not yet synced", () => {
    let presence = new Presence(channelStub)
    let onJoins = []
    let onLeaves = []

    presence.onJoin((id, current, newPres) => {
      onJoins.push(clone({id, current, newPres}))
    })
    presence.onLeave((id, current, leftPres) => {
      onLeaves.push(clone({id, current, leftPres}))
    })

    let user1 = {metas: [{id: 1, phx_ref: "1"}]}
    let user2 = {metas: [{id: 2, phx_ref: "2"}]}
    let user3 = {metas: [{id: 3, phx_ref: "3"}]}
    let newState = {u1: user1, u2: user2}
    let leaves = {u2: user2}

    channelStub.trigger("presence_diff", {joins: {}, leaves: leaves})

    expect(presence.list(listByFirst)).toEqual([])
    expect(presence.pendingDiffs).toEqual([{joins: {}, leaves: leaves}])

    channelStub.trigger("presence_state", newState)
    expect(onLeaves).toEqual([{id: "u2", current: {metas: []}, leftPres: {metas: [{id: 2, phx_ref: "2"}]}}])

    expect(presence.list(listByFirst)).toEqual([{id: 1, phx_ref: "1"}])
    expect(presence.pendingDiffs).toEqual([])
    expect(onJoins).toEqual([
      {id: "u1", current: undefined, newPres: {metas: [{id: 1, phx_ref: "1"}]}},
      {id: "u2", current: undefined, newPres: {metas: [{id: 2, phx_ref: "2"}]}},
    ])

    channelStub.simulateDisconnectAndReconnect()
    expect(presence.inPendingSyncState()).toBe(true)

    channelStub.trigger("presence_diff", {joins: {}, leaves: {u1: user1}})
    expect(presence.list(listByFirst)).toEqual([{id: 1, phx_ref: "1"}])

    channelStub.trigger("presence_state", {u1: user1, u3: user3})
    expect(presence.list(listByFirst)).toEqual([{id: 3, phx_ref: "3"}])
  })

  it("allows custom channel events", () => {
    let presence = new Presence(channelStub, {
      events: {
        state: "the_state",
        diff: "the_diff",
      },
    })

    let user1 = {metas: [{id: 1, phx_ref: "1"}]}
    channelStub.trigger("the_state", {user1})
    expect(presence.list(listByFirst)).toEqual([{id: 1, phx_ref: "1"}])
    channelStub.trigger("the_diff", {joins: {}, leaves: {user1}})
    expect(presence.list(listByFirst)).toEqual([])
  })

  it("updates existing meta for a presence update (leave + join)", () => {
    let presence = new Presence(channelStub)
    let onJoins = []
    let onLeaves = []

    let user1 = {metas: [{id: 1, phx_ref: "1"}]}
    let user2 = {metas: [{id: 2, name: "chris", phx_ref: "2"}]}
    let newState = {u1: user1, u2: user2}

    channelStub.trigger("presence_state", clone(newState))

    presence.onJoin((id, current, newPres) => {
      onJoins.push(clone({id, current, newPres}))
    })
    presence.onLeave((id, current, leftPres) => {
      onLeaves.push(clone({id, current, leftPres}))
    })

    expect(presence.list((id, {metas: metas}) => metas)).toEqual([
      [{id: 1, phx_ref: "1"}],
      [{id: 2, name: "chris", phx_ref: "2"}],
    ])

    let leaves = {u2: user2}
    let joins = {u2: {metas: [{id: 2, name: "chris.2", phx_ref: "2.2", phx_ref_prev: "2"}]}}
    channelStub.trigger("presence_diff", {joins, leaves})

    expect(presence.list((id, {metas: metas}) => metas)).toEqual([
      [{id: 1, phx_ref: "1"}],
      [{id: 2, name: "chris.2", phx_ref: "2.2", phx_ref_prev: "2"}],
    ])

    expect(onJoins).toEqual([
      {
        id: "u2",
        current: {metas: [{id: 2, name: "chris", phx_ref: "2"}]},
        newPres: {metas: [{id: 2, name: "chris.2", phx_ref: "2.2", phx_ref_prev: "2"}]},
      },
    ])
  })
})


================================================
FILE: assets/test/serializer.js
================================================

export const encode = (msg) => {
  let payload = [
    msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload
  ]
  return JSON.stringify(payload)
}

export const decode = (rawPayload) => {
  let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)

  return {join_ref, ref, topic, event, payload}
}


================================================
FILE: assets/test/serializer_test.js
================================================
/**
 * @jest-environment node
 */

import {TextEncoder, TextDecoder} from "util"
import {Serializer} from "../js/phoenix"

let exampleMsg = {join_ref: "0", ref: "1", topic: "t", event: "e", payload: {foo: 1}}

let binPayload = () => {
  let buffer = new ArrayBuffer(1)
  new DataView(buffer).setUint8(0, 1)
  return buffer
}

describe("JSON", () => {
  it("encodes general pushes", (done) => {
    Serializer.encode(exampleMsg, (result) => {
      expect(result).toBe("[\"0\",\"1\",\"t\",\"e\",{\"foo\":1}]")
      done()
    })
  })

  it("decodes", (done) => {
    Serializer.decode("[\"0\",\"1\",\"t\",\"e\",{\"foo\":1}]", (result) => {
      expect(result).toEqual(exampleMsg)
      done()
    })
  })
})

describe("binary", () => {
  it("encodes", (done) => {
    let buffer = binPayload()
    let bin = "\0\x01\x01\x01\x0101te\x01"
    let decoder = new TextDecoder()
    Serializer.encode({join_ref: "0", ref: "1", topic: "t", event: "e", payload: buffer}, (result) => {
      expect(decoder.decode(result)).toBe(bin)
      done()
    })
  })

  it("encodes variable length segments", (done) => {
    let buffer = binPayload()
    let bin = "\0\x02\x01\x03\x02101topev\x01"
    let decoder = new TextDecoder()
    Serializer.encode({join_ref: "10", ref: "1", topic: "top", event: "ev", payload: buffer}, (result) => {
      expect(decoder.decode(result)).toBe(bin)
      done()
    })
  })

  it("decodes push", (done) => {
    let bin = "\0\x03\x03\n123topsome-event\x01\x01"
    let buffer = new TextEncoder().encode(bin).buffer
    let decoder = new TextDecoder()
    Serializer.decode(buffer, (result) => {
      expect(result.join_ref).toBe("123")
      expect(result.ref).toBeNull()
      expect(result.topic).toBe("top")
      expect(result.event).toBe("some-event")
      expect(result.payload.constructor).toBe(ArrayBuffer)
      expect(decoder.decode(result.payload)).toBe("\x01\x01")
      done()
    })
  })

  it("decodes reply", (done) => {
    let bin = "\x01\x03\x02\x03\x0210012topok\x01\x01"
    let buffer = new TextEncoder().encode(bin).buffer
    let decoder = new TextDecoder()
    Serializer.decode(buffer, (result) => {
      expect(result.join_ref).toBe("100")
      expect(result.ref).toBe("12")
      expect(result.topic).toBe("top")
      expect(result.event).toBe("phx_reply")
      expect(result.payload.status).toBe("ok")
      expect(result.payload.response.constructor).toBe(ArrayBuffer)
      expect(decoder.decode(result.payload.response)).toBe("\x01\x01")
      done()
    })
  })

  it("decodes broadcast", (done) => {
    let bin = "\x02\x03\ntopsome-event\x01\x01"
    let buffer = new TextEncoder().encode(bin).buffer
    let decoder = new TextDecoder()
    Serializer.decode(buffer, (result) => {
      expect(result.join_ref).toBeNull()
      expect(result.ref).toBeNull()
      expect(result.topic).toBe("top")
      expect(result.event).toBe("some-event")
      expect(result.payload.constructor).toBe(ArrayBuffer)
      expect(decoder.decode(result.payload)).toBe("\x01\x01")
      done()
    })
  })
})


================================================
FILE: assets/test/socket_http_test.js
================================================
/**
 * @jest-environment jsdom
 * @jest-environment-options {"url": "http://example.com/"}
 */
import {Socket} from "../js/phoenix"

// sadly, jsdom can only be configured globally for a file

describe("protocol", function (){
  it("returns ws when location.protocol is http", function (){
    const socket = new Socket("/socket")
    expect(socket.protocol()).toBe("ws")
  })
})

describe("endpointURL", function (){
  it("returns endpoint for given path on http host", function (){
    const socket = new Socket("/socket")
    expect(socket.endPointURL()).toBe(
      "ws://example.com/socket/websocket?vsn=2.0.0",
    )
  })
})


================================================
FILE: assets/test/socket_test.js
================================================
import {jest} from "@jest/globals"
import {WebSocket, Server as WebSocketServer} from "mock-socket"
import {encode} from "./serializer"
import {Socket, LongPoll} from "../js/phoenix"
import {SOCKET_STATES} from "../js/phoenix/constants"

let socket

describe("with transports", function (){
  beforeAll(() => {
    window.WebSocket = WebSocket
    const mockOpen = jest.fn()
    const mockSend = jest.fn()
    const mockAbort = jest.fn()
    const mockSetRequestHeader = jest.fn()
    
    global.XMLHttpRequest = jest.fn(() => ({
      open: mockOpen,
      send: mockSend,
      abort: mockAbort,
      setRequestHeader: mockSetRequestHeader,
      readyState: 4,
      status: 200,
      responseText: JSON.stringify({}),
      onreadystatechange: null,
    }))
  })

  describe("constructor", function (){
    it("sets defaults", function (){
      socket = new Socket("/socket")

      expect(socket.channels.length).toBe(0)
      expect(socket.sendBuffer.length).toBe(0)
      expect(socket.ref).toBe(0)
      expect(socket.endPoint).toBe("/socket/websocket")
      expect(socket.stateChangeCallbacks).toEqual({open: [], close: [], error: [], message: []})
      expect(socket.transport).toBe(WebSocket)
      expect(socket.timeout).toBe(10000)
      expect(socket.longpollerTimeout).toBe(20000)
      expect(socket.heartbeatIntervalMs).toBe(30000)
      expect(socket.logger).toBeNull()
      expect(socket.binaryType).toBe("arraybuffer")
      expect(typeof socket.reconnectAfterMs).toBe("function")
    })

    it("supports closure or literal params", function (){
      socket = new Socket("/socket", {params: {one: "two"}})
      expect(socket.params()).toEqual({one: "two"})

      socket = new Socket("/socket", {params: function (){ return ({three: "four"}) }})
      expect(socket.params()).toEqual({three: "four"})
    })

    it("overrides some defaults with options", function (){
      const customTransport = function transport(){ }
      const customLogger = function logger(){ }
      const customReconnect = function reconnect(){ }

      socket = new Socket("/socket", {
        timeout: 40000,
        longpollerTimeout: 50000,
        heartbeatIntervalMs: 60000,
        transport: customTransport,
        logger: customLogger,
        reconnectAfterMs: customReconnect,
        params: {one: "two"},
      })

      expect(socket.timeout).toBe(40000)
      expect(socket.longpollerTimeout).toBe(50000)
      expect(socket.heartbeatIntervalMs).toBe(60000)
      expect(socket.transport).toBe(customTransport)
      expect(socket.logger).toBe(customLogger)
      expect(socket.params()).toEqual({one: "two"})
    })

    describe("with Websocket", function (){
      it("defaults to Websocket transport if available", function (done){
        let mockServer = new WebSocketServer("wss://example.com/")
        socket = new Socket("/socket")
        expect(socket.transport).toBe(WebSocket)
        mockServer.stop(() => done())
      })
    })

    describe("longPollFallbackMs", function (){
      it("falls back to longpoll when set after primary transport failure", function (done){
        let mockServer
        socket = new Socket("/socket", {longPollFallbackMs: 20})
        const replaceSpy = jest.spyOn(socket, "replaceTransport")
        mockServer = new WebSocketServer("wss://example.test/")
        mockServer.stop(() => {
          expect(socket.transport).toBe(WebSocket)
          socket.onError((_reason) => {
            setTimeout(() => {
              expect(replaceSpy).toHaveBeenCalledWith(LongPoll)
              done()
            }, 100)
          })
          socket.connect()
        })
      })
    })
  })

  describe("visibilitychange", function (){
    it("does not connect a socket that was never connected", function (){
      socket = new Socket("/socket")
      const teardownSpy = jest.spyOn(socket, "teardown")

      Object.defineProperty(document, "visibilityState", {value: "hidden", writable: true})
      window.dispatchEvent(new Event("visibilitychange"))

      Object.defineProperty(document, "visibilityState", {value: "visible", writable: true})
      window.dispatchEvent(new Event("visibilitychange"))

      expect(teardownSpy).not.toHaveBeenCalled()
    })

    it("reconnects on visibility change after unclean close", function (){
      socket = new Socket("/socket")
      socket.closeWasClean = false
      const teardownSpy = jest.spyOn(socket, "teardown")

      Object.defineProperty(document, "visibilityState", {value: "visible", writable: true})
      window.dispatchEvent(new Event("visibilitychange"))

      expect(teardownSpy).toHaveBeenCalledTimes(1)
    })

    it("does not reconnect on visibility change after clean close", function (){
      socket = new Socket("/socket")
      socket.closeWasClean = true
      const teardownSpy = jest.spyOn(socket, "teardown")

      Object.defineProperty(document, "visibilityState", {value: "visible", writable: true})
      window.dispatchEvent(new Event("visibilitychange"))

      expect(teardownSpy).not.toHaveBeenCalled()
    })
  })

  describe("protocol", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
    })

    it("returns wss when location.protocol is https", function (){
      expect(socket.protocol()).toBe("wss")
    })
  })

  describe("endpointURL", function (){
    it("returns endpoint for given full url", function (){
      socket = new Socket("wss://example.org/chat")
      expect(socket.endPointURL()).toBe("wss://example.org/chat/websocket?vsn=2.0.0")
    })

    it("returns endpoint for given protocol-relative url", function (){
      socket = new Socket("//example.org/chat")
      expect(socket.endPointURL()).toBe("wss://example.org/chat/websocket?vsn=2.0.0")
    })

    it("returns endpoint for given path on https host", function (){
      socket = new Socket("/socket")
      expect(socket.endPointURL()).toBe("wss://example.com/socket/websocket?vsn=2.0.0")
    })
  })

  describe("connect with WebSocket", function (){
    let mockServer

    beforeAll(function (){
      mockServer = new WebSocketServer("wss://example.com/")
    })

    afterAll(function (done){
      mockServer.stop(() => done())
    })

    beforeEach(function (){
      socket = new Socket("/socket")
    })

    it("establishes websocket connection with endpoint", function (){
      socket.connect()
      const conn = socket.conn
      expect(conn instanceof WebSocket).toBeTruthy()
      expect(conn.url).toBe(socket.endPointURL())
    })

    it("sets callbacks for connection", function (){
      let opens = 0
      socket.onOpen(() => ++opens)
      let closes = 0
      socket.onClose(() => ++closes)
      let lastError
      socket.onError((error) => lastError = error)
      let lastMessage
      socket.onMessage((message) => lastMessage = message.payload)

      socket.connect()

      socket.conn.onopen()
      expect(opens).toBe(1)

      socket.conn.onclose()
      expect(closes).toBe(1)

      socket.conn.onerror("error")
      expect(lastError).toBe("error")

      const data = {"topic": "topic", "event": "event", "payload": "payload", "status": "ok"}
      socket.conn.onmessage({data: encode(data)})
      expect(lastMessage).toBe("payload")
    })

    it("is idempotent", function (){
      socket.connect()
      const conn = socket.conn
      socket.connect()
      expect(conn).toBe(socket.conn)
    })
  })

  describe("connect with long poll", function (){
    beforeEach(function (){
      socket = new Socket("/socket", {transport: LongPoll})
    })

    it("establishes long poll connection with endpoint", function (){
      socket.connect()
      const conn = socket.conn
      expect(conn instanceof LongPoll).toBeTruthy()
      expect(conn.pollEndpoint).toBe("https://example.com/socket/longpoll?vsn=2.0.0")
      expect(conn.timeout).toBe(20000)
    })

    it("sets callbacks for connection", function (){
      let opens = 0
      socket.onOpen(() => ++opens)
      let closes = 0
      socket.onClose(() => ++closes)
      let lastError
      socket.onError((error) => lastError = error)
      let lastMessage
      socket.onMessage((message) => lastMessage = message.payload)

      socket.connect()

      socket.conn.onopen()
      expect(opens).toBe(1)

      socket.conn.onclose()
      expect(closes).toBe(1)

      socket.conn.onerror("error")
      expect(lastError).toBe("error")

      socket.connect()

      const data = {"topic": "topic", "event": "event", "payload": "payload", "status": "ok"}

      socket.conn.onmessage({data: encode(data)})
      expect(lastMessage).toBe("payload")
    })

    it("is idempotent", function (){
      socket.connect()
      const conn = socket.conn
      socket.connect()
      expect(conn).toBe(socket.conn)
    })
  })

  describe("disconnect", function (){
    let mockServer

    beforeAll(function (){
      mockServer = new WebSocketServer("wss://example.com/")
    })

    afterAll(function (done){
      mockServer.stop(() => done())
    })

    beforeEach(function (){
      socket = new Socket("/socket")
    })

    it("removes existing connection", function (done){
      socket.connect()
      socket.disconnect()
      socket.disconnect(() => {
        expect(socket.conn).toBeNull()
        done()
      })
    })

    it("calls callback", function (done){
      let count = 0
      socket.connect()
      socket.disconnect(() => {
        count++
        expect(count).toBe(1)
        done()
      })
    })

    it("calls connection close callback", function (done){
      socket.connect()
      const closeSpy = jest.spyOn(socket.conn, "close")

      socket.disconnect(() => {
        expect(closeSpy).toHaveBeenCalledWith(1000, "reason")
        done()
      }, 1000, "reason")
    })

    it("does not throw when no connection", function (){
      expect(() => {
        socket.disconnect()
      }).not.toThrow()
    })

    it("properly tears down old connection when immediately reconnecting", function (){
      const connections = []
      const mockWebSocket = function StubWebSocketNoAutoClose(_url){
        const conn = {
          readyState: SOCKET_STATES.open,
          get bufferedAmount(){ return 1 },
          binaryType: "arraybuffer",
          timeout: 20000,
          onopen: null,
          onerror: null,
          onmessage: null,
          onclose: null,
          close(_code, _reason){
            this.readyState = SOCKET_STATES.closing
            setTimeout(() => {
              this.readyState = SOCKET_STATES.closed
            }, 1000)
          },
          send(){},
        }
        connections.push(conn)
        return conn
      }

      jest.useFakeTimers()

      socket = new Socket("/socket", {
        heartbeatIntervalMs: 30000,
        heartbeatTimeoutMs: 30000,
        reconnectAfterMs: () => 10,
        transport: mockWebSocket
      })
      socket.connect()
      const originalConn = socket.conn

      // Disconnect triggers teardown, which waits for bufferedAmount to be zero or 2250ms,
      // then awaits SOCKET_STATES.closed before calling the callback.
      const disconnected = jest.fn()
      socket.disconnect(disconnected)

      // For now, the conn is still set.
      expect(socket.conn).toBeTruthy()

      // Advance time by > 2250ms, which means we are waiting for socket to transition to closed
      jest.advanceTimersByTime(3000)

      // Now we call connect, while the teardown is still running
      socket.connect()
      // By now, waitForSocketClosed should be done, but now there's a new conn!
      jest.advanceTimersByTime(3000)
      expect(socket.conn).not.toBe(originalConn)

      const openConns = connections.filter(c => c.readyState === SOCKET_STATES.open)
      expect(openConns.length).toBe(1)

      // Late teardown must not overwrite this.conn with null when it is already connB
      expect(socket.conn).not.toBeNull()

      // the original disconnected should have been called
      expect(disconnected).toHaveBeenCalled()

      jest.useRealTimers()
    })

    it("properly tears down old connection when disconnecting twice", function (){
      const connections = []
      const mockWebSocket = function StubWebSocketNoAutoClose(_url){
        const conn = {
          readyState: SOCKET_STATES.open,
          get bufferedAmount(){ return 1 },
          binaryType: "arraybuffer",
          timeout: 20000,
          onopen: null,
          onerror: null,
          onmessage: null,
          onclose: null,
          close(_code, _reason){
            this.readyState = SOCKET_STATES.closing
            setTimeout(() => {
              this.readyState = SOCKET_STATES.closed
            }, 1000)
          },
          send(){},
        }
        connections.push(conn)
        return conn
      }

      jest.useFakeTimers()

      socket = new Socket("/socket", {
        heartbeatIntervalMs: 30000,
        heartbeatTimeoutMs: 30000,
        reconnectAfterMs: () => 10,
        transport: mockWebSocket
      })
      socket.connect()

      const disconnected = jest.fn()
      socket.disconnect(disconnected)

      // For now, the conn is still set.
      expect(socket.conn).toBeTruthy()

      // Advance time by > 2250ms, which means we are waiting for socket to transition to closed
      jest.advanceTimersByTime(3000)

      // Now we call disconnect again, while the teardown is still running
      const disconnected2 = jest.fn()
      socket.disconnect(disconnected2)

      jest.advanceTimersByTime(10000)

      const openConns = connections.filter(c => c.readyState === SOCKET_STATES.open)
      expect(openConns.length).toBe(0)
      expect(socket.conn).toBeNull()

      // both disconnected functions should have been called
      expect(disconnected).toHaveBeenCalled()
      expect(disconnected2).toHaveBeenCalled()

      jest.useRealTimers()
    })
  })

  describe("connectionState", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
    })

    it("defaults to closed", function (){
      expect(socket.connectionState()).toBe("closed")
    })

    it("returns closed if readyState unrecognized", function (){
      socket.connect()
      socket.conn.readyState = 5678
      expect(socket.connectionState()).toBe("closed")
    })

    it("returns connecting", function (){
      socket.connect()
      socket.conn.readyState = 0
      expect(socket.connectionState()).toBe("connecting")
      expect(socket.isConnected()).toBe(false)
    })

    it("returns open", function (){
      socket.connect()
      socket.conn.readyState = 1
      expect(socket.connectionState()).toBe("open")
      expect(socket.isConnected()).toBe(true)
    })

    it("returns closing", function (){
      socket.connect()
      socket.conn.readyState = 2
      expect(socket.connectionState()).toBe("closing")
      expect(socket.isConnected()).toBe(false)
    })

    it("returns closed", function (){
      socket.connect()
      socket.conn.readyState = 3
      expect(socket.connectionState()).toBe("closed")
      expect(socket.isConnected()).toBe(false)
    })
  })

  describe("channel", function (){
    let channel

    beforeEach(function (){
      socket = new Socket("/socket")
    })

    it("returns channel with given topic and params", function (){
      channel = socket.channel("topic", {one: "two"})
      expect(channel.socket).toBe(socket)
      expect(channel.topic).toBe("topic")
      expect(channel.params()).toEqual({one: "two"})
    })

    it("adds channel to sockets channels list", function (){
      expect(socket.channels.length).toBe(0)
      channel = socket.channel("topic", {one: "two"})
      expect(socket.channels.length).toBe(1)
      const [foundChannel] = socket.channels
      expect(foundChannel).toBe(channel)
    })
  })

  describe("remove", function (){
    it("removes given channel from channels", function (){
      socket = new Socket("/socket")
      const channel1 = socket.channel("topic-1")
      const channel2 = socket.channel("topic-2")

      jest.spyOn(channel1, "joinRef").mockReturnValue(1)
      jest.spyOn(channel2, "joinRef").mockReturnValue(2)

      expect(socket.stateChangeCallbacks.open.length).toBe(2)

      socket.remove(channel1)

      expect(socket.stateChangeCallbacks.open.length).toBe(1)
      expect(socket.channels.length).toBe(1)

      const [foundChannel] = socket.channels
      expect(foundChannel).toBe(channel2)
    })
  })

  describe("push", function (){
    let data, json

    beforeEach(function (){
      data = {topic: "topic", event: "event", payload: "payload", ref: "ref"}
      json = encode(data)
      socket = new Socket("/socket")
    })

    it("sends data to connection when connected", function (){
      socket.connect()
      socket.conn.readyState = 1 // open

      const sendSpy = jest.spyOn(socket.conn, "send")

      socket.push(data)

      expect(sendSpy).toHaveBeenCalledWith(json)
    })

    it("buffers data when not connected", function (){
      socket.connect()
      socket.conn.readyState = 0 // connecting

      const sendSpy = jest.spyOn(socket.conn, "send").mockImplementation(() => {})

      expect(socket.sendBuffer.length).toBe(0)

      socket.push(data)

      expect(sendSpy).not.toHaveBeenCalledWith(json)
      expect(socket.sendBuffer.length).toBe(1)

      const [callback] = socket.sendBuffer
      callback()
      expect(sendSpy).toHaveBeenCalledWith(json)
    })
  })

  describe("makeRef", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
    })

    it("returns next message ref", function (){
      expect(socket.ref).toBe(0)
      expect(socket.makeRef()).toBe("1")
      expect(socket.ref).toBe(1)
      expect(socket.makeRef()).toBe("2")
      expect(socket.ref).toBe(2)
    })

    it("restarts for overflow", function (){
      socket.ref = Number.MAX_SAFE_INTEGER + 1
      expect(socket.makeRef()).toBe("0")
      expect(socket.ref).toBe(0)
    })
  })

  describe("sendHeartbeat", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
      socket.connect()
    })

    it("closes socket when heartbeat is not ack'd within heartbeat window", function (done){
      jest.useFakeTimers()
      let closed = false
      socket.conn.readyState = 1 // open
      socket.conn.close = () => closed = true
      socket.sendHeartbeat()
      expect(closed).toBe(false)

      jest.advanceTimersByTime(10000)
      expect(closed).toBe(false)

      jest.advanceTimersByTime(20010)
      expect(closed).toBe(true)

      jest.useRealTimers()
      done()
    })

    it("pushes heartbeat data when connected", function (){
      socket.conn.readyState = 1 // open

      const sendSpy = jest.spyOn(socket.conn, "send")
      const data = "[null,\"1\",\"phoenix\",\"heartbeat\",{}]"

      socket.sendHeartbeat()
      expect(sendSpy).toHaveBeenCalledWith(data)
    })

    it("no ops when not connected", function (){
      socket.conn.readyState = 0 // connecting

      const sendSpy = jest.spyOn(socket.conn, "send")
      const data = encode({topic: "phoenix", event: "heartbeat", payload: {}, ref: "1"})

      socket.sendHeartbeat()
      expect(sendSpy).not.toHaveBeenCalledWith(data)
    })
  })

  describe("flushSendBuffer", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
      socket.connect()
    })

    it("calls callbacks in buffer when connected", function (){
      socket.conn.readyState = 1 // open
      const spy1 = jest.fn()
      const spy2 = jest.fn()
      socket.sendBuffer.push(spy1)
      socket.sendBuffer.push(spy2)

      socket.flushSendBuffer()

      expect(spy1).toHaveBeenCalledTimes(1)
      expect(spy2).toHaveBeenCalledTimes(1)
    })

    it("empties sendBuffer", function (){
      socket.conn.readyState = 1 // open
      socket.sendBuffer.push(() => { })

      socket.flushSendBuffer()

      expect(socket.sendBuffer.length).toBe(0)
    })
  })

  describe("onConnOpen", function (){
    let mockServer

    beforeAll(function (){
      mockServer = new WebSocketServer("wss://example.com/")
    })

    afterAll(function (done){
      mockServer.stop(() => done())
    })

    beforeEach(function (){
      socket = new Socket("/socket", {
        reconnectAfterMs: () => 100000
      })
      socket.connect()
    })

    it("flushes the send buffer", function (){
      socket.conn.readyState = 1 // open
      const spy = jest.fn()
      socket.sendBuffer.push(spy)

      socket.onConnOpen()

      expect(spy).toHaveBeenCalledTimes(1)
    })

    it("resets reconnectTimer", function (){
      const resetSpy = jest.spyOn(socket.reconnectTimer, "reset")
      socket.onConnOpen()
      expect(resetSpy).toHaveBeenCalledTimes(1)
    })

    it("triggers onOpen callback", function (){
      const spy = jest.fn()
      socket.onOpen(spy)
      socket.onConnOpen()
      expect(spy).toHaveBeenCalledTimes(1)
    })
  })

  describe("onConnClose", function (){
    let mockServer

    beforeAll(function (){
      mockServer = new WebSocketServer("wss://example.com/")
    })

    afterAll(function (done){
      mockServer.stop(() => done())
    })

    beforeEach(function (){
      socket = new Socket("/socket", {
        reconnectAfterMs: () => 100000
      })
      socket.connect()
    })

    it("does not schedule reconnectTimer if normal close", function (){
      const scheduleSpy = jest.spyOn(socket.reconnectTimer, "scheduleTimeout")
      const event = {code: 1000}
      socket.onConnClose(event)
      expect(scheduleSpy).not.toHaveBeenCalled()
    })

    it("schedules reconnectTimer timeout if abnormal close", function (){
      const scheduleSpy = jest.spyOn(socket.reconnectTimer, "scheduleTimeout")
      const event = {code: 1006}
      socket.onConnClose(event)
      expect(scheduleSpy).toHaveBeenCalledTimes(1)
    })

    it("does not schedule reconnectTimer timeout if normal close after explicit disconnect", function (){
      const scheduleSpy = jest.spyOn(socket.reconnectTimer, "scheduleTimeout")
      socket.disconnect()
      expect(scheduleSpy).not.toHaveBeenCalled()
    })

    it("schedules reconnectTimer timeout if not normal close", function (){
      const scheduleSpy = jest.spyOn(socket.reconnectTimer, "scheduleTimeout")
      const event = {code: 1001}
      socket.onConnClose(event)
      expect(scheduleSpy).toHaveBeenCalledTimes(1)
    })

    it("schedules reconnectTimer timeout if connection cannot be made after a previous clean disconnect", function (done){
      const scheduleSpy = jest.spyOn(socket.reconnectTimer, "scheduleTimeout")
      socket.disconnect(() => {
        socket.connect()
        const event = {code: 1001}
        socket.onConnClose(event)
        expect(scheduleSpy).toHaveBeenCalledTimes(1)
        done()
      })
    })

    it("triggers onClose callback", function (){
      const spy = jest.fn()
      socket.onClose(spy)
      socket.onConnClose("event")
      expect(spy).toHaveBeenCalledWith("event")
    })

    it("triggers channel error if joining", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join()
      expect(channel.state).toBe("joining")
      socket.onConnClose()
      expect(triggerSpy).toHaveBeenCalledWith("phx_error")
    })

    it("triggers channel error if joined", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join().trigger("ok", {})
      expect(channel.state).toBe("joined")
      socket.onConnClose()
      expect(triggerSpy).toHaveBeenCalledWith("phx_error")
    })

    it("does not trigger channel error after leave", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join().trigger("ok", {})
      channel.leave()
      expect(channel.state).toBe("closed")
      socket.onConnClose()
      expect(triggerSpy).not.toHaveBeenCalledWith("phx_error")
    })

    it("does not send heartbeat after explicit disconnect", function (done){
      jest.useFakeTimers()
      const sendHeartbeatSpy = jest.spyOn(socket, "sendHeartbeat")
      socket.onConnOpen()
      socket.disconnect()
      jest.advanceTimersByTime(30000)
      expect(sendHeartbeatSpy).not.toHaveBeenCalled()
      jest.useRealTimers()
      done()
    })

    it("does not timeout the heartbeat after explicit disconnect", function (done){
      jest.useFakeTimers()
      const heartbeatTimeoutSpy = jest.spyOn(socket, "heartbeatTimeout")
      socket.onConnOpen()
      socket.disconnect()
      jest.advanceTimersByTime(60000)
      expect(heartbeatTimeoutSpy).not.toHaveBeenCalled()
      jest.useRealTimers()
      done()
    })
  })

  describe("onConnError", function (){
    let mockServer

    beforeAll(function (){
      mockServer = new WebSocketServer("wss://example.com/")
    })

    afterAll(function (done){
      mockServer.stop(() => done())
    })

    beforeEach(function (){
      socket = new Socket("/socket", {
        reconnectAfterMs: () => 100000
      })
      socket.connect()
    })

    it("triggers onClose callback", function (){
      const spy = jest.fn()
      socket.onError(spy)
      socket.onConnError("error")
      expect(spy).toHaveBeenCalledWith("error", expect.any(Function), 0)
    })

    it("triggers channel error if joining with open connection", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join()
      socket.onConnOpen()
      expect(channel.state).toBe("joining")
      socket.onConnError("error")
      expect(triggerSpy).toHaveBeenCalledWith("phx_error")
    })

    it("triggers channel error if joining with no connection", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join()
      expect(channel.state).toBe("joining")
      socket.onConnError("error")
      expect(triggerSpy).toHaveBeenCalledWith("phx_error")
    })

    it("triggers channel error if joined", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join().trigger("ok", {})
      socket.onConnOpen()
      expect(channel.state).toBe("joined")

      let connectionsCount = null
      let transport = null
      socket.onError((error, erroredTransport, conns) => {
        transport = erroredTransport
        connectionsCount = conns
      })

      socket.onConnError("error")

      expect(transport).toBe(WebSocket)
      expect(connectionsCount).toBe(1)
      expect(triggerSpy).toHaveBeenCalledWith("phx_error")
    })

    it("does not trigger channel error after leave", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join().trigger("ok", {})
      channel.leave()
      expect(channel.state).toBe("closed")
      socket.onConnError("error")
      expect(triggerSpy).not.toHaveBeenCalledWith("phx_error")
    })

    it("does not trigger channel error if transport replaced with no previous connection", function (){
      const channel = socket.channel("topic")
      const triggerSpy = jest.spyOn(channel, "trigger")
      channel.join()
      expect(channel.state).toBe("joining")

      let connectionsCount = null
      class FakeTransport { }

      socket.onError((error, transport, conns) => {
        socket.replaceTransport(FakeTransport)
        connectionsCount = conns
      })
      socket.onConnError("error")

      expect(connectionsCount).toBe(0)
      expect(socket.transport).toBe(FakeTransport)
      expect(triggerSpy).not.toHaveBeenCalledWith("phx_error")
    })
  })

  describe("onConnMessage", function (){
    let mockServer

    beforeAll(function (){
      mockServer = new WebSocketServer("wss://example.com/")
    })

    afterAll(function (done){
      mockServer.stop(() => done())
    })

    beforeEach(function (){
      socket = new Socket("/socket", {
        reconnectAfterMs: () => 100000
      })
      socket.connect()
    })

    it("parses raw message and triggers channel event", function (){
      const message = encode({topic: "topic", event: "event", payload: "payload", ref: "ref"})
      const data = {data: message}

      const targetChannel = socket.channel("topic")
      const otherChannel = socket.channel("off-topic")

      const targetSpy = jest.spyOn(targetChannel, "trigger")
      const otherSpy = jest.spyOn(otherChannel, "trigger")

      socket.onConnMessage(data)

      expect(targetSpy).toHaveBeenCalledWith("event", "payload", "ref", null)
      expect(targetSpy).toHaveBeenCalledTimes(1)
      expect(otherSpy).toHaveBeenCalledTimes(0)
    })

    it("triggers onMessage callback", function (){
      const message = {"topic": "topic", "event": "event", "payload": "payload", "ref": "ref"}
      const spy = jest.fn()
      socket.onMessage(spy)
      socket.onConnMessage({data: encode(message)})

      expect(spy).toHaveBeenCalledWith({
        "topic": "topic",
        "event": "event",
        "payload": "payload",
        "ref": "ref",
        "join_ref": null
      })
    })
  })

  describe("ping", function (){
    beforeEach(function (){
      socket = new Socket("/socket")
      socket.connect()
    })

    it("pushes when connected", function (done){
      let latency = 100
      socket.conn.readyState = 1 // open
      expect(socket.isConnected()).toBe(true)
      socket.push = (msg) => {
        setTimeout(() => {
          socket.onConnMessage({data: encode({topic: "phoenix", event: "phx_reply", ref: msg.ref})})
        }, latency)
      }

      const result = socket.ping(rtt => {
        // if we're unlucky we could also receive 99 as rtt, so let's be generous
        expect(rtt >= (latency - 10)).toBe(true)
        done()
      })
      expect(result).toBe(true)
    })

    it("returns false when disconnected", function (){
      socket.conn.readyState = 0
      expect(socket.isConnected()).toBe(false)
      const result = socket.ping(_rtt => true)
      expect(result).toBe(false)
    })
  })

  describe("custom encoder and decoder", function (){

    it("encodes to JSON array by default", function (){
      socket = new Socket("/socket")
      const payload = {topic: "topic", ref: "2", join_ref: "1", event: "join", payload: {foo: "bar"}}

      socket.encode(payload, encoded => {
        expect(encoded).toBe("[\"1\",\"2\",\"topic\",\"join\",{\"foo\":\"bar\"}]")
      })
    })

    it("allows custom encoding when using WebSocket transport", function (){
      const encoder = (payload, callback) => callback("encode works")
      socket = new Socket("/socket", {transport: WebSocket, encode: encoder})

      socket.encode({foo: "bar"}, encoded => {
        expect(encoded).toBe("encode works")
      })
    })

    it("forces JSON encoding when using LongPoll transport", function (){
      const encoder = (payload, callback) => callback("encode works")
      socket = new Socket("/socket", {transport: LongPoll, encode: encoder})
      const payload = {topic: "topic", ref: "2", join_ref: "1", event: "join", payload: {foo: "bar"}}

      socket.encode(payload, encoded => {
        expect(encoded).toBe("[\"1\",\"2\",\"topic\",\"join\",{\"foo\":\"bar\"}]")
      })
    })

    it("decodes JSON by default", function (){
      socket = new Socket("/socket")
      const encoded = "[\"1\",\"2\",\"topic\",\"join\",{\"foo\":\"bar\"}]"

      socket.decode(encoded, decoded => {
        expect(decoded).toEqual({topic: "topic", ref: "2", join_ref: "1", event: "join", payload: {foo: "bar"}})
      })
    })

    it("allows custom decoding when using WebSocket transport", function (){
      const decoder = (payload, callback) => callback("decode works")
      socket = new Socket("/socket", {transport: WebSocket, decode: decoder})

      socket.decode("...esoteric format...", decoded => {
        expect(decoded).toBe("decode works")
      })
    })

    it("forces JSON decoding when using LongPoll transport", function (){
      const decoder = (payload, callback) => callback("decode works")
      socket = new Socket("/socket", {transport: LongPoll, decode: decoder})
      const payload = {topic: "topic", ref: "2", join_ref: "1", event: "join", payload: {foo: "bar"}}

      socket.decode("[\"1\",\"2\",\"topic\",\"join\",{\"foo\":\"bar\"}]", decoded => {
        expect(decoded).toEqual(payload)
      })
    })
  })
})

window.XMLHttpRequest = jest.fn()
window.WebSocket = WebSocket


================================================
FILE: babel.config.json
================================================
{
    "presets": [
        "@babel/preset-env"
    ]
}


================================================
FILE: config/config.exs
================================================
import Config

config :logger, :console,
  colors: [enabled: false],
  format: "\n$time $metadata[$level] $message\n"

config :phoenix,
  json_library: Jason,
  stacktrace_depth: 20,
  trim_on_html_eex_engine: false,
  sort_verified_routes_query_params: true

if Mix.env() == :dev do
  esbuild = fn args ->
    [
      args: ~w(./js/phoenix --bundle) ++ args,
      cd: Path.expand("../assets", __DIR__),
      env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
    ]
  end

  config :esbuild,
    version: "0.25.4",
    module: esbuild.(~w(--format=esm --sourcemap --outfile=../priv/static/phoenix.mjs)),
    main: esbuild.(~w(--format=cjs --sourcemap --outfile=../priv/static/phoenix.cjs.js)),
    cdn:
      esbuild.(
        ~w(--target=es2016 --format=iife --global-name=Phoenix --outfile=../priv/static/phoenix.js)
      ),
    cdn_min:
      esbuild.(
        ~w(--target=es2016 --format=iife --global-name=Phoenix --minify --outfile=../priv/static/phoenix.min.js)
      )
end


================================================
FILE: eslint.config.mjs
================================================
import jest from "eslint-plugin-jest"
import js from "@eslint/js"
import stylistic from "@stylistic/eslint-plugin"

export default [
  {
    // eslint config is very unintuitive; they will match an js file in any
    // directory by default and you can only expand this;
    // moreover, to have a global ignore, it must be specified without
    // any other key as a separate object...
    ignores: [
      "integration_test/",
      "installer/",
      "doc/",
      "deps/",
      "coverage/",
      "priv/",
      "tmp/",
      "test/"
    ],
  },
  {
    ...js.configs.recommended,

    plugins: {
      jest,
      "@stylistic": stylistic
    },

    languageOptions: {
      globals: {
        ...jest.environments.globals.globals,
        global: "writable",
      },

      ecmaVersion: 12,
      sourceType: "module",
    },

    rules: {
      "@stylistic/indent": ["error", 2, {
        SwitchCase: 1,
      }],
      
      "@stylistic/linebreak-style": ["error", "unix"],
      "@stylistic/quotes": ["error", "double"],
      "@stylistic/semi": ["error", "never"],
      
      "@stylistic/object-curly-spacing": ["error", "never", {
        objectsInObjects: false,
        arraysInObjects: false,
      }],
      
      "@stylistic/array-bracket-spacing": ["error", "never"],
      
      "@stylistic/comma-spacing": ["error", {
        before: false,
        after: true,
      }],
      
      "@stylistic/computed-property-spacing": ["error", "never"],
      
      "@stylistic/space-before-blocks": ["error", {
        functions: "never",
        keywords: "never",
        classes: "always",
      }],
      
      "@stylistic/keyword-spacing": ["error", {
        overrides: {
          if: {
            after: false,
          },
      
          for: {
            after: false,
          },
      
          while: {
            after: false,
          },
      
          switch: {
            after: false,
          },
        },
      }],
      
      "@stylistic/eol-last": ["error", "always"],
      
      "no-unused-vars": ["error", {
        argsIgnorePattern: "^_",
        varsIgnorePattern: "^_",
      }],
      
      "no-useless-escape": "off",
      "no-cond-assign": "off",
      "no-case-declarations": "off",
    },
  }]


================================================
FILE: guides/asset_management.md
================================================
# Asset Management

Beside producing HTML, most web applications have various assets (JavaScript, CSS, images, fonts and so on).

From Phoenix v1.7, new applications use [esbuild](https://esbuild.github.io/) to prepare assets via the [Elixir esbuild wrapper](https://github.com/phoenixframework/esbuild), and [tailwindcss](https://tailwindcss.com) via the [Elixir tailwindcss wrapper](https://github.com/phoenixframework/tailwind) for CSS. The direct integration with `esbuild` and `tailwind` means that newly generated applications do not have dependencies on Node.js or an external build system (e.g. Webpack).

Your JavaScript is typically placed at "assets/js/app.js" and `esbuild` will extract it to "priv/static/assets/js/app.js". In development, this is done automatically via the `esbuild` watcher. In production, this is done by running `mix assets.deploy`.

`esbuild` can also handle your CSS files, but by default `tailwind` handles all CSS building.

Finally, all other assets, that usually don't have to be preprocessed, go directly to "priv/static".

## Third-party JS packages

If you want to import JavaScript dependencies, you have at least three options to add them to your application:

1. Vendor those dependencies inside your project and import them in your "assets/js/app.js" using a relative path:

   ```javascript
   import topbar from "../vendor/topbar"
   ```

2. Call `npm install topbar --prefix assets`, which will create `package.json` and `package-lock.json` inside your assets directory, and `esbuild` will be able to automatically pick them up:

   ```javascript
   import topbar from "topbar"
   ```

   To ensure that `npm install` is being run when checking out your project, or when building a release, add a `"cmd --cd assets npm ci"` step in `mix.exs` to the `assets.deploy` and  `assets.build` steps:

```elixir   
      "assets.build": ["cmd --cd assets npm ci", "tailwind your_app", "esbuild your_app"],
      "assets.deploy": [
        "cmd --cd assets npm ci",
        "tailwind your_app --minify",
        "esbuild your_app --minify",
        "phx.digest"
      ]
```

3. Use Mix to track the dependency from a source repository:

   ```elixir
   # mix.exs
   {:topbar, github: "buunguyen/topbar", app: false, compile: false}
   ```

   Run `mix deps.get` to fetch the dependency and then import it:

   ```javascript
   import topbar from "topbar"
   ```

   New applications use this third approach to import icons, such as Heroicons,
   to avoid vendoring a copy of all icons and to avoid additional system
   dependencies such as `npm`, while you can still track explicit versions
   thanks to Mix. It is important to note that git dependencies cannot be used
   by Hex packages, so if you intend to publish your project to Hex, consider
   alternatives approaches.

Note that if you use third party JS package managers, you might need to adjust your
deployment steps to properly include the packages. If you're using
`mix phx.gen.release --docker`, have a look at the
[documentation](Mix.Tasks.Phx.Gen.Release.html#module-docker) for further details.

## Images, fonts, and external files

If you reference an external file in your CSS or JavaScript files, `esbuild` will attempt to validate and manage them, unless told otherwise.

For example, imagine you want to reference `priv/static/images/bg.png`, served at `/images/bg.png`, from your CSS file:

```css
body {
  background-image: url(/images/bg.png);
}
```

The above may fail with the following message:

```text
error: Could not resolve "/images/bg.png" (mark it as external to exclude it from the bundle)
```

Given the images are already managed by Phoenix, you need to mark all resources from `/images` (and also `/fonts`) as external, as the error message says. This is what Phoenix does by default for new apps since v1.6.1+. In your `config/config.exs`, you will find:

```elixir
args: ~w(js/app.js --bundle --target=es2022 --outdir=../priv/static/assets/js --external:/fonts/* --external:/images/*),
```

If you need to reference other directories, you need to update the arguments above accordingly. Note running `mix phx.digest` will create digested files for all of the assets in `priv/static`, so your images and fonts are still cache-busted.

### Ensuring fonts and images from third-party libraries are loaded

If you import a Node package that depends on additional fonts or images, you might find them to fail to load. This is because they are referenced in the JS or CSS but by default Esbuild will not touch or process referenced files. You can add arguments to esbuild in `config/config.exs` to ensure that the referenced resources are copied to the output folder. The following example would copy all referenced font files to the output folder and prefix the paths with `/assets/`:

```elixir
args: ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/* --public-path=/assets/ --loader:.woff=copy  --loader:.ttf=copy --loader:.eot=copy --loader:.woff2=copy),
```
For more information, see [the esbuild documentation](https://esbuild.github.io/content-types/#copy).

## Esbuild plugins

Phoenix's default configuration of `esbuild` (via the Elixir wrapper) does not allow you to use [esbuild plugins](https://esbuild.github.io/plugins/). If you want to use an esbuild plugin, for example to compile SASS files to CSS, you can replace the default build system with a custom build script.

The following is an example of a custom build using esbuild via Node.js. First of all, you'll need to install Node.js in development and make it available for your production build step.

Then you'll need to add `esbuild` to your Node.js packages and the Phoenix packages. Inside the `assets` directory, run:

```console
$ npm install esbuild --save-dev
$ npm install ../deps/phoenix ../deps/phoenix_html ../deps/phoenix_live_view --save
```

or, for Yarn:

```console
$ yarn add --dev esbuild
$ yarn add ../deps/phoenix ../deps/phoenix_html ../deps/phoenix_live_view
```

Next, add a custom JavaScript build script. We'll call the example `assets/build.js`:

```javascript
const esbuild = require("esbuild");

const args = process.argv.slice(2);
const watch = args.includes('--watch');
const deploy = args.includes('--deploy');

const loader = {
  // Add loaders for images/fonts/etc, e.g. { '.svg': 'file' }
};

const plugins = [
  // Add and configure plugins here
];

// Define esbuild options
let opts = {
  entryPoints: ["js/app.js"],
  bundle: true,
  logLevel: "info",
  target: "es2022",
  outdir: "../priv/static/assets",
  external: ["*.css", "fonts/*", "images/*"],
  nodePaths: ["../deps"],
  loader: loader,
  plugins: plugins,
};

if (deploy) {
  opts = {
    ...opts,
    minify: true,
  };
}

if (watch) {
  opts = {
    ...opts,
    sourcemap: "inline",
  };
  esbuild
    .context(opts)
    .then((ctx) => {
      ctx.watch();
    })
    .catch((_error) => {
      process.exit(1);
    });
} else {
  esbuild.build(opts);
}
```

This script covers following use cases:

- `node build.js`: builds for development & testing (useful on CI)
- `node build.js --watch`: like above, but watches for changes continuously
- `node build.js --deploy`: builds minified assets for production

Modify `config/dev.exs` so that the script runs whenever you change files, replacing the existing `:esbuild` configuration under `watchers`:

```elixir
config :hello, HelloWeb.Endpoint,
  ...
  watchers: [
    node: ["build.js", "--watch", cd: Path.expand("../assets", __DIR__)]
  ],
  ...
```

Modify the `aliases` task in `mix.exs` to install `npm` packages during `mix setup` and use the new `esbuild` on `mix assets.deploy`:

```elixir
  defp aliases do
    [
      setup: ["deps.get", "ecto.setup", "cmd --cd assets npm install"],
      ...,
      "assets.deploy": ["cmd --cd assets node build.js --deploy", "phx.digest"]
    ]
  end
```

Finally, remove the `esbuild` configuration from `config/config.exs` and remove the dependency from the `deps` function in your `mix.exs`, and you are done!

## Alternative JS build tools

If you are writing an API or you want to use another asset build tool, you may want to remove the `esbuild` Hex package (see steps below). Then you must follow the additional steps required by the third-party tool.

### Remove esbuild

1. Remove the `esbuild` configuration in `config/config.exs` and `config/dev.exs`,
2. Remove the `assets.deploy` task defined in `mix.exs`,
3. Remove the `esbuild` dependency from `mix.exs`,
4. Unlock the `esbuild` dependency:

```console
$ mix deps.unlock esbuild
```

## Alternative CSS frameworks

By default, Phoenix generates CSS with the `tailwind` library and its default plugins.

If you want to use external `tailwind` plugins or another CSS framework, you should replace the `tailwind` Hex package (see steps below). Then you can use an `esbuild` plugin (as outlined above) or even bring a separate framework altogether.

### Remove tailwind

1. Remove the `tailwind` configuration in `config/config.exs` and `config/dev.exs`,
2. Remove the `assets.deploy` task defined in `mix.exs`,
3. Remove the `tailwind` dependency from `mix.exs`,
4. Unlock the `tailwind` dependency:

```console
$ mix deps.unlock tailwind
```

You may optionally remove and delete the `heroicons` dependency as well.

## Alternative icon libraries

Phoenix ships with the [Heroicons](https://heroicons.com/) library for icons support.
This is done by embedding icons as CSS classes, which guarantees only the icons actually
used by your application are sent to the client, thanks to Tailwind.

If you prefer to use an alternative icon set, it should be possible to adapt the
code that embeds Heroicons to use another library. Let's see exactly how to do that
using [Remix Icon](https://remixicon.com/) as an example:

First replace the `heroicon` repository in your `mix.exs` by `remixicons`:

```elixir
{:remixicons,
  github: "Remix-Design/RemixIcon",
  sparse: "icons",
  tag: "v4.6.0",
  app: false,
  compile: false,
  depth: 1},
```

Then replace `assets/vendor/heroicons.js`, which traverses the heroicons dependency, by `assets/vendor/remixicons.js`, which traverses remix icons instead:

```js
const plugin = require("tailwindcss/plugin")
const fs = require("fs")
const path = require("path")

module.exports = plugin(function({matchComponents, theme}) {
  let baseDir = path.join(__dirname, "../../deps/remixicons/icons");
  let values = {};
  let icons = fs
    .readdirSync(baseDir, { withFileTypes: true })
    .filter((dirent) => dirent.isDirectory())
    .map((dirent) => dirent.name);

  icons.forEach((dir) => {
    fs.readdirSync(path.join(baseDir, dir)).map((file) => {
      let name = path.basename(file, ".svg");
      values[name] = { name, fullPath: path.join(baseDir, dir, file) };
    });
  });

  matchComponents(
    {
      ri: ({ name, fullPath }) => {
        let content = fs
          .readFileSync(fullPath)
          .toString()
          .replace(/\r?\n|\r/g, "");

        return {
          [`--ri-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
          "-webkit-mask": `var(--ri-${name})`,
          mask: `var(--ri-${name})`,
          "background-color": "currentColor",
          "vertical-align": "middle",
          display: "inline-block",
          width: theme("spacing.10"),
          height: theme("spacing.10"),
        };
      },
    },
    { values },
  );
})
```

And then change `assets/css/app.css` to import your new plugin instead.

Finally, update the `icon` function in `lib/my_app_web/components/core_components.ex`
to match on `ri-` prefixes instead:

```
@doc """
Renders a [Remix Icon](https://remixicon.com).

You can customize the size and colors of the icons by
setting width, height, and background color classes.

## Examples

    <.icon name="ri-github-fill" />
    <.icon name="ri-github" class="ml-1 w-3 h-3 animate-spin" />
"""
attr :name, :string, required: true
attr :class, :any, default: "size-5"

def icon(%{name: "ri-" <> _} = assigns) do
  ~H"""
  <i class={[@name, @class]} aria-hidden="true"></i>
  """
end
```

Now replace the Heroicons in your application by Remix ones and you are good to go!

The approach above may also work with other libraries, it is a matter of adapting
the Tailwind plugin to traverse these libraries and generate the proper classes.
Some iconsets may also be available as regular Hex packages too.


================================================
FILE: guides/authn_authz/api_authentication.md
================================================
# API Authentication

> **Requirement**: This guide expects that you have gone through the [`mix phx.gen.auth`](mix_phx_gen_auth.html) guide.

This guide shows how to add API authentication on top of `mix phx.gen.auth`. Since the authentication generator already includes a token table, we use it to store API tokens too, following the best security practices.

We will break this guide in two parts: augmenting the context and the plug implementation. We will assume that the following `mix phx.gen.auth` command was executed:

```
$ mix phx.gen.auth Accounts User users
```

If you ran something else, it should be trivial to adapt the names.

## Adding API functions to the context

Our authentication system will require two functions. One to create the API token and another to verify it. Open up `lib/my_app/accounts.ex` and add these two new functions:

```elixir
  ## API

  @doc """
  Creates a new api token for a u
Download .txt
gitextract_fy0ivb69/

├── .formatter.exs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── config.yml
│   ├── dependabot.yml
│   └── workflows/
│       ├── assets.yml
│       ├── ci.yml
│       └── npm-publish.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── RELEASE.md
├── SECURITY.md
├── assets/
│   ├── js/
│   │   └── phoenix/
│   │       ├── ajax.js
│   │       ├── channel.js
│   │       ├── constants.js
│   │       ├── index.js
│   │       ├── longpoll.js
│   │       ├── presence.js
│   │       ├── push.js
│   │       ├── serializer.js
│   │       ├── socket.js
│   │       ├── timer.js
│   │       └── utils.js
│   └── test/
│       ├── channel_test.js
│       ├── longpoll_test.js
│       ├── presence_test.js
│       ├── serializer.js
│       ├── serializer_test.js
│       ├── socket_http_test.js
│       └── socket_test.js
├── babel.config.json
├── config/
│   └── config.exs
├── eslint.config.mjs
├── guides/
│   ├── asset_management.md
│   ├── authn_authz/
│   │   ├── api_authentication.md
│   │   ├── authn_authz.md
│   │   ├── mix_phx_gen_auth.md
│   │   └── scopes.md
│   ├── cheatsheets/
│   │   └── router.cheatmd
│   ├── components.md
│   ├── controllers.md
│   ├── data_modelling/
│   │   ├── contexts.md
│   │   ├── cross_context_boundaries.md
│   │   ├── faq.md
│   │   ├── in_context_relationships.md
│   │   ├── more_examples.md
│   │   └── your_first_context.md
│   ├── deployment/
│   │   ├── deployment.md
│   │   ├── fly.md
│   │   ├── gigalixir.md
│   │   ├── heroku.md
│   │   └── releases.md
│   ├── directory_structure.md
│   ├── ecto.md
│   ├── howto/
│   │   ├── custom_error_pages.md
│   │   ├── file_uploads.md
│   │   ├── swapping_databases.md
│   │   ├── using_ssl.md
│   │   └── writing_a_channels_client.md
│   ├── introduction/
│   │   ├── community.md
│   │   ├── installation.md
│   │   ├── overview.md
│   │   ├── packages_glossary.md
│   │   └── up_and_running.md
│   ├── json_and_apis.md
│   ├── live_view.md
│   ├── plug.md
│   ├── real_time/
│   │   ├── channels.md
│   │   └── presence.md
│   ├── request_lifecycle.md
│   ├── routing.md
│   ├── security.md
│   ├── telemetry.md
│   └── testing/
│       ├── testing.md
│       ├── testing_channels.md
│       ├── testing_contexts.md
│       └── testing_controllers.md
├── installer/
│   ├── .gitignore
│   ├── README.md
│   ├── lib/
│   │   ├── mix/
│   │   │   └── tasks/
│   │   │       ├── local.phx.ex
│   │   │       ├── phx.new.ecto.ex
│   │   │       ├── phx.new.ex
│   │   │       └── phx.new.web.ex
│   │   └── phx_new/
│   │       ├── ecto.ex
│   │       ├── generator.ex
│   │       ├── mailer.ex
│   │       ├── project.ex
│   │       ├── single.ex
│   │       ├── umbrella.ex
│   │       └── web.ex
│   ├── mix.exs
│   ├── recreate_default_css.exs
│   ├── templates/
│   │   ├── phx_assets/
│   │   │   ├── app.css.eex
│   │   │   ├── app.js.eex
│   │   │   ├── daisyui-theme.js.eex
│   │   │   ├── daisyui.js.eex
│   │   │   ├── heroicons.js.eex
│   │   │   ├── logo.svg.eex
│   │   │   ├── topbar.js.eex
│   │   │   └── tsconfig.json.eex
│   │   ├── phx_ecto/
│   │   │   ├── data_case.ex.eex
│   │   │   ├── formatter.exs.eex
│   │   │   ├── repo.ex.eex
│   │   │   └── seeds.exs.eex
│   │   ├── phx_gettext/
│   │   │   ├── en/
│   │   │   │   └── LC_MESSAGES/
│   │   │   │       └── errors.po.eex
│   │   │   ├── errors.pot.eex
│   │   │   └── gettext.ex.eex
│   │   ├── phx_mailer/
│   │   │   └── lib/
│   │   │       └── app_name/
│   │   │           └── mailer.ex.eex
│   │   ├── phx_single/
│   │   │   ├── README.md.eex
│   │   │   ├── config/
│   │   │   │   ├── config.exs.eex
│   │   │   │   ├── dev.exs.eex
│   │   │   │   ├── prod.exs.eex
│   │   │   │   ├── runtime.exs.eex
│   │   │   │   └── test.exs.eex
│   │   │   ├── formatter.exs.eex
│   │   │   ├── gitignore.eex
│   │   │   ├── lib/
│   │   │   │   ├── app_name/
│   │   │   │   │   └── application.ex.eex
│   │   │   │   ├── app_name.ex.eex
│   │   │   │   └── app_name_web.ex.eex
│   │   │   ├── mix.exs.eex
│   │   │   └── test/
│   │   │       └── test_helper.exs.eex
│   │   ├── phx_static/
│   │   │   ├── app.css
│   │   │   ├── app.js
│   │   │   ├── default.css
│   │   │   └── robots.txt
│   │   ├── phx_test/
│   │   │   ├── controllers/
│   │   │   │   ├── error_html_test.exs.eex
│   │   │   │   ├── error_json_test.exs.eex
│   │   │   │   └── page_controller_test.exs.eex
│   │   │   └── support/
│   │   │       └── conn_case.ex.eex
│   │   ├── phx_umbrella/
│   │   │   ├── README.md.eex
│   │   │   ├── apps/
│   │   │   │   ├── app_name/
│   │   │   │   │   ├── README.md.eex
│   │   │   │   │   ├── config/
│   │   │   │   │   │   └── config.exs.eex
│   │   │   │   │   ├── formatter.exs.eex
│   │   │   │   │   ├── gitignore.eex
│   │   │   │   │   ├── lib/
│   │   │   │   │   │   ├── app_name/
│   │   │   │   │   │   │   └── application.ex.eex
│   │   │   │   │   │   └── app_name.ex.eex
│   │   │   │   │   ├── mix.exs.eex
│   │   │   │   │   └── test/
│   │   │   │   │       └── test_helper.exs.eex
│   │   │   │   └── app_name_web/
│   │   │   │       ├── README.md.eex
│   │   │   │       ├── config/
│   │   │   │       │   ├── config.exs.eex
│   │   │   │       │   ├── dev.exs.eex
│   │   │   │       │   ├── prod.exs.eex
│   │   │   │       │   ├── runtime.exs.eex
│   │   │   │       │   └── test.exs.eex
│   │   │   │       ├── formatter.exs.eex
│   │   │   │       ├── gitignore.eex
│   │   │   │       ├── lib/
│   │   │   │       │   ├── app_name/
│   │   │   │       │   │   └── application.ex.eex
│   │   │   │       │   └── app_name.ex.eex
│   │   │   │       ├── mix.exs.eex
│   │   │   │       └── test/
│   │   │   │           └── test_helper.exs.eex
│   │   │   ├── config/
│   │   │   │   ├── config.exs.eex
│   │   │   │   ├── dev.exs.eex
│   │   │   │   ├── extra_config.exs.eex
│   │   │   │   ├── prod.exs.eex
│   │   │   │   ├── runtime.exs.eex
│   │   │   │   └── test.exs.eex
│   │   │   ├── formatter.exs.eex
│   │   │   ├── gitignore.eex
│   │   │   └── mix.exs.eex
│   │   ├── phx_web/
│   │   │   ├── components/
│   │   │   │   ├── core_components.ex.eex
│   │   │   │   ├── layouts/
│   │   │   │   │   └── root.html.heex.eex
│   │   │   │   └── layouts.ex.eex
│   │   │   ├── controllers/
│   │   │   │   ├── error_html.ex.eex
│   │   │   │   ├── error_json.ex.eex
│   │   │   │   ├── page_controller.ex.eex
│   │   │   │   ├── page_html/
│   │   │   │   │   └── home.html.heex.eex
│   │   │   │   └── page_html.ex.eex
│   │   │   ├── endpoint.ex.eex
│   │   │   ├── router.ex.eex
│   │   │   └── telemetry.ex.eex
│   │   └── usage-rules/
│   │       ├── assets.md
│   │       ├── phoenix.md
│   │       └── project.md
│   └── test/
│       ├── mix_helper.exs
│       ├── phx_new_ecto_test.exs
│       ├── phx_new_test.exs
│       ├── phx_new_umbrella_test.exs
│       ├── phx_new_web_test.exs
│       └── test_helper.exs
├── integration_test/
│   ├── README.md
│   ├── config/
│   │   └── config.exs
│   ├── docker-compose.yml
│   ├── docker.sh
│   ├── mix.exs
│   ├── test/
│   │   ├── code_generation/
│   │   │   ├── app_with_defaults_test.exs
│   │   │   ├── app_with_mssql_adapter_test.exs
│   │   │   ├── app_with_mysql_adapter_test.exs
│   │   │   ├── app_with_no_options_test.exs
│   │   │   ├── app_with_scopes_test.exs
│   │   │   ├── app_with_sqlite3_adapter_test.exs
│   │   │   └── umbrella_app_with_defaults_test.exs
│   │   ├── support/
│   │   │   └── code_generator_case.ex
│   │   └── test_helper.exs
│   └── test.sh
├── jest.config.js
├── lib/
│   ├── mix/
│   │   ├── phoenix/
│   │   │   ├── context.ex
│   │   │   ├── schema.ex
│   │   │   └── scope.ex
│   │   ├── phoenix.ex
│   │   └── tasks/
│   │       ├── compile.phoenix.ex
│   │       ├── phx.digest.clean.ex
│   │       ├── phx.digest.ex
│   │       ├── phx.ex
│   │       ├── phx.gen.auth/
│   │       │   ├── hashing_library.ex
│   │       │   ├── injector.ex
│   │       │   └── migration.ex
│   │       ├── phx.gen.auth.ex
│   │       ├── phx.gen.cert.ex
│   │       ├── phx.gen.channel.ex
│   │       ├── phx.gen.context.ex
│   │       ├── phx.gen.embedded.ex
│   │       ├── phx.gen.ex
│   │       ├── phx.gen.html.ex
│   │       ├── phx.gen.json.ex
│   │       ├── phx.gen.live.ex
│   │       ├── phx.gen.notifier.ex
│   │       ├── phx.gen.presence.ex
│   │       ├── phx.gen.release.ex
│   │       ├── phx.gen.schema.ex
│   │       ├── phx.gen.secret.ex
│   │       ├── phx.gen.socket.ex
│   │       ├── phx.routes.ex
│   │       └── phx.server.ex
│   ├── phoenix/
│   │   ├── channel/
│   │   │   └── server.ex
│   │   ├── channel.ex
│   │   ├── code_reloader/
│   │   │   ├── mix_listener.ex
│   │   │   ├── proxy.ex
│   │   │   └── server.ex
│   │   ├── code_reloader.ex
│   │   ├── config.ex
│   │   ├── controller/
│   │   │   └── pipeline.ex
│   │   ├── controller.ex
│   │   ├── debug.ex
│   │   ├── digester/
│   │   │   ├── compressor.ex
│   │   │   └── gzip.ex
│   │   ├── digester.ex
│   │   ├── endpoint/
│   │   │   ├── cowboy2_adapter.ex
│   │   │   ├── render_errors.ex
│   │   │   ├── supervisor.ex
│   │   │   ├── sync_code_reload_plug.ex
│   │   │   └── watcher.ex
│   │   ├── endpoint.ex
│   │   ├── exceptions.ex
│   │   ├── flash.ex
│   │   ├── logger.ex
│   │   ├── naming.ex
│   │   ├── param.ex
│   │   ├── presence.ex
│   │   ├── router/
│   │   │   ├── console_formatter.ex
│   │   │   ├── helpers.ex
│   │   │   ├── resource.ex
│   │   │   ├── route.ex
│   │   │   └── scope.ex
│   │   ├── router.ex
│   │   ├── socket/
│   │   │   ├── message.ex
│   │   │   ├── pool_supervisor.ex
│   │   │   ├── serializer.ex
│   │   │   ├── serializers/
│   │   │   │   ├── v1_json_serializer.ex
│   │   │   │   └── v2_json_serializer.ex
│   │   │   └── transport.ex
│   │   ├── socket.ex
│   │   ├── test/
│   │   │   ├── channel_test.ex
│   │   │   └── conn_test.ex
│   │   ├── token.ex
│   │   ├── transports/
│   │   │   ├── long_poll.ex
│   │   │   ├── long_poll_server.ex
│   │   │   └── websocket.ex
│   │   └── verified_routes.ex
│   └── phoenix.ex
├── mix.exs
├── package.json
├── priv/
│   ├── static/
│   │   ├── phoenix.cjs.js
│   │   ├── phoenix.js
│   │   └── phoenix.mjs
│   └── templates/
│       ├── phx.gen.auth/
│       │   ├── AGENTS.md.eex
│       │   ├── auth.ex.eex
│       │   ├── auth_test.exs.eex
│       │   ├── confirmation_live.ex.eex
│       │   ├── confirmation_live_test.exs.eex
│       │   ├── conn_case.exs.eex
│       │   ├── context_fixtures_functions.ex.eex
│       │   ├── context_functions.ex.eex
│       │   ├── login_live.ex.eex
│       │   ├── login_live_test.exs.eex
│       │   ├── migration.ex.eex
│       │   ├── notifier.ex.eex
│       │   ├── registration_controller.ex.eex
│       │   ├── registration_controller_test.exs.eex
│       │   ├── registration_html.ex.eex
│       │   ├── registration_live.ex.eex
│       │   ├── registration_live_test.exs.eex
│       │   ├── registration_new.html.heex.eex
│       │   ├── routes.ex.eex
│       │   ├── schema.ex.eex
│       │   ├── schema_token.ex.eex
│       │   ├── scope.ex.eex
│       │   ├── session_confirm.html.heex.eex
│       │   ├── session_controller.ex.eex
│       │   ├── session_controller_test.exs.eex
│       │   ├── session_html.ex.eex
│       │   ├── session_new.html.heex.eex
│       │   ├── settings_controller.ex.eex
│       │   ├── settings_controller_test.exs.eex
│       │   ├── settings_edit.html.heex.eex
│       │   ├── settings_html.ex.eex
│       │   ├── settings_live.ex.eex
│       │   ├── settings_live_test.exs.eex
│       │   └── test_cases.exs.eex
│       ├── phx.gen.channel/
│       │   ├── channel.ex.eex
│       │   ├── channel_case.ex.eex
│       │   └── channel_test.exs.eex
│       ├── phx.gen.context/
│       │   ├── access_no_schema.ex.eex
│       │   ├── access_no_schema_scope.ex.eex
│       │   ├── context.ex.eex
│       │   ├── context_test.exs.eex
│       │   ├── fixtures.ex.eex
│       │   ├── fixtures_module.ex.eex
│       │   ├── schema_access.ex.eex
│       │   ├── schema_access_scope.ex.eex
│       │   ├── test_cases.exs.eex
│       │   └── test_cases_scope.exs.eex
│       ├── phx.gen.embedded/
│       │   └── embedded_schema.ex.eex
│       ├── phx.gen.html/
│       │   ├── controller.ex.eex
│       │   ├── controller_test.exs.eex
│       │   ├── edit.html.heex.eex
│       │   ├── html.ex.eex
│       │   ├── index.html.heex.eex
│       │   ├── new.html.heex.eex
│       │   ├── resource_form.html.heex.eex
│       │   └── show.html.heex.eex
│       ├── phx.gen.json/
│       │   ├── changeset_json.ex.eex
│       │   ├── controller.ex.eex
│       │   ├── controller_test.exs.eex
│       │   ├── fallback_controller.ex.eex
│       │   └── json.ex.eex
│       ├── phx.gen.live/
│       │   ├── form.ex.eex
│       │   ├── index.ex.eex
│       │   ├── live_test.exs.eex
│       │   └── show.ex.eex
│       ├── phx.gen.notifier/
│       │   ├── notifier.ex.eex
│       │   └── notifier_test.exs.eex
│       ├── phx.gen.presence/
│       │   └── presence.ex.eex
│       ├── phx.gen.release/
│       │   ├── Dockerfile.eex
│       │   ├── dockerignore.eex
│       │   ├── rel/
│       │   │   ├── migrate.bat.eex
│       │   │   ├── migrate.sh.eex
│       │   │   ├── server.bat.eex
│       │   │   └── server.sh.eex
│       │   └── release.ex.eex
│       ├── phx.gen.schema/
│       │   ├── migration.exs.eex
│       │   └── schema.ex.eex
│       └── phx.gen.socket/
│           ├── socket.ex.eex
│           └── socket.js.eex
├── test/
│   ├── fixtures/
│   │   ├── digest/
│   │   │   ├── cleaner/
│   │   │   │   ├── cache_manifest.json
│   │   │   │   └── latest_not_most_recent_cache_manifest.json
│   │   │   ├── compile/
│   │   │   │   ├── cache_manifest.json
│   │   │   │   └── cache_manifest_upgrade.json
│   │   │   └── priv/
│   │   │       ├── output/
│   │   │       │   ├── foo-288ea8c7954498e65663c817382eeac4.css
│   │   │       │   └── foo-d978852bea6530fcd197b5445ed008fd.css
│   │   │       └── static/
│   │   │           ├── app.js
│   │   │           ├── css/
│   │   │           │   └── app.css
│   │   │           ├── foo.css
│   │   │           ├── manifest.json
│   │   │           └── precompressed.js.br
│   │   ├── hello.txt
│   │   ├── ssl/
│   │   │   ├── cert.pem
│   │   │   └── key.pem
│   │   ├── templates/
│   │   │   ├── custom.foo
│   │   │   ├── layout/
│   │   │   │   ├── app.html.eex
│   │   │   │   └── root.html.eex
│   │   │   ├── no_trim.text.eex
│   │   │   ├── path.html.eex
│   │   │   ├── safe.html.eex
│   │   │   ├── show.html.eex
│   │   │   ├── trim.html.eex
│   │   │   └── user/
│   │   │       ├── index.html.eex
│   │   │       ├── profiles/
│   │   │       │   └── admin.html.eex
│   │   │       ├── render_template.html.eex
│   │   │       └── show.json.exs
│   │   └── views.exs
│   ├── mix/
│   │   ├── phoenix_test.exs
│   │   └── tasks/
│   │       ├── phx.digest.clean_test.exs
│   │       ├── phx.digest_test.exs
│   │       ├── phx.gen.auth/
│   │       │   └── injector_test.exs
│   │       ├── phx.gen.auth_test.exs
│   │       ├── phx.gen.cert_test.exs
│   │       ├── phx.gen.channel_test.exs
│   │       ├── phx.gen.context_test.exs
│   │       ├── phx.gen.embedded_test.exs
│   │       ├── phx.gen.html_test.exs
│   │       ├── phx.gen.json_test.exs
│   │       ├── phx.gen.live_test.exs
│   │       ├── phx.gen.notifier_test.exs
│   │       ├── phx.gen.presence_test.exs
│   │       ├── phx.gen.release_test.exs
│   │       ├── phx.gen.schema_test.exs
│   │       ├── phx.gen.secret_test.exs
│   │       ├── phx.gen.socket_test.exs
│   │       ├── phx.routes_test.exs
│   │       └── phx_test.exs
│   ├── phoenix/
│   │   ├── channel_test.exs
│   │   ├── code_reloader_test.exs
│   │   ├── config_test.exs
│   │   ├── controller/
│   │   │   ├── controller_test.exs
│   │   │   ├── flash_test.exs
│   │   │   ├── pipeline_test.exs
│   │   │   └── render_test.exs
│   │   ├── debug_test.exs
│   │   ├── digester/
│   │   │   └── gzip_test.exs
│   │   ├── digester_test.exs
│   │   ├── endpoint/
│   │   │   ├── endpoint_test.exs
│   │   │   ├── render_errors_test.exs
│   │   │   ├── supervisor_test.exs
│   │   │   └── watcher_test.exs
│   │   ├── integration/
│   │   │   ├── endpoint_test.exs
│   │   │   ├── long_poll_channels_test.exs
│   │   │   ├── long_poll_socket_test.exs
│   │   │   ├── websocket_channels_test.exs
│   │   │   └── websocket_socket_test.exs
│   │   ├── logger_test.exs
│   │   ├── naming_test.exs
│   │   ├── param_test.exs
│   │   ├── presence_test.exs
│   │   ├── router/
│   │   │   ├── console_formatter_test.exs
│   │   │   ├── forward_test.exs
│   │   │   ├── helpers_test.exs
│   │   │   ├── pipeline_test.exs
│   │   │   ├── resource_test.exs
│   │   │   ├── resources_test.exs
│   │   │   ├── route_test.exs
│   │   │   ├── routing_test.exs
│   │   │   └── scope_test.exs
│   │   ├── socket/
│   │   │   ├── message_test.exs
│   │   │   ├── socket_test.exs
│   │   │   ├── transport_test.exs
│   │   │   ├── v1_json_serializer_test.exs
│   │   │   └── v2_json_serializer_test.exs
│   │   ├── test/
│   │   │   ├── channel_test.exs
│   │   │   └── conn_test.exs
│   │   ├── token_test.exs
│   │   └── verified_routes_test.exs
│   ├── support/
│   │   ├── endpoint_helper.exs
│   │   ├── http_client.exs
│   │   ├── router_helper.exs
│   │   └── websocket_client.exs
│   └── test_helper.exs
└── usage-rules/
    ├── ecto.md
    ├── elixir.md
    ├── html.md
    ├── liveview.md
    └── phoenix.md
Download .txt
SYMBOL INDEX (2488 symbols across 182 files)

FILE: assets/js/phoenix/ajax.js
  class Ajax (line 6) | class Ajax {
    method request (line 8) | static request(method, endPoint, headers, body, timeout, ontimeout, ca...
    method fetchRequest (line 23) | static fetchRequest(method, endPoint, headers, body, timeout, ontimeou...
    method xdomainRequest (line 49) | static xdomainRequest(req, method, endPoint, body, timeout, ontimeout,...
    method xhrRequest (line 65) | static xhrRequest(req, method, endPoint, headers, body, timeout, ontim...
    method parseJSON (line 84) | static parseJSON(resp){
    method serialize (line 95) | static serialize(obj, parentKey){
    method appendParams (line 110) | static appendParams(url, params){

FILE: assets/js/phoenix/channel.js
  class Channel (line 16) | class Channel {
    method constructor (line 17) | constructor(topic, params, socket){
    method join (line 79) | join(timeout = this.timeout){
    method onClose (line 94) | onClose(callback){
    method onError (line 102) | onError(callback){
    method on (line 123) | on(event, callback){
    method off (line 147) | off(event, ref){
    method canPush (line 156) | canPush(){ return this.socket.isConnected() && this.isJoined() }
    method push (line 174) | push(event, payload, timeout = this.timeout){
    method leave (line 206) | leave(timeout = this.timeout){
    method onMessage (line 236) | onMessage(_event, payload, _ref){ return payload }
    method isMember (line 241) | isMember(topic, event, payload, joinRef){
    method joinRef (line 255) | joinRef(){ return this.joinPush.ref }
    method rejoin (line 260) | rejoin(timeout = this.timeout){
    method trigger (line 270) | trigger(event, payload, ref, joinRef){
    method replyEventName (line 285) | replyEventName(ref){ return `chan_reply_${ref}` }
    method isClosed (line 290) | isClosed(){ return this.state === CHANNEL_STATES.closed }
    method isErrored (line 295) | isErrored(){ return this.state === CHANNEL_STATES.errored }
    method isJoined (line 300) | isJoined(){ return this.state === CHANNEL_STATES.joined }
    method isJoining (line 305) | isJoining(){ return this.state === CHANNEL_STATES.joining }
    method isLeaving (line 310) | isLeaving(){ return this.state === CHANNEL_STATES.leaving }

FILE: assets/js/phoenix/constants.js
  constant DEFAULT_VSN (line 4) | const DEFAULT_VSN = "2.0.0"
  constant SOCKET_STATES (line 5) | const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}
  constant DEFAULT_TIMEOUT (line 6) | const DEFAULT_TIMEOUT = 10000
  constant WS_CLOSE_NORMAL (line 7) | const WS_CLOSE_NORMAL = 1000
  constant CHANNEL_STATES (line 8) | const CHANNEL_STATES = {
  constant CHANNEL_EVENTS (line 15) | const CHANNEL_EVENTS = {
  constant TRANSPORTS (line 23) | const TRANSPORTS = {
  constant XHR_STATES (line 27) | const XHR_STATES = {
  constant AUTH_TOKEN_PREFIX (line 30) | const AUTH_TOKEN_PREFIX = "base64url.bearer.phx."

FILE: assets/js/phoenix/longpoll.js
  class LongPoll (line 17) | class LongPoll {
    method constructor (line 19) | constructor(endPoint, protocols){
    method normalizeEndpoint (line 43) | normalizeEndpoint(endPoint){
    method endpointURL (line 50) | endpointURL(){
    method closeAndRetry (line 54) | closeAndRetry(code, reason, wasClean){
    method ontimeout (line 59) | ontimeout(){
    method isActive (line 64) | isActive(){ return this.readyState === SOCKET_STATES.open || this.read...
    method poll (line 66) | poll(){
    method send (line 137) | send(body){
    method batchSend (line 152) | batchSend(messages){
    method close (line 166) | close(code, reason, wasClean){
    method ajax (line 180) | ajax(method, headers, body, onCallerTimeout, callback){

FILE: assets/js/phoenix/presence.js
  class Presence (line 7) | class Presence {
    method constructor (line 9) | constructor(channel, opts = {}){
    method onJoin (line 46) | onJoin(callback){ this.caller.onJoin = callback }
    method onLeave (line 48) | onLeave(callback){ this.caller.onLeave = callback }
    method onSync (line 50) | onSync(callback){ this.caller.onSync = callback }
    method list (line 52) | list(by){ return Presence.list(this.state, by) }
    method inPendingSyncState (line 54) | inPendingSyncState(){
    method syncState (line 68) | static syncState(currentState, newState, onJoin, onLeave){
    method syncDiff (line 109) | static syncDiff(state, diff, onJoin, onLeave){
    method list (line 147) | static list(presences, chooser){
    method map (line 157) | static map(obj, func){
    method clone (line 161) | static clone(obj){ return JSON.parse(JSON.stringify(obj)) }

FILE: assets/js/phoenix/push.js
  class Push (line 8) | class Push {
    method constructor (line 9) | constructor(channel, event, payload, timeout){
    method resend (line 24) | resend(timeout){
    method send (line 33) | send(){
    method receive (line 51) | receive(status, callback){
    method reset (line 63) | reset(){
    method matchReceive (line 74) | matchReceive({status, response, _ref}){
    method cancelRefEvent (line 82) | cancelRefEvent(){
    method cancelTimeout (line 90) | cancelTimeout(){
    method startTimeout (line 98) | startTimeout(){
    method hasReceived (line 118) | hasReceived(status){
    method trigger (line 125) | trigger(status, response){

FILE: assets/js/phoenix/serializer.js
  method encode (line 11) | encode(msg, callback){
  method decode (line 20) | decode(rawPayload, callback){
  method binaryEncode (line 31) | binaryEncode(message){
  method binaryDecode (line 55) | binaryDecode(buffer){
  method decodePush (line 66) | decodePush(buffer, view, decoder){
  method decodeReply (line 81) | decodeReply(buffer, view, decoder){
  method decodeBroadcast (line 100) | decodeBroadcast(buffer, view, decoder){

FILE: assets/js/phoenix/socket.js
  class Socket (line 113) | class Socket {
    method constructor (line 114) | constructor(endPoint, opts = {}){
    method getLongPollTransport (line 210) | getLongPollTransport(){ return LongPoll }
    method replaceTransport (line 218) | replaceTransport(newTransport){
    method protocol (line 235) | protocol(){ return location.protocol.match(/^https/) ? "wss" : "ws" }
    method endPointURL (line 242) | endPointURL(){
    method disconnect (line 260) | disconnect(callback, code, reason){
    method connect (line 279) | connect(params){
    method log (line 298) | log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }
    method hasLogger (line 303) | hasLogger(){ return this.logger !== null }
    method onOpen (line 312) | onOpen(callback){
    method onClose (line 322) | onClose(callback){
    method onError (line 335) | onError(callback){
    method onMessage (line 345) | onMessage(callback){
    method ping (line 357) | ping(callback){
    method transportName (line 376) | transportName(transport){
    method transportConnect (line 392) | transportConnect(){
    method getSession (line 410) | getSession(key){ return this.sessionStore && this.sessionStore.getItem...
    method storeSession (line 412) | storeSession(key, val){ this.sessionStore && this.sessionStore.setItem...
    method connectWithFallback (line 414) | connectWithFallback(fallbackTransport, fallbackThreshold = 2500){
    method clearHeartbeats (line 461) | clearHeartbeats(){
    method onConnOpen (line 466) | onConnOpen(){
    method heartbeatTimeout (line 481) | heartbeatTimeout(){
    method resetHeartbeat (line 491) | resetHeartbeat(){
    method teardown (line 498) | teardown(callback, code, reason){
    method waitForBufferDone (line 524) | waitForBufferDone(conn, callback, tries = 1){
    method waitForSocketClosed (line 535) | waitForSocketClosed(conn, callback, tries = 1){
    method onConnClose (line 546) | onConnClose(event){
    method onConnError (line 561) | onConnError(error){
    method triggerChanError (line 576) | triggerChanError(){
    method connectionState (line 587) | connectionState(){
    method isConnected (line 599) | isConnected(){ return this.connectionState() === "open" }
    method remove (line 606) | remove(channel){
    method off (line 617) | off(refs){
    method channel (line 632) | channel(topic, chanParams = {}){
    method push (line 641) | push(data){
    method makeRef (line 658) | makeRef(){
    method sendHeartbeat (line 665) | sendHeartbeat(){
    method flushSendBuffer (line 672) | flushSendBuffer(){
    method onConnMessage (line 679) | onConnMessage(rawMessage){
    method leaveOpenTopic (line 703) | leaveOpenTopic(topic){

FILE: assets/js/phoenix/timer.js
  class Timer (line 18) | class Timer {
    method constructor (line 19) | constructor(callback, timerCalc){
    method reset (line 26) | reset(){
    method scheduleTimeout (line 34) | scheduleTimeout(){

FILE: assets/test/channel_test.js
  class WSMock (line 9) | class WSMock {
    method constructor (line 10) | constructor(url, protocols){
    method close (line 14) | close(){}
    method send (line 15) | send(){}
  method receiveSocketOpen (line 168) | receiveSocketOpen(){
  method receiveOk (line 289) | receiveOk(){
  method receiveTimeout (line 295) | receiveTimeout(){
  method receiveError (line 299) | receiveError(){
  method getBindings (line 304) | getBindings(event){
  method send (line 409) | send(){}

FILE: assets/test/presence_test.js
  method joins (line 14) | joins(){
  method leaves (line 17) | leaves(){
  method state (line 20) | state(){
  method on (line 33) | on(event, callback){
  method trigger (line 37) | trigger(event, data){
  method joinRef (line 41) | joinRef(){
  method simulateDisconnectAndReconnect (line 45) | simulateDisconnectAndReconnect(){

FILE: assets/test/socket_test.js
  method bufferedAmount (line 332) | get bufferedAmount(){ return 1 }
  method close (line 339) | close(_code, _reason){
  method send (line 345) | send(){}
  method bufferedAmount (line 396) | get bufferedAmount(){ return 1 }
  method close (line 403) | close(_code, _reason){
  method send (line 409) | send(){}
  class FakeTransport (line 911) | class FakeTransport { }

FILE: installer/lib/mix/tasks/local.phx.ex
  class Mix.Tasks.Local.Phx (line 1) | defmodule Mix.Tasks.Local.Phx
    method run (line 15) | def run(args) do

FILE: installer/lib/mix/tasks/phx.new.ecto.ex
  class Mix.Tasks.Phx.New.Ecto (line 1) | defmodule Mix.Tasks.Phx.New.Ecto
    method run (line 53) | def run([]) do
    method run (line 57) | def run([path | _] = args) do

FILE: installer/lib/mix/tasks/phx.new.ex
  class Mix.Tasks.Phx.New (line 1) | defmodule Mix.Tasks.Phx.New
    method run (line 189) | def run(argv) do
    method run (line 229) | def run(argv, generator, path) do
    method generate (line 238) | defp generate(base_path, generator, path, opts) do
    method validate_project (line 250) | defp validate_project(%Project{opts: opts} = project, path) do
    method maybe_prompt_to_install_deps (line 259) | defp maybe_prompt_to_install_deps(%Project{} = project, generator, pat...
    method prompt_to_install_deps (line 268) | defp prompt_to_install_deps(%Project{} = project, generator, path_key) do
    method maybe_cd (line 324) | defp maybe_cd(path, func), do: path && File.cd!(path, func)
    method install_mix (line 326) | defp install_mix(project, install?) do
    method print_missing_steps (line 334) | defp print_missing_steps(steps) do
    method print_ecto_info (line 343) | defp print_ecto_info(Web), do: :ok
    method print_ecto_info (line 345) | defp print_ecto_info(_gen) do
    method print_mix_info (line 353) | defp print_mix_info(Ecto) do
    method print_mix_info (line 361) | defp print_mix_info(_gen) do
    method relative_app_path (line 373) | defp relative_app_path(path) do
    method cmd (line 382) | defp cmd(%Project{} = project, cmd, opts \\ []) do
    method cmd_opts (line 395) | defp cmd_opts(%Project{} = project) do
    method check_app_name! (line 403) | defp check_app_name!(name, from_app_flag) do
    method validate_not_reserved (line 414) | defp validate_not_reserved(_name), do: :ok
    method validate_app_name_format (line 416) | defp validate_app_name_format(name, from_app_flag) do
    method check_module_name_validity! (line 435) | defp check_module_name_validity!(name) do
    method check_module_name_availability! (line 443) | defp check_module_name_availability!(name) do
    method check_directory_existence! (line 458) | defp check_directory_existence!(path) do
    method elixir_version_check! (line 467) | defp elixir_version_check! do
    method git_available? (line 476) | defp git_available? do
    method inside_git_repo? (line 483) | defp inside_git_repo?(path) do
    method maybe_init_git (line 492) | defp maybe_init_git(%Project{} = project, path_key) do
    method maybe_copy_cached_build (line 510) | defp maybe_copy_cached_build(%Project{} = project, path_key) do
    method copy_cached_build (line 523) | defp copy_cached_build(%{project_path: project_path, cache_dir: cache_...
    method maybe_warn_outdated (line 530) | defp maybe_warn_outdated(latest_version) do

FILE: installer/lib/mix/tasks/phx.new.web.ex
  class Mix.Tasks.Phx.New.Web (line 1) | defmodule Mix.Tasks.Phx.New.Web
    method run (line 33) | def run([]) do
    method run (line 37) | def run([path | _] = args) do

FILE: installer/lib/phx_new/ecto.ex
  class Phx.New.Ecto (line 1) | defmodule Phx.New.Ecto
    method prepare_project (line 20) | def prepare_project(%Project{} = project) do
    method generate (line 27) | def generate(%Project{} = project) do

FILE: installer/lib/phx_new/generator.ex
  class Phx.New.Generator (line 1) | defmodule Phx.New.Generator
    method generate_agents_md (line 119) | def generate_agents_md(%Project{} = project) do
    method config_inject (line 169) | def config_inject(path, file, to_inject) do
    method prod_only_config_inject (line 187) | def prod_only_config_inject(path, file, to_inject) do
    method write_formatted! (line 213) | defp write_formatted!(file, contents) do
    method inject_umbrella_config_defaults (line 219) | def inject_umbrella_config_defaults(project) do
    method in_umbrella? (line 230) | def in_umbrella?(app_path) do
    method put_binding (line 238) | def put_binding(%Project{opts: opts} = project) do
    method elixir_install_otp_bin_path (line 335) | def elixir_install_otp_bin_path do
    method elixir_install_bin_path (line 344) | def elixir_install_bin_path do
    method namespaced? (line 353) | defp namespaced?(project) do
    method gen_ecto_config (line 357) | def gen_ecto_config(%Project{project_path: project_path, binding: bind...
    method get_pubsub_server (line 386) | defp get_pubsub_server(module) do
    method get_ecto_adapter (line 393) | defp get_ecto_adapter("mssql", app, module) do
    method get_ecto_adapter (line 397) | defp get_ecto_adapter("mysql", app, module) do
    method get_ecto_adapter (line 401) | defp get_ecto_adapter("postgres", app, module) do
    method get_ecto_adapter (line 405) | defp get_ecto_adapter("sqlite3", app, module) do
    method get_ecto_adapter (line 409) | defp get_ecto_adapter(db, _app, _mod) do
    method get_web_adapter (line 413) | defp get_web_adapter("cowboy"),
    method get_web_adapter (line 418) | defp get_web_adapter("bandit"),
    method get_web_adapter (line 423) | defp get_web_adapter(other), do: Mix.raise("Unknown web adapter #{insp...
    method fs_db_config (line 425) | defp fs_db_config(app, module) do
    method socket_db_config (line 458) | defp socket_db_config(app, module, user, pass) do
    method kw_to_config (line 504) | defp kw_to_config(kw) do
    method adapter_generators (line 511) | defp adapter_generators(adapter_config) do
    method nil_if_empty (line 517) | defp nil_if_empty([]), do: nil
    method nil_if_empty (line 518) | defp nil_if_empty(other), do: other
    method phoenix_path (line 520) | defp phoenix_path(%Project{} = project, true = _dev, umbrella_root?) do
    method phoenix_path (line 536) | defp phoenix_path(%Project{}, false = _dev, _umbrella_root?) do
    method phoenix_path_prefix (line 540) | defp phoenix_path_prefix(%Project{in_umbrella?: false}, _), do: ".."
    method phoenix_path_prefix (line 541) | defp phoenix_path_prefix(%Project{in_umbrella?: true}, true = _umbrell...
    method phoenix_path_prefix (line 542) | defp phoenix_path_prefix(%Project{in_umbrella?: true}, false = _umbrel...
    method phoenix_dep (line 561) | defp phoenix_dep(path),
    method phoenix_js_path (line 564) | defp phoenix_js_path("deps/phoenix"), do: "phoenix"
    method phoenix_js_path (line 565) | defp phoenix_js_path(path), do: "../../#{path}/"
    method random_string (line 567) | defp random_string(length) do
    method maybe_heex_attr_gettext (line 582) | def maybe_heex_attr_gettext(message, gettext?) do
    method maybe_eex_gettext (line 600) | def maybe_eex_gettext(message, gettext?) do

FILE: installer/lib/phx_new/mailer.ex
  class Phx.New.Mailer (line 1) | defmodule Phx.New.Mailer
    method prepare_project (line 10) | def prepare_project(%Project{} = project) do
    method generate (line 17) | def generate(%Project{} = project) do

FILE: installer/lib/phx_new/project.ex
  class Phx.New.Project (line 1) | defmodule Phx.New.Project
    method new (line 22) | def new(project_path, opts) do
    method ecto? (line 37) | def ecto?(%Project{binding: binding}) do
    method html? (line 41) | def html?(%Project{binding: binding}) do
    method gettext? (line 45) | def gettext?(%Project{binding: binding}) do
    method live? (line 49) | def live?(%Project{binding: binding}) do
    method dashboard? (line 53) | def dashboard?(%Project{binding: binding}) do
    method javascript? (line 57) | def javascript?(%Project{binding: binding}) do
    method css? (line 61) | def css?(%Project{binding: binding}) do
    method mailer? (line 65) | def mailer?(%Project{binding: binding}) do
    method verbose? (line 69) | def verbose?(%Project{opts: opts}) do
    method expand_path_with_bindings (line 81) | defp expand_path_with_bindings(path, %Project{} = project) do

FILE: installer/lib/phx_new/single.ex
  class Phx.New.Single (line 1) | defmodule Phx.New.Single
    method put_app (line 119) | defp put_app(%Project{base_path: base_path} = project) do
    method put_root_app (line 123) | defp put_root_app(%Project{app: app, opts: opts} = project) do
    method put_web_app (line 131) | defp put_web_app(%Project{app: app} = project) do
    method generate (line 141) | def generate(%Project{} = project) do
    method gen_html (line 155) | def gen_html(project) do
    method gen_gettext (line 159) | def gen_gettext(project) do
    method gen_ecto (line 163) | def gen_ecto(project) do
    method gen_assets (line 168) | def gen_assets(%Project{} = project) do
    method gen_mailer (line 186) | def gen_mailer(%Project{} = project) do

FILE: installer/lib/phx_new/umbrella.ex
  class Phx.New.Umbrella (line 1) | defmodule Phx.New.Umbrella
    method put_app (line 27) | defp put_app(project) do
    method put_web (line 34) | def put_web(%Project{app: app, opts: opts} = project) do
    method put_root_app (line 48) | defp put_root_app(%Project{app: app} = project) do
    method generate (line 56) | def generate(%Project{} = project) do
    method maybe_generate_mailer (line 71) | defp maybe_generate_mailer(project) do

FILE: installer/lib/phx_new/web.ex
  class Phx.New.Web (line 1) | defmodule Phx.New.Web
    method generate (line 74) | def generate(%Project{} = project) do
    method gen_html (line 85) | defp gen_html(%Project{} = project) do
    method gen_gettext (line 89) | defp gen_gettext(%Project{} = project) do

FILE: installer/mix.exs
  class Phx.New.MixProject (line 6) | defmodule Phx.New.MixProject
    method project (line 22) | def project do
    method cli (line 53) | def cli do
    method application (line 57) | def application do
    method deps (line 63) | def deps do
    method docs (line 69) | defp docs do
    method aliases (line 75) | defp aliases do
    method copy_agents_md (line 84) | defp copy_agents_md(_) do

FILE: installer/test/mix_helper.exs
  class MixHelper (line 5) | defmodule MixHelper
    method tmp_path (line 9) | def tmp_path do
    method random_string (line 13) | defp random_string(len) do
    method in_tmp (line 17) | def in_tmp(which, function) do
    method in_tmp_project (line 30) | def in_tmp_project(which, function) do
    method in_tmp_umbrella_project (line 57) | def in_tmp_umbrella_project(which, function) do
    method in_project (line 82) | def in_project(app, path, fun) do
    method assert_file (line 97) | def assert_file(file) do
    method refute_file (line 101) | def refute_file(file) do
    method assert_file (line 105) | def assert_file(file, match) do
    method write_file! (line 129) | defp write_file!(content, path) do
    method with_generator_env (line 133) | def with_generator_env(app_name \\ :phoenix, new_env, fun) do
    method with_scope_env (line 147) | def with_scope_env(app_name, new_env, fun) do
    method umbrella_mixfile_contents (line 161) | def umbrella_mixfile_contents do
    method flush (line 180) | def flush do

FILE: installer/test/phx_new_ecto_test.exs
  class Mix.Tasks.Phx.New.EctoTest (line 3) | defmodule Mix.Tasks.Phx.New.EctoTest

FILE: installer/test/phx_new_test.exs
  class Mix.Tasks.Phx.NewTest (line 3) | defmodule Mix.Tasks.Phx.NewTest

FILE: installer/test/phx_new_umbrella_test.exs
  class Mix.Tasks.Phx.New.UmbrellaTest (line 3) | defmodule Mix.Tasks.Phx.New.UmbrellaTest
    method decline_prompt (line 16) | defp decline_prompt do
    method root_path (line 20) | defp root_path(app, path \\ "") do
    method app_path (line 24) | defp app_path(app, path) do
    method web_path (line 28) | defp web_path(app, path) do

FILE: installer/test/phx_new_web_test.exs
  class Mix.Tasks.Phx.New.WebTest (line 3) | defmodule Mix.Tasks.Phx.New.WebTest

FILE: integration_test/mix.exs
  class Phoenix.Integration.MixProject (line 6) | defmodule Phoenix.Integration.MixProject
    method project (line 9) | def project do
    method elixirc_paths (line 20) | defp elixirc_paths(:test), do: ["lib", "test/support"]
    method elixirc_paths (line 21) | defp elixirc_paths(_), do: ["lib"]
    method application (line 23) | def application do
    method deps (line 32) | defp deps do

FILE: integration_test/test/code_generation/app_with_defaults_test.exs
  class Phoenix.Integration.CodeGeneration.AppWithDefaultsTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.AppWithDefaultsTest

FILE: integration_test/test/code_generation/app_with_mssql_adapter_test.exs
  class Phoenix.Integration.CodeGeneration.AppWithMSSQLAdapterTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.AppWithMSSQLAdapterTest

FILE: integration_test/test/code_generation/app_with_mysql_adapter_test.exs
  class Phoenix.Integration.CodeGeneration.AppWithMySqlAdapterTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.AppWithMySqlAdapterTest

FILE: integration_test/test/code_generation/app_with_no_options_test.exs
  class Phoenix.Integration.CodeGeneration.AppWithNoOptionsTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.AppWithNoOptionsTest
    method run_phx_server (line 55) | defp run_phx_server(app_root_path) do
    method request_with_retries (line 71) | defp request_with_retries(url, retries)
    method request_with_retries (line 73) | defp request_with_retries(_url, 0), do: {:error, :out_of_retries}
    method request_with_retries (line 75) | defp request_with_retries(url, retries) do

FILE: integration_test/test/code_generation/app_with_scopes_test.exs
  class Phoenix.Integration.CodeGeneration.AppWithScopesTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.AppWithScopesTest

FILE: integration_test/test/code_generation/app_with_sqlite3_adapter_test.exs
  class Phoenix.Integration.CodeGeneration.AppWithSQLite3AdapterTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.AppWithSQLite3AdapterTest

FILE: integration_test/test/code_generation/umbrella_app_with_defaults_test.exs
  class Phoenix.Integration.CodeGeneration.UmbrellaAppWithDefaultsTest (line 1) | defmodule Phoenix.Integration.CodeGeneration.UmbrellaAppWithDefaultsTest

FILE: integration_test/test/support/code_generator_case.ex
  class Phoenix.Integration.CodeGeneratorCase (line 1) | defmodule Phoenix.Integration.CodeGeneratorCase
    method assert_dir (line 55) | def assert_dir(path) do
    method assert_file (line 59) | def assert_file(file) do
    method refute_file (line 63) | def refute_file(file) do
    method assert_file (line 67) | def assert_file(file, match) do
    method assert_tests_pass (line 84) | def assert_tests_pass(app_path) do
    method assert_passes_formatter_check (line 88) | def assert_passes_formatter_check(app_path) do
    method assert_no_compilation_warnings (line 92) | def assert_no_compilation_warnings(app_path) do
    method installer_tmp_path (line 114) | defp installer_tmp_path do
    method write_file! (line 134) | defp write_file!(content, path) do
    method get_app_root_path (line 138) | defp get_app_root_path(tmp_dir, app_name, opts) do
    method random_string (line 149) | defp random_string(len) do

FILE: lib/mix/phoenix.ex
  class Mix.Phoenix (line 1) | defmodule Mix.Phoenix
    method eval_from (line 11) | def eval_from(apps, source_file_path, binding) do
    method inflect (line 111) | def inflect(singular) do
    method check_module_name_availability! (line 136) | def check_module_name_availability!(name) do
    method base (line 151) | def base do
    method context_base (line 162) | def context_base(ctx_app) do
    method app_base (line 166) | defp app_base(app) do
    method otp_app (line 176) | def otp_app do
    method modules (line 183) | def modules do
    method beam_to_module (line 190) | defp beam_to_module(path) do
    method generator_paths (line 200) | def generator_paths do
    method in_umbrella? (line 207) | def in_umbrella?(app_path) do
    method context_app (line 263) | def context_app do
    method fetch_context_app (line 285) | defp fetch_context_app(this_otp_app) do
    method mix_app_path (line 318) | defp mix_app_path(app, this_otp_app) do
    method prompt_for_conflicts (line 347) | def prompt_for_conflicts(generator_files) do
    method web_module (line 376) | def web_module(base) do
    method to_text (line 384) | def to_text(data) do
    method prepend_newline (line 388) | def prepend_newline(string) do
    method ensure_live_view_compat! (line 395) | def ensure_live_view_compat!(generator_mod) do
    method maybe_heex_attr_gettext (line 415) | defp maybe_heex_attr_gettext(message, gettext?) do
    method maybe_eex_gettext (line 433) | defp maybe_eex_gettext(message, gettext?) do

FILE: lib/mix/phoenix/context.ex
  class Mix.Phoenix.Context (line 1) | defmodule Mix.Phoenix.Context
    method valid? (line 22) | def valid?(context) do
    method new (line 26) | def new(context_name, opts) do
    method new (line 30) | def new(context_name, %Schema{} = schema, opts) do
    method pre_existing? (line 64) | def pre_existing?(%Context{file: file}), do: File.exists?(file)
    method pre_existing_tests? (line 66) | def pre_existing_tests?(%Context{test_file: file}), do: File.exists?(f...
    method pre_existing_test_fixtures? (line 68) | def pre_existing_test_fixtures?(%Context{test_fixtures_file: file}), d...
    method function_count (line 70) | def function_count(%Context{file: file}) do
    method file_count (line 84) | def file_count(%Context{dir: dir}) do
    method web_module (line 91) | defp web_module do

FILE: lib/mix/phoenix/schema.ex
  class Mix.Phoenix.Schema (line 1) | defmodule Mix.Phoenix.Schema
    method valid_types (line 69) | def valid_types, do: @valid_types
    method valid? (line 71) | def valid?(schema) do
    method new (line 75) | def new(schema_name, schema_plural, cli_attrs, opts) do
    method default_param (line 167) | def default_param(%Schema{} = schema, action) do
    method extract_attr_flags (line 174) | def extract_attr_flags(cli_attrs) do
    method split_flags (line 185) | defp split_flags(["unique" | rest], name, attrs, uniques, redacts),
    method split_flags (line 188) | defp split_flags(["redact" | rest], name, attrs, uniques, redacts),
    method split_flags (line 191) | defp split_flags(rest, name, attrs, uniques, redacts),
    method attrs (line 197) | def attrs(attrs) do
    method live_form_value (line 219) | def live_form_value(%Date{} = date), do: Calendar.strftime(date, "%Y-%...
    method live_form_value (line 221) | def live_form_value(%Time{} = time), do: Calendar.strftime(time, "%H:%M")
    method live_form_value (line 223) | def live_form_value(%NaiveDateTime{} = naive) do
    method live_form_value (line 227) | def live_form_value(%DateTime{} = naive) do
    method live_form_value (line 231) | def live_form_value(value), do: value
    method invalid_form_value (line 242) | def invalid_form_value(%{day: _day, month: _month, year: _year} = _date),
    method invalid_form_value (line 245) | def invalid_form_value(%{hour: _hour, minute: _minute}), do: %{hour: 1...
    method invalid_form_value (line 246) | def invalid_form_value(true), do: false
    method invalid_form_value (line 247) | def invalid_form_value(_value), do: nil
    method failed_render_change_message (line 252) | def failed_render_change_message(_schema) do
    method type_for_migration (line 256) | def type_for_migration({:enum, _}), do: :string
    method type_for_migration (line 257) | def type_for_migration(other), do: other
    method format_fields_for_schema (line 259) | def format_fields_for_schema(schema) do
    method required_fields (line 269) | def required_fields(schema) do
    method type_and_opts_for_schema (line 273) | def type_and_opts_for_schema({:enum, opts}),
    method type_and_opts_for_schema (line 276) | def type_and_opts_for_schema(other), do: inspect(other)
    method maybe_redact_field (line 278) | def maybe_redact_field(true), do: ", redact: true"
    method maybe_redact_field (line 279) | def maybe_redact_field(false), do: ""
    method value (line 284) | def value(schema, field, value) do
    method inspect_value (line 290) | defp inspect_value(:decimal, value), do: "Decimal.new(\"#{value}\")"
    method inspect_value (line 291) | defp inspect_value(_type, value), do: inspect(value)
    method list_to_attr (line 293) | defp list_to_attr([key]), do: {String.to_atom(key), :string}
    method list_to_attr (line 294) | defp list_to_attr([key, value]), do: {String.to_atom(key), String.to_a...
    method list_to_attr (line 296) | defp list_to_attr([key, comp, value]) do
    method type_to_default (line 302) | defp type_to_default(key, t, :create) do
    method type_to_default (line 367) | defp type_to_default(key, t, :update) do
    method build_array_values (line 389) | defp build_array_values(:string, :create),
    method build_array_values (line 392) | defp build_array_values(:integer, :create),
    method build_array_values (line 395) | defp build_array_values(:string, :update),
    method build_array_values (line 398) | defp build_array_values(:integer, :update),
    method build_array_values (line 401) | defp build_array_values(_, _),
    method build_enum_values (line 404) | defp build_enum_values(values, action) do
    method build_utc_datetime_usec (line 412) | defp build_utc_datetime_usec,
    method build_utc_datetime (line 415) | defp build_utc_datetime,
    method build_utc_naive_datetime_usec (line 418) | defp build_utc_naive_datetime_usec,
    method build_utc_naive_datetime (line 421) | defp build_utc_naive_datetime,
    method validate_attr! (line 431) | defp validate_attr!({name, :datetime}), do: {name, :naive_datetime}
    method validate_attr! (line 433) | defp validate_attr!({name, :array}) do
    method validate_attr! (line 442) | defp validate_attr!({_name, :enum}), do: Mix.raise(@enum_missing_value...
    method validate_attr! (line 446) | defp validate_attr!({_, type}) do
    method partition_attrs_and_assocs (line 453) | defp partition_attrs_and_assocs(schema_module, attrs, scope) do
    method validate_scope_and_reference_conflict! (line 484) | defp validate_scope_and_reference_conflict!(
    method validate_scope_and_reference_conflict! (line 493) | defp validate_scope_and_reference_conflict!(_scope, _source), do: :ok
    method schema_defaults (line 495) | defp schema_defaults(attrs) do
    method string_attr (line 502) | defp string_attr(types) do
    method types (line 509) | defp types(attrs) do
    method translate_enum_vals (line 517) | def translate_enum_vals(vals) do
    method schema_type (line 524) | defp schema_type(:text), do: :string
    method schema_type (line 525) | defp schema_type(:uuid), do: Ecto.UUID
    method schema_type (line 527) | defp schema_type(val) do
    method indexes (line 535) | defp indexes(table, assocs, uniques) do
    method migration_defaults (line 547) | defp migration_defaults(attrs) do
    method sample_id (line 554) | defp sample_id(opts) do
    method route_helper (line 562) | defp route_helper(web_path, singular) do
    method route_prefix (line 568) | defp route_prefix(web_path, plural) do
    method migration_module (line 573) | defp migration_module do
    method fixture_unique_functions (line 580) | defp fixture_unique_functions(singular, uniques, attrs) do
    method fixture_params (line 619) | defp fixture_params(attrs, fixture_unique_functions) do

FILE: lib/mix/phoenix/scope.ex
  class Mix.Phoenix.Scope (line 1) | defmodule Mix.Phoenix.Scope
    method new! (line 22) | def new!(name, opts) do
    method scopes_from_config (line 44) | def scopes_from_config(otp_app) do
    method default_scope (line 53) | def default_scope(otp_app) do
    method scope_from_opts (line 79) | def scope_from_opts(_otp_app, _name, true), do: nil
    method scope_from_opts (line 81) | def scope_from_opts(otp_app, nil, _), do: default_scope(otp_app)
    method scope_from_opts (line 83) | def scope_from_opts(otp_app, name, _) do
    method route_prefix (line 153) | def route_prefix(_scope_key, _schema), do: ""

FILE: lib/mix/tasks/compile.phoenix.ex
  class Mix.Tasks.Compile.Phoenix (line 1) | defmodule Mix.Tasks.Compile.Phoenix
    method run (line 7) | def run(_args) do
    method touch (line 25) | def touch do
    method touch_if_exists (line 34) | defp touch_if_exists(path) do
    method modules_for_recompilation (line 38) | defp modules_for_recompilation(modules) do
    method phoenix_recompile? (line 44) | defp phoenix_recompile?(mod) do
    method modules_to_file_paths (line 48) | defp modules_to_file_paths(modules) do

FILE: lib/mix/tasks/phx.digest.clean.ex
  class Mix.Tasks.Phx.Digest.Clean (line 1) | defmodule Mix.Tasks.Phx.Digest.Clean
    method run (line 43) | def run(all_args) do

FILE: lib/mix/tasks/phx.digest.ex
  class Mix.Tasks.Phx.Digest (line 1) | defmodule Mix.Tasks.Phx.Digest
    method run (line 58) | def run(all_args) do

FILE: lib/mix/tasks/phx.ex
  class Mix.Tasks.Phx (line 1) | defmodule Mix.Tasks.Phx
    method run (line 25) | def run(args) do
    method general (line 32) | defp general() do

FILE: lib/mix/tasks/phx.gen.auth.ex
  class Mix.Tasks.Phx.Gen.Auth (line 1) | defmodule Mix.Tasks.Phx.Gen.Auth
    method run (line 174) | def run(args, test_opts \\ []) do
    method web_app_name (line 254) | defp web_app_name(%Context{} = context) do
    method validate_args! (line 260) | defp validate_args!([_, _, _]), do: :ok
    method validate_args! (line 262) | defp validate_args!(_) do
    method validate_required_dependencies! (line 266) | defp validate_required_dependencies! do
    method generated_with_no_html? (line 293) | defp generated_with_no_html? do
    method generated_with_no_assets_or_esbuild? (line 304) | defp generated_with_no_assets_or_esbuild? do
    method build_hashing_library! (line 312) | defp build_hashing_library!(opts) do
    method default_hashing_library_option (line 328) | defp default_hashing_library_option do
    method scope_config (line 335) | defp scope_config(context, requested_scope, assign_key) do
    method find_scope_name (line 360) | defp find_scope_name(context, existing_scopes) do
    method is_new_scope? (line 388) | defp is_new_scope?(existing_scopes, bin_key) do
    method new_scope (line 393) | defp new_scope(context, key, default_scope, assign_key) do
    method scope_config_string (line 411) | defp scope_config_string(context, key, default_scope, assign_key) do
    method prompt_for_conflicts (line 428) | defp prompt_for_conflicts(binding) do
    method prompt_for_scope_conflicts (line 436) | defp prompt_for_scope_conflicts(binding) do
    method files_to_be_generated (line 478) | defp files_to_be_generated(binding) do
    method remap_files (line 605) | defp remap_files(files) do
    method copy_new_files (line 609) | defp copy_new_files(%Context{} = context, binding, paths) do
    method inject_context_functions (line 619) | defp inject_context_functions(%Context{file: file} = context, paths, b...
    method inject_tests (line 628) | defp inject_tests(%Context{test_file: test_file} = context, paths, bin...
    method inject_context_test_fixtures (line 637) | defp inject_context_test_fixtures(
    method inject_conn_case_helpers (line 650) | defp inject_conn_case_helpers(%Context{} = context, paths, binding) do
    method inject_routes (line 660) | defp inject_routes(%Context{context_app: ctx_app} = context, paths, bi...
    method maybe_inject_mix_dependency (line 671) | defp maybe_inject_mix_dependency(%Context{context_app: ctx_app} = cont...
    method maybe_inject_router_import (line 703) | defp maybe_inject_router_import(%Context{context_app: ctx_app} = conte...
    method maybe_inject_router_plug (line 750) | defp maybe_inject_router_plug(%Context{context_app: ctx_app} = context...
    method maybe_inject_app_layout_menu (line 777) | defp maybe_inject_app_layout_menu(%Context{} = context, binding) do
    method get_layout_html_path (line 821) | defp get_layout_html_path(%Context{} = context) do
    method potential_layout_file_paths (line 827) | defp potential_layout_file_paths(%Context{context_app: ctx_app}) do
    method inject_hashing_config (line 835) | defp inject_hashing_config(context, %HashingLibrary{} = hashing_librar...
    method maybe_inject_scope_config (line 870) | defp maybe_inject_scope_config(%Context{} = context, binding) do
    method inject_scope_config (line 878) | defp inject_scope_config(%Context{} = context, binding) do
    method maybe_inject_agents_md (line 914) | defp maybe_inject_agents_md(%Context{} = context, paths, binding) do
    method print_shell_instructions (line 959) | defp print_shell_instructions(%Context{} = context) do
    method router_scope (line 978) | defp router_scope(%Context{schema: schema} = context) do
    method web_path_prefix (line 988) | defp web_path_prefix(%Schema{web_path: nil}), do: ""
    method web_path_prefix (line 989) | defp web_path_prefix(%Schema{web_path: web_path}), do: "/" <> web_path
    method inject_before_final_end (line 991) | defp inject_before_final_end(content_to_inject, file_path) do
    method read_file (line 1016) | defp read_file(file_path) do
    method timestamp (line 1032) | defp timestamp do
    method pad (line 1038) | defp pad(i), do: to_string(i)
    method get_ecto_adapter! (line 1042) | defp get_ecto_adapter!(%Schema{repo: repo}) do
    method print_injecting (line 1050) | defp print_injecting(file_path, suffix \\ []) do
    method print_unable_to_read_file_error (line 1054) | defp print_unable_to_read_file_error(file_path, help_text) do
    method raise_with_help (line 1067) | def raise_with_help(msg) do
    method raise_with_help (line 1071) | defp raise_with_help(msg, :general) do
    method raise_with_help (line 1089) | defp raise_with_help(msg, :phx_generator_args) do
    method raise_with_help (line 1104) | defp raise_with_help(msg, :hashing_lib) do
    method test_case_options (line 1119) | defp test_case_options(Ecto.Adapters.Postgres), do: ", async: true"
    method datetime_module (line 1122) | defp datetime_module(%{timestamp_type: :naive_datetime}), do: NaiveDat...
    method datetime_module (line 1123) | defp datetime_module(%{timestamp_type: :utc_datetime}), do: DateTime
    method datetime_module (line 1124) | defp datetime_module(%{timestamp_type: :utc_datetime_usec}), do: DateTime
    method datetime_now (line 1126) | defp datetime_now(%{timestamp_type: :naive_datetime}), do: "NaiveDateT...
    method datetime_now (line 1127) | defp datetime_now(%{timestamp_type: :utc_datetime}), do: "DateTime.utc...
    method datetime_now (line 1128) | defp datetime_now(%{timestamp_type: :utc_datetime_usec}), do: "DateTim...
    method put_live_option (line 1130) | defp put_live_option(schema) do

FILE: lib/mix/tasks/phx.gen.auth/hashing_library.ex
  class Mix.Tasks.Phx.Gen.Auth.HashingLibrary (line 1) | defmodule Mix.Tasks.Phx.Gen.Auth.HashingLibrary
    method build (line 13) | def build("bcrypt") do
    method build (line 26) | def build("pbkdf2") do
    method build (line 39) | def build("argon2") do
    method build (line 52) | def build(other) do

FILE: lib/mix/tasks/phx.gen.auth/injector.ex
  class Mix.Tasks.Phx.Gen.Auth.Injector (line 1) | defmodule Mix.Tasks.Phx.Gen.Auth.Injector
    method mix_dependency_inject (line 14) | def mix_dependency_inject(mixfile, dependency) do
    method do_mix_dependency_inject (line 23) | defp do_mix_dependency_inject(mixfile, dependency) do
    method test_config_help_text (line 78) | def test_config_help_text(file_path, %HashingLibrary{} = hashing_libra...
    method test_config_code (line 86) | defp test_config_code(%HashingLibrary{test_config: test_config}) do
    method router_plug_help_text (line 121) | def router_plug_help_text(file_path, binding) do
    method router_plug_code (line 133) | defp router_plug_code(binding) do
    method router_plug_name (line 137) | defp router_plug_name(binding) do
    method app_layout_menu_inject (line 144) | def app_layout_menu_inject(binding, template_str) do
    method app_layout_menu_help_text (line 156) | def app_layout_menu_help_text(file_path, binding) do
    method app_layout_menu_code_to_inject (line 169) | def app_layout_menu_code_to_inject(binding, padding \\ 4, newline \\ "...
    method formatting_info (line 200) | defp formatting_info(template, tag) do
    method app_layout_menu_inject_at_end_of_nav_tag (line 211) | defp app_layout_menu_inject_at_end_of_nav_tag(binding, file) do
    method app_layout_menu_inject_after_opening_body_tag (line 223) | defp app_layout_menu_inject_after_opening_body_tag(binding, file) do
    method inject_unless_contains (line 244) | def inject_unless_contains(code, dup_check, inject_fn) do
    method ensure_not_already_injected (line 283) | defp ensure_not_already_injected(file, inject) do
    method split_with_self (line 292) | defp split_with_self(contents, text) do
    method normalize_line_endings_to_file (line 300) | defp normalize_line_endings_to_file(code, file) do
    method get_line_ending (line 305) | defp get_line_ending(file) do

FILE: lib/mix/tasks/phx.gen.auth/migration.ex
  class Mix.Tasks.Phx.Gen.Auth.Migration (line 1) | defmodule Mix.Tasks.Phx.Gen.Auth.Migration
    method extensions (line 14) | defp extensions(Ecto.Adapters.Postgres) do
    method extensions (line 18) | defp extensions(_), do: []
    method column_definitions (line 20) | defp column_definitions(ecto_adapter) do
    method column_definition (line 26) | defp column_definition(:email, Ecto.Adapters.Postgres), do: "add :emai...
    method column_definition (line 27) | defp column_definition(:email, Ecto.Adapters.SQLite3), do: "add :email...
    method column_definition (line 28) | defp column_definition(:email, _), do: "add :email, :string, null: fal...
    method column_definition (line 30) | defp column_definition(:token, Ecto.Adapters.Postgres), do: "add :toke...
    method column_definition (line 32) | defp column_definition(:token, _), do: "add :token, :binary, null: fal...

FILE: lib/mix/tasks/phx.gen.cert.ex
  class Mix.Tasks.Phx.Gen.Cert (line 1) | defmodule Mix.Tasks.Phx.Gen.Cert
    method run (line 47) | def run(all_args) do
    method certificate_and_key (line 89) | def certificate_and_key(key_size, name, hostnames) do
    method print_shell_instructions (line 115) | defp print_shell_instructions(keyfile, certfile) do
    method generate_rsa_key (line 156) | defp generate_rsa_key(keysize, e) do
    method extract_public_key (line 164) | defp extract_public_key(rsa_private_key(modulus: m, publicExponent: e)...
    method new_cert (line 234) | defp new_cert(public_key, common_name, hostnames) do
    method rdn (line 270) | defp rdn(common_name) do
    method extensions (line 278) | defp extensions(public_key, hostnames) do
    method key_identifier (line 308) | defp key_identifier(public_key) do

FILE: lib/mix/tasks/phx.gen.channel.ex
  class Mix.Tasks.Phx.Gen.Channel (line 1) | defmodule Mix.Tasks.Phx.Gen.Channel
    method run (line 28) | def run(args) do
    method raise_with_help (line 97) | defp raise_with_help do
    method validate_args! (line 106) | defp validate_args!(args) do
    method valid_name? (line 114) | defp valid_name?(name) do
    method paths (line 118) | defp paths do

FILE: lib/mix/tasks/phx.gen.context.ex
  class Mix.Tasks.Phx.Gen.Context (line 1) | defmodule Mix.Tasks.Phx.Gen.Context
    method run (line 112) | def run(args) do
    method prompt_for_conflicts (line 138) | defp prompt_for_conflicts(context) do
    method build (line 145) | def build(args, opts \\ []) do
    method parse_opts (line 160) | defp parse_opts(args) do
    method put_context_app (line 171) | defp put_context_app(opts, nil), do: opts
    method put_context_app (line 173) | defp put_context_app(opts, string) do
    method files_to_be_generated (line 178) | def files_to_be_generated(%Context{schema: schema}) do
    method copy_new_files (line 187) | def copy_new_files(%Context{schema: schema} = context, paths, binding) do
    method ensure_context_file_exists (line 197) | def ensure_context_file_exists(%Context{file: file} = context, paths, ...
    method inject_schema_access (line 206) | defp inject_schema_access(%Context{file: file} = context, paths, bindi...
    method write_file (line 217) | defp write_file(content, file) do
    method ensure_test_file_exists (line 222) | def ensure_test_file_exists(%Context{test_file: test_file} = context, ...
    method inject_tests (line 231) | defp inject_tests(%Context{test_file: test_file} = context, paths, bin...
    method ensure_test_fixtures_file_exists (line 247) | def ensure_test_fixtures_file_exists(
    method inject_test_fixture (line 260) | defp inject_test_fixture(
    method maybe_print_unimplemented_fixture_functions (line 275) | defp maybe_print_unimplemented_fixture_functions(%Context{} = context) do
    method indent (line 297) | defp indent(string, spaces) do
    method inject_eex_before_final_end (line 311) | defp inject_eex_before_final_end(content_to_inject, file_path, binding...
    method print_shell_instructions (line 330) | def print_shell_instructions(%Context{schema: schema}) do
    method schema_access_template (line 338) | defp schema_access_template(%Context{schema: schema}) do
    method validate_args! (line 354) | defp validate_args!(
    method validate_args! (line 418) | defp validate_args!(_, _, help) do
    method raise_with_help (line 423) | def raise_with_help(msg) do
    method prompt_for_code_injection (line 445) | def prompt_for_code_injection(%Context{generate?: false}), do: :ok
    method prompt_for_code_injection (line 447) | def prompt_for_code_injection(%Context{} = context) do
    method merge_with_existing_context? (line 453) | defp merge_with_existing_context?(%Context{} = context) do
    method singularize (line 480) | defp singularize(1, plural), do: "1 " <> String.trim_trailing(plural, ...
    method singularize (line 481) | defp singularize(amount, plural), do: "#{amount} #{plural}"

FILE: lib/mix/tasks/phx.gen.embedded.ex
  class Mix.Tasks.Phx.Gen.Embedded (line 1) | defmodule Mix.Tasks.Phx.Gen.Embedded
    method run (line 39) | def run(args) do
    method build (line 54) | def build(args) do
    method validate_args! (line 68) | def validate_args!([schema | _] = args) do
    method validate_args! (line 75) | def validate_args!(_) do
    method raise_with_help (line 81) | def raise_with_help(msg) do
    method prompt_for_conflicts (line 93) | defp prompt_for_conflicts(schema) do
    method files_to_be_generated (line 100) | def files_to_be_generated(%Schema{} = schema) do
    method copy_new_files (line 105) | def copy_new_files(%Schema{} = schema, paths, binding) do

FILE: lib/mix/tasks/phx.gen.ex
  class Mix.Tasks.Phx.Gen (line 1) | defmodule Mix.Tasks.Phx.Gen
    method run (line 49) | def run(_args) do

FILE: lib/mix/tasks/phx.gen.html.ex
  class Mix.Tasks.Phx.Gen.Html (line 1) | defmodule Mix.Tasks.Phx.Gen.Html
    method run (line 121) | def run(args) do
    method prompt_for_conflicts (line 175) | defp prompt_for_conflicts(context) do
    method context_files (line 182) | defp context_files(%Context{generate?: true} = context) do
    method context_files (line 186) | defp context_files(%Context{generate?: false}) do
    method files_to_be_generated (line 191) | def files_to_be_generated(%Context{schema: schema, context_app: contex...
    method copy_new_files (line 214) | def copy_new_files(%Context{} = context, paths, binding) do
    method print_shell_instructions (line 222) | def print_shell_instructions(%Context{schema: schema, context_app: ctx...
    method inputs (line 260) | def inputs(%Schema{} = schema) do
    method default_options (line 318) | defp default_options({:array, :string}),
    method default_options (line 321) | defp default_options({:array, :integer}),
    method default_options (line 324) | defp default_options({:array, _}), do: []
    method label (line 326) | defp label(key), do: Phoenix.Naming.humanize(to_string(key))
    method scope_assign_route_prefix (line 335) | defp scope_assign_route_prefix(_), do: ""
    method indent_inputs (line 338) | def indent_inputs(inputs, column_padding) do

FILE: lib/mix/tasks/phx.gen.json.ex
  class Mix.Tasks.Phx.Gen.Json (line 1) | defmodule Mix.Tasks.Phx.Gen.Json
    method run (line 122) | def run(args) do
    method prompt_for_conflicts (line 174) | defp prompt_for_conflicts(context) do
    method context_files (line 181) | defp context_files(%Context{generate?: true} = context) do
    method context_files (line 185) | defp context_files(%Context{generate?: false}) do
    method files_to_be_generated (line 190) | def files_to_be_generated(%Context{schema: schema, context_app: contex...
    method copy_new_files (line 208) | def copy_new_files(%Context{} = context, paths, binding) do
    method print_shell_instructions (line 217) | def print_shell_instructions(%Context{schema: schema, context_app: ctx...

FILE: lib/mix/tasks/phx.gen.live.ex
  class Mix.Tasks.Phx.Gen.Live (line 1) | defmodule Mix.Tasks.Phx.Gen.Live
    method run (line 125) | def run(args) do
    method validate_context! (line 186) | defp validate_context!(context) do
    method prompt_for_conflicts (line 198) | defp prompt_for_conflicts(context) do
    method context_files (line 205) | defp context_files(%Context{generate?: true} = context) do
    method context_files (line 209) | defp context_files(%Context{generate?: false}) do
    method files_to_be_generated (line 213) | defp files_to_be_generated(%Context{schema: schema, context_app: conte...
    method copy_new_files (line 231) | defp copy_new_files(%Context{} = context, binding, paths) do
    method maybe_inject_imports (line 252) | defp maybe_inject_imports(%Context{context_app: ctx_app} = context) do
    method do_inject_imports (line 268) | defp do_inject_imports(context, file, file_path, inject) do
    method print_shell_instructions (line 298) | def print_shell_instructions(%Context{schema: schema, context_app: ctx...
    method maybe_print_upgrade_info (line 333) | defp maybe_print_upgrade_info do
    method live_route_instructions (line 344) | defp live_route_instructions(schema) do
    method inputs (line 362) | def inputs(%Schema{} = schema) do
    method default_options (line 423) | defp default_options({:array, :string}),
    method default_options (line 426) | defp default_options({:array, :integer}),
    method default_options (line 429) | defp default_options({:array, _}), do: []
    method label (line 431) | defp label(key), do: Phoenix.Naming.humanize(to_string(key))
    method scope_param (line 433) | defp scope_param(%{scope: nil}), do: ""
    method scope_param (line 438) | defp scope_param(_), do: "_scope"
    method scope_param_prefix (line 440) | defp scope_param_prefix(schema) do
    method scope_assign_route_prefix (line 452) | defp scope_assign_route_prefix(_), do: ""

FILE: lib/mix/tasks/phx.gen.notifier.ex
  class Mix.Tasks.Phx.Gen.Notifier (line 1) | defmodule Mix.Tasks.Phx.Gen.Notifier
    method run (line 36) | def run(args) do
    method build (line 67) | def build(args, help \\ __MODULE__) do
    method parse_opts (line 78) | defp parse_opts(args) do
    method put_context_app (line 89) | defp put_context_app(opts, nil), do: opts
    method put_context_app (line 91) | defp put_context_app(opts, string) do
    method validate_args! (line 95) | defp validate_args!([context, notifier | messages] = args, help) do
    method validate_args! (line 127) | defp validate_args!(_, help) do
    method valid_notifier? (line 131) | defp valid_notifier?(notifier) do
    method valid_message? (line 135) | defp valid_message?(message_name) do
    method raise_with_help (line 141) | def raise_with_help(msg) do
    method copy_new_files (line 159) | defp copy_new_files(%Context{} = context, binding, paths) do
    method files_to_be_generated (line 167) | defp files_to_be_generated(%Context{} = context) do
    method prompt_for_conflicts (line 174) | defp prompt_for_conflicts(context) do
    method maybe_print_mailer_installation_instructions (line 182) | def maybe_print_mailer_installation_instructions(%Context{} = context) do

FILE: lib/mix/tasks/phx.gen.presence.ex
  class Mix.Tasks.Phx.Gen.Presence (line 1) | defmodule Mix.Tasks.Phx.Gen.Presence
    method run (line 19) | def run([]) do
    method run (line 23) | def run([alias_name]) do
    method paths (line 66) | defp paths do

FILE: lib/mix/tasks/phx.gen.release.ex
  class Mix.Tasks.Phx.Gen.Release (line 1) | defmodule Mix.Tasks.Phx.Gen.Release
    method run (line 80) | def run(args) do
    method parse_args (line 186) | defp parse_args(args) do
    method ecto_instructions (line 200) | defp ecto_instructions(app) do
    method docker_instructions (line 208) | defp docker_instructions do
    method paths (line 219) | defp paths do
    method post_install_instructions (line 223) | defp post_install_instructions(path, matching, msg) do
    method ecto_sql_installed? (line 233) | defp ecto_sql_installed?, do: Mix.Project.deps_paths() |> Map.has_key?...
    method socket_db_adaptor_installed? (line 235) | defp socket_db_adaptor_installed? do
    method elixir_and_debian_vsn (line 242) | defp elixir_and_debian_vsn(elixir_vsn, otp_vsn) do
    method gen_docker (line 258) | defp gen_docker(binding, opts) do
    method ensure_app! (line 311) | defp ensure_app!(app) do
    method fetch_body! (line 319) | defp fetch_body!(url) do
    method protocol_versions (line 361) | defp protocol_versions do
    method otp_vsn (line 366) | def otp_vsn do

FILE: lib/mix/tasks/phx.gen.schema.ex
  class Mix.Tasks.Phx.Gen.Schema (line 1) | defmodule Mix.Tasks.Phx.Gen.Schema
    method run (line 160) | def run(args) do
    method prompt_for_conflicts (line 181) | defp prompt_for_conflicts(schema) do
    method build (line 188) | def build(args, parent_opts, help \\ __MODULE__) do
    method maybe_update_repo_module (line 201) | defp maybe_update_repo_module(opts) do
    method put_context_app (line 209) | defp put_context_app(opts, nil), do: opts
    method put_context_app (line 210) | defp put_context_app(opts, string) do
    method files_to_be_generated (line 215) | def files_to_be_generated(%Schema{} = schema) do
    method copy_new_files (line 220) | def copy_new_files(%Schema{context_app: ctx_app, repo: repo, opts: opt...
    method print_shell_instructions (line 249) | def print_shell_instructions(%Schema{} = schema) do
    method validate_args! (line 261) | def validate_args!([schema, plural | _] = args, help) do
    method validate_args! (line 271) | def validate_args!(_, help) do
    method raise_with_help (line 277) | def raise_with_help(msg) do
    method timestamp (line 289) | defp timestamp do
    method pad (line 294) | defp pad(i), do: to_string(i)

FILE: lib/mix/tasks/phx.gen.secret.ex
  class Mix.Tasks.Phx.Gen.Secret (line 1) | defmodule Mix.Tasks.Phx.Gen.Secret
    method run (line 16) | def run([]),    do: run(["64"])
    method run (line 17) | def run([int]), do: int |> parse!() |> random_string() |> Mix.shell()....
    method run (line 18) | def run([_|_]), do: invalid_args!()
    method parse! (line 20) | defp parse!(int) do
    method random_string (line 30) | defp random_string(_), do: Mix.raise "The secret should be at least 32...
    method invalid_args! (line 33) | defp invalid_args! do

FILE: lib/mix/tasks/phx.gen.socket.ex
  class Mix.Tasks.Phx.Gen.Socket (line 1) | defmodule Mix.Tasks.Phx.Gen.Socket
    method run (line 28) | def run(args) do
    method raise_with_help (line 82) | defp raise_with_help do
    method validate_args! (line 91) | defp validate_args!([name, "--from-channel", pre_existing_channel]) do
    method validate_args! (line 99) | defp validate_args!([name]) do
    method validate_args! (line 107) | defp validate_args!(_), do: raise_with_help()
    method valid_name? (line 109) | defp valid_name?(name) do
    method paths (line 113) | defp paths do

FILE: lib/mix/tasks/phx.routes.ex
  class Mix.Tasks.Phx.Routes (line 1) | defmodule Mix.Tasks.Phx.Routes
    method run (line 65) | def run(args, base \\ Mix.Phoenix.base()) do
    method get_url_info (line 92) | def get_url_info(url, {router_mod, opts}) do
    method endpoint (line 117) | defp endpoint(nil, base) do
    method endpoint (line 121) | defp endpoint(module, _base) do
    method router (line 125) | defp router(nil, base) do
    method router (line 156) | defp router(router_name, _base) do
    method loaded (line 161) | defp loaded(module) do
    method app_mod (line 165) | defp app_mod(base, name), do: Module.concat([base, name])
    method web_mod (line 167) | defp web_mod(base, name), do: Module.concat(["#{base}Web", name])
    method get_file_path (line 169) | defp get_file_path(module_name) do
    method get_line_number (line 175) | defp get_line_number(_, nil), do: nil
    method get_line_number (line 177) | defp get_line_number(module, function_name) do

FILE: lib/mix/tasks/phx.server.ex
  class Mix.Tasks.Phx.Server (line 1) | defmodule Mix.Tasks.Phx.Server
    method run (line 34) | def run(args) do
    method iex_running? (line 39) | defp iex_running? do
    method open_args (line 43) | defp open_args(args) do
    method run_args (line 52) | defp run_args do

FILE: lib/phoenix.ex
  class Phoenix (line 1) | defmodule Phoenix
    method start (line 10) | def start(_type, _args) do
    method json_library (line 47) | def json_library do
    method plug_init_mode (line 61) | def plug_init_mode do
    method warn_on_missing_json_library (line 65) | defp warn_on_missing_json_library do

FILE: lib/phoenix/channel.ex
  class Phoenix.Channel (line 1) | defmodule Phoenix.Channel
    method __on_definition__ (line 540) | def __on_definition__(_env, _kind, _name, _args, _guards, _body) do
    method broadcast (line 559) | def broadcast(socket, event, message) do
    method broadcast! (line 567) | def broadcast!(socket, event, message) do
    method broadcast_from (line 588) | def broadcast_from(socket, event, message) do
    method broadcast_from! (line 598) | def broadcast_from!(socket, event, message) do
    method push (line 628) | def push(socket, event, message) do
    method reply (line 678) | def reply({transport_pid, serializer, topic, ref, join_ref}, {status, ...
    method socket_ref (line 692) | def socket_ref(_socket) do
    method assert_joined! (line 698) | defp assert_joined!(%Socket{joined: true} = socket) do
    method assert_joined! (line 702) | defp assert_joined!(%Socket{joined: false}) do

FILE: lib/phoenix/channel/server.ex
  class Phoenix.Channel.Server (line 1) | defmodule Phoenix.Channel.Server
    method join (line 17) | def join(socket, channel, message, opts) do
    method socket (line 66) | def socket(pid) do
    method close (line 76) | def close(pid, timeout) do
    method dispatch (line 94) | def dispatch(subscribers, from, %Broadcast{event: event} = msg) do
    method dispatch (line 124) | def dispatch(entries, :none, message) do
    method dispatch (line 132) | def dispatch(entries, from, message) do
    method init (line 270) | def init({_endpoint, {pid, _}}) do
    method handle_call (line 275) | def handle_call(:socket, _from, socket) do
    method handle_call (line 280) | def handle_call(msg, from, socket) do
    method handle_cast (line 287) | def handle_cast(:close, socket) do
    method handle_cast (line 292) | def handle_cast(msg, socket) do
    method handle_info (line 299) | def handle_info({Phoenix.Channel, auth_payload, {pid, _} = from, socke...
    method handle_info (line 323) | def handle_info(%Message{topic: topic, event: "phx_leave", ref: ref}, ...
    method handle_info (line 327) | def handle_info(
    method handle_info (line 339) | def handle_info(
    method handle_info (line 347) | def handle_info(
    method handle_info (line 356) | def handle_info({:DOWN, ref, _, _, reason}, ref) do
    method handle_info (line 360) | def handle_info({:DOWN, _, _, transport_pid, reason}, %{transport_pid:...
    method handle_info (line 365) | def handle_info(msg, %{channel: channel} = socket) do
    method code_change (line 377) | def code_change(old, %{channel: channel} = socket, extra) do
    method terminate (line 386) | def terminate(reason, %{channel: channel} = socket) do
    method terminate (line 394) | def terminate(_reason, _socket) do
    method channel_join (line 400) | defp channel_join(channel, topic, auth_payload, socket) do
    method init_join (line 424) | defp init_join(socket, channel, topic) do
    method handle_result (line 451) | defp handle_result({:stop, reason, socket}, _callback) do
    method handle_result (line 462) | defp handle_result({:reply, resp, socket}, :handle_call) do
    method handle_result (line 471) | defp handle_result({:noreply, socket}, _callback) do
    method handle_result (line 475) | defp handle_result({:noreply, socket, timeout_or_hibernate}, _callback...
    method handle_result (line 479) | defp handle_result(result, :handle_in) do
    method handle_result (line 495) | defp handle_result(result, callback) do
    method send_socket_close (line 507) | defp send_socket_close(%{transport_pid: transport_pid}, reason) do
    method handle_in (line 513) | defp handle_in({:reply, reply, %Socket{} = socket}) do
    method handle_in (line 518) | defp handle_in({:stop, reason, reply, socket}) do
    method handle_in (line 523) | defp handle_in(other) do
    method handle_reply (line 542) | defp handle_reply(_socket, reply) do
    method warn_unexpected_msg (line 559) | defp warn_unexpected_msg(fun, arity, msg, channel) do

FILE: lib/phoenix/code_reloader.ex
  class Phoenix.CodeReloader (line 1) | defmodule Phoenix.CodeReloader
    method reload (line 55) | def reload(endpoint, opts \\ []) do
    method init (line 105) | def init(opts) do
    method call (line 112) | def call(conn, opts) do
    method template (line 125) | defp template(output) do
    method format_output (line 330) | defp format_output(output) do
    method remove_ansi_escapes (line 337) | defp remove_ansi_escapes(text) do

FILE: lib/phoenix/code_reloader/mix_listener.ex
  class Phoenix.CodeReloader.MixListener (line 1) | defmodule Phoenix.CodeReloader.MixListener
    method start_link (line 9) | def start_link(_opts) do
    method started? (line 14) | def started? do
    method purge (line 24) | def purge(apps) do
    method init (line 29) | def init({}) do
    method handle_call (line 34) | def handle_call({:purge, apps}, _from, state) do
    method handle_info (line 43) | def handle_info({:modules_compiled, info}, state) do
    method handle_info (line 62) | def handle_info(_message, state) do
    method purge_modules (line 66) | defp purge_modules(modules) do

FILE: lib/phoenix/code_reloader/proxy.ex
  class Phoenix.CodeReloader.Proxy (line 3) | defmodule Phoenix.CodeReloader.Proxy
    method start (line 7) | def start() do
    method diagnostics (line 11) | def diagnostics(proxy, diagnostics) do
    method stop (line 15) | def stop(proxy) do
    method init (line 21) | def init(:ok) do
    method handle_cast (line 25) | def handle_cast({:diagnostics, diagnostics}, output) do
    method handle_call (line 29) | def handle_call(:stop, _from, output) do
    method handle_info (line 33) | def handle_info(msg, output) do
    method put_chars (line 56) | defp put_chars(from, reply, chars, output) do
    method diagnostic_to_chars (line 61) | defp diagnostic_to_chars(%{severity: :error, message: "**" <> _ = mess...
    method diagnostic_to_chars (line 69) | defp diagnostic_to_chars(%{severity: severity, message: message}) do
    method position (line 73) | defp position({line, col}), do: ":#{line}:#{col}"
    method position (line 75) | defp position(_), do: ""

FILE: lib/phoenix/code_reloader/server.ex
  class Phoenix.CodeReloader.Server (line 1) | defmodule Phoenix.CodeReloader.Server
    method start_link (line 8) | def start_link(_) do
    method check_symlinks (line 12) | def check_symlinks do
    method reload! (line 16) | def reload!(endpoint, opts) do
    method sync (line 20) | def sync do
    method init (line 33) | def init(:ok) do
    method handle_call (line 37) | def handle_call(:check_symlinks, _from, state) do
    method handle_call (line 62) | def handle_call({:reload!, endpoint, opts}, from, state) do
    method handle_cast (line 116) | def handle_cast({:sync, pid, ref}, state) do
    method handle_info (line 121) | def handle_info(_, state) do
    method default_reloadable_apps (line 125) | defp default_reloadable_apps() do
    method os_symlink (line 133) | defp os_symlink({:win32, _}),
    method os_symlink (line 139) | defp os_symlink(_),
    method can_symlink? (line 142) | defp can_symlink?() do
    method load_backup (line 160) | defp load_backup(mod) do
    method read_backup (line 173) | defp read_backup(_path), do: :error
    method write_backup (line 175) | defp write_backup({:ok, path, file}), do: File.write!(path, file)
    method write_backup (line 176) | defp write_backup(:error), do: :ok
    method all_waiting (line 178) | defp all_waiting(acc, endpoint) do
    method mix_compile (line 210) | defp mix_compile(
    method mix_compile (line 252) | defp mix_compile({:error, _reason}, _, _, _, _, _) do
    method mix_compile_deps (line 259) | defp mix_compile_deps(
    method mix_compile_project (line 275) | defp mix_compile_project(nil, _, _, _, _, _, _), do: :ok
    method mix_compile_project (line 277) | defp mix_compile_project(
    method mix_compile_unless_stale_config (line 291) | defp mix_compile_unless_stale_config(compilers, compile_args, timestam...
    method mix_compile (line 320) | defp mix_compile(compilers, compile_args, config, consolidation_path) do
    method timestamp (line 367) | defp timestamp, do: System.system_time(:second)
    method purge_modules (line 369) | defp purge_modules(path) do
    method purge_module (line 382) | defp purge_module(module) do
    method proxy_io (line 387) | defp proxy_io(fun) do
    method run_compilers (line 402) | defp run_compilers([], _, status, diagnostics) do
    method run_compilers (line 406) | defp run_compilers([compiler | rest], args, status, diagnostics) do
    method run_compiler (line 417) | defp run_compiler(compiler, args) do
    method normalize (line 422) | defp normalize(result, name) do
    method with_logger_app (line 443) | defp with_logger_app(config, fun) do

FILE: lib/phoenix/config.ex
  class Phoenix.Config (line 1) | defmodule Phoenix.Config
    method start_link (line 14) | def start_link({module, config, defaults, opts}) do
    method put (line 22) | def put(module, key, value) do
    method permanent (line 31) | def permanent(module, key, value) do
    method cache (line 47) | def cache(module, key, fun) do
    method clear_cache (line 79) | def clear_cache(module) do
    method from_env (line 89) | def from_env(otp_app, module, defaults) do
    method fetch_config (line 95) | defp fetch_config(otp_app, module) do
    method merge (line 107) | def merge(a, b), do: Keyword.merge(a, b, &merger/3)
    method merger (line 109) | defp merger(_k, v1, v2) do
    method config_change (line 125) | def config_change(module, changed, removed) do
    method init (line 132) | def init({module, config, permanent}) do
    method handle_call (line 139) | def handle_call({:permanent, key, value}, _from, {module, permanent}) do
    method handle_call (line 144) | def handle_call({:config_change, changed, removed}, _from, {module, pe...
    method update (line 159) | defp update(module, config, permanent) do

FILE: lib/phoenix/controller.ex
  class Phoenix.Controller (line 1) | defmodule Phoenix.Controller
    method expand_alias (line 255) | defp expand_alias({:__aliases__, _, _} = alias, env),
    method expand_alias (line 258) | defp expand_alias(other, _env), do: other
    method action_name (line 322) | def action_name(conn), do: conn.private.phoenix_action
    method controller_module (line 328) | def controller_module(conn), do: conn.private.phoenix_controller
    method router_module (line 334) | def router_module(conn), do: conn.private.phoenix_router
    method endpoint_module (line 340) | def endpoint_module(conn), do: conn.private.phoenix_endpoint
    method view_template (line 347) | def view_template(conn) do
    method json (line 363) | def json(conn, data) do
    method allow_jsonp (line 392) | def allow_jsonp(conn, opts \\ []) do
    method json_response? (line 417) | defp json_response?(conn) do
    method jsonp_body (line 425) | defp jsonp_body(data, callback) do
    method validate_jsonp_callback! (line 439) | defp validate_jsonp_callback!(<<>>), do: :ok
    method validate_jsonp_callback! (line 441) | defp validate_jsonp_callback!(_),
    method text (line 455) | def text(conn, data) do
    method html (line 468) | def html(conn, data) do
    method url (line 499) | defp url(opts) do
    method validate_local_url (line 508) | defp validate_local_url("//" <> _ = to), do: raise_invalid_url(to)
    method validate_local_url (line 510) | defp validate_local_url("/" <> _ = to) do
    method validate_local_url (line 518) | defp validate_local_url(to), do: raise_invalid_url(to)
    method raise_invalid_url (line 521) | defp raise_invalid_url(url) do
    method put_view (line 540) | def put_view(%Plug.Conn{} = conn, module) do
    method put_private_view (line 557) | defp put_private_view(conn, priv_key, kind, value) do
    method put_new_view (line 586) | def put_new_view(%Plug.Conn{} = conn, module) do
    method view_module (line 603) | def view_module(conn, format \\ nil) do
    method put_layout (line 650) | def put_layout(%Plug.Conn{state: state} = conn, layout) do
    method put_private_layout (line 705) | defp put_private_layout(conn, private_key, kind, no_format) do
    method put_root_layout (line 792) | def put_root_layout(%Plug.Conn{state: state} = conn, layout) do
    method put_layout_formats (line 815) | def put_layout_formats(%Plug.Conn{} = conn, _formats) do
    method layout_formats (line 828) | def layout_formats(conn) do
    method layout (line 838) | def layout(conn, format \\ nil) do
    method root_layout (line 848) | def root_layout(conn, format \\ nil) do
    method get_private_layout (line 852) | defp get_private_layout(conn, priv_key, format) do
    method render (line 870) | def render(conn, template_or_assigns \\ [])
    method render (line 876) | def render(conn, assigns) do
    method render_and_send (line 978) | defp render_and_send(conn, format, template, assigns) do
    method render_with_layouts (line 988) | defp render_with_layouts(conn, view, template, format) do
    method template_render (line 1003) | defp template_render(view, template, format, assigns) do
    method template_render_to_iodata (line 1011) | defp template_render_to_iodata(view, template, format, assigns) do
    method prepare_assigns (line 1019) | defp prepare_assigns(conn, assigns, template, format) do
    method assigns_layout (line 1038) | defp assigns_layout(_conn, %{layout: layout}, _format), do: layout
    method assigns_layout (line 1040) | defp assigns_layout(conn, _assigns, format) do
    method send_resp (line 1094) | defp send_resp(conn, default_status, default_content_type, body) do
    method ensure_resp_content_type (line 1100) | defp ensure_resp_content_type(%Plug.Conn{resp_headers: resp_headers} =...
    method put_router_url (line 1131) | def put_router_url(conn, %URI{} = uri) do
    method put_static_url (line 1146) | def put_static_url(conn, %URI{} = uri) do
    method put_format (line 1164) | def put_format(conn, format), do: put_private(conn, :phoenix_format, t...
    method get_format (line 1174) | def get_format(conn) do
    method get_safe_format (line 1178) | defp get_safe_format(conn) do
    method send_download (line 1243) | def send_download(conn, kind, opts \\ [])
    method send_download (line 1245) | def send_download(conn, {:file, path}, opts) do
    method send_download (line 1255) | def send_download(conn, {:binary, contents}, opts) do
    method prepare_send_download (line 1264) | defp prepare_send_download(conn, filename, opts) do
    method encode_filename (line 1284) | defp encode_filename(filename, false), do: filename
    method encode_filename (line 1285) | defp encode_filename(filename, true), do: URI.encode(filename, &URI.ch...
    method get_disposition_type (line 1287) | defp get_disposition_type(:attachment), do: "attachment"
    method get_disposition_type (line 1288) | defp get_disposition_type(:inline), do: "inline"
    method get_disposition_type (line 1290) | defp get_disposition_type(other),
    method ajax? (line 1297) | defp ajax?(conn) do
    method warn_if_ajax (line 1304) | defp warn_if_ajax(conn) do
    method scrub_param (line 1349) | defp scrub_param(%{} = param) do
    method scrub_param (line 1359) | defp scrub_param(param) do
    method scrub? (line 1363) | defp scrub?(" " <> rest), do: scrub?(rest)
    method scrub? (line 1364) | defp scrub?(""), do: true
    method scrub? (line 1365) | defp scrub?(_), do: false
    method protect_from_forgery (line 1376) | def protect_from_forgery(conn, opts \\ []) do
    method put_secure_browser_headers (line 1403) | def put_secure_browser_headers(conn, headers \\ %{})
    method put_secure_browser_headers (line 1405) | def put_secure_browser_headers(conn, []) do
    method put_secure_defaults (line 1415) | defp put_secure_defaults(%Plug.Conn{resp_headers: resp_headers} = conn...
    method accepts (line 1521) | def accepts(conn, [_ | _] = accepted) do
    method handle_params_accept (line 1531) | defp handle_params_accept(conn, format, accepted) do
    method handle_header_accept (line 1550) | defp handle_header_accept(conn, [header | _], accepted) do
    method parse_header_accept (line 1558) | defp parse_header_accept(conn, [h | t], acc, accepted) do
    method parse_header_accept (line 1575) | defp parse_header_accept(conn, [], acc, accepted) do
    method parse_header_accept (line 1582) | defp parse_header_accept(conn, {_, _, exts}, accepted) do
    method parse_q (line 1588) | defp parse_q(args) do
    method parse_exts (line 1601) | defp parse_exts("*", "*"), do: "*/*"
    method parse_exts (line 1602) | defp parse_exts(type, "*"), do: type
    method parse_exts (line 1603) | defp parse_exts(type, subtype), do: MIME.extensions(type <> "/" <> sub...
    method find_format (line 1605) | defp find_format("*/*", accepted), do: Enum.fetch!(accepted, 0)
    method find_format (line 1607) | defp find_format(_type_range, []), do: nil
    method find_format (line 1609) | defp find_format(type_range, [h | t]) do
    method refuse (line 1619) | defp refuse(_conn, given, accepted) do
    method fetch_flash (line 1643) | def fetch_flash(conn, _opts \\ []) do
    method merge_flash (line 1680) | def merge_flash(conn, enumerable) do
    method put_flash (line 1701) | def put_flash(conn, key, message) do
    method get_flash (line 1723) | def get_flash(conn) do
    method get_flash (line 1739) | def get_flash(conn, key) do
    method status_message_from_template (line 1754) | def status_message_from_template(template) do
    method clear_flash (line 1767) | def clear_flash(conn) do
    method persist_flash (line 1774) | defp persist_flash(conn, value) do
    method current_path (line 1790) | def current_path(%Plug.Conn{query_string: ""} = conn) do
    method current_path (line 1794) | def current_path(%Plug.Conn{query_string: query_string} = conn) do
    method current_path (line 1826) | def current_path(%Plug.Conn{} = conn, params) do
    method normalized_request_path (line 1830) | defp normalized_request_path(%{path_info: info, script_name: script}) do
    method current_url (line 1842) | def current_url(%Plug.Conn{} = conn) do
    method current_url (line 1888) | def current_url(%Plug.Conn{} = conn, %{} = params) do
    method assign (line 1909) | def assign(conn, keyword_or_map_or_fun)
    method __plugs__ (line 1918) | def __plugs__(controller_module, opts) do

FILE: lib/phoenix/controller/pipeline.ex
  class Phoenix.Controller.Pipeline (line 1) | defmodule Phoenix.Controller.Pipeline
    method __action_fallback__ (line 39) | def __action_fallback__(plug, caller) do
    method validate_fallback (line 51) | def validate_fallback(plug, module, fallback) do
    method build_fallback (line 121) | defp build_fallback(:unregistered) do
    method build_fallback (line 125) | defp build_fallback({:module, plug}) do
    method build_fallback (line 134) | defp build_fallback({:function, plug}) do
    method __catch__ (line 144) | def __catch__(
    method __catch__ (line 155) | def __catch__(%Plug.Conn{} = conn, reason, _controller, _action, stack...
    method plug (line 178) | defp plug(plug, opts, guards, caller) do
    method expand_alias (line 200) | defp expand_alias({:__aliases__, _, _} = alias, env),
    method expand_alias (line 203) | defp expand_alias(other, _env), do: other
    method escape_guards (line 209) | defp escape_guards({left, meta, right}),
    method escape_guards (line 212) | defp escape_guards({left, right}),
    method escape_guards (line 215) | defp escape_guards([_ | _] = list),
    method escape_guards (line 218) | defp escape_guards(node),

FILE: lib/phoenix/debug.ex
  class Phoenix.Debug (line 1) | defmodule Phoenix.Debug
    method list_sockets (line 37) | def list_sockets do
    method keyfind (line 44) | defp keyfind(list, key) do
    method socket_process_dict (line 51) | defp socket_process_dict(pid) do
    method socket_process? (line 77) | def socket_process?(pid) do
    method channel_process? (line 86) | def channel_process?(pid) do
    method list_channels (line 125) | def list_channels(socket_pid) do
    method socket (line 159) | def socket(channel_pid) do

FILE: lib/phoenix/digester.ex
  class Phoenix.Digester (line 1) | defmodule Phoenix.Digester
    method now (line 9) | defp now() do
    method compile (line 20) | def compile(input_path, output_path, with_vsn?) do
    method filter_files (line 40) | defp filter_files(input_path) do
    method maybe_fixup_sourcemap (line 52) | defp maybe_fixup_sourcemap(sourcemap, files) do
    method fixup_sourcemap (line 60) | defp fixup_sourcemap(%{} = sourcemap, files) do
    method generate_latest (line 72) | defp generate_latest(files) do
    method load_compile_digests (line 82) | defp load_compile_digests(output_path) do
    method load_manifest (line 87) | defp load_manifest(output_path) do
    method migrate_manifest (line 100) | defp migrate_manifest(%{"version" => @manifest_version} = manifest, _o...
    method migrate_manifest (line 101) | defp migrate_manifest(_latest, _output_path), do: @empty_manifest
    method save_manifest (line 103) | defp save_manifest(files, latest, old_digests, output_path) do
    method write_manifest (line 115) | defp write_manifest(latest, digests, output_path) do
    method remove_manifest (line 130) | defp remove_manifest(output_path) do
    method generate_digests (line 134) | defp generate_digests(files) do
    method build_digest (line 144) | defp build_digest(file) do
    method manifest_join (line 154) | defp manifest_join(".", filename), do: filename
    method manifest_join (line 155) | defp manifest_join(path, filename), do: Path.join(path, filename)
    method compiled_file? (line 157) | defp compiled_file?(file_path) do
    method compressed_extensions (line 165) | defp compressed_extensions do
    method map_file (line 170) | defp map_file(file_path, input_path) do
    method write_to_disk (line 191) | defp write_to_disk(file, output_path) do
    method digested_contents (line 230) | defp digested_contents(file, latest, with_vsn?) do
    method digest_stylesheet_asset_references (line 244) | defp digest_stylesheet_asset_references(file, latest, with_vsn?) do
    method digest_javascript_asset_references (line 260) | defp digest_javascript_asset_references(file, latest) do
    method digest_javascript_map_asset_references (line 268) | defp digest_javascript_map_asset_references(file, latest) do
    method digested_url (line 276) | defp digested_url("/" <> relative_path, _file, latest, with_vsn?) do
    method digested_url (line 283) | defp digested_url(url, file, latest, with_vsn?) do
    method relative_digested_path (line 305) | defp relative_digested_path(digested_path, true),
    method relative_digested_path (line 308) | defp relative_digested_path(digested_path, false),
    method relative_digested_path (line 311) | defp relative_digested_path(digested_path),
    method absolute_digested_url (line 314) | defp absolute_digested_url(url, digested_path, true),
    method absolute_digested_url (line 317) | defp absolute_digested_url(url, digested_path, false),
    method absolute_digested_url (line 320) | defp absolute_digested_url(url, digested_path),
    method clean (line 335) | def clean(path, age, keep, now \\ now()) do
    method clean_all (line 356) | def clean_all(path) do
    method files_to_clean (line 376) | defp files_to_clean(latest, digests, max_age, keep) do
    method versions_to_clean (line 384) | defp versions_to_clean(versions, max_age, keep) do
    method group_by_logical_path (line 393) | defp group_by_logical_path(digests) do
    method remove_files (line 397) | defp remove_files(files, output_path) do
    method remove_compressed_files (line 407) | defp remove_compressed_files(files, output_path) do
    method remove_compressed_file (line 411) | defp remove_compressed_file(file, output_path) do

FILE: lib/phoenix/digester/compressor.ex
  class Phoenix.Digester.Compressor (line 1) | defmodule Phoenix.Digester.Compressor

FILE: lib/phoenix/digester/gzip.ex
  class Phoenix.Digester.Gzip (line 1) | defmodule Phoenix.Digester.Gzip
    method compress_file (line 7) | def compress_file(file_path, content) do
    method file_extensions (line 15) | def file_extensions do

FILE: lib/phoenix/endpoint.ex
  class Phoenix.Endpoint (line 1) | defmodule Phoenix.Endpoint
    method config (line 419) | defp config(opts) do
    method pubsub (line 437) | defp pubsub() do
    method plug (line 478) | defp plug() do
    method server (line 513) | defp server() do
    method __force_ssl__ (line 637) | def __force_ssl__(module, force_ssl) do
    method socket_paths (line 702) | defp socket_paths(endpoint, path, socket, opts) do
    method put_auth_token (line 769) | defp put_auth_token(true, enabled), do: [auth_token: enabled]
    method put_auth_token (line 770) | defp put_auth_token(opts, enabled), do: Keyword.put(opts, :auth_token,...
    method socket_path (line 772) | defp socket_path(path, config) do
    method maybe_validate_keys (line 802) | defp maybe_validate_keys(other, _), do: other

FILE: lib/phoenix/endpoint/cowboy2_adapter.ex
  class Phoenix.Endpoint.Cowboy2Adapter (line 1) | defmodule Phoenix.Endpoint.Cowboy2Adapter
    method child_specs (line 52) | def child_specs(endpoint, config) do
    method child_spec (line 78) | defp child_spec(scheme, endpoint, config, code_reloader?) do
    method start_link (line 98) | def start_link(scheme, endpoint, {m, f, [ref | _] = a}) do
    method info (line 119) | defp info(scheme, endpoint, ref) do
    method bound_address (line 124) | defp bound_address(scheme, ref) do
    method port_to_integer (line 137) | defp port_to_integer({:system, env_var}), do: port_to_integer(System.g...
    method server_info (line 141) | def server_info(endpoint, scheme) do
    method make_ref (line 152) | defp make_ref(endpoint, scheme) do

FILE: lib/phoenix/endpoint/render_errors.ex
  class Phoenix.Endpoint.RenderErrors (line 1) | defmodule Phoenix.Endpoint.RenderErrors
    method __catch__ (line 54) | def __catch__(%Plug.Conn{} = conn, kind, reason, stack, opts) do
    method instrument_render_and_send (line 69) | defp instrument_render_and_send(conn, kind, reason, stack, opts) do
    method error_conn (line 92) | defp error_conn(_conn, :error, %NoRouteError{conn: conn}), do: conn
    method error_conn (line 93) | defp error_conn(conn, _kind, _reason), do: conn
    method maybe_raise (line 95) | defp maybe_raise(:error, %NoRouteError{}, _stack), do: :ok
    method maybe_raise (line 96) | defp maybe_raise(kind, reason, stack), do: :erlang.raise(kind, reason,...
    method __debugger_banner__ (line 101) | def __debugger_banner__(_conn, _status, _kind, %NoRouteError{router: r...
    method __debugger_banner__ (line 108) | def __debugger_banner__(_conn, _status, _kind, _reason, _stack), do: nil
    method render (line 110) | defp render(conn, status, kind, reason, stack, opts) do
    method maybe_fetch_query_params (line 127) | defp maybe_fetch_query_params(%Plug.Conn{} = conn) do
    method fetch_view_format (line 137) | defp fetch_view_format(conn, opts) do
    method put_formats (line 156) | defp put_formats(conn, formats) do
    method status (line 192) | defp status(:error, error), do: Plug.Exception.status(error)
    method status (line 193) | defp status(:throw, _throw), do: 500
    method status (line 194) | defp status(:exit, _exit), do: 500

FILE: lib/phoenix/endpoint/supervisor.ex
  class Phoenix.Endpoint.Supervisor (line 1) | defmodule Phoenix.Endpoint.Supervisor
    method start_link (line 12) | def start_link(otp_app, mod, opts \\ []) do
    method init (line 28) | def init({otp_app, mod, opts}) do
    method pubsub_children (line 113) | defp pubsub_children(mod, conf) do
    method socket_children (line 139) | defp socket_children(endpoint, conf, fun) do
    method apply_or_ignore (line 148) | defp apply_or_ignore(socket, fun, args) do
    method check_origin_or_csrf_checked! (line 157) | defp check_origin_or_csrf_checked!(endpoint_conf, socket_opts) do
    method config_children (line 173) | defp config_children(mod, conf, default_conf) do
    method warmup_children (line 178) | defp warmup_children(mod) do
    method server_children (line 182) | defp server_children(mod, config, server?) do
    method watcher_children (line 202) | defp watcher_children(_mod, conf, server?) do
    method defaults (line 225) | defp defaults(otp_app, module) do
    method render_errors (line 259) | defp render_errors(module) do
    method config_change (line 269) | def config_change(endpoint, changed, removed) do
    method static_lookup (line 288) | def static_lookup(_endpoint, "//" <> _ = path) do
    method static_lookup (line 292) | def static_lookup(_endpoint, "/" <> _ = path) do
    method raise_invalid_path (line 304) | defp raise_invalid_path(path) do
    method host_to_binary (line 309) | defp host_to_binary({:system, env_var}), do: host_to_binary(System.get...
    method host_to_binary (line 310) | defp host_to_binary(host), do: host
    method port_to_integer (line 313) | defp port_to_integer({:system, env_var}), do: port_to_integer(System.g...
    method warn_on_deprecated_system_env_tuples (line 317) | defp warn_on_deprecated_system_env_tuples(otp_app, mod, conf, key) do
    method warmup (line 351) | def warmup(endpoint) do
    method warmup_persistent (line 368) | defp warmup_persistent(endpoint) do
    method empty_string_if_root (line 391) | defp empty_string_if_root("/"), do: ""
    method empty_string_if_root (line 392) | defp empty_string_if_root(other), do: other
    method build_url (line 394) | defp build_url(endpoint, url) do
    method warmup_static (line 418) | defp warmup_static(endpoint, %{"latest" => latest, "digests" => digest...
    method warmup_static (line 429) | defp warmup_static(_endpoint, _manifest) do
    method static_cache (line 433) | defp static_cache(digests, value, true) do
    method static_cache (line 437) | defp static_cache(digests, value, false) do
    method static_integrity (line 441) | defp static_integrity(nil), do: nil
    method static_integrity (line 442) | defp static_integrity(sha), do: "sha512-#{sha}"
    method cache_static_manifest (line 444) | defp cache_static_manifest(endpoint) do
    method log_access_url (line 468) | defp log_access_url(endpoint, conf) do
    method browser_open (line 474) | defp browser_open(endpoint, conf) do

FILE: lib/phoenix/endpoint/sync_code_reload_plug.ex
  class Phoenix.Endpoint.SyncCodeReloadPlug (line 1) | defmodule Phoenix.Endpoint.SyncCodeReloadPlug
    method init (line 16) | def init({endpoint, opts}), do: {endpoint, endpoint.init(opts)}
    method call (line 18) | def call(conn, {endpoint, opts}), do: do_call(conn, endpoint, opts, true)
    method do_call (line 20) | defp do_call(conn, endpoint, opts, retry?) do

FILE: lib/phoenix/endpoint/watcher.ex
  class Phoenix.Endpoint.Watcher (line 1) | defmodule Phoenix.Endpoint.Watcher
    method child_spec (line 5) | def child_spec(args) do
    method start_link (line 13) | def start_link({cmd, args}) do
    method watch (line 17) | def watch(_cmd, {mod, fun, args}) do
    method cd (line 59) | defp cd(opts), do: opts[:cd] || File.cwd!()

FILE: lib/phoenix/exceptions.ex
  class Phoenix.NotAcceptableError (line 1) | defmodule Phoenix.NotAcceptableError
  class Phoenix.MissingParamError (line 18) | defmodule Phoenix.MissingParamError
    method exception (line 34) | def exception([key: value]) do
  class Phoenix.ActionClauseError (line 41) | defmodule Phoenix.ActionClauseError
    method message (line 50) | def message(exception) do
    method blame (line 57) | def blame(exception, stacktrace) do

FILE: lib/phoenix/flash.ex
  class Phoenix.Flash (line 1) | defmodule Phoenix.Flash

FILE: lib/phoenix/logger.ex
  class Phoenix.Logger (line 1) | defmodule Phoenix.Logger
    method install (line 135) | def install do
    method duration (line 153) | def duration(duration) do
    method compile_filter (line 164) | def compile_filter({:compiled, _key, _value} = filter), do: filter
    method compile_filter (line 165) | def compile_filter({:discard, params}), do: compile_discard(params)
    method compile_filter (line 166) | def compile_filter({:keep, params}), do: {:keep, params}
    method compile_filter (line 167) | def compile_filter(params), do: compile_discard(params)
    method compile_discard (line 169) | defp compile_discard([]) do
    method filter_values (line 180) | def filter_values(values, filter \\ Application.get_env(:phoenix, :fil...
    method discard_values (line 191) | defp discard_values(%{} = map, key_match, value_match) do
    method discard_values (line 206) | defp discard_values([_ | _] = list, key_match, value_match) do
    method discard_values (line 210) | defp discard_values(other, _key_match, _value_match), do: other
    method keep_values (line 214) | defp keep_values(%{} = map, match) do
    method keep_values (line 224) | defp keep_values([_ | _] = list, match) do
    method keep_values (line 228) | defp keep_values(_other, _match), do: "[FILTERED]"
    method log_level (line 230) | defp log_level(nil, _conn), do: :info
    method phoenix_endpoint_start (line 240) | def phoenix_endpoint_start(_, _, %{conn: conn} = metadata, _) do
    method phoenix_endpoint_stop (line 254) | def phoenix_endpoint_stop(_, %{duration: duration}, %{conn: conn} = me...
    method connection_type (line 268) | defp connection_type(:set_chunked), do: "Chunked"
    method connection_type (line 269) | defp connection_type(_), do: "Sent"
    method phoenix_error_rendered (line 274) | def phoenix_error_rendered(_, _, %{log: false}, _), do: :ok
    method phoenix_error_rendered (line 276) | def phoenix_error_rendered(_, _, %{log: level, status: status, kind: k...
    method status_to_string (line 290) | defp status_to_string(status) do
    method error_banner (line 294) | defp error_banner(:error, %type{}), do: inspect(type)
    method error_banner (line 295) | defp error_banner(_kind, reason), do: inspect(reason)
    method phoenix_router_dispatch_start (line 300) | def phoenix_router_dispatch_start(_, _, %{log: false}, _), do: :ok
    method phoenix_router_dispatch_start (line 302) | def phoenix_router_dispatch_start(_, _, metadata, _) do
    method mfa (line 332) | defp mfa(mod, fun, arity),
    method params (line 335) | defp params(%Plug.Conn.Unfetched{}), do: "[UNFETCHED]"
    method params (line 336) | defp params(params), do: params |> filter_values() |> inspect()
    method phoenix_socket_connected (line 341) | def phoenix_socket_connected(_, _, %{log: false}, _), do: :ok
    method phoenix_socket_connected (line 343) | def phoenix_socket_connected(_, %{duration: duration}, %{log: level} =...
    method connect_result (line 368) | defp connect_result(:ok), do: "CONNECTED TO "
    method connect_result (line 369) | defp connect_result(:error), do: "REFUSED CONNECTION TO "
    method phoenix_socket_drain (line 372) | def phoenix_socket_drain(_, _, %{log: false}, _), do: :ok
    method phoenix_socket_drain (line 374) | def phoenix_socket_drain(
    method phoenix_channel_joined (line 395) | def phoenix_channel_joined(_, %{duration: duration}, %{socket: socket}...
    method join_result (line 410) | defp join_result(:ok), do: "JOINED "
    method join_result (line 411) | defp join_result(:error), do: "REFUSED JOIN "
    method phoenix_channel_handled_in (line 416) | def phoenix_channel_handled_in(_, %{duration: duration}, %{socket: soc...
    method channel_log (line 435) | defp channel_log(_log_option, %{topic: "phoenix" <> _}, _fun), do: :ok
    method channel_log (line 437) | defp channel_log(log_option, %{private: private}, fun) do

FILE: lib/phoenix/naming.ex
  class Phoenix.Naming (line 1) | defmodule Phoenix.Naming
    method resource_name (line 19) | def resource_name(alias, suffix \\ "") do
    method unsuffix (line 41) | def unsuffix(value, suffix) do
    method underscore (line 68) | def underscore(value), do: Macro.underscore(value)
    method to_lower_char (line 71) | defp to_lower_char(char), do: char
    method camelize (line 94) | def camelize(value), do: Macro.camelize(value)
    method camelize (line 97) | def camelize("", :lower), do: ""
    method camelize (line 98) | def camelize(<<?_, t :: binary>>, :lower) do
    method camelize (line 101) | def camelize(<<h, _t :: binary>> = value, :lower) do

FILE: lib/phoenix/presence.ex
  class Phoenix.Presence (line 1) | defmodule Phoenix.Presence
    method start_link (line 453) | def start_link(module, task_supervisor, opts) do
    method init (line 475) | def init({module, task_supervisor, pubsub_server, dispatcher}) do
    method handle_diff (line 517) | def handle_diff(diff, state) do
    method handle_info (line 522) | def handle_info({task_ref, {:phoenix, ref, computed_diffs}}, state) do
    method list (line 548) | def list(module, topic) do
    method get_by_key (line 558) | def get_by_key(module, topic, key) do
    method group (line 573) | def group(presences) do
    method send_continue (line 583) | defp send_continue(%Task{} = task, ref), do: send(task.pid, {ref, :con...
    method next_task (line 585) | defp next_task(state) do
    method do_handle_metas (line 596) | defp do_handle_metas(state, computed_diffs) do
    method async_merge (line 620) | defp async_merge(state, diff) do
    method merge_diff (line 646) | defp merge_diff(topics, topic, %{leaves: leaves, joins: joins} = _diff...
    method handle_join (line 667) | defp handle_join({joined_key, presence}, {topics, topic}) do
    method handle_leave (line 672) | defp handle_leave({left_key, presence}, {topics, topic}) do
    method add_new_presence_or_metas (line 676) | defp add_new_presence_or_metas(
    method remove_presence_or_metas (line 700) | defp remove_presence_or_metas(
    method add_new_topic (line 719) | defp add_new_topic(topics, topic) do
    method remove_topic (line 723) | defp remove_topic(topics, topic) do
    method topic_presences_count (line 727) | defp topic_presences_count(topics, topic) do
  class Tracker (line 427) | defmodule Tracker
    method start_link (line 431) | def start_link({module, task_supervisor, opts}) do
    method init (line 444) | def init(state), do: Phoenix.Presence.init(state)
    method handle_diff (line 446) | def handle_diff(diff, state), do: Phoenix.Presence.handle_diff(diff, s...
    method handle_info (line 448) | def handle_info(msg, state),

FILE: lib/phoenix/router.ex
  class Phoenix.Router (line 1) | defmodule Phoenix.Router
    method prelude (line 297) | defp prelude(opts) do
    method defs (line 321) | defp defs() do
    method __call__ (line 381) | def __call__(
    method __call__ (line 396) | def __call__(%{private: %{phoenix_bypass: :all}} = conn, metadata, pre...
    method __call__ (line 400) | def __call__(conn, metadata, prepare, pipeline, {plug, opts}) do
    method match_dispatch (line 441) | defp match_dispatch() do
    method verified_routes (line 473) | defp verified_routes() do
    method build_verify (line 568) | defp build_verify(path, routes_per_path) do
    method build_match (line 594) | defp build_match({route, expr}, {acc_pipes, known_pipes}) do
    method build_match_pipes (line 621) | defp build_match_pipes(route, acc_pipes, known_pipes) do
    method build_metadata (line 636) | defp build_metadata(route, path_params) do
    method build_pipes (line 657) | defp build_pipes(name, []) do
    method build_pipes (line 663) | defp build_pipes(name, pipe_through) do
    method add_route (line 738) | defp add_route(kind, verb, path, plug, plug_opts, options) do
    method expand_plug_and_opts (line 840) | defp expand_plug_and_opts(plug, opts, caller) do
    method expand_alias (line 860) | defp expand_alias({:__aliases__, _, _} = alias, env),
    method expand_alias (line 863) | defp expand_alias(other, _env), do: other
    method add_resources (line 1021) | defp add_resources(path, controller, options, do: context) do
    method do_scope (line 1147) | defp do_scope(options, context) do
    method scoped_alias (line 1172) | def scoped_alias(router_module, alias) do
    method scoped_path (line 1180) | def scoped_path(router_module, path) do
    method routes (line 1228) | def routes(router) do
    method __formatted_routes__ (line 1275) | def __formatted_routes__(router) do
    method __verified_route__? (line 1313) | def __verified_route__?(router, split_path) do
  class NoRouteError (line 2) | defmodule NoRouteError
    method exception (line 8) | def exception(opts) do
  class MalformedURIError (line 21) | defmodule MalformedURIError

FILE: lib/phoenix/router/console_formatter.ex
  class Phoenix.Router.ConsoleFormatter (line 1) | defmodule Phoenix.Router.ConsoleFormatter
    method format (line 12) | def format(router, endpoint \\ nil) do
    method format_endpoint (line 23) | defp format_endpoint(nil, _router), do: ""
    method format_endpoint (line 25) | defp format_endpoint(endpoint, widths) do
    method format_websocket (line 37) | defp format_websocket({_path, Phoenix.LiveReloader.Socket, _opts}, _),...
    method format_websocket (line 39) | defp format_websocket({path, module, opts}, widths) do
    method format_longpoll (line 56) | defp format_longpoll({_path, Phoenix.LiveReloader.Socket, _opts}, _), ...
    method format_longpoll (line 58) | defp format_longpoll({path, module, opts}, widths) do
    method calculate_column_widths (line 77) | defp calculate_column_widths(router, routes, endpoint) do
    method format_route (line 104) | defp format_route(route, router, column_widths) do
    method route_name (line 124) | defp route_name(_router, nil), do: ""
    method route_name (line 126) | defp route_name(router, name) do
    method verb_name (line 134) | defp verb_name(verb), do: verb |> to_string() |> String.upcase()
    method socket_verbs (line 136) | defp socket_verbs(socket_opts) do

FILE: lib/phoenix/router/helpers.ex
  class Phoenix.Router.Helpers (line 1) | defmodule Phoenix.Router.Helpers
    method define (line 11) | def define(env, routes) do
    method defhelper (line 250) | def defhelper(%Route{} = route, exprs) do
    method defhelper_catch_all (line 270) | def defhelper_catch_all({helper, routes_and_exprs}) do
    method raise_route_error (line 306) | def raise_route_error(mod, fun, arity, action, routes, params) do
    method invalid_route_error (line 321) | defp invalid_route_error(prelude, fun, routes) do
    method invalid_param_error (line 332) | defp invalid_param_error(mod, fun, arity, action, routes) do
    method encode_param (line 350) | def encode_param(str), do: URI.encode(str, &URI.char_unreserved?/1)
    method expand_segments (line 352) | defp expand_segments([]), do: "/"
    method expand_segments (line 358) | defp expand_segments(segments) do
    method expand_segments (line 362) | defp expand_segments([{:|, _, [h, t]}], acc),
    method expand_segments (line 373) | defp expand_segments([h | t], acc),
    method expand_segments (line 380) | defp expand_segments([], acc),

FILE: lib/phoenix/router/resource.ex
  class Phoenix.Router.Resource (line 1) | defmodule Phoenix.Router.Resource
    method extract_actions (line 51) | defp extract_actions(opts, singleton) do
    method validate_actions (line 68) | defp validate_actions(type, singleton, actions) do
    method default_actions (line 84) | defp default_actions(true = _singleton),  do: @actions -- [:index]
    method default_actions (line 85) | defp default_actions(false = _singleton), do: @actions

FILE: lib/phoenix/router/route.ex
  class Phoenix.Router.Route (line 1) | defmodule Phoenix.Router.Route
    method init (line 48) | def init(opts), do: opts
    method call (line 51) | def call(%{path_info: path, script_name: script} = conn, {fwd_segments...
    method exprs (line 121) | def exprs(route) do
    method build_host_match (line 135) | def build_host_match([]), do: [Plug.Router.Utils.build_host_match(nil)]
    method build_host_match (line 137) | def build_host_match([_ | _] = hosts) do
    method verb_match (line 141) | defp verb_match(:*), do: Macro.var(:_verb, nil)
    method verb_match (line 142) | defp verb_match(verb), do: verb |> to_string() |> String.upcase()
    method build_path_params (line 144) | defp build_path_params(binding), do: {:%{}, [], binding}
    method build_path_and_binding (line 146) | defp build_path_and_binding(%Route{path: path} = route) do
    method rewrite_segments (line 157) | defp rewrite_segments(segments) do
    method build_prepare (line 172) | defp build_prepare(route) do
    method build_prepare_expr (line 188) | defp build_prepare_expr(key, data) do
    method build_dispatch (line 194) | defp build_dispatch(%Route{kind: :match, plug: plug, plug_opts: plug_o...
    method build_dispatch (line 200) | defp build_dispatch(%Route{
    method build_params (line 214) | defp build_params() do
    method merge_params (line 230) | def merge_params(%Plug.Conn.Unfetched{}, path_params), do: path_params
    method merge_params (line 231) | def merge_params(params, path_params), do: Map.merge(params, path_params)

FILE: lib/phoenix/router/scope.ex
  class Phoenix.Router.Scope (line 1) | defmodule Phoenix.Router.Scope
    method init (line 22) | def init(module) do
    method route (line 31) | def route(line, module, kind, verb, path, plug, plug_opts, opts) do
    method validate_forward! (line 93) | defp validate_forward!(_, plug) do
    method validate_path (line 100) | def validate_path("/" <> _ = path), do: path
    method validate_path (line 107) | def validate_path(path) do
    method pipe_through (line 121) | def pipe_through(module, new_pipes) do
    method deprecated_trailing_slash (line 178) | defp deprecated_trailing_slash(opts, top) do
    method validate_hosts! (line 194) | defp validate_hosts!(nil), do: []
    method validate_hosts! (line 205) | defp validate_hosts!(invalid), do: raise_invalid_host(invalid)
    method raise_invalid_host (line 207) | defp raise_invalid_host(host) do
    method append_unless_false (line 212) | defp append_unless_false(top, opts, key, fun) do
    method pop (line 223) | def pop(module) do
    method expand_alias (line 233) | def expand_alias(module, alias) do
    method full_path (line 240) | def full_path(module, path) do
    method join (line 251) | defp join(top, path, alias, alias?, as, private, assigns) do
    method join_path (line 263) | defp join_path(top, path) do
    method join_as (line 274) | defp join_as(_top, nil), do: nil
    method get_top (line 277) | defp get_top(module) do
    method update_stack (line 281) | defp update_stack(module, fun) do
    method update_pipes (line 285) | defp update_pipes(module, fun) do
    method put_top (line 289) | defp put_top(module, value) do
    method get_attribute (line 294) | defp get_attribute(module, attr) do
    method update_attribute (line 299) | defp update_attribute(module, attr, fun) do

FILE: lib/phoenix/socket.ex
  class Phoenix.Socket (line 1) | defmodule Phoenix.Socket
    method assign (line 342) | def assign(%Socket{} = socket, key, value) do
    method expand_alias (line 412) | defp expand_alias({:__aliases__, _, _} = alias, env),
    method expand_alias (line 415) | defp expand_alias(other, _env), do: other
    method to_topic_match (line 442) | defp to_topic_match(topic_pattern) do
    method defchannel (line 450) | defp defchannel(topic_match, channel_module, opts) do
    method __child_spec__ (line 458) | def __child_spec__(handler, opts, socket_options) do
    method __drainer_spec__ (line 466) | def __drainer_spec__(handler, opts, socket_options) do
    method __connect__ (line 488) | def __connect__(user_socket, map, socket_options) do
    method result (line 527) | defp result({:ok, _}), do: :ok
    method result (line 528) | defp result(:error), do: :error
    method result (line 529) | defp result({:error, _}), do: :error
    method set_label (line 531) | defp set_label(socket) do
    method __init__ (line 536) | def __init__({state, %{id: id, endpoint: endpoint} = socket}) do
    method __in__ (line 542) | def __in__({payload, opts}, {state, socket}) do
    method __info__ (line 547) | def __info__({:DOWN, ref, _, pid, reason}, {state, socket}) do
    method __info__ (line 558) | def __info__(%Broadcast{event: "disconnect"}, state) do
    method __info__ (line 562) | def __info__(:socket_drain, state) do
    method __info__ (line 567) | def __info__({:socket_push, opcode, payload}, state) do
    method __info__ (line 571) | def __info__({:socket_close, pid, _reason}, state) do
    method __info__ (line 575) | def __info__(:garbage_collect, state) do
    method __info__ (line 580) | def __info__({:debug_channels, ref, reply_to}, {state, socket}) do
    method __info__ (line 590) | def __info__(_, state) do
    method __terminate__ (line 594) | def __terminate__(_reason, _state_socket) do
    method user_connect (line 622) | defp user_connect(handler, endpoint, transport, serializer, params, co...
    method handle_in (line 684) | defp handle_in(_, %{ref: ref, topic: "phoenix", event: "heartbeat"}, s...
    method handle_in (line 695) | defp handle_in(
    method handle_in (line 734) | defp handle_in({pid, _ref, status}, %{event: "phx_join", topic: topic}...
    method handle_in (line 753) | defp handle_in({pid, _ref, _status}, %{event: "phx_leave"} = msg, stat...
    method handle_in (line 768) | defp handle_in({pid, _ref, _status}, msg, state, socket) do
    method handle_in (line 783) | defp handle_in(
    method handle_in (line 800) | defp handle_in(nil, message, state, socket) do
    method put_channel (line 806) | defp put_channel(state, pid, topic, join_ref) do
    method delete_channel (line 817) | defp delete_channel(state, pid, topic, monitor_ref) do
    method encode_on_exit (line 828) | defp encode_on_exit(socket, topic, ref, _reason) do
    method encode_ignore (line 833) | defp encode_ignore(socket, %{ref: ref, topic: topic}) do
    method encode_reply (line 838) | defp encode_reply(%{serializer: serializer}, message) do
    method encode_close (line 843) | defp encode_close(socket, topic, join_ref) do
    method shutdown_duplicate_channel (line 855) | defp shutdown_duplicate_channel(pid) do
    method socket_close (line 868) | defp socket_close(pid, {state, socket}) do
    method update_channel_status (line 880) | defp update_channel_status(state, pid, topic, status) do
  class InvalidMessageError (line 250) | defmodule InvalidMessageError

FILE: lib/phoenix/socket/message.ex
  class Phoenix.Socket.Message (line 1) | defmodule Phoenix.Socket.Message
  class Phoenix.Socket.Reply (line 53) | defmodule Phoenix.Socket.Reply
  class Phoenix.Socket.Broadcast (line 71) | defmodule Phoenix.Socket.Broadcast

FILE: lib/phoenix/socket/pool_supervisor.ex
  class Phoenix.Socket.PoolSupervisor (line 1) | defmodule Phoenix.Socket.PoolSupervisor
    method start_link (line 5) | def start_link({endpoint, name, partitions}) do
    method start_child (line 13) | def start_child(socket, key, spec) do
    method start_pooled (line 35) | def start_pooled(ref, i) do
    method init (line 47) | def init({endpoint, name, partitions}) do
  class Phoenix.Socket.PoolDrainer (line 66) | defmodule Phoenix.Socket.PoolDrainer
    method child_spec (line 70) | def child_spec({_endpoint, name, opts} = tuple) do
    method start_link (line 81) | def start_link(tuple) do
    method init (line 86) | def init({endpoint, name, opts}) do
    method terminate (line 95) | def terminate(_reason, {endpoint, name, size, interval, log_level}) do

FILE: lib/phoenix/socket/serializer.ex
  class Phoenix.Socket.Serializer (line 1) | defmodule Phoenix.Socket.Serializer

FILE: lib/phoenix/socket/serializers/v1_json_serializer.ex
  class Phoenix.Socket.V1.JSONSerializer (line 1) | defmodule Phoenix.Socket.V1.JSONSerializer
    method fastlane! (line 8) | def fastlane!(%Broadcast{} = msg) do
    method encode! (line 14) | def encode!(%Reply{} = reply) do
    method encode! (line 25) | def encode!(%Message{} = map) do
    method decode! (line 30) | def decode!(message, _opts) do
    method encode_v1_fields_only (line 42) | defp encode_v1_fields_only(%Message{} = msg) do

FILE: lib/phoenix/socket/serializers/v2_json_serializer.ex
  class Phoenix.Socket.V2.JSONSerializer (line 1) | defmodule Phoenix.Socket.V2.JSONSerializer
    method fastlane! (line 12) | def fastlane!(%Broadcast{payload: {:binary, data}} = msg) do
    method fastlane! (line 28) | def fastlane!(%Broadcast{payload: %{}} = msg) do
    method fastlane! (line 33) | def fastlane!(%Broadcast{payload: invalid}) do
    method encode! (line 38) | def encode!(%Reply{payload: {:binary, data}} = reply) do
    method encode! (line 63) | def encode!(%Reply{} = reply) do
    method encode! (line 75) | def encode!(%Message{payload: {:binary, data}} = msg) do
    method encode! (line 95) | def encode!(%Message{payload: %{}} = msg) do
    method encode! (line 100) | def encode!(%Message{payload: invalid}) do
    method decode! (line 105) | def decode!(raw_message, opts) do
    method decode_text (line 112) | defp decode_text(raw_message) do
    method decode_binary (line 124) | defp decode_binary(<<
    method byte_size! (line 145) | defp byte_size!(bin, kind, max) do

FILE: lib/phoenix/socket/transport.ex
  class Phoenix.Socket.Transport (line 1) | defmodule Phoenix.Socket.Transport
    method load_config (line 252) | def load_config(true, module),
    method load_config (line 255) | def load_config(config, module),
    method load_config (line 259) | def load_config(config) do
    method init_session (line 300) | defp init_session({_, _, _} = mfa) do
    method code_reload (line 307) | def code_reload(conn, endpoint, opts) do
    method transport_log (line 320) | def transport_log(conn, level) do
    method check_origin (line 338) | def check_origin(conn, handler, endpoint, opts, sender \\ &Plug.Conn.s...
    method check_origin (line 340) | def check_origin(%Plug.Conn{halted: true} = conn, _handler, _endpoint,...
    method check_origin (line 343) | def check_origin(conn, handler, endpoint, opts, sender) do
    method check_subprotocols (line 397) | def check_subprotocols(conn, subprotocols)
    method check_subprotocols (line 399) | def check_subprotocols(%Plug.Conn{halted: true} = conn, _subprotocols)...
    method check_subprotocols (line 400) | def check_subprotocols(conn, nil), do: conn
    method check_subprotocols (line 421) | def check_subprotocols(conn, subprotocols), do: subprotocols_error_res...
    method subprotocols_error_response (line 423) | defp subprotocols_error_response(conn, subprotocols) do
    method connect_info (line 477) | def connect_info(conn, endpoint, keys, opts \\ []) do
    method connect_session (line 510) | defp connect_session(conn, endpoint, {key, store, {csrf_token_key, ini...
    method connect_session (line 524) | defp connect_session(conn, endpoint, {:mfa, {module, function, args}},...
    method fetch_headers (line 535) | defp fetch_headers(conn, prefix) do
    method fetch_trace_context_headers (line 541) | defp fetch_trace_context_headers(conn) do
    method fetch_uri (line 547) | defp fetch_uri(conn) do
    method fetch_user_agent (line 558) | defp fetch_user_agent(conn) do
    method csrf_token_valid? (line 564) | defp csrf_token_valid?(conn, session, csrf_token_key) do
    method check_origin_config (line 572) | defp check_origin_config(handler, endpoint, opts) do
    method parse_origin (line 597) | defp parse_origin(origin) do
    method origin_allowed? (line 610) | defp origin_allowed?({module, function, arguments}, uri, _endpoint, _c...
    method origin_allowed? (line 613) | defp origin_allowed?(:conn, uri, _endpoint, %Plug.Conn{} = conn) do
    method origin_allowed? (line 619) | defp origin_allowed?(_check_origin, %{host: nil}, _endpoint, _conn),
    method origin_allowed? (line 622) | defp origin_allowed?(true, uri, endpoint, _conn),
    method origin_allowed? (line 628) | defp origin_allowed?(uri, allowed_origins) do
    method compare? (line 638) | defp compare?(request_val, allowed_val) do
    method compare_host? (line 642) | defp compare_host?(_request_host, nil),
    method compare_host? (line 645) | defp compare_host?(request_host, "*." <> allowed_host),
    method compare_host? (line 648) | defp compare_host?(request_host, allowed_host),
    method host_to_binary (line 652) | defp host_to_binary({:system, env_var}), do: host_to_binary(System.get...
    method host_to_binary (line 653) | defp host_to_binary(host), do: host

FILE: lib/phoenix/test/channel_test.ex
  class Phoenix.ChannelTest (line 1) | defmodule Phoenix.ChannelTest
    method socket (line 239) | defp socket(module, id, assigns, options, caller) do
    method __socket__ (line 256) | def __socket__(socket, id, assigns, endpoint, options) do
    method first_socket! (line 269) | defp first_socket!(endpoint) do
    method fetch_test_supervisor! (line 276) | defp fetch_test_supervisor!(options) do
    method __connect__ (line 326) | def __connect__(endpoint, handler, params, options) do
    method push (line 472) | def push(%Socket{} = socket, event, payload \\ %{}) do
    method leave (line 487) | def leave(%Socket{} = socket) do
    method close (line 501) | def close(%Socket{} = socket, timeout \\ 5000) do
    method broadcast_from (line 517) | def broadcast_from(%Socket{} = socket, event, message) do
    method broadcast_from! (line 525) | def broadcast_from!(%Socket{} = socket, event, message) do
    method match_topic_to_channel! (line 701) | defp match_topic_to_channel!(socket, topic) do
    method extract_guard (line 720) | defp extract_guard({:when, _, [payload, guard]}), do: {payload, guard}
    method extract_guard (line 721) | defp extract_guard(payload), do: {payload, nil}
    method apply_guard (line 723) | defp apply_guard(pattern, nil), do: pattern
    method apply_guard (line 724) | defp apply_guard(pattern, guard), do: {:when, [], [pattern, guard]}
    method extract_pattern_and_apply_guard (line 726) | defp extract_pattern_and_apply_guard(event, payload, struct_module) do
    method __stringify__ (line 737) | def __stringify__(%{__struct__: _} = struct),
    method __stringify__ (line 739) | def __stringify__(%{} = params),
    method __stringify__ (line 743) | def __stringify__(other),
    method stringify_kv (line 746) | defp stringify_kv({k, v}),
  class NoopSerializer (line 159) | defmodule NoopSerializer
    method fastlane! (line 163) | def fastlane!(%Broadcast{} = msg) do
    method encode! (line 171) | def encode!(%Reply{} = reply), do: reply
    method encode! (line 172) | def encode!(%Message{} = msg), do: msg
    method decode! (line 173) | def decode!(message, _opts), do: message

FILE: lib/phoenix/test/conn_test.ex
  class Phoenix.ConnTest (line 1) | defmodule Phoenix.ConnTest
    method build_conn (line 139) | def build_conn() do
    method build_conn (line 151) | def build_conn(method, path, params_or_body \\ nil) do
    method dispatch (line 212) | def dispatch(conn, endpoint, method, path_or_action, params_or_body \\...
    method dispatch (line 213) | def dispatch(%Plug.Conn{} = conn, endpoint, method, path_or_action, pa...
    method dispatch (line 229) | def dispatch(conn, _endpoint, method, _path_or_action, _params_or_body...
    method from_set_to_sent (line 246) | defp from_set_to_sent(%Conn{state: :set} = conn), do: Conn.send_resp(c...
    method from_set_to_sent (line 247) | defp from_set_to_sent(conn), do: conn
    method get_flash (line 278) | def get_flash(conn), do: conn.assigns.flash
    method get_flash (line 285) | def get_flash(conn, key) do
    method response_content_type? (line 326) | defp response_content_type?(header, format) do
    method parse_content_type (line 337) | defp parse_content_type(header) do
    method response (line 357) | def response(%Conn{state: :unset}, _status) do
    method response (line 367) | def response(%Conn{status: status, resp_body: body}, given) do
    method html_response (line 386) | def html_response(conn, status) do
    method text_response (line 401) | def text_response(conn, status) do
    method json_response (line 418) | def json_response(conn, status) do
    method redirected_to (line 438) | def redirected_to(conn, status \\ 302)
    method redirected_to (line 440) | def redirected_to(%Conn{state: :unset}, _status) do
    method redirected_to (line 448) | def redirected_to(%Conn{status: status} = conn, status) do
    method redirected_to (line 453) | def redirected_to(conn, status) do
    method recycle (line 476) | def recycle(conn, headers \\ ~w(accept accept-language authorization)) do
    method copy_headers (line 485) | defp copy_headers(conn, headers, copy) do
    method ensure_recycled (line 496) | def ensure_recycled(conn) do
    method bypass_through (line 552) | def bypass_through(conn) do
    method bypass_through (line 562) | def bypass_through(conn, router) do
    method bypass_through (line 572) | def bypass_through(conn, router, pipelines) do
    method redirected_params (line 590) | def redirected_params(%Plug.Conn{} = conn, status \\ 302) do
    method remove_script_name (line 603) | defp remove_script_name(conn, router, path) do
    method assert_error_sent (line 677) | def assert_error_sent(status_int_or_atom, func) do
    method receive_response (line 689) | defp receive_response({:ok, conn}, expected_status) do
    method receive_response (line 696) | defp receive_response({:error, {_kind, exception, stack}}, expected_st...
    method discard_previously_sent (line 717) | defp discard_previously_sent() do
    method wrap_request (line 726) | defp wrap_request(func) do

FILE: lib/phoenix/token.ex
  class Phoenix.Token (line 1) | defmodule Phoenix.Token
    method get_key_base (line 252) | defp get_key_base(%Plug.Conn{} = conn),
    method get_key_base (line 255) | defp get_key_base(%_{endpoint: endpoint}),
    method get_endpoint_key_base (line 264) | defp get_endpoint_key_base(endpoint) do

FILE: lib/phoenix/transports/long_poll.ex
  class Phoenix.Transports.LongPoll (line 1) | defmodule Phoenix.Transports.LongPoll
    method default_config (line 12) | def default_config() do
    method init (line 23) | def init(opts), do: opts
    method call (line 25) | def call(conn, {endpoint, handler, opts}) do
    method dispatch (line 35) | defp dispatch(%{halted: true} = conn, _, _, _) do
    method dispatch (line 41) | defp dispatch(%{method: "OPTIONS"} = conn, _, _, _) do
    method dispatch (line 52) | defp dispatch(%{method: "GET"} = conn, endpoint, handler, opts) do
    method dispatch (line 63) | defp dispatch(%{method: "POST"} = conn, endpoint, _, opts) do
    method dispatch (line 74) | defp dispatch(conn, _, _, _) do
    method publish (line 78) | defp publish(conn, server_ref, endpoint, opts) do
    method safe_decode64! (line 111) | defp safe_decode64!(base64) do
    method transport_dispatch (line 119) | defp transport_dispatch(endpoint, server_ref, body, opts) do
    method new_session (line 133) | defp new_session(conn, endpoint, handler, opts) do
    method listen (line 160) | defp listen(conn, server_ref, endpoint, opts) do
    method resume_session (line 193) | defp resume_session(%Plug.Conn{} = conn, %{"token" => token}, endpoint...
    method resume_session (line 219) | defp resume_session(%Plug.Conn{}, _params, _endpoint, _opts), do: :error
    method sign_token (line 252) | defp sign_token(endpoint, data, opts) do
    method verify_token (line 261) | defp verify_token(endpoint, signed, opts) do
    method maybe_auth_token_from_header (line 270) | defp maybe_auth_token_from_header(conn, true) do
    method maybe_auth_token_from_header (line 280) | defp maybe_auth_token_from_header(conn, _), do: conn
    method status_json (line 282) | defp status_json(conn) do
    method status_token_messages_json (line 286) | defp status_token_messages_json(conn, token, messages) do
    method send_json (line 290) | defp send_json(conn, data) do

FILE: lib/phoenix/transports/long_poll_server.ex
  class Phoenix.Transports.LongPoll.Server (line 1) | defmodule Phoenix.Transports.LongPoll.Server
    method start_link (line 7) | def start_link(arg) do
    method init (line 11) | def init({endpoint, handler, options, params, priv_topic, connect_info...
    method handle_info (line 48) | def handle_info({:dispatch, client_ref, {body, opcode}, ref}, state) do
    method handle_info (line 70) | def handle_info({:subscribe, client_ref, ref}, state) do
    method handle_info (line 75) | def handle_info({:flush, client_ref, ref}, state) do
    method handle_info (line 86) | def handle_info({:expired, client_ref, ref}, state) do
    method handle_info (line 96) | def handle_info(:shutdown_if_inactive, state) do
    method handle_info (line 105) | def handle_info(message, state) do
    method terminate (line 123) | def terminate(reason, state) do
    method publish_reply (line 144) | defp publish_reply(state, reply) do
    method notify_client_now_available (line 149) | defp notify_client_now_available(state) do
    method now_ms (line 156) | defp now_ms, do: System.system_time(:millisecond)
    method schedule_inactive_shutdown (line 158) | defp schedule_inactive_shutdown(window_ms) do

FILE: lib/phoenix/transports/websocket.ex
  class Phoenix.Transports.WebSocket (line 1) | defmodule Phoenix.Transports.WebSocket
    method default_config (line 26) | def default_config() do
    method init (line 37) | def init(opts), do: opts
    method call (line 39) | def call(%{method: "GET"} = conn, {endpoint, handler, opts}) do
    method call (line 95) | def call(conn, _), do: send_resp(conn, 400, "")
    method handle_error (line 97) | def handle_error(conn, _reason), do: send_resp(conn, 403, "")
    method maybe_auth_token_from_header (line 99) | defp maybe_auth_token_from_header(conn, true) do
    method maybe_auth_token_from_header (line 124) | defp maybe_auth_token_from_header(conn, _), do: conn
    method set_actual_subprotocols (line 126) | defp set_actual_subprotocols(conn, []), do: delete_req_header(conn, "s...
    method set_actual_subprotocols (line 128) | defp set_actual_subprotocols(conn, subprotocols),

FILE: lib/phoenix/verified_routes.ex
  class Phoenix.VerifiedRoutes (line 1) | defmodule Phoenix.VerifiedRoutes
    method __using__ (line 246) | def __using__(mod, opts) do
    method split_test_path (line 326) | defp split_test_path(test_path) do
    method expand_alias (line 335) | defp expand_alias({:__aliases__, _, _} = alias, env),
    method expand_alias (line 338) | defp expand_alias(other, _env), do: other
    method inject_path (line 372) | defp inject_path(
    method inject_url (line 384) | defp inject_url(
    method validate_sigil_p! (line 401) | defp validate_sigil_p!([]), do: :ok
    method validate_sigil_p! (line 403) | defp validate_sigil_p!(extra) do
    method raise_invalid_route (line 407) | defp raise_invalid_route(ast) do
    method static_url (line 588) | def static_url(conn_or_socket_or_endpoint, path)
    method static_url (line 590) | def static_url(%Plug.Conn{private: private}, path) do
    method static_url (line 597) | def static_url(%_{endpoint: endpoint}, path) do
    method static_url (line 605) | def static_url(other, path) do
    method guarded_unverified_url (line 627) | defp guarded_unverified_url(%Plug.Conn{private: private}, path, params...
    method guarded_unverified_url (line 634) | defp guarded_unverified_url(%_{endpoint: endpoint}, path, params) do
    method guarded_unverified_url (line 638) | defp guarded_unverified_url(%URI{} = uri, path, params) do
    method guarded_unverified_url (line 646) | defp guarded_unverified_url(other, path, _params) do
    method static_path (line 677) | def static_path(conn_or_socket_or_endpoint_or_uri, path)
    method static_path (line 679) | def static_path(%Plug.Conn{private: private}, path) do
    method static_path (line 686) | def static_path(%URI{} = uri, path) do
    method static_path (line 690) | def static_path(%_{endpoint: endpoint}, path) do
    method unverified_path (line 709) | def unverified_path(conn_or_socket_or_endpoint_or_uri, router, path, p...
    method unverified_path (line 711) | def unverified_path(%Plug.Conn{} = conn, router, path, params) do
    method unverified_path (line 719) | def unverified_path(%URI{} = uri, _router, path, params) do
    method unverified_path (line 723) | def unverified_path(%_{endpoint: endpoint}, router, path, params) do
    method unverified_path (line 731) | def unverified_path(other, router, path, _params) do
    method __encode_segment__ (line 744) | def __encode_segment__(data) do
    method encode_segment (line 752) | defp encode_segment(data) do
    method verify_segment (line 759) | defp verify_segment(["/" <> _ | _] = segments, route), do: verify_segm...
    method verify_segment (line 761) | defp verify_segment(_, route) do
    method verify_segment (line 766) | defp verify_segment(["/" | rest], route, acc), do: verify_segment(rest...
    method verify_segment (line 769) | defp verify_segment(["/" <> _ = segment | rest], route, acc) do
    method verify_segment (line 781) | defp verify_segment(["?" <> query], _route, acc) do
    method verify_segment (line 786) | defp verify_segment(["?" <> static_query_segment | rest], route, acc) do
    method verify_segment (line 808) | defp verify_segment([_ | _], route, _acc) do
    method verify_segment (line 814) | defp verify_segment([], _route, acc), do: {Enum.reverse(acc), _query =...
    method verify_query (line 816) | defp verify_query(
    method verify_query (line 837) | defp verify_query([], _route, acc), do: Enum.reverse(acc)
    method verify_query (line 839) | defp verify_query(["=" | rest], route, acc) do
    method verify_query (line 843) | defp verify_query(["&" <> _ = param | rest], route, acc) do
    method verify_query (line 852) | defp verify_query(_other, route, _acc) do
    method raise_invalid_query (line 856) | defp raise_invalid_query(route) do
    method static_integrity (line 877) | def static_integrity(conn_or_socket_or_endpoint, path)
    method static_integrity (line 879) | def static_integrity(%Plug.Conn{private: %{phoenix_endpoint: endpoint}...
    method static_integrity (line 883) | def static_integrity(%_{endpoint: endpoint}, path) do
    method __encode_query__ (line 892) | def __encode_query__(dict, sort? \\ false)
    method __encode_query__ (line 902) | def __encode_query__(val, _sort?), do: val |> to_param() |> URI.encode...
    method maybe_sort_query (line 904) | defp maybe_sort_query(query_str, false), do: query_str
    method maybe_sort_query (line 906) | defp maybe_sort_query(query, true),
    method to_param (line 911) | defp to_param(false), do: "false"
    method to_param (line 912) | defp to_param(true), do: "true"
    method to_param (line 913) | defp to_param(data), do: Phoenix.Param.to_param(data)
    method build_route (line 915) | defp build_route(route_ast, sigil_p, env, endpoint_ctx, router) do
    method warn_location (line 944) | defp warn_location(meta, %{line: line, file: file, function: function,...
    method rewrite_path (line 949) | defp rewrite_path(route, endpoint, router, config) do
    method materialize_path (line 993) | defp materialize_path(path) do
    method compile_prefixes (line 997) | defp compile_prefixes(path_prefixes, meta) do
    method attr! (line 1012) | defp attr!(%{function: nil}, _) do
    method attr! (line 1016) | defp attr!(env, :endpoint) do
    method attr! (line 1027) | defp attr!(env, name) do
    method static_path? (line 1031) | defp static_path?(path, statics) do
    method build_own_forward_path (line 1035) | defp build_own_forward_path(conn, router, path) do
    method build_conn_forward_path (line 1045) | defp build_conn_forward_path(%Plug.Conn{} = conn, router, path) do
    method path_with_script (line 1055) | defp path_with_script(path, []), do: path
    method path_with_script (line 1056) | defp path_with_script(path, script), do: "/" <> Enum.join(script, "/")...

FILE: mix.exs
  class Phoenix.MixProject (line 1) | defmodule Phoenix.MixProject
    method project (line 19) | def project do
    method cli (line 53) | def cli do
    method elixirc_paths (line 59) | defp elixirc_paths(:docs), do: ["lib", "installer/lib"]
    method elixirc_paths (line 60) | defp elixirc_paths(_), do: ["lib"]
    method extra_applications (line 62) | defp extra_applications(:test), do: [:inets]
    method extra_applications (line 63) | defp extra_applications(_), do: []
    method application (line 65) | def application do
    method deps (line 80) | defp deps do
    method package (line 118) | defp package do
    method docs (line 133) | defp docs do
    method extras (line 170) | defp extras do
    method groups_for_extras (line 221) | defp groups_for_extras do
    method groups_for_modules (line 235) | defp groups_for_modules do
    method aliases (line 275) | defp aliases do
    method generate_js_docs (line 287) | defp generate_js_docs(_) do
    method raise_on_archive_build (line 293) | defp raise_on_archive_build(_) do
    method copy_core_components (line 300) | defp copy_core_components(_) do

FILE: priv/static/phoenix.cjs.js
  method constructor (line 75) | constructor(channel, event, payload, timeout) {
  method resend (line 91) | resend(timeout) {
  method send (line 99) | send() {
  method receive (line 118) | receive(status, callback) {
  method reset (line 128) | reset() {
  method matchReceive (line 138) | matchReceive({ status, response, _ref }) {
  method cancelRefEvent (line 144) | cancelRefEvent() {
  method cancelTimeout (line 153) | cancelTimeout() {
  method startTimeout (line 160) | startTimeout() {
  method hasReceived (line 179) | hasReceived(status) {
  method trigger (line 185) | trigger(status, response) {
  method constructor (line 192) | constructor(callback, timerCalc) {
  method reset (line 198) | reset() {
  method scheduleTimeout (line 205) | scheduleTimeout() {
  method constructor (line 216) | constructor(topic, params, socket) {
  method join (line 289) | join(timeout = this.timeout) {
  method onClose (line 303) | onClose(callback) {
  method onError (line 310) | onError(callback) {
  method on (line 330) | on(event, callback) {
  method off (line 353) | off(event, ref) {
  method canPush (line 361) | canPush() {
  method push (line 380) | push(event, payload, timeout = this.timeout) {
  method leave (line 412) | leave(timeout = this.timeout) {
  method onMessage (line 440) | onMessage(_event, payload, _ref) {
  method isMember (line 446) | isMember(topic, event, payload, joinRef) {
  method joinRef (line 460) | joinRef() {
  method rejoin (line 466) | rejoin(timeout = this.timeout) {
  method trigger (line 477) | trigger(event, payload, ref, joinRef) {
  method replyEventName (line 491) | replyEventName(ref) {
  method isClosed (line 497) | isClosed() {
  method isErrored (line 503) | isErrored() {
  method isJoined (line 509) | isJoined() {
  method isJoining (line 515) | isJoining() {
  method isLeaving (line 521) | isLeaving() {
  method request (line 528) | static request(method, endPoint, headers, body, timeout, ontimeout, call...
  method fetchRequest (line 541) | static fetchRequest(method, endPoint, headers, body, timeout, ontimeout,...
  method xdomainRequest (line 562) | static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, c...
  method xhrRequest (line 577) | static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeo...
  method parseJSON (line 596) | static parseJSON(resp) {
  method serialize (line 607) | static serialize(obj, parentKey) {
  method appendParams (line 623) | static appendParams(url, params) {
  method constructor (line 643) | constructor(endPoint, protocols) {
  method normalizeEndpoint (line 667) | normalizeEndpoint(endPoint) {
  method endpointURL (line 670) | endpointURL() {
  method closeAndRetry (line 673) | closeAndRetry(code, reason, wasClean) {
  method ontimeout (line 677) | ontimeout() {
  method isActive (line 681) | isActive() {
  method poll (line 684) | poll() {
  method send (line 733) | send(body) {
  method batchSend (line 749) | batchSend(messages) {
  method close (line 762) | close(code, reason, wasClean) {
  method ajax (line 777) | ajax(method, headers, body, onCallerTimeout, callback) {
  method constructor (line 795) | constructor(channel, opts = {}) {
  method onJoin (line 829) | onJoin(callback) {
  method onLeave (line 832) | onLeave(callback) {
  method onSync (line 835) | onSync(callback) {
  method list (line 838) | list(by) {
  method inPendingSyncState (line 841) | inPendingSyncState() {
  method syncState (line 853) | static syncState(currentState, newState, onJoin, onLeave) {
  method syncDiff (line 892) | static syncDiff(state, diff, onJoin, onLeave) {
  method list (line 936) | static list(presences, chooser) {
  method map (line 947) | static map(obj, func) {
  method clone (line 950) | static clone(obj) {
  method encode (line 960) | encode(msg, callback) {
  method decode (line 968) | decode(rawPayload, callback) {
  method binaryEncode (line 977) | binaryEncode(message) {
  method binaryDecode (line 997) | binaryDecode(buffer) {
  method decodePush (line 1010) | decodePush(buffer, view, decoder) {
  method decodeReply (line 1024) | decodeReply(buffer, view, decoder) {
  method decodeBroadcast (line 1042) | decodeBroadcast(buffer, view, decoder) {
  method constructor (line 1057) | constructor(endPoint, opts = {}) {
  method getLongPollTransport (line 1150) | getLongPollTransport() {
  method replaceTransport (line 1159) | replaceTransport(newTransport) {
  method protocol (line 1175) | protocol() {
  method endPointURL (line 1183) | endPointURL() {
  method disconnect (line 1205) | disconnect(callback, code, reason) {
  method connect (line 1223) | connect(params) {
  method log (line 1243) | log(kind, msg, data) {
  method hasLogger (line 1249) | hasLogger() {
  method onOpen (line 1259) | onOpen(callback) {
  method onClose (line 1268) | onClose(callback) {
  method onError (line 1280) | onError(callback) {
  method onMessage (line 1289) | onMessage(callback) {
  method ping (line 1300) | ping(callback) {
  method transportName (line 1320) | transportName(transport) {
  method transportConnect (line 1331) | transportConnect() {
  method getSession (line 1346) | getSession(key) {
  method storeSession (line 1349) | storeSession(key, val) {
  method connectWithFallback (line 1352) | connectWithFallback(fallbackTransport, fallbackThreshold = 2500) {
  method clearHeartbeats (line 1398) | clearHeartbeats() {
  method onConnOpen (line 1402) | onConnOpen() {
  method heartbeatTimeout (line 1415) | heartbeatTimeout() {
  method resetHeartbeat (line 1426) | resetHeartbeat() {
  method teardown (line 1434) | teardown(callback, code, reason) {
  method waitForBufferDone (line 1461) | waitForBufferDone(conn, callback, tries = 1) {
  method waitForSocketClosed (line 1470) | waitForSocketClosed(conn, callback, tries = 1) {
  method onConnClose (line 1479) | onConnClose(event) {
  method onConnError (line 1494) | onConnError(error) {
  method triggerChanError (line 1508) | triggerChanError() {
  method connectionState (line 1518) | connectionState() {
  method isConnected (line 1533) | isConnected() {
  method remove (line 1541) | remove(channel) {
  method off (line 1551) | off(refs) {
  method channel (line 1565) | channel(topic, chanParams = {}) {
  method push (line 1573) | push(data) {
  method makeRef (line 1588) | makeRef() {
  method sendHeartbeat (line 1597) | sendHeartbeat() {
  method flushSendBuffer (line 1605) | flushSendBuffer() {
  method onConnMessage (line 1611) | onConnMessage(rawMessage) {
  method leaveOpenTopic (line 1633) | leaveOpenTopic(topic) {

FILE: priv/static/phoenix.js
  method constructor (line 75) | constructor(channel, event, payload, timeout) {
  method resend (line 91) | resend(timeout) {
  method send (line 99) | send() {
  method receive (line 118) | receive(status, callback) {
  method reset (line 128) | reset() {
  method matchReceive (line 138) | matchReceive({ status, response, _ref }) {
  method cancelRefEvent (line 144) | cancelRefEvent() {
  method cancelTimeout (line 153) | cancelTimeout() {
  method startTimeout (line 160) | startTimeout() {
  method hasReceived (line 179) | hasReceived(status) {
  method trigger (line 185) | trigger(status, response) {
  method constructor (line 192) | constructor(callback, timerCalc) {
  method reset (line 198) | reset() {
  method scheduleTimeout (line 205) | scheduleTimeout() {
  method constructor (line 216) | constructor(topic, params, socket) {
  method join (line 289) | join(timeout = this.timeout) {
  method onClose (line 303) | onClose(callback) {
  method onError (line 310) | onError(callback) {
  method on (line 330) | on(event, callback) {
  method off (line 353) | off(event, ref) {
  method canPush (line 361) | canPush() {
  method push (line 380) | push(event, payload, timeout = this.timeout) {
  method leave (line 412) | leave(timeout = this.timeout) {
  method onMessage (line 440) | onMessage(_event, payload, _ref) {
  method isMember (line 446) | isMember(topic, event, payload, joinRef) {
  method joinRef (line 460) | joinRef() {
  method rejoin (line 466) | rejoin(timeout = this.timeout) {
  method trigger (line 477) | trigger(event, payload, ref, joinRef) {
  method replyEventName (line 491) | replyEventName(ref) {
  method isClosed (line 497) | isClosed() {
  method isErrored (line 503) | isErrored() {
  method isJoined (line 509) | isJoined() {
  method isJoining (line 515) | isJoining() {
  method isLeaving (line 521) | isLeaving() {
  method request (line 528) | static request(method, endPoint, headers, body, timeout, ontimeout, call...
  method fetchRequest (line 541) | static fetchRequest(method, endPoint, headers, body, timeout, ontimeout,...
  method xdomainRequest (line 562) | static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, c...
  method xhrRequest (line 577) | static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeo...
  method parseJSON (line 596) | static parseJSON(resp) {
  method serialize (line 607) | static serialize(obj, parentKey) {
  method appendParams (line 623) | static appendParams(url, params) {
  method constructor (line 643) | constructor(endPoint, protocols) {
  method normalizeEndpoint (line 667) | normalizeEndpoint(endPoint) {
  method endpointURL (line 670) | endpointURL() {
  method closeAndRetry (line 673) | closeAndRetry(code, reason, wasClean) {
  method ontimeout (line 677) | ontimeout() {
  method isActive (line 681) | isActive() {
  method poll (line 684) | poll() {
  method send (line 733) | send(body) {
  method batchSend (line 749) | batchSend(messages) {
  method close (line 762) | close(code, reason, wasClean) {
  method ajax (line 777) | ajax(method, headers, body, onCallerTimeout, callback) {
  method constructor (line 795) | constructor(channel, opts = {}) {
  method onJoin (line 829) | onJoin(callback) {
  method onLeave (line 832) | onLeave(callback) {
  method onSync (line 835) | onSync(callback) {
  method list (line 838) | list(by) {
  method inPendingSyncState (line 841) | inPendingSyncState() {
  method syncState (line 853) | static syncState(currentState, newState, onJoin, onLeave) {
  method syncDiff (line 892) | static syncDiff(state, diff, onJoin, onLeave) {
  method list (line 936) | static list(presences, chooser) {
  method map (line 947) | static map(obj, func) {
  method clone (line 950) | static clone(obj) {
  method encode (line 960) | encode(msg, callback) {
  method decode (line 968) | decode(rawPayload, callback) {
  method binaryEncode (line 977) | binaryEncode(message) {
  method binaryDecode (line 997) | binaryDecode(buffer) {
  method decodePush (line 1010) | decodePush(buffer, view, decoder) {
  method decodeReply (line 1024) | decodeReply(buffer, view, decoder) {
  method decodeBroadcast (line 1042) | decodeBroadcast(buffer, view, decoder) {
  method constructor (line 1057) | constructor(endPoint, opts = {}) {
  method getLongPollTransport (line 1150) | getLongPollTransport() {
  method replaceTransport (line 1159) | replaceTransport(newTransport) {
  method protocol (line 1175) | protocol() {
  method endPointURL (line 1183) | endPointURL() {
  method disconnect (line 1205) | disconnect(callback, code, reason) {
  method connect (line 1223) | connect(params) {
  method log (line 1243) | log(kind, msg, data) {
  method hasLogger (line 1249) | hasLogger() {
  method onOpen (line 1259) | onOpen(callback) {
  method onClose (line 1268) | onClose(callback) {
  method onError (line 1280) | onError(callback) {
  method onMessage (line 1289) | onMessage(callback) {
  method ping (line 1300) | ping(callback) {
  method transportName (line 1320) | transportName(transport) {
  method transportConnect (line 1331) | transportConnect() {
  method getSession (line 1346) | getSession(key) {
  method storeSession (line 1349) | storeSession(key, val) {
  method connectWithFallback (line 1352) | connectWithFallback(fallbackTransport, fallbackThreshold = 2500) {
  method clearHeartbeats (line 1398) | clearHeartbeats() {
  method onConnOpen (line 1402) | onConnOpen() {
  method heartbeatTimeout (line 1415) | heartbeatTimeout() {
  method resetHeartbeat (line 1426) | resetHeartbeat() {
  method teardown (line 1434) | teardown(callback, code, reason) {
  method waitForBufferDone (line 1461) | waitForBufferDone(conn, callback, tries = 1) {
  method waitForSocketClosed (line 1470) | waitForSocketClosed(conn, callback, tries = 1) {
  method onConnClose (line 1479) | onConnClose(event) {
  method onConnError (line 1494) | onConnError(error) {
  method triggerChanError (line 1508) | triggerChanError() {
  method connectionState (line 1518) | connectionState() {
  method isConnected (line 1533) | isConnected() {
  method remove (line 1541) | remove(channel) {
  method off (line 1551) | off(refs) {
  method channel (line 1565) | channel(topic, chanParams = {}) {
  method push (line 1573) | push(data) {
  method makeRef (line 1588) | makeRef() {
  method sendHeartbeat (line 1597) | sendHeartbeat() {
  method flushSendBuffer (line 1605) | flushSendBuffer() {
  method onConnMessage (line 1611) | onConnMessage(rawMessage) {
  method leaveOpenTopic (line 1633) | leaveOpenTopic(topic) {

FILE: priv/static/phoenix.mjs
  method constructor (line 46) | constructor(channel, event, payload, timeout) {
  method resend (line 62) | resend(timeout) {
  method send (line 70) | send() {
  method receive (line 89) | receive(status, callback) {
  method reset (line 99) | reset() {
  method matchReceive (line 109) | matchReceive({ status, response, _ref }) {
  method cancelRefEvent (line 115) | cancelRefEvent() {
  method cancelTimeout (line 124) | cancelTimeout() {
  method startTimeout (line 131) | startTimeout() {
  method hasReceived (line 150) | hasReceived(status) {
  method trigger (line 156) | trigger(status, response) {
  method constructor (line 163) | constructor(callback, timerCalc) {
  method reset (line 169) | reset() {
  method scheduleTimeout (line 176) | scheduleTimeout() {
  method constructor (line 187) | constructor(topic, params, socket) {
  method join (line 260) | join(timeout = this.timeout) {
  method onClose (line 274) | onClose(callback) {
  method onError (line 281) | onError(callback) {
  method on (line 301) | on(event, callback) {
  method off (line 324) | off(event, ref) {
  method canPush (line 332) | canPush() {
  method push (line 351) | push(event, payload, timeout = this.timeout) {
  method leave (line 383) | leave(timeout = this.timeout) {
  method onMessage (line 411) | onMessage(_event, payload, _ref) {
  method isMember (line 417) | isMember(topic, event, payload, joinRef) {
  method joinRef (line 431) | joinRef() {
  method rejoin (line 437) | rejoin(timeout = this.timeout) {
  method trigger (line 448) | trigger(event, payload, ref, joinRef) {
  method replyEventName (line 462) | replyEventName(ref) {
  method isClosed (line 468) | isClosed() {
  method isErrored (line 474) | isErrored() {
  method isJoined (line 480) | isJoined() {
  method isJoining (line 486) | isJoining() {
  method isLeaving (line 492) | isLeaving() {
  method request (line 499) | static request(method, endPoint, headers, body, timeout, ontimeout, call...
  method fetchRequest (line 512) | static fetchRequest(method, endPoint, headers, body, timeout, ontimeout,...
  method xdomainRequest (line 533) | static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, c...
  method xhrRequest (line 548) | static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeo...
  method parseJSON (line 567) | static parseJSON(resp) {
  method serialize (line 578) | static serialize(obj, parentKey) {
  method appendParams (line 594) | static appendParams(url, params) {
  method constructor (line 614) | constructor(endPoint, protocols) {
  method normalizeEndpoint (line 638) | normalizeEndpoint(endPoint) {
  method endpointURL (line 641) | endpointURL() {
  method closeAndRetry (line 644) | closeAndRetry(code, reason, wasClean) {
  method ontimeout (line 648) | ontimeout() {
  method isActive (line 652) | isActive() {
  method poll (line 655) | poll() {
  method send (line 704) | send(body) {
  method batchSend (line 720) | batchSend(messages) {
  method close (line 733) | close(code, reason, wasClean) {
  method ajax (line 748) | ajax(method, headers, body, onCallerTimeout, callback) {
  method constructor (line 766) | constructor(channel, opts = {}) {
  method onJoin (line 800) | onJoin(callback) {
  method onLeave (line 803) | onLeave(callback) {
  method onSync (line 806) | onSync(callback) {
  method list (line 809) | list(by) {
  method inPendingSyncState (line 812) | inPendingSyncState() {
  method syncState (line 824) | static syncState(currentState, newState, onJoin, onLeave) {
  method syncDiff (line 863) | static syncDiff(state, diff, onJoin, onLeave) {
  method list (line 907) | static list(presences, chooser) {
  method map (line 918) | static map(obj, func) {
  method clone (line 921) | static clone(obj) {
  method encode (line 931) | encode(msg, callback) {
  method decode (line 939) | decode(rawPayload, callback) {
  method binaryEncode (line 948) | binaryEncode(message) {
  method binaryDecode (line 968) | binaryDecode(buffer) {
  method decodePush (line 981) | decodePush(buffer, view, decoder) {
  method decodeReply (line 995) | decodeReply(buffer, view, decoder) {
  method decodeBroadcast (line 1013) | decodeBroadcast(buffer, view, decoder) {
  method constructor (line 1028) | constructor(endPoint, opts = {}) {
  method getLongPollTransport (line 1121) | getLongPollTransport() {
  method replaceTransport (line 1130) | replaceTransport(newTransport) {
  method protocol (line 1146) | protocol() {
  method endPointURL (line 1154) | endPointURL() {
  method disconnect (line 1176) | disconnect(callback, code, reason) {
  method connect (line 1194) | connect(params) {
  method log (line 1214) | log(kind, msg, data) {
  method hasLogger (line 1220) | hasLogger() {
  method onOpen (line 1230) | onOpen(callback) {
  method onClose (line 1239) | onClose(callback) {
  method onError (line 1251) | onError(callback) {
  method onMessage (line 1260) | onMessage(callback) {
  method ping (line 1271) | ping(callback) {
  method transportName (line 1291) | transportName(transport) {
  method transportConnect (line 1302) | transportConnect() {
  method getSession (line 1317) | getSession(key) {
  method storeSession (line 1320) | storeSession(key, val) {
  method connectWithFallback (line 1323) | connectWithFallback(fallbackTransport, fallbackThreshold = 2500) {
  method clearHeartbeats (line 1369) | clearHeartbeats() {
  method onConnOpen (line 1373) | onConnOpen() {
  method heartbeatTimeout (line 1386) | heartbeatTimeout() {
  method resetHeartbeat (line 1397) | resetHeartbeat() {
  method teardown (line 1405) | teardown(callback, code, reason) {
  method waitForBufferDone (line 1432) | waitForBufferDone(conn, callback, tries = 1) {
  method waitForSocketClosed (line 1441) | waitForSocketClosed(conn, callback, tries = 1) {
  method onConnClose (line 1450) | onConnClose(event) {
  method onConnError (line 1465) | onConnError(error) {
  method triggerChanError (line 1479) | triggerChanError() {
  method connectionState (line 1489) | connectionState() {
  method isConnected (line 1504) | isConnected() {
  method remove (line 1512) | remove(channel) {
  method off (line 1522) | off(refs) {
  method channel (line 1536) | channel(topic, chanParams = {}) {
  method push (line 1544) | push(data) {
  method makeRef (line 1559) | makeRef() {
  method sendHeartbeat (line 1568) | sendHeartbeat() {
  method flushSendBuffer (line 1576) | flushSendBuffer() {
  method onConnMessage (line 1582) | onConnMessage(rawMessage) {
  method leaveOpenTopic (line 1604) | leaveOpenTopic(topic) {

FILE: test/fixtures/views.exs
  class MyApp.View (line 1) | defmodule MyApp.View
    method escaped_title (line 4) | def escaped_title(title) do
  class MyApp.LayoutView (line 9) | defmodule MyApp.LayoutView
    method default_title (line 12) | def default_title do
  class MyApp.User (line 17) | defmodule MyApp.User
  class MyApp.PathView (line 21) | defmodule MyApp.PathView
  class MyApp.UserView (line 25) | defmodule MyApp.UserView
    method escaped_title (line 30) | def escaped_title(title) do
    method render (line 34) | def render("message.html", _assigns) do
    method render (line 39) | def render("show.text", %{user: user, prefix: prefix}) do
    method render (line 43) | def render("show.text", %{user: user}) do
    method render (line 47) | def render("data.text", %{data: data}) do
    method render (line 51) | def render("edit.html", %{} = assigns) do
    method render (line 55) | def render("existing.html", _), do: "rendered existing"
    method render (line 57) | def render("inner.html", assigns) do
    method render (line 63) | def render("render_template.html" = tpl, %{name: name}) do
    method render (line 67) | def render("to_iodata.html", %{to_iodata: to_iodata}) do
  class MyApp.Templates.UserView (line 72) | defmodule MyApp.Templates.UserView
    method escaped_title (line 75) | def escaped_title(title) do
  class MyApp.Nested.User (line 80) | defmodule MyApp.Nested.User
  class MyApp.Nested.UserView (line 84) | defmodule MyApp.Nested.UserView
    method render (line 87) | def render("show.text", %{user: user}) do
    method escaped_title (line 91) | def escaped_title(title) do

FILE: test/mix/phoenix_test.exs
  class Mix.PhoenixTest (line 1) | defmodule Mix.PhoenixTest
    method build_utc_datetime (line 96) | defp build_utc_datetime,
    method build_utc_naive_datetime (line 99) | defp build_utc_naive_datetime,

FILE: test/mix/tasks/phx.digest.clean_test.exs
  class Mix.Tasks.Phx.Digest.CleanTest (line 1) | defmodule Mix.Tasks.Phx.Digest.CleanTest

FILE: test/mix/tasks/phx.digest_test.exs
  class Mix.Tasks.Phx.DigestTest (line 3) | defmodule Mix.Tasks.Phx.DigestTest

FILE: test/mix/tasks/phx.gen.auth/injector_test.exs
  class Mix.Tasks.Phx.Gen.Auth.InjectorTest (line 1) | defmodule Mix.Tasks.Phx.Gen.Auth.InjectorTest

FILE: test/mix/tasks/phx.gen.auth_test.exs
  class Mix.Tasks.Phx.Gen.AuthTest (line 3) | defmodule Mix.Tasks.Phx.Gen.AuthTest
    method in_tmp_phx_project (line 17) | defp in_tmp_phx_project(test, additional_args \\ [], func) do
    method in_tmp_phx_umbrella_project (line 27) | defp in_tmp_phx_umbrella_project(test, func) do

FILE: test/mix/tasks/phx.gen.cert_test.exs
  class Mix.Tasks.Phx.CertTest (line 3) | defmodule Mix.Tasks.Phx.CertTest

FILE: test/mix/tasks/phx.gen.channel_test.exs
  class PhoenixWeb.DupChannel (line 3) | defmodule PhoenixWeb.DupChannel
  class Ecto.Adapters.SQL (line 6) | defmodule Ecto.Adapters.SQL
  class Mix.Tasks.Phx.Gen.ChannelTest (line 9) | defmodule Mix.Tasks.Phx.Gen.ChannelTest

FILE: test/mix/tasks/phx.gen.context_test.exs
  class Phoenix.DupContext (line 3) | defmodule Phoenix.DupContext
  class Mix.Tasks.Phx.Gen.ContextTest (line 6) | defmodule Mix.Tasks.Phx.Gen.ContextTest

FILE: test/mix/tasks/phx.gen.embedded_test.exs
  class Mix.Tasks.Phx.Gen.EmbeddedTest (line 3) | defmodule Mix.Tasks.Phx.Gen.EmbeddedTest

FILE: test/mix/tasks/phx.gen.html_test.exs
  class Mix.Tasks.Phx.Gen.HtmlTest (line 3) | defmodule Mix.Tasks.Phx.Gen.HtmlTest

FILE: test/mix/tasks/phx.gen.json_test.exs
  class Mix.Tasks.Phx.Gen.JsonTest (line 3) | defmodule Mix.Tasks.Phx.Gen.JsonTest

FILE: test/mix/tasks/phx.gen.live_test.exs
  class Mix.Tasks.Phx.Gen.LiveTest (line 3) | defmodule Mix.Tasks.Phx.Gen.LiveTest
    method in_tmp_live_project (line 13) | defp in_tmp_live_project(test, func) do
    method in_tmp_live_umbrella_project (line 22) | defp in_tmp_live_umbrella_project(test, func) do

FILE: test/mix/tasks/phx.gen.notifier_test.exs
  class MyTestApp.Mailer (line 3) | defmodule MyTestApp.Mailer
  class Mix.Tasks.Phx.Gen.NotifierTest (line 6) | defmodule Mix.Tasks.Phx.Gen.NotifierTest

FILE: test/mix/tasks/phx.gen.presence_test.exs
  class Mix.Tasks.Phx.Gen.PresenceTest (line 3) | defmodule Mix.Tasks.Phx.Gen.PresenceTest

FILE: test/mix/tasks/phx.gen.release_test.exs
  class Mix.Tasks.Phx.Gen.ReleaseTest (line 3) | defmodule Mix.Tasks.Phx.Gen.ReleaseTest

FILE: test/mix/tasks/phx.gen.schema_test.exs
  class Phoenix.DupSchema (line 3) | defmodule Phoenix.DupSchema
  class Mix.Tasks.Phx.Gen.SchemaTest (line 6) | defmodule Mix.Tasks.Phx.Gen.SchemaTest

FILE: test/mix/tasks/phx.gen.secret_test.exs
  class Mix.Tasks.Phx.Gen.SecretTest (line 3) | defmodule Mix.Tasks.Phx.Gen.SecretTest

FILE: test/mix/tasks/phx.gen.socket_test.exs
  class PhoenixWeb.DupSocket (line 3) | defmodule PhoenixWeb.DupSocket
  class Mix.Tasks.Phx.Gen.SocketTest (line 6) | defmodule Mix.Tasks.Phx.Gen.SocketTest

FILE: test/mix/tasks/phx.routes_test.exs
  class PageController (line 3) | defmodule PageController
    method init (line 4) | def init(opts), do: opts
    method call (line 5) | def call(conn, _opts), do: conn
  class Live (line 7) | defmodule Live
    method init (line 8) | def init(opts), do: opts
  class PhoenixTestWeb.Router (line 12) | defmodule PhoenixTestWeb.Router
  class PhoenixTestOld.Router (line 17) | defmodule PhoenixTestOld.Router
  class PhoenixTestLiveWeb.Router (line 22) | defmodule PhoenixTestLiveWeb.Router
  class PhoenixTestWeb.ForwardedRouter (line 27) | defmodule PhoenixTestWeb.ForwardedRouter
  class PhoenixTestWeb.PlugRouterWithVerifiedRoutes (line 33) | defmodule PhoenixTestWeb.PlugRouterWithVerifiedRoutes
    method formatted_routes (line 43) | def formatted_routes(_plug_opts) do
    method verified_route? (line 50) | def verified_route?(_plug_opts, path) do
  class Mix.Tasks.Phx.RoutesTest (line 55) | defmodule Mix.Tasks.Phx.RoutesTest

FILE: test/mix/tasks/phx_test.exs
  class Mix.Tasks.Phx.Test (line 1) | defmodule Mix.Tasks.Phx.Test

FILE: test/phoenix/channel_test.exs
  class Phoenix.Channel.ChannelTest (line 1) | defmodule Phoenix.Channel.ChannelTest

FILE: test/phoenix/code_reloader_test.exs
  class Phoenix.CodeReloaderTest (line 1) | defmodule Phoenix.CodeReloaderTest
    method reload (line 10) | def reload(_, _) do
    method wait_until_is_up (line 67) | defp wait_until_is_up(process) do
  class Endpoint (line 5) | defmodule Endpoint
    method config (line 6) | def config(:reloadable_compilers), do: [:unknown_compiler, :elixir]
    method config (line 7) | def config(:reloadable_apps), do: nil

FILE: test/phoenix/config_test.exs
  class Phoenix.ConfigTest (line 1) | defmodule Phoenix.ConfigTest

FILE: test/phoenix/controller/controller_test.exs
  class Phoenix.Controller.ControllerTest (line 1) | defmodule Phoenix.Controller.ControllerTest
    method get_resp_content_type (line 14) | defp get_resp_content_type(conn) do
    method with_accept (line 453) | defp with_accept(header) do
    method sent_conn (line 932) | defp sent_conn do

FILE: test/phoenix/controller/flash_test.exs
  class Phoenix.Controller.FlashTest (line 1) | defmodule Phoenix.Controller.FlashTest
    method with_session (line 21) | def with_session(conn) do

FILE: test/phoenix/controller/pipeline_test.exs
  class Phoenix.Controller.PipelineTest (line 1) | defmodule Phoenix.Controller.PipelineTest
    method init (line 106) | def init(opts), do: opts
    method call (line 107) | def call(conn, :not_a_conn), do: Plug.Conn.send_resp(conn, 200, "fallb...
    method call (line 108) | def call(_conn, :bad_fallback), do: :bad_fallback
    method stack_conn (line 235) | defp stack_conn() do
  class MyController (line 7) | defmodule MyController
    method show (line 19) | def show(conn, _) do
    method no_fallback (line 23) | def no_fallback(_conn, _) do
    method create (line 27) | def create(conn, _) do
    method secret (line 31) | def secret(conn, _) do
    method no_match (line 35) | def no_match(_conn, %{"no" => "match"}) do
    method non_top_level_function_clause_error (line 39) | def non_top_level_function_clause_error(conn, params) do
    method trigger_func_clause_error (line 43) | defp trigger_func_clause_error(%{"no" => "match"}), do: "shouldn't eve...
    method do_halt (line 45) | defp do_halt(conn, _), do: halt(conn)
    method prepend (line 47) | defp prepend(conn, val) do
  class NoViewsController (line 52) | defmodule NoViewsController
    method show (line 55) | def show(conn, _), do: conn
  class FallbackFunctionController (line 58) | defmodule FallbackFunctionController
    method fallback (line 65) | def fallback(_conn, _), do: :not_a_conn
    method bad_fallback (line 67) | def bad_fallback(_conn, _), do: :bad_fallback
    method function_plug (line 69) | defp function_plug(%Plug.Conn{} = conn, :not_a_conn) do
    method function_plug (line 73) | defp function_plug(%Plug.Conn{}, :bad_fallback), do: :bad_function_fal...
    method put_assign (line 75) | defp put_assign(conn, _), do: assign(conn, :value_before_action, :a_va...
  class ActionController (line 78) | defmodule ActionController
    method action (line 85) | def action(conn, _) do
    method show (line 89) | def show(conn, _, _), do: text(conn, "show")
    method no_match (line 91) | def no_match(_conn, _, %{"no" => "match"}) do
    method fallback (line 95) | def fallback(_conn, _, _) do
    method bad_fallback (line 99) | def bad_fallback(_conn, _, _) do
    method put_assign (line 103) | defp put_assign(conn, _), do: assign(conn, :value_before_action, :a_va...

FILE: test/phoenix/controller/render_test.exs
  class Phoenix.Controller.RenderTest (line 3) | defmodule Phoenix.Controller.RenderTest
    method conn (line 9) | defp conn() do
    method layout_conn (line 13) | defp layout_conn() do
    method html_response? (line 17) | defp html_response?(conn) do

FILE: test/phoenix/debug_test.exs
  class Phoenix.DebugTest (line 1) | defmodule Phoenix.DebugTest
  class FakeSocket (line 9) | defmodule FakeSocket
    method init (line 12) | def init(channel_pid) do
    method handle_info (line 17) | def handle_info({:debug_channels, ref, reply_to}, state) do
  class FakeChannel (line 27) | defmodule FakeChannel
    method init (line 30) | def init(state) do
    method handle_call (line 35) | def handle_call(:socket, _from, state) do

FILE: test/phoenix/digester/gzip_test.exs
  class Phoenix.Digester.GzipTest (line 1) | defmodule Phoenix.Digester.GzipTest

FILE: test/phoenix/digester_test.exs
  class Phoenix.DigesterTest (line 1) | defmodule Phoenix.DigesterTest
    method assets_files (line 561) | defp assets_files(path) do
    method now (line 569) | defp now do
    method json_read! (line 573) | defp json_read!(path) do
    method add_digest_test_compressor (line 579) | defp add_digest_test_compressor() do
  class DigestTestCompressor (line 8) | defmodule DigestTestCompressor
    method compress_file (line 10) | def compress_file(_file_path, _content), do: :error
    method file_extensions (line 11) | def file_extensions, do: [".digest_test"]

FILE: test/phoenix/endpoint/endpoint_test.exs
  class Phoenix.Endpoint.EndpointTest (line 6) | defmodule Phoenix.Endpoint.EndpointTest
    method validate_init_event (line 133) | def validate_init_event(event, measurements, metadata, _config) do
  class Endpoint (line 24) | defmodule Endpoint
  class NoConfigEndpoint (line 33) | defmodule NoConfigEndpoint
  class SystemTupleEndpoint (line 37) | defmodule SystemTupleEndpoint
  class TelemetryEventEndpoint (line 41) | defmodule TelemetryEventEndpoint

FILE: test/phoenix/endpoint/render_errors_test.exs
  class Phoenix.Endpoint.RenderErrorsTest (line 1) | defmodule Phoenix.Endpoint.RenderErrorsTest
    method render (line 10) | def render("app.html", assigns) do
    method render (line 14) | def render("404.html", %{
    method render (line 25) | def render("404.json", %{
    method render (line 36) | def render("415.html", %{
    method render (line 47) | def render("500.html", %{
    method render (line 58) | def render("500.text", _) do
    method put_endpoint (line 191) | defp put_endpoint(conn) do
    method assert_render (line 195) | defp assert_render(status, conn, opts, func) do
  class Router (line 62) | defmodule Router
  class Endpoint (line 102) | defmodule Endpoint

FILE: test/phoenix/endpoint/supervisor_test.exs
  class Phoenix.Endpoint.SupervisorTest (line 1) | defmodule Phoenix.Endpoint.SupervisorTest
    method persistent! (line 59) | defp persistent!(endpoint), do: :persistent_term.get({Phoenix.Endpoint...
  class HTTPSEndpoint (line 5) | defmodule HTTPSEndpoint
    method config (line 6) | def config(:otp_app), do: :phoenix
    method config (line 7) | def config(:https), do: [port: 443]
    method config (line 8) | def config(:http), do: false
    method config (line 9) | def config(:url), do: [host: "example.com"]
    method config (line 10) | def config(_), do: nil
  class HTTPEndpoint (line 13) | defmodule HTTPEndpoint
    method config (line 14) | def config(:otp_app), do: :phoenix
    method config (line 15) | def config(:https), do: false
    method config (line 16) | def config(:http), do: [port: 80]
    method config (line 17) | def config(:url), do: [host: "example.com"]
    method config (line 18) | def config(_), do: nil
  class HTTPEnvVarEndpoint (line 21) | defmodule HTTPEnvVarEndpoint
    method config (line 22) | def config(:otp_app), do: :phoenix
    method config (line 23) | def config(:https), do: false
    method config (line 24) | def config(:http), do: [port: {:system, "PHOENIX_PORT"}]
    method config (line 25) | def config(:url), do: [host: {:system, "PHOENIX_HOST"}]
    method config (line 26) | def config(_), do: nil
  class URLEndpoint (line 29) | defmodule URLEndpoint
    method config (line 30) | def config(:https), do: false
    method config (line 31) | def config(:http), do: false
    method config (line 32) | def config(:url), do: [host: "example.com", port: 678, scheme: "random"]
    method config (line 33) | def config(_), do: nil
  class StaticURLEndpoint (line 36) | defmodule StaticURLEndpoint
    method config (line 37) | def config(:https), do: false
    method config (line 38) | def config(:http), do: []
    method config (line 39) | def config(:url), do: []
    method config (line 40) | def config(:static_url), do: [host: "static.example.com"]
    method config (line 41) | def config(_), do: nil
  class ServerEndpoint (line 44) | defmodule ServerEndpoint
    method __sockets__ (line 45) | def __sockets__(), do: []

FILE: test/phoenix/endpoint/watcher_test.exs
  class Phoenix.Endpoint.WatcherTest (line 1) | defmodule Phoenix.Endpoint.WatcherTest

FILE: test/phoenix/integration/endpoint_test.exs
  class Phoenix.Integration.EndpointTest (line 4) | defmodule Phoenix.Integration.EndpointTest
    method serve_endpoints (line 203) | defp serve_endpoints(bool) do
  class Router (line 31) | defmodule Router
    method __routes__ (line 53) | def __routes__ do
  class Wrapper (line 58) | defmodule Wrapper

FILE: test/phoenix/integration/long_poll_channels_test.exs
  class Phoenix.Integration.LongPollChannelsTest (line 3) | defmodule Phoenix.Integration.LongPollChannelsTest
    method assert_down (line 160) | def assert_down(topic) do
    method poll (line 170) | def poll(method, path, vsn, params, json \\ nil, headers \\ %{}) do
    method serializer (line 187) | defp serializer("2." <> _, json), do: {V2.JSONSerializer, json}
    method serializer (line 188) | defp serializer(_, nil), do: {V1.JSONSerializer, nil}
    method serializer (line 194) | defp serializer(_, %{} = json) do
    method decode_body (line 198) | defp decode_body(serializer, %{} = resp) do
    method encode (line 208) | defp encode(_vsn, nil), do: ""
    method encode (line 216) | defp encode(V2.JSONSerializer, %{} = map) do
    method encode (line 251) | defp encode(V1.JSONSerializer, %{} = map), do: Phoenix.json_library()....
    method join (line 262) | def join(
    method join (line 273) | def join(path, topic, vsn, join_ref, :local, payload, params, headers) do
    method join (line 301) | def join(path, topic, vsn, join_ref, :pubsub, payload, params, headers...
  class RoomChannel (line 26) | defmodule RoomChannel
    method join (line 31) | def join(topic, message, socket) do
    method handle_info (line 37) | def handle_info({:after_join, message}, socket) do
    method handle_in (line 43) | def handle_in("bin", {:binary, _bin}, socket) do
    method handle_in (line 48) | def handle_in("new_msg", message, socket) do
    method handle_in (line 53) | def handle_in("boom", _message, _socket) do
    method handle_out (line 57) | def handle_out(event, payload, socket) do
  class UserSocketConnectInfo (line 63) | defmodule UserSocketConnectInfo
    method connect (line 68) | def connect(params, socket, connect_info) do
    method id (line 90) | def id(socket) do
  class UserSocket (line 95) | defmodule UserSocket
    method connect (line 100) | def connect(%{"reject" => "true"}, _socket) do
    method connect (line 104) | def connect(%{"custom_error" => "true"}, _socket) do
    method connect (line 108) | def connect(params, socket) do
    method id (line 113) | def id(socket) do
  class Endpoint (line 118) | defmodule Endpoint

FILE: test/phoenix/integration/long_poll_socket_test.exs
  class Phoenix.Integration.LongPollSocketTest (line 3) | defmodule Phoenix.Integration.LongPollSocketTest
    method poll (line 93) | def poll(method, path, params, body \\ nil, headers \\ %{}) do
  class UserSocket (line 28) | defmodule UserSocket
    method child_spec (line 31) | def child_spec(opts) do
    method connect (line 36) | def connect(map) do
    method init (line 41) | def init({:params, _} = state) do
    method handle_in (line 45) | def handle_in({"params", opts}, {:params, params} = state) do
    method handle_in (line 50) | def handle_in({"ping", opts}, state) do
    method handle_info (line 56) | def handle_info(:ping, state) do
    method terminate (line 60) | def terminate(_reason, {:params, _}) do
  class Endpoint (line 65) | defmodule Endpoint

FILE: test/phoenix/integration/websocket_channels_test.exs
  class Phoenix.Integration.WebSocketChannelsTest (line 3) | defmodule Phoenix.Integration.WebSocketChannelsTest
    method lobby (line 26) | defp lobby do
  class RoomChannel (line 30) | defmodule RoomChannel
    method join (line 35) | def join(topic, message, socket) do
    method handle_info (line 41) | def handle_info({:after_join, message}, socket) do
    method handle_in (line 47) | def handle_in("new_msg", message, socket) do
    method handle_in (line 52) | def handle_in("boom", _message, _socket) do
    method handle_in (line 56) | def handle_in("binary_event", {:binary, data}, socket) do
    method handle_out (line 61) | def handle_out("new_msg", payload, socket) do
    method terminate (line 66) | def terminate(_reason, socket) do
  class CustomChannel (line 72) | defmodule CustomChannel
    method start_link (line 75) | def start_link(from) do
    method init (line 79) | def init({_, _}) do
    method handle_info (line 83) | def handle_info({Phoenix.Channel, payload, from, socket}, :init) do
    method handle_info (line 99) | def handle_info(%Message{event: "close"}, socket) do
    method handle_info (line 104) | def handle_info(:stop, socket) do
  class UserSocketConnectInfo (line 109) | defmodule UserSocketConnectInfo
    method connect (line 114) | def connect(params, socket, connect_info) do
    method id (line 134) | def id(socket) do
  class UserSocket (line 139) | defmodule UserSocket
    method connect (line 145) | def connect(%{"reject" => "true"}, _socket) do
    method connect (line 149) | def connect(%{"ratelimit" => "true"}, _socket) do
    method connect (line 153) | def connect(params, socket) do
    method id (line 158) | def id(socket) do
    method handle_error (line 162) | def handle_error(conn, :rate_limit), do: Plug.Conn.send_resp(conn, 429...
  class Endpoint (line 165) | defmodule Endpoint
    method put_session (line 206) | defp put_session(conn, _) do

FILE: test/phoenix/integration/websocket_socket_test.exs
  class Phoenix.Integration.WebSocketTest (line 4) | defmodule Phoenix.Integration.WebSocketTest
  class UserSocket (line 27) | defmodule UserSocket
    method child_spec (line 30) | def child_spec(opts) do
    method connect (line 35) | def connect(map) do
    method init (line 40) | def init({:params, _} = state) do
    method handle_in (line 44) | def handle_in({"params", opts}, {:params, params} = state) do
    method handle_in (line 49) | def handle_in({"ping", opts}, state) do
    method handle_info (line 55) | def handle_info(:ping, state) do
    method terminate (line 59) | def terminate(_reason, {:params, _}) do
  class PingSocket (line 64) | defmodule PingSocket
    method child_spec (line 67) | def child_spec(_opts), do: :ignore
    method connect (line 68) | def connect(_), do: {:ok, %{}}
    method init (line 69) | def init(state), do: {:ok, state}
    method handle_in (line 71) | def handle_in({"ping:start", _}, state) do
    method handle_in (line 75) | def handle_in({"ping:start:" <> payload, _}, state) do
    method handle_info (line 79) | def handle_info(_, state), do: {:ok, state}
    method handle_control (line 81) | def handle_control({payload, opts}, state) do
    method terminate (line 86) | def terminate(_reason, _state), do: :ok
  class Endpoint (line 89) | defmodule Endpoint

FILE: test/phoenix/logger_test.exs
  class Phoenix.LoggerTest (line 1) | defmodule Phoenix.LoggerTest

FILE: test/phoenix/naming_test.exs
  class Phoenix.NamingTest (line 1) | defmodule Phoenix.NamingTest

FILE: test/phoenix/param_test.exs
  class Phoenix.ParamTest (line 1) | defmodule Phoenix.ParamTest

FILE: test/phoenix/presence_test.exs
  class Phoenix.PresenceTest (line 1) | defmodule Phoenix.PresenceTest
  class DefaultPresence (line 5) | defmodule DefaultPresence
  class MyPresence (line 9) | defmodule MyPresence
    method fetch (line 12) | def fetch(_topic, entries) do
  class MetasPresence (line 19) | defmodule MetasPresence
    method init (line 22) | def init(state), do: {:ok, state}
    method handle_metas (line 24) | def handle_metas(topic, diff, presences, state) do
  class MetasMissingInitPresence (line 30) | defmodule MetasMissingInitPresence
    method init_presence (line 33) | def init_presence do
    method handle_metas (line 42) | def handle_metas(_topic, _diff, _presences, _state) do
  class CustomDispatcher (line 47) | defmodule CustomDispatcher
    method dispatch (line 48) | def dispatch(entries, from, message) do
  class CustomDispatcherPresence (line 55) | defmodule CustomDispatcherPresence

FILE: test/phoenix/router/console_formatter_test.exs
  class Phoenix.Router.ConsoleFormatterTest (line 8) | defmodule Phoenix.Router.ConsoleFormatterTest
    method __sockets__ (line 20) | def __sockets__, do: []
    method draw (line 128) | defp draw(router, endpoint \\ nil) do
  class RouterTestSingleRoutes (line 12) | defmodule RouterTestSingleRoutes
  class FormatterEndpoint (line 22) | defmodule FormatterEndpoint
  class RouterTestResources (line 36) | defmodule RouterTestResources
  class RouterTestResource (line 54) | defmodule RouterTestResource
  class HelpersFalseRouter (line 96) | defmodule HelpersFalseRouter

FILE: test/phoenix/router/forward_test.exs
  class Phoenix.Router.HealthController (line 1) | defmodule Phoenix.Router.HealthController
    method health (line 3) | def health(conn, _params), do: text(conn, "health")
  class Phoenix.Router.ForwardTest (line 6) | defmodule Phoenix.Router.ForwardTest
  class Controller (line 10) | defmodule Controller
    method index (line 14) | def index(conn, _params), do: text(conn, "admin index")
    method stats (line 15) | def stats(conn, _params), do: text(conn, "stats")
    method api_users (line 16) | def api_users(conn, _params), do: text(conn, "api users")
    method api_root (line 17) | def api_root(conn, _params), do: text(conn, "api root")
    method assign_fwd_script (line 18) | defp assign_fwd_script(conn, _), do: assign(conn, :fwd_script, conn.sc...
  class ApiRouter (line 21) | defmodule ApiRouter
  class AdminDashboard (line 32) | defmodule AdminDashboard
  class InitPlug (line 40) | defmodule InitPlug
    method init (line 41) | def init(_), do: %{non: :literal}
    method call (line 42) | def call(conn, %{non: :literal} = opts), do: assign(conn, :opts, opts)
  class AssignOptsPlug (line 45) | defmodule AssignOptsPlug
    method init (line 46) | def init(opts), do: opts
    method call (line 47) | def call(conn, opts), do: assign(conn, :opts, opts)
  class Router (line 50) | defmodule Router

FILE: test/phoenix/router/helpers_test.exs
  class Phoenix.Router.HelpersTest (line 19) | defmodule Phoenix.Router.HelpersTest
    method conn_with_endpoint (line 392) | defp conn_with_endpoint do
    method socket_with_endpoint (line 396) | defp socket_with_endpoint do
    method uri (line 400) | defp uri do
    method conn_with_script_name (line 490) | def conn_with_script_name(script_name \\ ~w(api)) do
    method uri_with_script_name (line 498) | defp uri_with_script_name do
  class Router (line 23) | defmodule Router
  class Endpoint (line 71) | defmodule Endpoint
    method url (line 72) | def url do
    method static_url (line 76) | def static_url do
    method path (line 80) | def path(path) do
    method static_path (line 84) | def static_path(path) do
    method static_integrity (line 88) | def static_integrity(_path), do: nil
  class ScriptName (line 472) | defmodule ScriptName
    method url (line 473) | def url do
    method static_url (line 477) | def static_url do
    method path (line 481) | def path(path) do
    method static_path (line 485) | def static_path(path) do

FILE: test/phoenix/router/pipeline_test.exs
  class Phoenix.Router.PipelineTest (line 81) | defmodule Phoenix.Router.PipelineTest
    class Phoenix.Router.PipelineTest.SampleController (line 1) | defmodule Phoenix.Router.PipelineTest.SampleController
      method index (line 3) | def index(conn, _params), do: text(conn, "index")
      method crash (line 4) | def crash(_conn, _params), do: raise("crash!")
      method noop_plug (line 5) | def noop_plug(conn, _opts), do: conn
    class Phoenix.Router.PipelineTest.Router (line 10) | defmodule Phoenix.Router.PipelineTest.Router
      method stop (line 66) | defp stop(conn, _) do
      method put_assign (line 70) | defp put_assign(conn, value) do
      method put_params (line 74) | defp put_params(conn, _) do

FILE: test/phoenix/router/resource_test.exs
  class Phoenix.Router.ResourceTest (line 1) | defmodule Phoenix.Router.ResourceTest
  class Api.GenericController (line 5) | defmodule Api.GenericController
    method show (line 7) | def show(conn, _params), do: text(conn, "show")
    method new (line 8) | def new(conn, _params), do: text(conn, "new")
    method edit (line 9) | def edit(conn, _params), do: text(conn, "edit")
    method create (line 10) | def create(conn, _params), do: text(conn, "create")
    method update (line 11) | def update(conn, _params), do: text(conn, "update")
    method delete (line 12) | def delete(conn, _params), do: text(conn, "delete")
  class Router (line 15) | defmodule Router

FILE: test/phoenix/router/resources_test.exs
  class Phoenix.Router.ResourcesTest (line 1) | defmodule Phoenix.Router.ResourcesTest
  class UserController (line 5) | defmodule UserController
    method show (line 7) | def show(conn, _params), do: text(conn, "show users")
    method index (line 8) | def index(conn, _params), do: text(conn, "index users")
    method new (line 9) | def new(conn, _params), do: text(conn, "new users")
    method edit (line 10) | def edit(conn, _params), do: text(conn, "edit users")
    method create (line 11) | def create(conn, _params), do: text(conn, "create users")
    method update (line 12) | def update(conn, _params), do: text(conn, "update users")
    method delete (line 13) | def delete(conn, _params), do: text(conn, "delete users")
  class Api.FileController (line 16) | defmodule Api.FileController
    method show (line 18) | def show(conn, _params), do: text(conn, "show files")
    method index (line 19) | def index(conn, _params), do: text(conn, "index files")
    method new (line 20) | def new(conn, _params), do: text(conn, "new files")
  class Api.CommentController (line 23) | defmodule Api.CommentController
    method show (line 25) | def show(conn, _params), do: text(conn, "show comments")
    method index (line 26) | def index(conn, _params), do: text(conn, "index comments")
    method new (line 27) | def new(conn, _params), do: text(conn, "new comments")
    method create (line 28) | def create(conn, _params), do: text(conn, "create comments")
    method update (line 29) | def update(conn, _params), do: text(conn, "update comments")
    method delete (line 30) | def delete(conn, _params), do: text(conn, "delete comments")
    method special (line 31) | def special(conn, _params), do: text(conn, "special comments")
  class Router (line 34) | defmodule Router

FILE: test/phoenix/router/route_test.exs
  class Phoenix.Router.RouteTest (line 1) | defmodule Phoenix.Router.RouteTest
    method init (line 6) | def init(opts), do: opts
  class AdminRouter (line 8) | defmodule AdminRouter
    method init (line 9) | def init(opts), do: opts
    method call (line 10) | def call(conn, _), do: Plug.Conn.assign(conn, :fwd_conn, conn)

FILE: test/phoenix/router/routing_test.exs
  class Phoenix.Router.RoutingTest (line 1) | defmodule Phoenix.Router.RoutingTest
  class SomePlug (line 7) | defmodule SomePlug
    method init (line 8) | def init(opts), do: opts
    method call (line 9) | def call(conn, _opts), do: conn
  class UserController (line 12) | defmodule UserController
    method index (line 14) | def index(conn, _params), do: text(conn, "users index")
    method show (line 15) | def show(conn, _params), do: text(conn, "users show")
    method top (line 16) | def top(conn, _params), do: text(conn, "users top")
    method options (line 17) | def options(conn, _params), do: text(conn, "users options")
    method connect (line 18) | def connect(conn, _params), do: text(conn, "users connect")
    method trace (line 19) | def trace(conn, _params), do: text(conn, "users trace")
    method not_found (line 20) | def not_found(conn, _params), do: text(put_status(conn, :not_found), "...
    method image (line 21) | def image(conn, _params), do: text(conn, conn.params["path"] || "show ...
    method move (line 22) | def move(conn, _params), do: text(conn, "users move")
    method any (line 23) | def any(conn, _params), do: text(conn, "users any")
    method raise (line 24) | def raise(_conn, _params), do: raise("boom")
    method exit (line 25) | def exit(_conn, _params), do: exit(:boom)
    method halt (line 27) | def halt(conn, _params) do
  class LogLevel (line 34) | defmodule LogLevel
    method log_level (line 35) | def log_level(%{params: %{"level" => "info"}}), do: :info
    method log_level (line 36) | def log_level(%{params: %{"level" => "error"}}), do: :error
    method log_level (line 37) | def log_level(_), do: :debug
  class Router (line 40) | defmodule Router
    method noop (line 82) | defp noop(conn, _), do: conn
    method halt (line 84) | defp halt(conn, _) do

FILE: test/phoenix/router/scope_test.exs
  class Phoenix.Router.ScopedRoutingTest (line 1) | defmodule Phoenix.Router.ScopedRoutingTest
  class Api.V1.UserController (line 7) | defmodule Api.V1.UserController
    method show (line 9) | def show(conn, _params), do: text(conn, "api v1 users show")
    method delete (line 10) | def delete(conn, _params), do: text(conn, "api v1 users delete")
    method edit (line 11) | def edit(conn, _params), do: text(conn, "api v1 users edit")
    method foo_host (line 12) | def foo_host(conn, _params), do: text(conn, "foo request from #{conn.h...
    method baz_host (line 13) | def baz_host(conn, _params), do: text(conn, "baz request from #{conn.h...
    method multi_host (line 14) | def multi_host(conn, _params), do: text(conn, "multi_host request from...
    method other_subdomain (line 16) | def other_subdomain(conn, _params),
    method proxy (line 19) | def proxy(conn, _) do
  class Api.V1.VenueController (line 25) | defmodule Api.V1.VenueController
    method init (line 26) | def init(opts), do: opts
    method call (line 27) | def call(conn, _opts), do: conn
  class Router (line 35) | defmodule Router

FILE: test/phoenix/socket/message_test.exs
  class Phoenix.Socket.MessageTest (line 1) | defmodule Phoenix.Socket.MessageTest

FILE: test/phoenix/socket/socket_test.exs
  class Phoenix.SocketTest (line 1) | defmodule Phoenix.SocketTest
  class UserSocket (line 7) | defmodule UserSocket
    method connect (line 13) | def connect(_params, socket, _connect_info), do: {:ok, socket}
    method id (line 14) | def id(_socket), do: nil

FILE: test/phoenix/socket/transport_test.exs
  class Phoenix.Socket.TransportTest (line 1) | defmodule Phoenix.Socket.TransportTest
  class Endpoint (line 18) | defmodule Endpoint
    method session_config (line 27) | def session_config(overrides \\ []), do: Keyword.merge(@session_config...
    method put_csrf (line 34) | defp put_csrf(conn, _opts) do
    method put_session (line 41) | defp put_session(conn, _) do

FILE: test/phoenix/socket/v1_json_serializer_test.exs
  class Phoenix.Socket.V1.JSONSerializerTest (line 1) | defmodule Phoenix.Socket.V1.JSONSerializerTest
    method encode! (line 11) | def encode!(serializer, msg) do
    method decode! (line 16) | def decode!(serializer, msg, opts), do: serializer.decode!(msg, opts)
    method fastlane! (line 18) | def fastlane!(serializer, msg) do

FILE: test/phoenix/socket/v2_json_serializer_test.exs
  class Phoenix.Socket.V2.JSONSerializerTest (line 1) | defmodule Phoenix.Socket.V2.JSONSerializerTest
    method encode! (line 63) | def encode!(serializer, msg) do
    method decode! (line 75) | def decode!(serializer, msg, opts \\ []) do
    method fastlane! (line 79) | def fastlane!(serializer, msg) do

FILE: test/phoenix/test/channel_test.exs
  class Phoenix.Test.ChannelTest (line 1) | defmodule Phoenix.Test.ChannelTest
    method assert_graceful_exit (line 183) | defp assert_graceful_exit(pid) do
  class Endpoint (line 13) | defmodule Endpoint
  class EmptyChannel (line 19) | defmodule EmptyChannel
    method join (line 22) | def join(_, _, socket), do: {:ok, socket}
    method handle_in (line 24) | def handle_in(_event, _params, socket) do
  class Channel (line 29) | defmodule Channel
    method join (line 34) | def join("foo:ok", _, socket) do
    method join (line 38) | def join("foo:external", _, socket) do
    method join (line 43) | def join("foo:timeout", _, socket) do
    method join (line 48) | def join("foo:socket", _, socket) do
    method join (line 53) | def join("foo:error", %{"error" => reason}, _socket) do
    method join (line 57) | def join("foo:crash", %{}, _socket) do
    method join (line 61) | def join("foo:payload", %{"string" => _payload}, socket) do
    method handle_in (line 65) | def handle_in("broadcast", broadcast, socket) do
    method handle_in (line 70) | def handle_in("noreply", %{"req" => arg}, socket) do
    method handle_in (line 75) | def handle_in("reply", %{"req" => arg}, socket) do
    method handle_in (line 79) | def handle_in("reply", %{}, socket) do
    method handle_in (line 83) | def handle_in("crash", %{}, _socket) do
    method handle_in (line 87) | def handle_in("async_reply", %{"req" => arg}, socket) do
    method handle_in (line 93) | def handle_in("stop", %{"reason" => stop}, socket) do
    method handle_in (line 97) | def handle_in("stop_and_reply", %{"req" => arg}, socket) do
    method handle_in (line 101) | def handle_in("stop_and_reply", %{}, socket) do
    method handle_out (line 105) | def handle_out("stop", _payload, socket) do
    method handle_info (line 109) | def handle_info(%Broadcast{event: event, payload: payload}, socket) do
    method handle_info (line 114) | def handle_info(:stop, socket) do
    method handle_info (line 118) | def handle_info(:push, socket) do
    method handle_info (line 123) | def handle_info(:broadcast, socket) do
    method handle_call (line 128) | def handle_call(:ping, _from, socket) do
    method handle_cast (line 132) | def handle_cast({:ping, ref, sender}, socket) do
    method terminate (line 137) | def terminate(_reason, %{topic: "foo:timeout"}) do
    method terminate (line 141) | def terminate(reason, socket) do
  class CodeChangeChannel (line 147) | defmodule CodeChangeChannel
    method join (line 150) | def join(_topic, _params, socket), do: {:ok, socket}
    method code_change (line 152) | def code_change(_old, _socket, _extra) do
  class UserSocket (line 157) | defmodule UserSocket
    method connect (line 162) | def connect(params, socket) do
    method id (line 170) | def id(_), do: "123"

FILE: test/phoenix/test/conn_test.exs
  class ConnError (line 2) | defmodule ConnError
  class Phoenix.Test.ConnTest (line 52) | defmodule Phoenix.Test.ConnTest
    class Phoenix.Test.ConnTest.CatchAll (line 1) | defmodule Phoenix.Test.ConnTest.CatchAll
      method init (line 6) | def init(opts), do: opts
      method call (line 8) | def call(conn, :stat) do
      method call (line 17) | def call(conn, _opts), do: Plug.Conn.assign(conn, :catch_all, true)
    class Phoenix.Test.ConnTest.RedirRouter (line 22) | defmodule Phoenix.Test.ConnTest.RedirRouter
    class Phoenix.Test.ConnTest.Router (line 28) | defmodule Phoenix.Test.ConnTest.Router
      method put_bypass (line 46) | def put_bypass(conn, pipeline) do
  class Endpoint (line 62) | defmodule Endpoint
    method init (line 64) | def init(opts), do: opts
    method call (line 65) | def call(conn, :set), do: resp(conn, 200, "ok")
    method call (line 67) | def call(conn, opts) do

FILE: test/phoenix/token_test.exs
  class Phoenix.TokenTest (line 1) | defmodule Phoenix.TokenTest
    method socket (line 184) | defp socket() do
    method conn (line 188) | defp conn() do
  class TokenEndpoint (line 12) | defmodule TokenEndpoint
    method config (line 13) | def config(:secret_key_base), do: "abc123"

FILE: test/phoenix/verified_routes_test.exs
  class PlugRouterWithVerifiedRoutes (line 13) | defmodule PlugRouterWithVerifiedRoutes
    method formatted_routes (line 23) | def formatted_routes(_plug_opts) do
    method verified_route? (line 30) | def verified_route?(_plug_opts, path) do
  class Phoenix.VerifiedRoutesTest (line 35) | defmodule Phoenix.VerifiedRoutesTest
    method conn_with_endpoint (line 119) | defp conn_with_endpoint(endpoint \\ Endpoint) do
    method socket_with_endpoint (line 123) | defp socket_with_endpoint(endpoint \\ Endpoint), do: %Phoenix.Socket{e...
    method conn_with_script_name (line 125) | def conn_with_script_name(script_name \\ ~w(api)) do
    method uri_with_script_name (line 131) | defp uri_with_script_name do
  class AdminRouter (line 42) | defmodule AdminRouter
  class Router (line 48) | defmodule Router
  class CatchAllWarningRouter (line 80) | defmodule CatchAllWarningRouter
  class ForwardedRouter (line 88) | defmodule ForwardedRouter
  class Endpoint (line 96) | defmodule Endpoint
    method url (line 97) | def url, do: "https://example.com"
    method static_url (line 98) | def static_url, do: "https://static.example.com"
    method path (line 99) | def path(path), do: path
    method static_path (line 100) | def static_path(path), do: path
    method static_integrity (line 101) | def static_integrity(_path), do: nil
  class ScriptName (line 104) | defmodule ScriptName
    method url (line 105) | def url, do: "https://example.com"
    method static_url (line 106) | def static_url, do: "https://static.example.com"
    method path (line 107) | def path(path), do: "/api" <> path
    method static_path (line 108) | def static_path(path), do: "/api" <> path
  class StaticPath (line 111) | defmodule StaticPath
    method url (line 112) | def url, do: "https://example.com"
    method static_url (line 113) | def static_url, do: "https://example.com"
    method path (line 114) | def path(path), do: path
    method static_path (line 115) | def static_path(path), do: "/static" <> path
    method static_integrity (line 116) | def static_integrity(_path), do: nil

FILE: test/support/endpoint_helper.exs
  class Phoenix.Integration.EndpointHelper (line 1) | defmodule Phoenix.Integration.EndpointHelper
    method listen_on_os_assigned_port (line 17) | defp listen_on_os_assigned_port(_) do
    method get_port_number_and_close (line 22) | defp get_port_number_and_close(socket) do

FILE: test/support/http_client.exs
  class Phoenix.Integration.HTTPClient (line 1) | defmodule Phoenix.Integration.HTTPClient
    method request (line 23) | def request(method, url, headers, body \\ "")
    method request (line 27) | def request(method, url, headers, body) do
    method format_resp (line 50) | defp format_resp({:ok, {{_http, status, _status_phrase}, headers, body...
    method format_resp (line 53) | defp format_resp({:error, reason}), do: {:error, reason}

FILE: test/support/router_helper.exs
  class RouterHelper (line 1) | defmodule RouterHelper
    method call (line 20) | def call(router, verb, path, params \\ nil, script_name \\ []) do
    method action (line 28) | def action(controller, verb, action, params \\ nil) do

FILE: test/support/websocket_client.exs
  class Phoenix.Integration.WebsocketClient (line 1) | defmodule Phoenix.Integration.WebsocketClient
    method connect (line 32) | def connect(sender, url, serializer, headers \\ []) do
    method close (line 42) | def close(socket) do
    method send_event (line 49) | def send_event(socket, topic, event, msg) do
    method send (line 56) | def send(socket, msg) do
    method send_heartbeat (line 63) | def send_heartbeat(socket) do
    method join (line 70) | def join(socket, topic, msg) do
    method leave (line 77) | def leave(socket, topic, msg) do
    method init (line 84) | def init({sender, serializer}) do
    method handle_call (line 91) | def handle_call({:connect, url, headers}, from, state) do
    method handle_call (line 125) | def handle_call({:send, msg}, _from, state) do
    method handle_cast (line 135) | def handle_cast(:close, state) do
    method do_close (line 139) | defp do_close(state) do
    method handle_info (line 148) | def handle_info(message, state) do
    method handle_responses (line 163) | defp handle_responses(state, responses)
    method handle_responses (line 165) | defp handle_responses(%{request_ref: ref} = state, [{:status, ref, sta...
    method handle_responses (line 170) | defp handle_responses(%{request_ref: ref} = state, [{:headers, ref, re...
    method handle_responses (line 175) | defp handle_responses(%{request_ref: ref} = state, [{:done, ref} | res...
    method handle_responses (line 204) | defp handle_responses(state, [_response | rest]) do
    method handle_responses (line 208) | defp handle_responses(state, []), do: state
    method handle_frames (line 210) | defp handle_frames(state, frames) do
    method stream_frame (line 247) | defp stream_frame(state, frame) do
    method reply (line 262) | defp reply(state, response) do
    method serialize_msg (line 267) | defp serialize_msg(msg, %{serializer: :noop} = state), do: {msg, state}
    method serialize_msg (line 269) | defp serialize_msg(%Message{payload: {:binary, _}} = msg, %{ref: ref} ...
    method serialize_msg (line 275) | defp serialize_msg(%Message{} = msg, %{ref: ref} = state) do
    method serialize_msg (line 281) | defp serialize_msg(msg, state), do: {msg, state}
    method join_ref_for (line 283) | defp join_ref_for(
    method join_ref_for (line 291) | defp join_ref_for(%{topic: topic}, %{topics: topics} = state) do
    method encode! (line 295) | defp encode!(map, state) do
    method binary_encode_push! (line 300) | defp binary_encode_push!(%Message{payload: {:binary, data}} = msg) do
    method binary_decode (line 323) | defp binary_decode(<<
    method binary_decode (line 337) | defp binary_decode(<<

FILE: test/test_helper.exs
  class Phoenix.ErrorView (line 8) | defmodule Phoenix.ErrorView
    method render (line 9) | def render("404.json", %{kind: kind, reason: _reason, stack: _stack, c...
    method render (line 13) | def render(template, %{conn: conn}) do
Condensed preview — 454 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,422K chars).
[
  {
    "path": ".formatter.exs",
    "chars": 1697,
    "preview": "locals_without_parens = [\n  # Phoenix.Channel\n  intercept: 1,\n\n  # Phoenix.Router\n  connect: 3,\n  connect: 4,\n  delete: "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 525,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n### Environment\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 351,
    "preview": "---\nblank_issues_enabled: true\n\ncontact_links:\n  - name: Ask questions, support, and general discussions\n    url: https:"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 419,
    "preview": "version: 2\nupdates:\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: "
  },
  {
    "path": ".github/workflows/assets.yml",
    "chars": 1889,
    "preview": "name: Assets\n\non:\n  push:\n    branches:\n      - main\n      - \"v*.*\"\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    r"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 3914,
    "preview": "name: CI\non: [push, pull_request]\npermissions:\n  contents: read\n\njobs:\n  mix_test:\n    name: mix test (OTP ${{matrix.otp"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "chars": 1025,
    "preview": "# https://docs.npmjs.com/trusted-publishers\nname: NPM Publish\n\non:\n  push:\n    tags:\n      - \"v*\"\n\npermissions:\n  id-tok"
  },
  {
    "path": ".gitignore",
    "chars": 330,
    "preview": "/_build/\n/deps/\n/doc/\n/node_modules/\n/tmp/\n/cover/\n\n/assets/node_modules/\n\n/installer/_build/\n/installer/assets/\n/instal"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 8073,
    "preview": "# Changelog for v1.8\n\nThis release requires Erlang/OTP 25+.\n\n## Streamlined generators\n\n  * Extend tailwindcss support i"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 2028,
    "preview": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, and in the interest of fostering an open"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 8058,
    "preview": "# Contributing to Phoenix\n\nPlease take a moment to review this document in order to make the contribution\nprocess easy a"
  },
  {
    "path": "LICENSE.md",
    "chars": 1071,
    "preview": "# MIT License\n\nCopyright (c) 2014 Chris McCord\n\nPermission is hereby granted, free of charge, to any person obtaining\na "
  },
  {
    "path": "README.md",
    "chars": 3286,
    "preview": "<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"./priv/static/phoenix-orange.png\" />\n  <source media=\"("
  },
  {
    "path": "RELEASE.md",
    "chars": 988,
    "preview": "# Release Instructions\n\n  1. Check related deps for required version bumps and compatibility (`phoenix_ecto`, `phoenix_h"
  },
  {
    "path": "SECURITY.md",
    "chars": 682,
    "preview": "# Security Policy\n\n## Supported versions\n\nPhoenix applies bug fixes only to the latest minor branch. Security patches ar"
  },
  {
    "path": "assets/js/phoenix/ajax.js",
    "chars": 3539,
    "preview": "import {\n  global,\n  XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n  static request(method, endPoint, he"
  },
  {
    "path": "assets/js/phoenix/channel.js",
    "chars": 8730,
    "preview": "import {closure} from \"./utils\"\nimport {\n  CHANNEL_EVENTS,\n  CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./p"
  },
  {
    "path": "assets/js/phoenix/constants.js",
    "chars": 846,
    "preview": "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefine"
  },
  {
    "path": "assets/js/phoenix/index.js",
    "chars": 7375,
    "preview": "/**\n * Phoenix Channels JavaScript client\n *\n * ## Socket Connection\n *\n * A single connection is established to the ser"
  },
  {
    "path": "assets/js/phoenix/longpoll.js",
    "chars": 6344,
    "preview": "import {\n  SOCKET_STATES,\n  TRANSPORTS,\n  AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBu"
  },
  {
    "path": "assets/js/phoenix/presence.js",
    "chars": 4871,
    "preview": "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n *     "
  },
  {
    "path": "assets/js/phoenix/push.js",
    "chars": 2556,
    "preview": "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example "
  },
  {
    "path": "assets/js/phoenix/serializer.js",
    "chars": 4305,
    "preview": "/* The default serializer for encoding and decoding messages */\nimport {\n  CHANNEL_EVENTS\n} from \"./constants\"\n\nexport d"
  },
  {
    "path": "assets/js/phoenix/socket.js",
    "chars": 22359,
    "preview": "import {\n  global,\n  phxWindow,\n  CHANNEL_EVENTS,\n  DEFAULT_TIMEOUT,\n  DEFAULT_VSN,\n  SOCKET_STATES,\n  TRANSPORTS,\n  WS_"
  },
  {
    "path": "assets/js/phoenix/timer.js",
    "chars": 1041,
    "preview": "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponent"
  },
  {
    "path": "assets/js/phoenix/utils.js",
    "chars": 213,
    "preview": "// wraps value in closure or returns closure\nexport let closure = (value) => {\n  if(typeof value === \"function\"){\n    re"
  },
  {
    "path": "assets/test/channel_test.js",
    "chars": 28265,
    "preview": "import {jest} from \"@jest/globals\"\nimport {Channel, Socket} from \"../js/phoenix\"\n\nlet channel, socket\n\nconst defaultRef "
  },
  {
    "path": "assets/test/longpoll_test.js",
    "chars": 8625,
    "preview": "import {jest} from \"@jest/globals\"\nimport {LongPoll} from \"../js/phoenix\"\nimport {Socket} from \"../js/phoenix\"\nimport {A"
  },
  {
    "path": "assets/test/presence_test.js",
    "chars": 8338,
    "preview": "import {Presence} from \"../js/phoenix\"\n\nconst clone = (obj) => {\n  let cloned = JSON.parse(JSON.stringify(obj))\n  Object"
  },
  {
    "path": "assets/test/serializer.js",
    "chars": 314,
    "preview": "\nexport const encode = (msg) => {\n  let payload = [\n    msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload\n  ]\n  r"
  },
  {
    "path": "assets/test/serializer_test.js",
    "chars": 3056,
    "preview": "/**\n * @jest-environment node\n */\n\nimport {TextEncoder, TextDecoder} from \"util\"\nimport {Serializer} from \"../js/phoenix"
  },
  {
    "path": "assets/test/socket_http_test.js",
    "chars": 631,
    "preview": "/**\n * @jest-environment jsdom\n * @jest-environment-options {\"url\": \"http://example.com/\"}\n */\nimport {Socket} from \"../"
  },
  {
    "path": "assets/test/socket_test.js",
    "chars": 32619,
    "preview": "import {jest} from \"@jest/globals\"\nimport {WebSocket, Server as WebSocketServer} from \"mock-socket\"\nimport {encode} from"
  },
  {
    "path": "babel.config.json",
    "chars": 55,
    "preview": "{\n    \"presets\": [\n        \"@babel/preset-env\"\n    ]\n}\n"
  },
  {
    "path": "config/config.exs",
    "chars": 988,
    "preview": "import Config\n\nconfig :logger, :console,\n  colors: [enabled: false],\n  format: \"\\n$time $metadata[$level] $message\\n\"\n\nc"
  },
  {
    "path": "eslint.config.mjs",
    "chars": 2266,
    "preview": "import jest from \"eslint-plugin-jest\"\nimport js from \"@eslint/js\"\nimport stylistic from \"@stylistic/eslint-plugin\"\n\nexpo"
  },
  {
    "path": "guides/asset_management.md",
    "chars": 12440,
    "preview": "# Asset Management\n\nBeside producing HTML, most web applications have various assets (JavaScript, CSS, images, fonts and"
  },
  {
    "path": "guides/authn_authz/api_authentication.md",
    "chars": 7265,
    "preview": "# API Authentication\n\n> **Requirement**: This guide expects that you have gone through the [`mix phx.gen.auth`](mix_phx_"
  },
  {
    "path": "guides/authn_authz/authn_authz.md",
    "chars": 1090,
    "preview": "# Introduction to Auth\n\nAuthentication (authn) and authorization (authz) are two important concepts in security. Authent"
  },
  {
    "path": "guides/authn_authz/mix_phx_gen_auth.md",
    "chars": 12262,
    "preview": "# mix phx.gen.auth\n\nThe `mix phx.gen.auth` command generates a flexible, pre-built authentication system into your Phoen"
  },
  {
    "path": "guides/authn_authz/scopes.md",
    "chars": 20305,
    "preview": "# Scopes\n\nA scope is a data structure used to keep information about the current request or session, such as the current"
  },
  {
    "path": "guides/cheatsheets/router.cheatmd",
    "chars": 1819,
    "preview": "# Routing cheatsheet\n\n> Those need to be declared in the correct router module and scope.\n\nA quick reference to the comm"
  },
  {
    "path": "guides/components.md",
    "chars": 11220,
    "preview": "# Components and HEEx\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](instal"
  },
  {
    "path": "guides/controllers.md",
    "chars": 19670,
    "preview": "# Controllers\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.h"
  },
  {
    "path": "guides/data_modelling/contexts.md",
    "chars": 4320,
    "preview": "# 1. Intro to Contexts\n\nPhoenix guides are broken into several major sections. The main building blocks are outlined und"
  },
  {
    "path": "guides/data_modelling/cross_context_boundaries.md",
    "chars": 31385,
    "preview": "# 4. Cross-context Boundaries\n\nNow that we have the beginnings of our product catalog features, let's begin to work on t"
  },
  {
    "path": "guides/data_modelling/faq.md",
    "chars": 2995,
    "preview": "# 6. FAQ\n\nHere we list frequently asked questions about contexts.\n\n## When to use code generators?\n\nIn this guide, we ha"
  },
  {
    "path": "guides/data_modelling/in_context_relationships.md",
    "chars": 13195,
    "preview": "# 3. In-context Relationships\n\nOur basic catalog features are nice, but let's take it up a notch by categorizing product"
  },
  {
    "path": "guides/data_modelling/more_examples.md",
    "chars": 15589,
    "preview": "# 5. Bringing It Home\n\nWith our `Catalog` and `ShoppingCart` contexts, we're seeing first-hand how our well-considered m"
  },
  {
    "path": "guides/data_modelling/your_first_context.md",
    "chars": 18493,
    "preview": "# 2. Your First Context\n\nAn ecommerce platform has wide-reaching coupling across a codebase so it's important to think a"
  },
  {
    "path": "guides/deployment/deployment.md",
    "chars": 8636,
    "preview": "# Introduction to Deployment\n\nOnce we have a working application, we're ready to deploy it. If you're not quite finished"
  },
  {
    "path": "guides/deployment/fly.md",
    "chars": 10186,
    "preview": "# Deploying on Fly.io\n\nThe main goal for this guide is to get a Phoenix application running on [Fly.io](https://fly.io)."
  },
  {
    "path": "guides/deployment/gigalixir.md",
    "chars": 5367,
    "preview": "# Deploying on Gigalixir\n\nOur main goal for this guide is to get a Phoenix application running on Gigalixir.\n\n## What we"
  },
  {
    "path": "guides/deployment/heroku.md",
    "chars": 20911,
    "preview": "# Deploying on Heroku\n\nOur main goal for this guide is to get a Phoenix application running on Heroku.\n\n## What we'll ne"
  },
  {
    "path": "guides/deployment/releases.md",
    "chars": 14766,
    "preview": "# Deploying with Releases\n\nOur main goal for this guide is to package your Phoenix application into a self-contained dir"
  },
  {
    "path": "guides/directory_structure.md",
    "chars": 8631,
    "preview": "# Directory structure\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](instal"
  },
  {
    "path": "guides/ecto.md",
    "chars": 29357,
    "preview": "# Ecto\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html) an"
  },
  {
    "path": "guides/howto/custom_error_pages.md",
    "chars": 8065,
    "preview": "# Custom Error Pages\n\nNew Phoenix projects have two error views called `ErrorHTML` and `ErrorJSON`, which live in `lib/h"
  },
  {
    "path": "guides/howto/file_uploads.md",
    "chars": 8415,
    "preview": "# File Uploads\n\nOne common task for web applications is uploading files. These files might be images, videos, PDFs, or f"
  },
  {
    "path": "guides/howto/swapping_databases.md",
    "chars": 3200,
    "preview": "# Swapping Databases\n\nPhoenix applications are configured to use PostgreSQL by default, but what if we want to use anoth"
  },
  {
    "path": "guides/howto/using_ssl.md",
    "chars": 6916,
    "preview": "# Using SSL\n\nTo prepare an application to serve requests over SSL, we need to add a little bit of configuration and two "
  },
  {
    "path": "guides/howto/writing_a_channels_client.md",
    "chars": 4017,
    "preview": "# Writing a Channels Client\n\nClient libraries for Phoenix Channels already exist in [several languages](https://hexdocs."
  },
  {
    "path": "guides/introduction/community.md",
    "chars": 4826,
    "preview": "# Community\n\nThe Elixir and Phoenix communities are friendly and welcoming. All questions and comments are valuable, so "
  },
  {
    "path": "guides/introduction/installation.md",
    "chars": 3591,
    "preview": "# Installation\n\nIn order to build a Phoenix application, we will need a few dependencies installed in our Operating Syst"
  },
  {
    "path": "guides/introduction/overview.md",
    "chars": 2075,
    "preview": "# Overview\n\nPhoenix is a web development framework written in Elixir which implements the server-side Model View Control"
  },
  {
    "path": "guides/introduction/packages_glossary.md",
    "chars": 2305,
    "preview": "# Packages Glossary\n\nBy default, Phoenix applications depend on several packages with different purposes.\nThis page is a"
  },
  {
    "path": "guides/introduction/up_and_running.md",
    "chars": 6073,
    "preview": "# Up and Running\n\nThere are two mechanisms to start a new Phoenix application: the express option, supported on some OSe"
  },
  {
    "path": "guides/json_and_apis.md",
    "chars": 15875,
    "preview": "# JSON and APIs\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation"
  },
  {
    "path": "guides/live_view.md",
    "chars": 7119,
    "preview": "# LiveView\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html"
  },
  {
    "path": "guides/plug.md",
    "chars": 12347,
    "preview": "# Plug\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html) an"
  },
  {
    "path": "guides/real_time/channels.md",
    "chars": 27430,
    "preview": "# Channels\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html"
  },
  {
    "path": "guides/real_time/presence.md",
    "chars": 10167,
    "preview": "# Presence\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html"
  },
  {
    "path": "guides/request_lifecycle.md",
    "chars": 16910,
    "preview": "# Request life-cycle\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](install"
  },
  {
    "path": "guides/routing.md",
    "chars": 28738,
    "preview": "# Routing\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html)"
  },
  {
    "path": "guides/security.md",
    "chars": 21616,
    "preview": "# Security \n\nAll software exposed to the public internet will be attacked. Most Phoenix applications fall into this cate"
  },
  {
    "path": "guides/telemetry.md",
    "chars": 21894,
    "preview": "# Telemetry\n\nIn this guide, we will show you how to instrument and report\non `:telemetry` events in your Phoenix applica"
  },
  {
    "path": "guides/testing/testing.md",
    "chars": 18885,
    "preview": "# Introduction to Testing\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](in"
  },
  {
    "path": "guides/testing/testing_channels.md",
    "chars": 8383,
    "preview": "# Testing Channels\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installat"
  },
  {
    "path": "guides/testing/testing_contexts.md",
    "chars": 7267,
    "preview": "# Testing Contexts\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](installat"
  },
  {
    "path": "guides/testing/testing_controllers.md",
    "chars": 17323,
    "preview": "# Testing Controllers\n\n> **Requirement**: This guide expects that you have gone through the [introductory guides](instal"
  },
  {
    "path": "installer/.gitignore",
    "chars": 31,
    "preview": "templates/phoenix-usage-rules/\n"
  },
  {
    "path": "installer/README.md",
    "chars": 336,
    "preview": "## mix phx.new\n\nProvides `phx.new` installer as an archive.\n\nTo install from Hex, run:\n\n    $ mix archive.install hex ph"
  },
  {
    "path": "installer/lib/mix/tasks/local.phx.ex",
    "chars": 386,
    "preview": "defmodule Mix.Tasks.Local.Phx do\n  use Mix.Task\n\n  @shortdoc \"Updates the Phoenix project generator locally\"\n\n  @moduled"
  },
  {
    "path": "installer/lib/mix/tasks/phx.new.ecto.ex",
    "chars": 1875,
    "preview": "defmodule Mix.Tasks.Phx.New.Ecto do\n  @moduledoc \"\"\"\n  Creates a new Ecto project within an umbrella project.\n\n  This ta"
  },
  {
    "path": "installer/lib/mix/tasks/phx.new.ex",
    "chars": 17082,
    "preview": "defmodule Mix.Tasks.Phx.New do\n  @moduledoc \"\"\"\n  Creates a new Phoenix project.\n\n  It expects the path of the project a"
  },
  {
    "path": "installer/lib/mix/tasks/phx.new.web.ex",
    "chars": 1132,
    "preview": "defmodule Mix.Tasks.Phx.New.Web do\n  @moduledoc \"\"\"\n  Creates a new Phoenix web project within an umbrella project.\n\n  I"
  },
  {
    "path": "installer/lib/phx_new/ecto.ex",
    "chars": 1080,
    "preview": "defmodule Phx.New.Ecto do\n  @moduledoc false\n  use Phx.New.Generator\n  alias Phx.New.{Project}\n\n  @pre \"phx_umbrella/app"
  },
  {
    "path": "installer/lib/phx_new/generator.ex",
    "chars": 20012,
    "preview": "defmodule Phx.New.Generator do\n  @moduledoc false\n  import Mix.Generator\n  alias Phx.New.{Project}\n\n  @phoenix Path.expa"
  },
  {
    "path": "installer/lib/phx_new/mailer.ex",
    "chars": 592,
    "preview": "defmodule Phx.New.Mailer do\n  @moduledoc false\n  use Phx.New.Generator\n  alias Phx.New.{Project}\n\n  template(:new, [\n   "
  },
  {
    "path": "installer/lib/phx_new/project.ex",
    "chars": 2155,
    "preview": "defmodule Phx.New.Project do\n  @moduledoc false\n  alias Phx.New.Project\n\n  defstruct base_path: nil,\n            app: ni"
  },
  {
    "path": "installer/lib/phx_new/single.ex",
    "chars": 6592,
    "preview": "defmodule Phx.New.Single do\n  @moduledoc false\n  use Phx.New.Generator\n  alias Phx.New.{Project}\n\n  template(:new, [\n   "
  },
  {
    "path": "installer/lib/phx_new/umbrella.ex",
    "chars": 2234,
    "preview": "defmodule Phx.New.Umbrella do\n  @moduledoc false\n  use Phx.New.Generator\n  alias Phx.New.{Ecto, Web, Project, Mailer}\n\n "
  },
  {
    "path": "installer/lib/phx_new/web.ex",
    "chars": 3736,
    "preview": "defmodule Phx.New.Web do\n  @moduledoc false\n  use Phx.New.Generator\n  alias Phx.New.{Project}\n\n  @pre \"phx_umbrella/apps"
  },
  {
    "path": "installer/mix.exs",
    "chars": 1980,
    "preview": "for path <- :code.get_path(),\n    Regex.match?(~r/phx_new-[\\w\\.\\-]+\\/ebin$/, List.to_string(path)) do\n  Code.delete_path"
  },
  {
    "path": "installer/recreate_default_css.exs",
    "chars": 1110,
    "preview": "File.rm_rf!(\"installer/dayzee\")\n\nshell! = fn command, opts ->\n  {_, 0} =\n    System.shell(\n      command,\n      Keyword."
  },
  {
    "path": "installer/templates/phx_assets/app.css.eex",
    "chars": 4131,
    "preview": "/* See the Tailwind configuration guide for advanced usage\n   https://tailwindcss.com/docs/configuration */\n\n@import \"ta"
  },
  {
    "path": "installer/templates/phx_assets/app.js.eex",
    "chars": 4256,
    "preview": "// If you want to use Phoenix channels, run `mix help phx.gen.channel`\n// to get started and then uncomment the line bel"
  },
  {
    "path": "installer/templates/phx_assets/daisyui-theme.js.eex",
    "chars": 46754,
    "preview": "/** 🌼\n *  @license MIT\n *  daisyUI bundle\n *  https://daisyui.com/\n */\n\nvar __defProp = Object.defineProperty;\nvar __get"
  },
  {
    "path": "installer/templates/phx_assets/daisyui.js.eex",
    "chars": 251588,
    "preview": "/** 🌼\n *  @license MIT\n *  daisyUI bundle\n *  https://daisyui.com/\n */\n\nvar __defProp = Object.defineProperty;\nvar __get"
  },
  {
    "path": "installer/templates/phx_assets/heroicons.js.eex",
    "chars": 1430,
    "preview": "const plugin = require(\"tailwindcss/plugin\")\nconst fs = require(\"fs\")\nconst path = require(\"path\")\n\nmodule.exports = plu"
  },
  {
    "path": "installer/templates/phx_assets/logo.svg.eex",
    "chars": 3072,
    "preview": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 71 48\" fill=\"currentColor\" aria-hidden=\"true\">\n  <path\n    d=\"m26.3"
  },
  {
    "path": "installer/templates/phx_assets/topbar.js.eex",
    "chars": 4391,
    "preview": "/**\n * @license MIT\n * topbar 3.0.0\n * http://buunguyen.github.io/topbar\n * Copyright (c) 2024 Buu Nguyen\n */\n(function "
  },
  {
    "path": "installer/templates/phx_assets/tsconfig.json.eex",
    "chars": 980,
    "preview": "// This file is needed on most editors to enable the intelligent autocompletion\n// of LiveView's JavaScript API methods."
  },
  {
    "path": "installer/templates/phx_ecto/data_case.ex.eex",
    "chars": 1597,
    "preview": "defmodule <%= @app_module %>.DataCase do\n  @moduledoc \"\"\"\n  This module defines the setup for tests requiring\n  access t"
  },
  {
    "path": "installer/templates/phx_ecto/formatter.exs.eex",
    "chars": 52,
    "preview": "[\n  import_deps: [:ecto_sql],\n  inputs: [\"*.exs\"]\n]\n"
  },
  {
    "path": "installer/templates/phx_ecto/repo.ex.eex",
    "chars": 134,
    "preview": "defmodule <%= @app_module %>.Repo do\n  use Ecto.Repo,\n    otp_app: :<%= @app_name %>,\n    adapter: <%= inspect @adapter_"
  },
  {
    "path": "installer/templates/phx_ecto/seeds.exs.eex",
    "chars": 373,
    "preview": "# Script for populating the database. You can run it as:\n#\n#     mix run priv/repo/seeds.exs\n#\n# Inside the script, you "
  },
  {
    "path": "installer/templates/phx_gettext/en/LC_MESSAGES/errors.po.eex",
    "chars": 2570,
    "preview": "## `msgid`s in this file come from POT (.pot) files.\n##\n## Do not add, change, or remove `msgid`s manually here as\n## th"
  },
  {
    "path": "installer/templates/phx_gettext/errors.pot.eex",
    "chars": 2598,
    "preview": "## This is a PO Template file.\n##\n## `msgid`s here are often extracted from source code.\n## Add new translations manuall"
  },
  {
    "path": "installer/templates/phx_gettext/gettext.ex.eex",
    "chars": 859,
    "preview": "defmodule <%= @web_namespace %>.Gettext do\n  @moduledoc \"\"\"\n  A module providing Internationalization with a gettext-bas"
  },
  {
    "path": "installer/templates/phx_mailer/lib/app_name/mailer.ex.eex",
    "chars": 91,
    "preview": "defmodule <%= @app_module %>.Mailer do\n  use Swoosh.Mailer, otp_app: :<%= @app_name %>\nend\n"
  },
  {
    "path": "installer/templates/phx_single/README.md.eex",
    "chars": 645,
    "preview": "# <%= @app_module %>\n\nTo start your Phoenix server:\n\n* Run `mix setup` to install and setup dependencies\n* Start Phoenix"
  },
  {
    "path": "installer/templates/phx_single/config/config.exs.eex",
    "chars": 2523,
    "preview": "# This file is responsible for configuring your application\n# and its dependencies with the aid of the Config module.\n#\n"
  },
  {
    "path": "installer/templates/phx_single/config/dev.exs.eex",
    "chars": 3460,
    "preview": "import Config\n\n# For development, we disable any cache and enable\n# debugging and code reloading.\n#\n# The watchers confi"
  },
  {
    "path": "installer/templates/phx_single/config/prod.exs.eex",
    "chars": 1234,
    "preview": "import Config\n\n<%= if @javascript or @css do %>\n# Note we also include the path to a cache manifest\n# containing the dig"
  },
  {
    "path": "installer/templates/phx_single/config/runtime.exs.eex",
    "chars": 4082,
    "preview": "import Config\n\n# config/runtime.exs is executed for all environments, including\n# during releases. It is executed after "
  },
  {
    "path": "installer/templates/phx_single/config/test.exs.eex",
    "chars": 998,
    "preview": "import Config\n\n# We don't run a server during test. If one is required,\n# you can enable the server option below.\nconfig"
  },
  {
    "path": "installer/templates/phx_single/formatter.exs.eex",
    "chars": 417,
    "preview": "[\n  import_deps: [<%= if @ecto do %>:ecto, :ecto_sql, <% end %>:phoenix],<%= if @ecto do %>\n  subdirectories: [\"priv/*/m"
  },
  {
    "path": "installer/templates/phx_single/gitignore.eex",
    "chars": 991,
    "preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
  },
  {
    "path": "installer/templates/phx_single/lib/app_name/application.ex.eex",
    "chars": 1572,
    "preview": "defmodule <%= @app_module %>.Application do\n  # See https://hexdocs.pm/elixir/Application.html\n  # for more information "
  },
  {
    "path": "installer/templates/phx_single/lib/app_name.ex.eex",
    "chars": 273,
    "preview": "defmodule <%= @app_module %> do\n  @moduledoc \"\"\"\n  <%= @app_module %> keeps the contexts that define your domain\n  and b"
  },
  {
    "path": "installer/templates/phx_single/lib/app_name_web.ex.eex",
    "chars": 2711,
    "preview": "defmodule <%= @web_namespace %> do\n  @moduledoc \"\"\"\n  The entrypoint for defining your web interface, such\n  as controll"
  },
  {
    "path": "installer/templates/phx_single/mix.exs.eex",
    "chars": 3461,
    "preview": "defmodule <%= @app_module %>.MixProject do\n  use Mix.Project\n\n  def project do\n    [\n      app: :<%= @app_name %>,\n     "
  },
  {
    "path": "installer/templates/phx_single/test/test_helper.exs.eex",
    "chars": 82,
    "preview": "ExUnit.start()<%= if @ecto do %>\n<%= @adapter_config[:test_setup_all] %><% end %>\n"
  },
  {
    "path": "installer/templates/phx_static/app.css",
    "chars": 49,
    "preview": "/* This file is for your main application CSS */\n"
  },
  {
    "path": "installer/templates/phx_static/app.js",
    "chars": 717,
    "preview": "// For Phoenix.HTML support, including form and button helpers\n// copy the following scripts into your javascript bundle"
  },
  {
    "path": "installer/templates/phx_static/default.css",
    "chars": 80104,
    "preview": "/* These are daisyUI styles for styling the default CoreComponents and generator files\n * included to prevent shipping a"
  },
  {
    "path": "installer/templates/phx_static/robots.txt",
    "chars": 203,
    "preview": "# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file\n#\n# To ban all spider"
  },
  {
    "path": "installer/templates/phx_test/controllers/error_html_test.exs.eex",
    "chars": 487,
    "preview": "defmodule <%= @web_namespace %>.ErrorHTMLTest do\n  use <%= @web_namespace %>.ConnCase, async: true\n\n  # Bring render_to_"
  },
  {
    "path": "installer/templates/phx_test/controllers/error_json_test.exs.eex",
    "chars": 398,
    "preview": "defmodule <%= @web_namespace %>.ErrorJSONTest do\n  use <%= @web_namespace %>.ConnCase, async: true\n\n  test \"renders 404\""
  },
  {
    "path": "installer/templates/phx_test/controllers/page_controller_test.exs.eex",
    "chars": 247,
    "preview": "defmodule <%= @web_namespace %>.PageControllerTest do\n  use <%= @web_namespace %>.ConnCase\n\n  test \"GET /\", %{conn: conn"
  },
  {
    "path": "installer/templates/phx_test/support/conn_case.ex.eex",
    "chars": 1272,
    "preview": "defmodule <%= @web_namespace %>.ConnCase do\n  @moduledoc \"\"\"\n  This module defines the test case to be used by\n  tests t"
  },
  {
    "path": "installer/templates/phx_umbrella/README.md.eex",
    "chars": 26,
    "preview": "# <%= @root_app_module %>\n"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/README.md.eex",
    "chars": 48,
    "preview": "# <%= @app_module %>\n\n**TODO: Add description**\n"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/config/config.exs.eex",
    "chars": 630,
    "preview": "import Config\n\n<%= if @namespaced? || @ecto do %>\n# Configure Mix tasks and generators\nconfig :<%= @app_name %><%= if @n"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/formatter.exs.eex",
    "chars": 380,
    "preview": "[<%= if @ecto do %>\n  import_deps: [:ecto, :ecto_sql],\n  subdirectories: [\"priv/*/migrations\"],<% end %><%= if @html do "
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/gitignore.eex",
    "chars": 636,
    "preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/lib/app_name/application.ex.eex",
    "chars": 1094,
    "preview": "defmodule <%= @app_module %>.Application do\n  # See https://hexdocs.pm/elixir/Application.html\n  # for more information "
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/lib/app_name.ex.eex",
    "chars": 273,
    "preview": "defmodule <%= @app_module %> do\n  @moduledoc \"\"\"\n  <%= @app_module %> keeps the contexts that define your domain\n  and b"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/mix.exs.eex",
    "chars": 1729,
    "preview": "defmodule <%= @app_module %>.MixProject do\n  use Mix.Project\n\n  def project do\n    [\n      app: :<%= @app_name %>,\n     "
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name/test/test_helper.exs.eex",
    "chars": 82,
    "preview": "ExUnit.start()<%= if @ecto do %>\n<%= @adapter_config[:test_setup_all] %><% end %>\n"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/README.md.eex",
    "chars": 605,
    "preview": "# <%= @web_namespace %>\n\nTo start your Phoenix server:\n\n* Run `mix setup` to install and setup dependencies\n* Start Phoe"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/config/config.exs.eex",
    "chars": 1484,
    "preview": "import Config\n\n<%= if @namespaced? || @ecto || @generators do %>\nconfig :<%= @web_app_name %><%= if @namespaced? do %>,\n"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs.eex",
    "chars": 2574,
    "preview": "import Config\n\n# For development, we disable any cache and enable\n# debugging and code reloading.\n#\n# The watchers confi"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/config/prod.exs.eex",
    "chars": 866,
    "preview": "import Config\n\n# Note we also include the path to a cache manifest\n# containing the digested version of static files. Th"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/config/runtime.exs.eex",
    "chars": 2929,
    "preview": "# The secret key base is used to sign/encrypt cookies and other secrets.\n# A default value is used in config/dev.exs and"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/config/test.exs.eex",
    "chars": 278,
    "preview": "import Config\n\n# We don't run a server during test. If one is required,\n# you can enable the server option below.\nconfig"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/formatter.exs.eex",
    "chars": 257,
    "preview": "[\n  import_deps: [:phoenix],<%= if @html do %>\n  plugins: [Phoenix.LiveView.HTMLFormatter],<% end %>\n  inputs: [<%= if @"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/gitignore.eex",
    "chars": 995,
    "preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/lib/app_name/application.ex.eex",
    "chars": 959,
    "preview": "defmodule <%= @web_namespace %>.Application do\n  # See https://hexdocs.pm/elixir/Application.html\n  # for more informati"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/lib/app_name.ex.eex",
    "chars": 2706,
    "preview": "defmodule <%= @web_namespace %> do\n  @moduledoc \"\"\"\n  The entrypoint for defining your web interface, such\n  as controll"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/mix.exs.eex",
    "chars": 2900,
    "preview": "defmodule <%= @web_namespace %>.MixProject do\n  use Mix.Project\n\n  def project do\n    [\n      app: :<%= @web_app_name %>"
  },
  {
    "path": "installer/templates/phx_umbrella/apps/app_name_web/test/test_helper.exs.eex",
    "chars": 82,
    "preview": "ExUnit.start()<%= if @ecto do %>\n<%= @adapter_config[:test_setup_all] %><% end %>\n"
  },
  {
    "path": "installer/templates/phx_umbrella/config/config.exs.eex",
    "chars": 437,
    "preview": "# This file is responsible for configuring your umbrella\n# and **all applications** and their dependencies with the\n# he"
  },
  {
    "path": "installer/templates/phx_umbrella/config/dev.exs.eex",
    "chars": 920,
    "preview": "import Config\n\n# Do not include metadata nor timestamps in development logs\nconfig :logger, :default_formatter, format: "
  },
  {
    "path": "installer/templates/phx_umbrella/config/extra_config.exs.eex",
    "chars": 402,
    "preview": "import Config\n\n# Configure Elixir's Logger\nconfig :logger, :default_formatter,\n  format: \"$time $metadata[$level] $messa"
  },
  {
    "path": "installer/templates/phx_umbrella/config/prod.exs.eex",
    "chars": 381,
    "preview": "import Config\n\n<%= if @mailer do %>\n# Configure Swoosh API Client\nconfig :swoosh, :api_client, Swoosh.ApiClient.Req\n\n# D"
  },
  {
    "path": "installer/templates/phx_umbrella/config/runtime.exs.eex",
    "chars": 659,
    "preview": "import Config\n\n# config/runtime.exs is executed for all environments, including\n# during releases. It is executed after "
  },
  {
    "path": "installer/templates/phx_umbrella/config/test.exs.eex",
    "chars": 739,
    "preview": "import Config\n\n# Print only warnings and errors during test\nconfig :logger, level: :warning<%= if @mailer do %>\n\n# In te"
  },
  {
    "path": "installer/templates/phx_umbrella/formatter.exs.eex",
    "chars": 144,
    "preview": "[<%= if @html do %>\n  plugins: [Phoenix.LiveView.HTMLFormatter],<% end %>\n  inputs: [\"mix.exs\", \"config/*.exs\"],\n  subdi"
  },
  {
    "path": "installer/templates/phx_umbrella/gitignore.eex",
    "chars": 641,
    "preview": "# The directory Mix will write compiled artifacts to.\n/_build/\n\n# If you run \"mix test --cover\", coverage assets end up "
  },
  {
    "path": "installer/templates/phx_umbrella/mix.exs.eex",
    "chars": 1613,
    "preview": "defmodule <%= @root_app_module %>.MixProject do\n  use Mix.Project\n\n  def project do\n    [\n      apps_path: \"apps\",\n     "
  },
  {
    "path": "installer/templates/phx_web/components/core_components.ex.eex",
    "chars": 16282,
    "preview": "defmodule <%= @web_namespace %>.CoreComponents do\n  @moduledoc \"\"\"\n  Provides core UI components.\n\n  At first glance, th"
  },
  {
    "path": "installer/templates/phx_web/components/layouts/root.html.heex.eex",
    "chars": 1821,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "installer/templates/phx_web/components/layouts.ex.eex",
    "chars": 5112,
    "preview": "defmodule <%= @web_namespace %>.Layouts do\n  @moduledoc \"\"\"\n  This module holds layouts and related functionality\n  used"
  },
  {
    "path": "installer/templates/phx_web/controllers/error_html.ex.eex",
    "chars": 764,
    "preview": "defmodule <%= @web_namespace %>.ErrorHTML do\n  @moduledoc \"\"\"\n  This module is invoked by your endpoint in case of error"
  },
  {
    "path": "installer/templates/phx_web/controllers/error_json.ex.eex",
    "chars": 634,
    "preview": "defmodule <%= @web_namespace %>.ErrorJSON do\n  @moduledoc \"\"\"\n  This module is invoked by your endpoint in case of error"
  },
  {
    "path": "installer/templates/phx_web/controllers/page_controller.ex.eex",
    "chars": 155,
    "preview": "defmodule <%= @web_namespace %>.PageController do\n  use <%= @web_namespace %>, :controller\n\n  def home(conn, _params) do"
  },
  {
    "path": "installer/templates/phx_web/controllers/page_html/home.html.heex.eex",
    "chars": 17345,
    "preview": "<Layouts.flash_group flash={@flash} />\n<div class=\"left-[40rem] fixed inset-y-0 right-0 z-0 hidden lg:block xl:left-[50r"
  },
  {
    "path": "installer/templates/phx_web/controllers/page_html.ex.eex",
    "chars": 258,
    "preview": "defmodule <%= @web_namespace %>.PageHTML do\n  @moduledoc \"\"\"\n  This module contains pages rendered by PageController.\n\n "
  },
  {
    "path": "installer/templates/phx_web/endpoint.ex.eex",
    "chars": 2038,
    "preview": "defmodule <%= @endpoint_module %> do\n  use Phoenix.Endpoint, otp_app: :<%= @web_app_name %>\n\n  # The session will be sto"
  },
  {
    "path": "installer/templates/phx_web/router.ex.eex",
    "chars": 1767,
    "preview": "defmodule <%= @web_namespace %>.Router do\n  use <%= @web_namespace %>, :router<%= if @html do %>\n\n  pipeline :browser do"
  },
  {
    "path": "installer/templates/phx_web/telemetry.ex.eex",
    "chars": 3092,
    "preview": "defmodule <%= @web_namespace %>.Telemetry do\n  use Supervisor\n  import Telemetry.Metrics\n\n  def start_link(arg) do\n    S"
  },
  {
    "path": "installer/templates/usage-rules/assets.md",
    "chars": 1374,
    "preview": "### JS and CSS guidelines\n\n- **Use Tailwind CSS classes and custom CSS rules** to create polished, responsive, and visua"
  },
  {
    "path": "installer/templates/usage-rules/phoenix.md",
    "chars": 1357,
    "preview": "### Phoenix v1.8 guidelines\n\n- **Always** begin your LiveView templates with `<Layouts.app flash={@flash} ...>` which wr"
  },
  {
    "path": "installer/templates/usage-rules/project.md",
    "chars": 389,
    "preview": "This is a web application written using the Phoenix web framework.\n\n## Project guidelines\n\n- Use `mix precommit` alias w"
  },
  {
    "path": "installer/test/mix_helper.exs",
    "chars": 4412,
    "preview": "# Get Mix output sent to the current\n# process to avoid polluting tests.\nMix.shell(Mix.Shell.Process)\n\ndefmodule MixHelp"
  },
  {
    "path": "installer/test/phx_new_ecto_test.exs",
    "chars": 1771,
    "preview": "Code.require_file \"mix_helper.exs\", __DIR__\n\ndefmodule Mix.Tasks.Phx.New.EctoTest do\n  use ExUnit.Case\n  import MixHelpe"
  },
  {
    "path": "installer/test/phx_new_test.exs",
    "chars": 30819,
    "preview": "Code.require_file(\"mix_helper.exs\", __DIR__)\n\ndefmodule Mix.Tasks.Phx.NewTest do\n  use ExUnit.Case, async: false\n  impor"
  },
  {
    "path": "installer/test/phx_new_umbrella_test.exs",
    "chars": 31906,
    "preview": "Code.require_file(\"mix_helper.exs\", __DIR__)\n\ndefmodule Mix.Tasks.Phx.New.UmbrellaTest do\n  use ExUnit.Case, async: fals"
  },
  {
    "path": "installer/test/phx_new_web_test.exs",
    "chars": 2368,
    "preview": "Code.require_file \"mix_helper.exs\", __DIR__\n\ndefmodule Mix.Tasks.Phx.New.WebTest do\n  use ExUnit.Case\n  import MixHelper"
  },
  {
    "path": "installer/test/test_helper.exs",
    "chars": 15,
    "preview": "ExUnit.start()\n"
  },
  {
    "path": "integration_test/README.md",
    "chars": 1369,
    "preview": "## Phoenix Integration Tests\n\nThis project contains integration tests for phoenix's generated projects.\n\n## Running test"
  },
  {
    "path": "integration_test/config/config.exs",
    "chars": 192,
    "preview": "import Config\n\nconfig :phoenix, :json_library, Jason\n\nconfig :swoosh, api_client: false\n\nconfig :tailwind, :version, \"4."
  },
  {
    "path": "integration_test/docker-compose.yml",
    "chars": 416,
    "preview": "version: '3'\nservices:\n  postgres:\n    image: postgres\n    ports:\n      - \"5432:5432\"\n    environment:\n      POSTGRES_PA"
  },
  {
    "path": "integration_test/docker.sh",
    "chars": 948,
    "preview": "#!/usr/bin/env sh -e\n\n# adapt with versions from .github/versions/ci.yml if necessary;\n# you can also override these wit"
  },
  {
    "path": "integration_test/mix.exs",
    "chars": 1868,
    "preview": "for path <- :code.get_path(),\n    Regex.match?(~r/phx_new-[\\w\\.\\-]+\\/ebin$/, List.to_string(path)) do\n  Code.delete_path"
  },
  {
    "path": "integration_test/test/code_generation/app_with_defaults_test.exs",
    "chars": 7433,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.AppWithDefaultsTest do\n  use Phoenix.Integration.CodeGeneratorCase, async: "
  },
  {
    "path": "integration_test/test/code_generation/app_with_mssql_adapter_test.exs",
    "chars": 6332,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.AppWithMSSQLAdapterTest do\n  use Phoenix.Integration.CodeGeneratorCase, asy"
  },
  {
    "path": "integration_test/test/code_generation/app_with_mysql_adapter_test.exs",
    "chars": 4691,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.AppWithMySqlAdapterTest do\n  use Phoenix.Integration.CodeGeneratorCase, asy"
  },
  {
    "path": "integration_test/test/code_generation/app_with_no_options_test.exs",
    "chars": 2797,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.AppWithNoOptionsTest do\n  use Phoenix.Integration.CodeGeneratorCase, async:"
  },
  {
    "path": "integration_test/test/code_generation/app_with_scopes_test.exs",
    "chars": 23077,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.AppWithScopesTest do\n  use Phoenix.Integration.CodeGeneratorCase, async: tr"
  },
  {
    "path": "integration_test/test/code_generation/app_with_sqlite3_adapter_test.exs",
    "chars": 4563,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.AppWithSQLite3AdapterTest do\n  use Phoenix.Integration.CodeGeneratorCase, a"
  },
  {
    "path": "integration_test/test/code_generation/umbrella_app_with_defaults_test.exs",
    "chars": 8412,
    "preview": "defmodule Phoenix.Integration.CodeGeneration.UmbrellaAppWithDefaultsTest do\n  use Phoenix.Integration.CodeGeneratorCase,"
  },
  {
    "path": "integration_test/test/support/code_generator_case.ex",
    "chars": 3975,
    "preview": "defmodule Phoenix.Integration.CodeGeneratorCase do\n  use ExUnit.CaseTemplate\n\n  using do\n    quote do\n      import unquo"
  },
  {
    "path": "integration_test/test/test_helper.exs",
    "chars": 289,
    "preview": "# Copy _build/test to _build/dev so it only has to be compiled once\nFile.rm_rf!(Path.expand(\"../_build/dev\", __DIR__))\n\n"
  },
  {
    "path": "integration_test/test.sh",
    "chars": 520,
    "preview": "#!/bin/sh -e\n\nmix local.rebar --force\nmix local.hex --force\n\n# Install Dependencies\napk add --no-progress --update git s"
  },
  {
    "path": "jest.config.js",
    "chars": 796,
    "preview": "/*\n * For a detailed explanation regarding each configuration property, visit:\n * https://jestjs.io/docs/configuration\n "
  },
  {
    "path": "lib/mix/phoenix/context.ex",
    "chars": 2852,
    "preview": "defmodule Mix.Phoenix.Context do\n  @moduledoc false\n\n  alias Mix.Phoenix.{Context, Schema}\n\n  defstruct name: nil,\n     "
  },
  {
    "path": "lib/mix/phoenix/schema.ex",
    "chars": 18365,
    "preview": "defmodule Mix.Phoenix.Schema do\n  @moduledoc false\n\n  alias Mix.Phoenix.Schema\n\n  defstruct module: nil,\n            rep"
  }
]

// ... and 254 more files (download for full content)

About this extraction

This page contains the full source code of the phoenixframework/phoenix GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 454 files (3.1 MB), approximately 833.7k tokens, and a symbol index with 2488 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.

Copied to clipboard!