├── .dockerignore
├── .editorconfig
├── .formatter.exs
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
├── imgs
│ └── welcome.png
├── scripts
│ └── app
│ │ ├── build_macos.sh
│ │ └── build_windows.sh
└── workflows
│ ├── assets.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── README.md
├── RELEASE.md
├── SECURITY.md
├── assets
├── .babelrc
├── build.js
├── css
│ ├── ansi.css
│ ├── app.css
│ ├── components.css
│ ├── js_interop.css
│ ├── live_view.css
│ ├── markdown.css
│ ├── notebook.css
│ ├── tooltips.css
│ └── utilities.css
├── js
│ ├── app.js
│ ├── confirm.js
│ ├── dom.js
│ ├── events.js
│ ├── hooks
│ │ ├── app_auth.js
│ │ ├── audio_input.js
│ │ ├── cell.js
│ │ ├── cell_editor.js
│ │ ├── cell_editor
│ │ │ ├── live_editor.js
│ │ │ └── live_editor
│ │ │ │ ├── codemirror
│ │ │ │ ├── collab.js
│ │ │ │ ├── collab_markers.js
│ │ │ │ ├── commands.js
│ │ │ │ ├── doctests.js
│ │ │ │ ├── formatter.js
│ │ │ │ ├── hover_details.js
│ │ │ │ ├── languages.js
│ │ │ │ ├── read_only_hint.js
│ │ │ │ ├── selecting_class.js
│ │ │ │ ├── signature.js
│ │ │ │ ├── theme.js
│ │ │ │ ├── toggle_with.js
│ │ │ │ └── tree_utils.js
│ │ │ │ ├── collab_client.js
│ │ │ │ ├── connection.js
│ │ │ │ └── highlight.js
│ │ ├── custom_view_settings.js
│ │ ├── dropzone.js
│ │ ├── editor_settings.js
│ │ ├── emoji_picker.js
│ │ ├── focus_on_update.js
│ │ ├── headline.js
│ │ ├── highlight.js
│ │ ├── image_input.js
│ │ ├── image_output.js
│ │ ├── index.js
│ │ ├── js_view.js
│ │ ├── js_view
│ │ │ ├── channel.js
│ │ │ └── iframe.js
│ │ ├── keyboard_control.js
│ │ ├── markdown_renderer.js
│ │ ├── scroll_on_update.js
│ │ ├── session.js
│ │ ├── session
│ │ │ └── cursor_history.js
│ │ ├── textarea_autosize.js
│ │ ├── timer.js
│ │ ├── user_form.js
│ │ ├── utc_datetime_input.js
│ │ ├── utc_time_input.js
│ │ └── virtualized_lines.js
│ └── lib
│ │ ├── app.js
│ │ ├── attribute.js
│ │ ├── cache_lru.js
│ │ ├── codec.js
│ │ ├── delta.js
│ │ ├── emitter.js
│ │ ├── key_buffer.js
│ │ ├── live_store.js
│ │ ├── markdown.js
│ │ ├── markdown
│ │ └── mermaid.js
│ │ ├── notebook.js
│ │ ├── pubsub.js
│ │ ├── settings.js
│ │ ├── storage.js
│ │ ├── text_utils.js
│ │ ├── user.js
│ │ └── utils.js
├── package-lock.json
├── package.json
├── tailwind.config.js
└── test
│ ├── hooks
│ ├── cell_editor
│ │ └── live_editor
│ │ │ └── collab_client.test.js
│ └── session
│ │ └── cursor_history.test.js
│ └── lib
│ ├── delta.test.js
│ ├── emitter.test.js
│ ├── pubsub.test.js
│ └── text_utils.test.js
├── config
├── config.exs
├── dev.exs
├── prod.exs
├── runtime.exs
└── test.exs
├── docs
├── authentication.md
├── authentication
│ ├── basic_auth.md
│ ├── cloudflare.md
│ ├── custom_auth.md
│ ├── google_iap.md
│ └── tailscale.md
├── deployment
│ ├── clustering.md
│ ├── docker.md
│ ├── fips.md
│ └── nginx_https.md
├── images
│ ├── add_app_server_to_deployment_group.png
│ ├── add_shared_file_storage.png
│ ├── add_shared_secret.png
│ ├── add_shared_secret_from_notebook.png
│ ├── app_preview.png
│ ├── app_server_authentication.png
│ ├── app_server_docker.png
│ ├── app_server_setup.png
│ ├── app_server_setup_message.png
│ ├── auth_via_teams.png
│ ├── deploy_button.png
│ ├── deployed_app.png
│ ├── deployed_apps.png
│ ├── deployment_group_form.png
│ ├── deployment_groups_inside_workspace.png
│ ├── email_domain_auth.png
│ ├── github_stars_notebook.png
│ ├── instructions_setup_app_server.png
│ └── oidc_groups_auth.png
├── stamping.md
├── teams
│ ├── deploy_app.md
│ ├── email_domain.md
│ ├── intro_to_teams.md
│ ├── oidc_groups.md
│ ├── oidc_sso.md
│ ├── shared_file_storages.md
│ ├── shared_secrets.md
│ └── teams_concepts.md
└── use_cases.md
├── elixirkit
├── .formatter.exs
├── .gitignore
├── README.md
├── demo
│ ├── .formatter.exs
│ ├── .gitignore
│ ├── README.md
│ ├── lib
│ │ └── demo.ex
│ ├── mix.exs
│ ├── rel
│ │ ├── appkit
│ │ │ ├── .gitignore
│ │ │ ├── App.entitlements
│ │ │ ├── Package.swift
│ │ │ ├── README.md
│ │ │ ├── Sources
│ │ │ │ └── Demo
│ │ │ │ │ └── Demo.swift
│ │ │ ├── build_app.sh
│ │ │ ├── build_dmg.sh
│ │ │ ├── run.sh
│ │ │ └── run_app.sh
│ │ ├── dotnet
│ │ │ ├── .gitignore
│ │ │ ├── Demo.cs
│ │ │ ├── Demo.csproj
│ │ │ ├── README.md
│ │ │ └── run.sh
│ │ ├── swift
│ │ │ ├── .gitignore
│ │ │ ├── Package.swift
│ │ │ ├── README.md
│ │ │ ├── Sources
│ │ │ │ └── Demo
│ │ │ │ │ └── Demo.swift
│ │ │ └── run.sh
│ │ └── winforms
│ │ │ ├── .gitignore
│ │ │ ├── Demo.cs
│ │ │ ├── Demo.csproj
│ │ │ ├── README.md
│ │ │ └── run.sh
│ └── test
│ │ ├── demo_test.exs
│ │ └── test_helper.exs
├── elixirkit_dotnet
│ ├── .gitignore
│ ├── ElixirKit.cs
│ └── ElixirKit.csproj
├── elixirkit_swift
│ ├── .gitignore
│ ├── Package.swift
│ ├── README.md
│ ├── Scripts
│ │ ├── build_macos_app.sh
│ │ └── build_macos_dmg.sh
│ ├── Sources
│ │ └── ElixirKit
│ │ │ └── ElixirKit.swift
│ └── Tests
│ │ └── ElixirKitTests
│ │ └── ElixirKitTests.swift
├── lib
│ ├── elixirkit.ex
│ └── elixirkit
│ │ ├── application.ex
│ │ └── server.ex
├── mix.exs
├── otp_bootstrap
│ ├── .gitignore
│ ├── build.sh
│ └── build_macos_universal.sh
└── test
│ ├── elixirkit_test.exs
│ └── test_helper.exs
├── iframe
├── .formatter.exs
├── .gitignore
├── Dockerfile
├── README.md
├── fly.toml
├── lib
│ ├── livebook_space
│ │ └── application.ex
│ └── livebook_space_web
│ │ └── plug.ex
├── mix.exs
├── mix.lock
└── priv
│ └── static
│ ├── iframe
│ ├── v1.html
│ ├── v2.html
│ ├── v3.html
│ ├── v4.html
│ └── v5.html
│ ├── images
│ ├── favicon.svg
│ └── logo.png
│ ├── index.html
│ └── robots.txt
├── lib
├── livebook.ex
├── livebook
│ ├── app.ex
│ ├── application.ex
│ ├── apps.ex
│ ├── apps
│ │ ├── app_spec.ex
│ │ ├── deployer.ex
│ │ ├── deployment_supervisor.ex
│ │ ├── manager.ex
│ │ ├── manager_watcher.ex
│ │ ├── path_app_spec.ex
│ │ ├── preview_app_spec.ex
│ │ └── teams_app_spec.ex
│ ├── config.ex
│ ├── ecto_types
│ │ └── hex_color.ex
│ ├── epmd.ex
│ ├── epmd
│ │ └── node_pool.ex
│ ├── file_system.ex
│ ├── file_system
│ │ ├── file.ex
│ │ ├── local.ex
│ │ ├── s3.ex
│ │ ├── s3
│ │ │ ├── client.ex
│ │ │ └── xml.ex
│ │ └── utils.ex
│ ├── file_systems.ex
│ ├── fly_api.ex
│ ├── hubs.ex
│ ├── hubs
│ │ ├── broadcasts.ex
│ │ ├── dockerfile.ex
│ │ ├── metadata.ex
│ │ ├── personal.ex
│ │ ├── provider.ex
│ │ ├── team.ex
│ │ └── team_client.ex
│ ├── intellisense.ex
│ ├── intellisense
│ │ ├── docs.ex
│ │ ├── identifier_matcher.ex
│ │ └── signature_matcher.ex
│ ├── k8s
│ │ ├── pod.ex
│ │ └── pvc.ex
│ ├── k8s_api.ex
│ ├── live_markdown.ex
│ ├── live_markdown
│ │ ├── export.ex
│ │ ├── import.ex
│ │ └── markdown_helpers.ex
│ ├── migration.ex
│ ├── notebook.ex
│ ├── notebook
│ │ ├── app_settings.ex
│ │ ├── cell.ex
│ │ ├── cell
│ │ │ ├── code.ex
│ │ │ ├── markdown.ex
│ │ │ └── smart.ex
│ │ ├── content_loader.ex
│ │ ├── export
│ │ │ └── elixir.ex
│ │ ├── learn.ex
│ │ ├── learn
│ │ │ ├── chat_app.livemd
│ │ │ ├── distributed_portals_with_elixir.livemd
│ │ │ ├── files
│ │ │ │ ├── portal-drop.jpeg
│ │ │ │ └── portal-list.jpeg
│ │ │ ├── github_stars.livemd
│ │ │ ├── intro_to_explorer.livemd
│ │ │ ├── intro_to_livebook.livemd
│ │ │ ├── intro_to_maplibre.livemd
│ │ │ ├── intro_to_vega_lite.livemd
│ │ │ └── kino
│ │ │ │ ├── custom_kinos.livemd
│ │ │ │ ├── intro_to_kino.livemd
│ │ │ │ ├── smart_cells.livemd
│ │ │ │ └── vm_introspection.livemd
│ │ └── section.ex
│ ├── notebook_manager.ex
│ ├── proxy
│ │ ├── adapter.ex
│ │ ├── handler.ex
│ │ └── server.ex
│ ├── release.ex
│ ├── runtime.ex
│ ├── runtime
│ │ ├── attached.ex
│ │ ├── definitions.ex
│ │ ├── dependencies.ex
│ │ ├── embedded.ex
│ │ ├── epmd.ex
│ │ ├── erl_dist.ex
│ │ ├── erl_dist
│ │ │ ├── evaluator_supervisor.ex
│ │ │ ├── io_forward_gl.ex
│ │ │ ├── logger_gl_handler.ex
│ │ │ ├── node_manager.ex
│ │ │ ├── runtime_server.ex
│ │ │ └── smart_cell_gl.ex
│ │ ├── evaluator.ex
│ │ ├── evaluator
│ │ │ ├── client_tracker.ex
│ │ │ ├── doctests.ex
│ │ │ ├── formatter.ex
│ │ │ ├── io_proxy.ex
│ │ │ ├── object_tracker.ex
│ │ │ └── tracer.ex
│ │ ├── fly.ex
│ │ ├── k8s.ex
│ │ ├── remote_utils.ex
│ │ └── standalone.ex
│ ├── secrets.ex
│ ├── secrets
│ │ └── secret.ex
│ ├── session.ex
│ ├── session
│ │ ├── data.ex
│ │ ├── file_guard.ex
│ │ └── worker.ex
│ ├── sessions.ex
│ ├── settings.ex
│ ├── settings
│ │ └── env_var.ex
│ ├── stamping.ex
│ ├── storage.ex
│ ├── system_resources.ex
│ ├── teams.ex
│ ├── teams
│ │ ├── agent.ex
│ │ ├── agent_key.ex
│ │ ├── app_deployment.ex
│ │ ├── authorization_group.ex
│ │ ├── broadcasts.ex
│ │ ├── connection.ex
│ │ ├── deployment_group.ex
│ │ ├── environment_variable.ex
│ │ ├── org.ex
│ │ ├── requests.ex
│ │ └── web_socket.ex
│ ├── text
│ │ ├── delta.ex
│ │ ├── delta
│ │ │ ├── operation.ex
│ │ │ └── transformation.ex
│ │ ├── js.ex
│ │ └── selection.ex
│ ├── tracker.ex
│ ├── update_check.ex
│ ├── users.ex
│ ├── users
│ │ └── user.ex
│ ├── utils.ex
│ ├── utils
│ │ ├── ansi.ex
│ │ ├── graph.ex
│ │ ├── supervision_step.ex
│ │ ├── time.ex
│ │ └── unique_task.ex
│ ├── zta.ex
│ └── zta
│ │ ├── basic_auth.ex
│ │ ├── cloudflare.ex
│ │ ├── google_iap.ex
│ │ ├── livebook_teams.ex
│ │ ├── pass_through.ex
│ │ └── tailscale.ex
├── livebook_app.ex
├── livebook_cli.ex
├── livebook_cli
│ ├── server.ex
│ └── task.ex
├── livebook_web.ex
├── livebook_web
│ ├── channels
│ │ ├── js_view_channel.ex
│ │ └── socket.ex
│ ├── components
│ │ ├── app_components.ex
│ │ ├── confirm.ex
│ │ ├── core_components.ex
│ │ ├── file_system_components.ex
│ │ ├── form_components.ex
│ │ ├── layout_components.ex
│ │ ├── layouts.ex
│ │ ├── layouts
│ │ │ ├── app.html.heex
│ │ │ ├── live.html.heex
│ │ │ └── root.html.heex
│ │ ├── notebook_components.ex
│ │ └── user_components.ex
│ ├── controllers
│ │ ├── auth_controller.ex
│ │ ├── auth_html.ex
│ │ ├── auth_html
│ │ │ └── index.html.heex
│ │ ├── error_html.ex
│ │ ├── error_json.ex
│ │ ├── health_controller.ex
│ │ ├── session_controller.ex
│ │ ├── user_controller.ex
│ │ └── user_html.ex
│ ├── endpoint.ex
│ ├── errors.ex
│ ├── helpers
│ │ ├── ansi_helpers.ex
│ │ ├── codec_helpers.ex
│ │ ├── html_helpers.ex
│ │ └── session_helpers.ex
│ ├── iframe_endpoint.ex
│ ├── live
│ │ ├── app_auth_live.ex
│ │ ├── app_live.ex
│ │ ├── app_session_live.ex
│ │ ├── app_session_live
│ │ │ ├── cell_outputs.ex
│ │ │ └── source_component.ex
│ │ ├── apps_dashboard_live.ex
│ │ ├── apps_live.ex
│ │ ├── file_select_component.ex
│ │ ├── file_system_writer.ex
│ │ ├── home_live.ex
│ │ ├── home_live
│ │ │ └── session_list_component.ex
│ │ ├── hooks
│ │ │ ├── app_auth_hook.ex
│ │ │ ├── auth_hook.ex
│ │ │ ├── sidebar_hook.ex
│ │ │ └── user_hook.ex
│ │ ├── hub
│ │ │ ├── edit
│ │ │ │ ├── personal_component.ex
│ │ │ │ └── team_component.ex
│ │ │ ├── edit_live.ex
│ │ │ ├── file_system_form_component.ex
│ │ │ ├── file_system_list_component.ex
│ │ │ ├── new_live.ex
│ │ │ ├── secret_form_component.ex
│ │ │ ├── secret_list_component.ex
│ │ │ └── teams
│ │ │ │ ├── deployment_group_agent_component.ex
│ │ │ │ ├── deployment_group_component.ex
│ │ │ │ └── deployment_group_form_component.ex
│ │ ├── js_view_component.ex
│ │ ├── learn_live.ex
│ │ ├── notebook_cards_component.ex
│ │ ├── open_live.ex
│ │ ├── open_live
│ │ │ ├── file_component.ex
│ │ │ ├── source_component.ex
│ │ │ ├── upload_component.ex
│ │ │ └── url_component.ex
│ │ ├── output.ex
│ │ ├── output
│ │ │ ├── audio_input_component.ex
│ │ │ ├── control_component.ex
│ │ │ ├── control_form_component.ex
│ │ │ ├── file_input_component.ex
│ │ │ ├── frame_component.ex
│ │ │ ├── grid_component.ex
│ │ │ ├── image_component.ex
│ │ │ ├── image_input_component.ex
│ │ │ ├── input_component.ex
│ │ │ ├── markdown_component.ex
│ │ │ ├── plain_text_component.ex
│ │ │ ├── tabs_component.ex
│ │ │ └── terminal_text_component.ex
│ │ ├── session_live.ex
│ │ ├── session_live
│ │ │ ├── add_file_entry_file_component.ex
│ │ │ ├── add_file_entry_unlisted_component.ex
│ │ │ ├── add_file_entry_upload_component.ex
│ │ │ ├── add_file_entry_url_component.ex
│ │ │ ├── app_docker_component.ex
│ │ │ ├── app_info_component.ex
│ │ │ ├── app_settings_component.ex
│ │ │ ├── app_teams_live.ex
│ │ │ ├── attached_runtime_component.ex
│ │ │ ├── bin_component.ex
│ │ │ ├── cell_component.ex
│ │ │ ├── code_cell_settings_component.ex
│ │ │ ├── custom_view_component.ex
│ │ │ ├── embedded_runtime_component.ex
│ │ │ ├── export_component.ex
│ │ │ ├── export_elixir_component.ex
│ │ │ ├── export_live_markdown_component.ex
│ │ │ ├── files_list_component.ex
│ │ │ ├── fly_runtime_component.ex
│ │ │ ├── insert_buttons_component.ex
│ │ │ ├── insert_file_component.ex
│ │ │ ├── insert_image_component.ex
│ │ │ ├── k8s_runtime_component.ex
│ │ │ ├── package_search_live.ex
│ │ │ ├── persistence_component.ex
│ │ │ ├── rename_file_entry_component.ex
│ │ │ ├── render.ex
│ │ │ ├── runtime_component.ex
│ │ │ ├── save_runtime_config_component.ex
│ │ │ ├── secrets_component.ex
│ │ │ ├── secrets_list_component.ex
│ │ │ ├── section_component.ex
│ │ │ ├── shortcuts_component.ex
│ │ │ └── standalone_runtime_component.ex
│ │ ├── settings_live.ex
│ │ ├── settings_live
│ │ │ ├── env_var_component.ex
│ │ │ └── env_vars_component.ex
│ │ └── user_component.ex
│ ├── plugs
│ │ ├── auth_plug.ex
│ │ ├── configured_plug.ex
│ │ ├── proxy_plug.ex
│ │ └── user_plug.ex
│ ├── router.ex
│ ├── telemetry.ex
│ └── verified_routes.ex
└── mix
│ └── tasks
│ └── compile.livebook_priv.ex
├── mix.exs
├── mix.lock
├── proto
├── .formatter.exs
├── .gitignore
├── README.md
├── lib
│ ├── livebook_proto.ex
│ └── livebook_proto
│ │ ├── agent.pb.ex
│ │ ├── agent_connected.pb.ex
│ │ ├── agent_joined.pb.ex
│ │ ├── agent_key.pb.ex
│ │ ├── agent_left.pb.ex
│ │ ├── app_deployment.pb.ex
│ │ ├── app_deployment_started.pb.ex
│ │ ├── app_deployment_status.pb.ex
│ │ ├── app_deployment_status_report.pb.ex
│ │ ├── app_deployment_status_type.pb.ex
│ │ ├── app_deployment_stopped.pb.ex
│ │ ├── app_deployment_updated.pb.ex
│ │ ├── authorization_group.pb.ex
│ │ ├── billing_status.pb.ex
│ │ ├── billing_status_canceled.pb.ex
│ │ ├── billing_status_canceling.pb.ex
│ │ ├── billing_status_trial_ended.pb.ex
│ │ ├── billing_status_trialing.pb.ex
│ │ ├── deployment_group.pb.ex
│ │ ├── deployment_group_created.pb.ex
│ │ ├── deployment_group_deleted.pb.ex
│ │ ├── deployment_group_secret.pb.ex
│ │ ├── deployment_group_updated.pb.ex
│ │ ├── environment_variable.pb.ex
│ │ ├── error.pb.ex
│ │ ├── event.pb.ex
│ │ ├── file_system.pb.ex
│ │ ├── file_system_created.pb.ex
│ │ ├── file_system_deleted.pb.ex
│ │ ├── file_system_updated.pb.ex
│ │ ├── org_status_cancel.pb.ex
│ │ ├── org_status_trial.pb.ex
│ │ ├── org_updated.pb.ex
│ │ ├── secret.pb.ex
│ │ ├── secret_created.pb.ex
│ │ ├── secret_deleted.pb.ex
│ │ ├── secret_updated.pb.ex
│ │ ├── user_connected.pb.ex
│ │ └── user_deleted.pb.ex
├── messages.proto
├── mix.exs
└── mix.lock
├── rel
├── app
│ ├── env.bat.eex
│ ├── env.sh.eex
│ ├── macos
│ │ ├── .gitignore
│ │ ├── App.entitlements
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── AppIcon-128px.png
│ │ │ │ ├── AppIcon-16px.png
│ │ │ │ ├── AppIcon-256px-1.png
│ │ │ │ ├── AppIcon-256px.png
│ │ │ │ ├── AppIcon-32px-1.png
│ │ │ │ ├── AppIcon-32px.png
│ │ │ │ ├── AppIcon-512px-1.png
│ │ │ │ ├── AppIcon-512px.png
│ │ │ │ ├── AppIcon-64px.png
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── Icon.iconset
│ │ │ │ ├── icon_128x128.png
│ │ │ │ ├── icon_128x128@2x.png
│ │ │ │ ├── icon_16x16.png
│ │ │ │ ├── icon_16x16@2x.png
│ │ │ │ ├── icon_256x256.png
│ │ │ │ ├── icon_256x256@2x.png
│ │ │ │ ├── icon_32x32.png
│ │ │ │ ├── icon_32x32@2x.png
│ │ │ │ ├── icon_512x512.png
│ │ │ │ └── icon_512x512@2x.png
│ │ ├── Info.plist
│ │ ├── Package.swift
│ │ ├── README.md
│ │ ├── Sources
│ │ │ └── Livebook
│ │ │ │ └── Livebook.swift
│ │ ├── build_app.sh
│ │ ├── build_dmg.sh
│ │ └── run.sh
│ ├── standalone.exs
│ ├── vm.args.eex
│ └── windows
│ │ ├── .gitignore
│ │ ├── App.manifest
│ │ ├── Installer.nsi
│ │ ├── Livebook.cs
│ │ ├── Livebook.csproj
│ │ ├── README.md
│ │ ├── Resources
│ │ └── AppIcon.ico
│ │ ├── build.sh
│ │ ├── build_installer.sh
│ │ ├── env.sh
│ │ └── run.sh
└── server
│ ├── env.bat.eex
│ ├── env.sh.eex
│ ├── overlays
│ ├── bin
│ │ ├── server
│ │ ├── start_flame.exs
│ │ ├── start_runtime.exs
│ │ ├── warmup_apps
│ │ └── warmup_apps.bat
│ └── user
│ │ └── extensions
│ │ └── .gitkeep
│ ├── remote.vm.args.eex
│ └── vm.args.eex
├── static
├── assets
│ ├── KaTeX_AMS-Regular-CYEKBG2K.woff
│ ├── KaTeX_AMS-Regular-JKX5W2C4.ttf
│ ├── KaTeX_AMS-Regular-U6PRYMIZ.woff2
│ ├── KaTeX_Caligraphic-Bold-5QL5CMTE.woff2
│ ├── KaTeX_Caligraphic-Bold-WZ3QSGD3.woff
│ ├── KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf
│ ├── KaTeX_Caligraphic-Regular-3LKEU76G.woff
│ ├── KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf
│ ├── KaTeX_Caligraphic-Regular-KX5MEWCF.woff2
│ ├── KaTeX_Fraktur-Bold-2QVFK6NQ.woff2
│ ├── KaTeX_Fraktur-Bold-T4SWXBMT.woff
│ ├── KaTeX_Fraktur-Bold-WGHVTYOR.ttf
│ ├── KaTeX_Fraktur-Regular-2PEIFJSJ.woff2
│ ├── KaTeX_Fraktur-Regular-5U4OPH2X.ttf
│ ├── KaTeX_Fraktur-Regular-PQMHCIK6.woff
│ ├── KaTeX_Main-Bold-2GA4IZIN.woff
│ ├── KaTeX_Main-Bold-W5FBVCZM.ttf
│ ├── KaTeX_Main-Bold-YP5VVQRP.woff2
│ ├── KaTeX_Main-BoldItalic-4P4C7HJH.woff
│ ├── KaTeX_Main-BoldItalic-N4V3DX7S.woff2
│ ├── KaTeX_Main-BoldItalic-ODMLBJJQ.ttf
│ ├── KaTeX_Main-Italic-I43T2HSR.ttf
│ ├── KaTeX_Main-Italic-RELBIK7M.woff2
│ ├── KaTeX_Main-Italic-SASNQFN2.woff
│ ├── KaTeX_Main-Regular-ARRPAO67.woff2
│ ├── KaTeX_Main-Regular-P5I74A2A.woff
│ ├── KaTeX_Main-Regular-W74P5G27.ttf
│ ├── KaTeX_Math-BoldItalic-6EBV3DK5.woff
│ ├── KaTeX_Math-BoldItalic-K4WTGH3J.woff2
│ ├── KaTeX_Math-BoldItalic-VB447A4D.ttf
│ ├── KaTeX_Math-Italic-6KGCHLFN.woff2
│ ├── KaTeX_Math-Italic-KKK3USB2.woff
│ ├── KaTeX_Math-Italic-SON4MRCA.ttf
│ ├── KaTeX_SansSerif-Bold-RRNVJFFW.woff2
│ ├── KaTeX_SansSerif-Bold-STQ6RXC7.ttf
│ ├── KaTeX_SansSerif-Bold-X5M5EMOD.woff
│ ├── KaTeX_SansSerif-Italic-HMPFTM52.woff2
│ ├── KaTeX_SansSerif-Italic-PSN4QKYX.woff
│ ├── KaTeX_SansSerif-Italic-WTBAZBGY.ttf
│ ├── KaTeX_SansSerif-Regular-2TL3USAE.ttf
│ ├── KaTeX_SansSerif-Regular-OQCII6EP.woff
│ ├── KaTeX_SansSerif-Regular-XIQ62X4E.woff2
│ ├── KaTeX_Script-Regular-72OLXYNA.ttf
│ ├── KaTeX_Script-Regular-A5IFOEBS.woff
│ ├── KaTeX_Script-Regular-APUWIHLP.woff2
│ ├── KaTeX_Size1-Regular-4HRHTS65.woff
│ ├── KaTeX_Size1-Regular-5LRUTBFT.woff2
│ ├── KaTeX_Size1-Regular-7K6AASVL.ttf
│ ├── KaTeX_Size2-Regular-222HN3GT.ttf
│ ├── KaTeX_Size2-Regular-K5ZHAIS6.woff
│ ├── KaTeX_Size2-Regular-LELKET5D.woff2
│ ├── KaTeX_Size3-Regular-TLFPAHDE.woff
│ ├── KaTeX_Size3-Regular-UFCO6WCA.ttf
│ ├── KaTeX_Size3-Regular-WQRQ47UD.woff2
│ ├── KaTeX_Size4-Regular-7PGNVPQK.ttf
│ ├── KaTeX_Size4-Regular-CDMV7U5C.woff2
│ ├── KaTeX_Size4-Regular-PKMWZHNC.woff
│ ├── KaTeX_Typewriter-Regular-3F5K6SQ6.ttf
│ ├── KaTeX_Typewriter-Regular-MJMFSK64.woff
│ ├── KaTeX_Typewriter-Regular-VBYJ4NRC.woff2
│ ├── app.css
│ ├── app.js
│ ├── architecture-I3QFYML2-6IR3LVRE.js
│ ├── architectureDiagram-UYN6MBPD-B3JL7HDC.js
│ ├── blockDiagram-ZHA2E4KO-PLGM37UV.js
│ ├── c4Diagram-6F5ED5ID-5HDWF5MA.js
│ ├── chunk-24JW6VB3.js
│ ├── chunk-47P5NBBB.js
│ ├── chunk-5JLOMFCN.js
│ ├── chunk-5XJO6U7A.js
│ ├── chunk-6IOWLTMD.js
│ ├── chunk-6KI6TUY3.js
│ ├── chunk-BQJTSJOB.js
│ ├── chunk-E4RBDSW5.js
│ ├── chunk-FIA6NTGC.js
│ ├── chunk-GJ6CDUB5.js
│ ├── chunk-HTMW6R2V.js
│ ├── chunk-IDH2HD35.js
│ ├── chunk-IJWB56EA.js
│ ├── chunk-JVEBZTDG.js
│ ├── chunk-KRX7QNR4.js
│ ├── chunk-L3A5WJI3.js
│ ├── chunk-LGVNNTMI.js
│ ├── chunk-MF5TUIBL.js
│ ├── chunk-MGYUK2XN.js
│ ├── chunk-NCYNIMJ4.js
│ ├── chunk-NKCQAART.js
│ ├── chunk-RVGDP346.js
│ ├── chunk-SA2EGKJT.js
│ ├── chunk-SISR4MA5.js
│ ├── chunk-TZMIYTTY.js
│ ├── chunk-UY4K4Z6W.js
│ ├── chunk-VJZZWRCC.js
│ ├── chunk-VW6A6SAT.js
│ ├── chunk-W7LTUAGZ.js
│ ├── chunk-WNVR7S66.js
│ ├── chunk-WYMAA4MH.js
│ ├── chunk-XHGORZV2.js
│ ├── chunk-ZP5QXAVO.js
│ ├── chunk-ZPWCXWQI.js
│ ├── classDiagram-LNE6IOMH-QE5QBIDP.js
│ ├── classDiagram-v2-MQ7JQ4JX-6A4BS74I.js
│ ├── dagre-4EVJKHTY-TI3ZPXZV.js
│ ├── diagram-QW4FP2JN-W3LGLVR5.js
│ ├── erDiagram-6RL3IURR-TILZZ7DE.js
│ ├── flowDiagram-7ASYPVHJ-6FSFQFKV.js
│ ├── ganttDiagram-NTVNEXSI-I375UW4Z.js
│ ├── gitGraph-YCYPL57B-J4JOQTHP.js
│ ├── gitGraphDiagram-NRZ2UAAF-LIITHXBO.js
│ ├── info-46DW6VJ7-VCNA5FQQ.js
│ ├── infoDiagram-A4XQUW5V-2UH4OEMN.js
│ ├── inter-cyrillic-400-normal-JTDOFDEL.woff
│ ├── inter-cyrillic-400-normal-KR3WP37A.woff2
│ ├── inter-cyrillic-500-normal-L3ERAO2A.woff2
│ ├── inter-cyrillic-500-normal-YGY63224.woff
│ ├── inter-cyrillic-600-normal-RRU6DOYW.woff2
│ ├── inter-cyrillic-600-normal-YAIFIQFF.woff
│ ├── inter-cyrillic-ext-400-normal-ERANL6CF.woff2
│ ├── inter-cyrillic-ext-400-normal-NYJ3VVCY.woff
│ ├── inter-cyrillic-ext-500-normal-3D2TL7BV.woff2
│ ├── inter-cyrillic-ext-500-normal-MVWVUDWY.woff
│ ├── inter-cyrillic-ext-600-normal-3WK3AIAE.woff2
│ ├── inter-cyrillic-ext-600-normal-ML7GDJ53.woff
│ ├── inter-greek-400-normal-22W3W6EP.woff
│ ├── inter-greek-400-normal-K452ZUNI.woff2
│ ├── inter-greek-500-normal-SR6UVMZM.woff2
│ ├── inter-greek-500-normal-V5MWXBXZ.woff
│ ├── inter-greek-600-normal-52AI77RO.woff2
│ ├── inter-greek-600-normal-MHDVNT3B.woff
│ ├── inter-greek-ext-400-normal-6GLOS2AC.woff
│ ├── inter-greek-ext-400-normal-7CMSSAC3.woff2
│ ├── inter-greek-ext-500-normal-GTVYGHNV.woff
│ ├── inter-greek-ext-500-normal-L7SAHJMO.woff2
│ ├── inter-greek-ext-600-normal-2BPZID6V.woff2
│ ├── inter-greek-ext-600-normal-URVVZOUP.woff
│ ├── inter-latin-400-normal-GYFJJKJF.woff2
│ ├── inter-latin-400-normal-HCV22YHT.woff
│ ├── inter-latin-500-normal-5GHDSDAY.woff2
│ ├── inter-latin-500-normal-PGDKKSLY.woff
│ ├── inter-latin-600-normal-56WZKGNJ.woff2
│ ├── inter-latin-600-normal-RLL3LKKR.woff
│ ├── inter-latin-ext-400-normal-2H3KLIXK.woff
│ ├── inter-latin-ext-400-normal-OMTSHYQS.woff2
│ ├── inter-latin-ext-500-normal-R6CV3WNH.woff2
│ ├── inter-latin-ext-500-normal-WO7CCPI4.woff
│ ├── inter-latin-ext-600-normal-IPOBZ3TF.woff2
│ ├── inter-latin-ext-600-normal-Q5LOCDKP.woff
│ ├── inter-vietnamese-400-normal-3ZH4IT4J.woff2
│ ├── inter-vietnamese-400-normal-CQIZECWR.woff
│ ├── inter-vietnamese-500-normal-PQFGSX3P.woff2
│ ├── inter-vietnamese-500-normal-YWCA5IC5.woff
│ ├── inter-vietnamese-600-normal-RMFHJUBF.woff
│ ├── inter-vietnamese-600-normal-TVIYLGI7.woff2
│ ├── jetbrains-mono-cyrillic-400-normal-C5JECUCT.woff2
│ ├── jetbrains-mono-cyrillic-400-normal-ZHDMAAUB.woff
│ ├── jetbrains-mono-cyrillic-ext-400-normal-C5DDHB7N.woff
│ ├── jetbrains-mono-cyrillic-ext-400-normal-C7IFWGF6.woff2
│ ├── jetbrains-mono-greek-400-normal-KF6EKC2J.woff
│ ├── jetbrains-mono-greek-400-normal-O7JTTR3P.woff2
│ ├── jetbrains-mono-latin-400-normal-3OOWLGQ2.woff2
│ ├── jetbrains-mono-latin-400-normal-4JODGGIE.woff
│ ├── jetbrains-mono-latin-ext-400-normal-3RACNWYK.woff
│ ├── jetbrains-mono-latin-ext-400-normal-JVR3IR4Z.woff2
│ ├── jetbrains-mono-vietnamese-400-normal-7A56GUVN.woff
│ ├── jetbrains-mono-vietnamese-400-normal-KU7YLUPA.woff2
│ ├── journeyDiagram-G5WM74LC-IOGZ4ER4.js
│ ├── kanban-definition-QRCXZQQD-665S4FCL.js
│ ├── katex-R4V72ZOM.js
│ ├── mermaid.core-PK4ATILI.js
│ ├── mindmap-definition-GWI6TPTV-TXRXVVPF.js
│ ├── packet-W2GHVCYJ-FC4ERNLH.js
│ ├── pie-BEWT4RHE-5KCD7UAH.js
│ ├── pieDiagram-YF2LJOPJ-F5SQDVTC.js
│ ├── quadrantDiagram-OS5C2QUG-JOFAEUBY.js
│ ├── red-hat-text-latin-400-normal-OV4KODUR.woff2
│ ├── red-hat-text-latin-400-normal-YWAZCVDO.woff
│ ├── red-hat-text-latin-ext-400-normal-H2UN6E6K.woff
│ ├── red-hat-text-latin-ext-400-normal-PD2NZNLC.woff2
│ ├── remixicon-6QPK2BSM.ttf
│ ├── remixicon-7463F4HZ.woff2
│ ├── remixicon-DH65ADK5.woff
│ ├── remixicon-OJBB7CW7.eot
│ ├── remixicon-TZP22IVM.svg
│ ├── requirementDiagram-MIRIMTAZ-3ZJXLEGT.js
│ ├── sankeyDiagram-Y46BX6SQ-E54YZX3S.js
│ ├── sequenceDiagram-G6AWOVSC-YPQCR6DD.js
│ ├── stateDiagram-MAYHULR4-5MBSR5JM.js
│ ├── stateDiagram-v2-4JROLMXI-VMRUNBYI.js
│ ├── timeline-definition-U7ZMHBDA-JF4H43ED.js
│ └── xychartDiagram-6QU3TZC5-6SD7MGQD.js
├── favicons
│ ├── favicon-errored.svg
│ ├── favicon-evaluating.svg
│ ├── favicon-stale.svg
│ ├── favicon.png
│ └── favicon.svg
├── images
│ ├── elixir-portal.jpeg
│ ├── elixir.png
│ ├── explorer.png
│ ├── fly.svg
│ ├── github-stars.png
│ ├── kino.png
│ ├── learn-deploy.svg
│ ├── logo-with-text.png
│ ├── logo.png
│ ├── maplibre.png
│ ├── teams.png
│ └── vega_lite.png
└── robots.txt
├── test
├── README.md
├── livebook
│ ├── app_test.exs
│ ├── apps
│ │ ├── deployer_test.exs
│ │ ├── manager_test.exs
│ │ └── path_app_spec_test.exs
│ ├── apps_test.exs
│ ├── config_test.exs
│ ├── ecto_types
│ │ └── hex_color_test.exs
│ ├── epmd
│ │ └── node_pool_test.exs
│ ├── epmd_test.exs
│ ├── file_system
│ │ ├── file_test.exs
│ │ ├── local_test.exs
│ │ └── s3_test.exs
│ ├── hubs
│ │ ├── dockerfile_test.exs
│ │ ├── personal_test.exs
│ │ └── provider_test.exs
│ ├── intellisense_test.exs
│ ├── learn_test.exs
│ ├── live_markdown
│ │ ├── export_test.exs
│ │ ├── import_test.exs
│ │ └── markdown_helpers_test.exs
│ ├── notebook
│ │ ├── content_loader_test.exs
│ │ └── export
│ │ │ └── elixir_test.exs
│ ├── notebook_test.exs
│ ├── runtime
│ │ ├── attached_test.exs
│ │ ├── dependencies_test.exs
│ │ ├── erl_dist
│ │ │ ├── io_forward_gl_test.exs
│ │ │ ├── node_manager_test.exs
│ │ │ ├── runtime_server_test.exs
│ │ │ └── smart_cell_gl_test.exs
│ │ ├── evaluator
│ │ │ ├── client_tracker_test.exs
│ │ │ ├── io_proxy_test.exs
│ │ │ └── object_tracker_test.exs
│ │ ├── evaluator_test.exs
│ │ ├── fly_test.exs
│ │ ├── k8s_test.exs
│ │ └── standalone_test.exs
│ ├── secrets_test.exs
│ ├── session
│ │ ├── data_test.exs
│ │ └── file_guard_test.exs
│ ├── session_test.exs
│ ├── sessions_test.exs
│ ├── settings_test.exs
│ ├── storage_test.exs
│ ├── text
│ │ ├── delta
│ │ │ └── transformation_test.exs
│ │ ├── delta_test.exs
│ │ └── js_test.exs
│ ├── users
│ │ └── user_test.exs
│ ├── users_test.exs
│ ├── utils
│ │ ├── ansi_test.exs
│ │ ├── time_test.exs
│ │ └── unique_task_test.exs
│ ├── utils_test.exs
│ └── zta
│ │ ├── basic_auth_test.exs
│ │ ├── cloudflare_test.exs
│ │ ├── google_iap_test.exs
│ │ └── tailscale_test.exs
├── livebook_teams
│ ├── apps_test.exs
│ ├── hubs
│ │ └── team_client_test.exs
│ ├── hubs_test.exs
│ ├── teams_test.exs
│ ├── web
│ │ ├── admin_live_test.exs
│ │ ├── app_session_live_test.exs
│ │ ├── apps_live_test.exs
│ │ ├── hub
│ │ │ ├── deployment_group_test.exs
│ │ │ ├── edit_live_test.exs
│ │ │ └── new_live_test.exs
│ │ └── session_live_test.exs
│ └── zta
│ │ └── livebook_teams_test.exs
├── livebook_test.exs
├── livebook_web
│ ├── channels
│ │ └── js_view_channel_test.exs
│ ├── controllers
│ │ ├── endpoint_test.exs
│ │ ├── errors_test.exs
│ │ ├── session_controller_test.exs
│ │ └── user_controller_test.exs
│ ├── helpers
│ │ ├── ansi_helpers_test.exs
│ │ └── html_helpers_test.exs
│ ├── live
│ │ ├── app_auth_live_test.exs
│ │ ├── app_live_test.exs
│ │ ├── app_session_live_test.exs
│ │ ├── apps_dashboard_live_test.exs
│ │ ├── file_select_component_test.exs
│ │ ├── home_live_test.exs
│ │ ├── hub
│ │ │ └── edit_live_test.exs
│ │ ├── learn_live_test.exs
│ │ ├── open_live_test.exs
│ │ ├── output
│ │ │ └── plain_text_component_test.exs
│ │ ├── session_live_test.exs
│ │ └── settings_live_test.exs
│ └── plugs
│ │ ├── auth_plug_test.exs
│ │ ├── proxy_plug_test.exs
│ │ └── user_plug_test.exs
├── support
│ ├── app_helpers.ex
│ ├── assets.tar.gz
│ ├── channel_case.ex
│ ├── conn_case.ex
│ ├── data_case.ex
│ ├── factory.ex
│ ├── hub_helpers.ex
│ ├── integration
│ │ ├── teams_rpc.ex
│ │ ├── teams_server.ex
│ │ └── teams_tests.ex
│ ├── k8s_cluster_stub.ex
│ ├── noop_runtime.ex
│ ├── notebook_app_spec.ex
│ ├── notebooks
│ │ ├── basic.livemd
│ │ └── with_two_sections.livemd
│ ├── session_helpers.ex
│ ├── static
│ │ ├── icon.ico
│ │ └── js
│ │ │ └── app.js
│ ├── teams_integration_case.ex
│ ├── test_helpers.ex
│ └── test_modules
│ │ ├── bad_inspect.ex
│ │ └── hidden.ex
└── test_helper.exs
└── versions
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Mirrors .gitignore
2 |
3 | /_build/
4 | /cover/
5 | /deps/
6 | /doc/
7 | /.fetch
8 | erl_crash.dump
9 | *.ez
10 | livebook-*.tar
11 | npm-debug.log
12 | /assets/node_modules/
13 | /tmp/
14 | /livebook
15 | /priv/static
16 | /priv/iframe_static
17 | # Ignore app release files, including build artifacts
18 | /rel/app
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://EditorConfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 |
--------------------------------------------------------------------------------
/.formatter.exs:
--------------------------------------------------------------------------------
1 | [
2 | import_deps: [:phoenix, :ecto],
3 | plugins: [Phoenix.LiveView.HTMLFormatter],
4 | inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "rel/*/overlays/**/*.exs"]
5 | ]
6 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # convert all CRLF to LF
2 | * text=auto eol=lf
3 |
4 | # ensure binary files are not marked as text
5 | *.{ico,png,jpg,jpeg,gif,webp,woff,woff2} binary
6 |
7 | iframe/priv/static/iframe/*.html text eol=lf
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Environment
4 |
5 |
6 |
7 | * Elixir & Erlang/OTP versions (elixir --version):
8 | * Operating system:
9 | * How have you started Livebook (mix phx.server, livebook CLI, Docker, etc):
10 | * Livebook version (use `git rev-parse HEAD` if running with mix):
11 | * Browsers that reproduce this bug (the more the merrier):
12 | * Include what is logged in the browser console:
13 | * Include what is logged to the server console:
14 |
15 | ## Current behavior
16 |
17 | Include detailed steps to reproduce error, errors and stacktraces if appropriate.
18 |
19 | ## Expected behavior
20 |
21 | A short description on how you expect the code to behave.
22 |
--------------------------------------------------------------------------------
/.github/imgs/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/.github/imgs/welcome.png
--------------------------------------------------------------------------------
/.github/scripts/app/build_windows.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Usage:
4 | #
5 | # $ sh .github/scripts/app/build_windows.sh
6 | # $ rel/app/windows/bin/LivebookInstall.exe
7 | # $ start livebook://github.com/livebook-dev/livebook/blob/main/test/support/notebooks/basic.livemd
8 | # $ start ./test/support/notebooks/basic.livemd
9 | #
10 | # Note: This script builds the Windows installer. If you just want to test the Windows app locally, run:
11 | #
12 | # $ cd rel/app/windows && ./run.sh
13 | #
14 | # See rel/app/windows/README.md for more information.
15 | set -e
16 |
17 | mix local.hex --force --if-missing
18 | mix local.rebar --force --if-missing
19 |
20 | export MIX_ENV=prod
21 | export MIX_TARGET=app
22 | export ELIXIRKIT_CONFIGURATION=Release
23 |
24 | mix deps.get --only prod
25 |
26 | cd rel/app/windows
27 | ./build_installer.sh
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | livebook-*.tar
24 |
25 | # If NPM crashes, it generates a log, let's ignore it too.
26 | npm-debug.log
27 |
28 | # The directory NPM downloads your dependencies sources to.
29 | /assets/node_modules/
30 |
31 | # The directory used by ExUnit :tmp_dir
32 | /tmp/
33 |
34 | # The built Escript
35 | /livebook
36 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog for Livebook v0.17
2 |
3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4 |
5 | ## [Unreleased](https://github.com/livebook-dev/livebook/tree/main)
6 |
7 | ## v0.16
8 |
9 | The CHANGELOG for v0.16 releases can be found in the [v0.16](https://github.com/livebook-dev/livebook/tree/v0.16/CHANGELOG.md) branch.
10 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Releasing Livebook
2 |
3 | 0. If applicable, release Kino first and update built-in notebooks to reference
4 | the new version.
5 | 1. Make sure the latest CI "Assets" workflow finished.
6 | 2. Switch to (or create) vx.y branch.
7 | 3. If applicable cherry-pick the relevant commits from main onto the vx.y branch.
8 | 1. If you do that, push, wait for CI "Assets" workflow to finish, and pull.
9 | 3. Update version in `mix.exs` and finish changelog.
10 | 4. Run `mix hex.build` as a sanity check.
11 | 5. `git tag vx.y.z`, `git push --tags`
12 | 1. Wait for CI to finish (Docker and Desktop)
13 | 6. Run `mix hex.publish`.
14 | 7. Publish GH release with copied changelog notes.
15 | 8. If you created a branch in step 2., update main changelog to point to
16 | the branch and bump version in mix.exs (with `-dev` suffix).
17 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Currently we support security patches for the two most recent minor
6 | versions. For example, if Livebook v0.12.0 is released, we will
7 | backport security fixes for v0.12.x and v0.11.x. Once Livebook reaches
8 | v1.0, we plan to introduce more long-term security guarantees.
9 | If you have any questions, please reach our to them on Discussion page.
10 |
11 | ## Reporting a Vulnerability
12 |
13 | To report a security vulnerability, you can send an email to `security@livebook.dev`
14 | or [privately disclose it on our GitHub page](https://github.com/livebook-dev/livebook/security).
15 | If you don't receive an acknowledgement of the report within 24h,
16 | feel free to ping us in our public forums (but without disclosing
17 | the contents of the vulnerability).
18 |
--------------------------------------------------------------------------------
/assets/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/assets/css/app.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss/base";
2 | @import "tailwindcss/components";
3 | @import "tailwindcss/utilities";
4 |
5 | @import "./components.css";
6 | @import "./utilities.css";
7 | @import "./live_view.css";
8 | @import "./markdown.css";
9 | @import "./ansi.css";
10 | @import "./js_interop.css";
11 | @import "./tooltips.css";
12 | @import "./notebook.css";
13 |
--------------------------------------------------------------------------------
/assets/css/live_view.css:
--------------------------------------------------------------------------------
1 | /* Hide Phoenix live reload frame */
2 | iframe[hidden] {
3 | display: none;
4 | }
5 |
6 | /* LiveView specific classes */
7 |
8 | .phx-no-feedback.invalid-feedback,
9 | .phx-no-feedback .invalid-feedback {
10 | display: none;
11 | }
12 |
13 | .phx-click-loading {
14 | opacity: 0.5;
15 | transition: opacity 1s ease-out;
16 | }
17 |
18 | .phx-loading {
19 | cursor: wait;
20 | }
21 |
--------------------------------------------------------------------------------
/assets/css/notebook.css:
--------------------------------------------------------------------------------
1 | /*
2 | Specific icons used in the built-in notebooks
3 |
4 | Note: we use the ri- prefix to use the existing
5 | Remix icon CSS
6 | */
7 |
8 | .ri-livebook-sections,
9 | .ri-livebook-runtime,
10 | .ri-livebook-shortcuts,
11 | .ri-livebook-secrets,
12 | .ri-livebook-deploy,
13 | .ri-livebook-terminal,
14 | .ri-livebook-save {
15 | @apply text-xl align-middle;
16 | }
17 |
18 | .ri-livebook-sections:before {
19 | content: "\eade";
20 | }
21 |
22 | .ri-livebook-runtime:before {
23 | content: "\ebf0";
24 | }
25 |
26 | .ri-livebook-shortcuts:before {
27 | content: "\ee72";
28 | }
29 |
30 | .ri-livebook-secrets:before {
31 | content: "\eed0";
32 | }
33 |
34 | .ri-livebook-deploy:before {
35 | content: "\f096";
36 | }
37 |
38 | .ri-livebook-terminal:before {
39 | content: "\f1f8";
40 | }
41 |
42 | .ri-livebook-save:before {
43 | content: "\f0b3";
44 | }
45 |
--------------------------------------------------------------------------------
/assets/js/confirm.js:
--------------------------------------------------------------------------------
1 | import { load, store } from "./lib/storage";
2 |
3 | const OPT_OUT_IDS_KEY = "confirm-opted-out-ids";
4 |
5 | export function loadConfirmOptOutIds() {
6 | return load(OPT_OUT_IDS_KEY) || [];
7 | }
8 |
9 | export function registerGlobalEventHandlersForConfirm() {
10 | window.addEventListener("phx:add_confirm_opt_out_id", (event) => {
11 | const optedOutIds = load(OPT_OUT_IDS_KEY) || [];
12 | const optOutId = event.detail.opt_out_id;
13 | optedOutIds.push(optOutId);
14 | store(OPT_OUT_IDS_KEY, optedOutIds);
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/assets/js/dom.js:
--------------------------------------------------------------------------------
1 | export const morphdomOptions = {
2 | onBeforeElUpdated(from, to) {
3 | // Keep element attributes starting with data-js-
4 | // which we set on the client.
5 | for (const attr of from.attributes) {
6 | if (attr.name.startsWith("data-js-")) {
7 | to.setAttribute(attr.name, attr.value);
8 | }
9 |
10 | if (attr.name === "data-keep-attribute") {
11 | if (from.hasAttribute(attr.value)) {
12 | to.setAttribute(attr.value, from.getAttribute(attr.value));
13 | } else {
14 | to.removeAttribute(attr.value);
15 | }
16 | }
17 | }
18 | },
19 |
20 | onNodeAdded(node) {
21 | // Mimic autofocus for dynamically inserted elements
22 | if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute("autofocus")) {
23 | node.focus();
24 |
25 | if (node.setSelectionRange && node.value) {
26 | const lastIndex = node.value.length;
27 | node.setSelectionRange(lastIndex, lastIndex);
28 | }
29 | }
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/assets/js/hooks/app_auth.js:
--------------------------------------------------------------------------------
1 | import { storeAppAuthToken } from "../lib/app";
2 |
3 | /**
4 | * A hook for the app auth page.
5 | */
6 | const AppAuth = {
7 | mounted() {
8 | this.handleEvent("persist_app_auth", ({ slug, token }) => {
9 | storeAppAuthToken(slug, token);
10 | this.pushEvent("app_auth_persisted");
11 | });
12 | },
13 | };
14 |
15 | export default AppAuth;
16 |
--------------------------------------------------------------------------------
/assets/js/hooks/cell_editor/live_editor/codemirror/commands.js:
--------------------------------------------------------------------------------
1 | import { closeCompletion } from "@codemirror/autocomplete";
2 | import { insertBlankLine } from "@codemirror/commands";
3 | import { closeSignature } from "./signature";
4 |
5 | /**
6 | * This command, when multi-cursor is active, collapses the selection
7 | * to the main cursor only.
8 | */
9 | export function exitMulticursor(view) {
10 | const selection = view.state.selection;
11 |
12 | if (selection.ranges.length > 1) {
13 | view.dispatch({ selection: selection.asSingle() });
14 | return true;
15 | }
16 |
17 | return false;
18 | }
19 |
20 | /**
21 | * Calls `insertBlankLine` and closes active intellisense hints.
22 | */
23 | export function insertBlankLineAndCloseHints(view) {
24 | if (insertBlankLine(view)) {
25 | closeCompletion(view);
26 | closeSignature(view);
27 | return true;
28 | }
29 |
30 | return false;
31 | }
32 |
--------------------------------------------------------------------------------
/assets/js/hooks/cell_editor/live_editor/codemirror/hover_details.js:
--------------------------------------------------------------------------------
1 | import { Decoration, EditorView, hoverTooltip } from "@codemirror/view";
2 | import { RangeSet } from "@codemirror/state";
3 |
4 | /**
5 | * Returns an extension that enables hover details tooltip.
6 | */
7 | export function hoverDetails(hoverTooltipSource) {
8 | const tooltipExtension = hoverTooltip(hoverTooltipSource);
9 |
10 | const decorationsExtension = EditorView.decorations.from(
11 | tooltipExtension.active,
12 | (tooltips) => {
13 | const backgroundDecoration = Decoration.mark({
14 | class: "cm-hoverDocsSelection",
15 | });
16 |
17 | const decorationRanges = tooltips.map((tooltip) =>
18 | backgroundDecoration.range(tooltip.pos, tooltip.end),
19 | );
20 |
21 | return RangeSet.of(decorationRanges, true);
22 | },
23 | );
24 |
25 | return [tooltipExtension, decorationsExtension];
26 | }
27 |
--------------------------------------------------------------------------------
/assets/js/hooks/cell_editor/live_editor/codemirror/selecting_class.js:
--------------------------------------------------------------------------------
1 | import { EditorView } from "@codemirror/view";
2 |
3 | const attributesFacet = EditorView.editorAttributes.compute(
4 | ["selection"],
5 | (state) => {
6 | const allRangesEmpty = state.selection.ranges.every((range) => range.empty);
7 | return allRangesEmpty ? {} : { class: "cm-selecting" };
8 | },
9 | );
10 |
11 | /**
12 | * Returns an extension that adds `cm-selecting` class to the root
13 | * element, whenever the editor has a non-empty selection range.
14 | */
15 | export function selectingClass() {
16 | return [attributesFacet];
17 | }
18 |
--------------------------------------------------------------------------------
/assets/js/hooks/cell_editor/live_editor/codemirror/toggle_with.js:
--------------------------------------------------------------------------------
1 | import { EditorView, keymap } from "@codemirror/view";
2 | import { Compartment } from "@codemirror/state";
3 |
4 | /**
5 | * Returns an extension that toggles the given extension with the given
6 | * keyboard shortcut.
7 | */
8 | export function toggleWith(key, extension) {
9 | const compartment = new Compartment();
10 |
11 | function toggle(view) {
12 | const isEnabled = compartment.get(view.state) === extension;
13 |
14 | view.dispatch({
15 | effects: compartment.reconfigure(isEnabled ? [] : extension),
16 | });
17 |
18 | return true;
19 | }
20 |
21 | return [compartment.of([]), keymap.of({ key, run: toggle })];
22 | }
23 |
--------------------------------------------------------------------------------
/assets/js/hooks/cell_editor/live_editor/codemirror/tree_utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Finds the closest node with the given name (parent or self).
3 | */
4 | export function closestNode(node, names) {
5 | while (node) {
6 | if (names.includes(node.type.name)) return node;
7 |
8 | node = node.parent;
9 | }
10 |
11 | return null;
12 | }
13 |
14 | /**
15 | * Goes up the tree using the given path.
16 | *
17 | * Path is a list of parent node names, from innermost to outermost.
18 | * Returns the alst node on the path, but only if the path matches
19 | * exactly.
20 | */
21 | export function ancestorNode(node, path) {
22 | let i = 0;
23 |
24 | while (i < path.length && node.parent) {
25 | if (node.parent.type.name !== path[i]) return null;
26 |
27 | node = node.parent;
28 | i++;
29 | }
30 |
31 | return i === path.length && node ? node : null;
32 | }
33 |
--------------------------------------------------------------------------------
/assets/js/hooks/dropzone.js:
--------------------------------------------------------------------------------
1 | const DRAGGING_ATTR = "data-js-dragging";
2 |
3 | /**
4 | * A hook used to highlight drop zone when dragging a file.
5 | */
6 | const Dropzone = {
7 | mounted() {
8 | this.el.addEventListener("dragenter", (event) => {
9 | this.el.setAttribute(DRAGGING_ATTR, "");
10 | });
11 |
12 | this.el.addEventListener("dragleave", (event) => {
13 | if (!this.el.contains(event.relatedTarget)) {
14 | this.el.removeAttribute(DRAGGING_ATTR);
15 | }
16 | });
17 |
18 | this.el.addEventListener("drop", (event) => {
19 | this.el.removeAttribute(DRAGGING_ATTR);
20 | });
21 | },
22 | };
23 |
24 | export default Dropzone;
25 |
--------------------------------------------------------------------------------
/assets/js/hooks/emoji_picker.js:
--------------------------------------------------------------------------------
1 | import { createPopup } from "@picmo/popup-picker";
2 |
3 | /**
4 | * A hook for the emoji picker input.
5 | */
6 | const EmojiPicker = {
7 | mounted() {
8 | const button = this.el.querySelector("[data-emoji-button]");
9 | const preview = this.el.querySelector("[data-emoji-preview]");
10 | const input = this.el.querySelector("[data-emoji-input]");
11 |
12 | const picker = createPopup(
13 | { showPreview: false },
14 | {
15 | triggerElement: button,
16 | referenceElement: button,
17 | position: "bottom",
18 | },
19 | );
20 |
21 | picker.addEventListener("emoji:select", ({ emoji }) => {
22 | preview.innerHTML = emoji;
23 | input.value = emoji;
24 | });
25 |
26 | button.addEventListener("click", (_) => {
27 | picker.toggle();
28 | });
29 | },
30 | };
31 |
32 | export default EmojiPicker;
33 |
--------------------------------------------------------------------------------
/assets/js/hooks/focus_on_update.js:
--------------------------------------------------------------------------------
1 | import { isEditableElement } from "../lib/utils";
2 |
3 | /**
4 | * A hook used to focus an element whenever it receives LV update.
5 | */
6 | const FocusOnUpdate = {
7 | mounted() {
8 | this.focus();
9 | },
10 |
11 | updated() {
12 | if (this.el !== document.activeElement) {
13 | this.focus();
14 | }
15 | },
16 |
17 | focus() {
18 | if (isEditableElement(document.activeElement)) {
19 | return;
20 | }
21 |
22 | this.el.focus();
23 | this.el.selectionStart = this.el.selectionEnd = this.el.value.length;
24 | this.el.scrollLeft = this.el.scrollWidth;
25 | },
26 | };
27 |
28 | export default FocusOnUpdate;
29 |
--------------------------------------------------------------------------------
/assets/js/hooks/highlight.js:
--------------------------------------------------------------------------------
1 | import { parseHookProps } from "../lib/attribute";
2 | import { highlight } from "./cell_editor/live_editor/highlight";
3 | import { findChildOrThrow } from "../lib/utils";
4 |
5 | /**
6 | * A hook used to highlight source code in the root element.
7 | *
8 | * ## Props
9 | *
10 | * * `language` - language of the source code
11 | *
12 | * ## Children
13 | *
14 | * * `[data-source]` - an element containing the source code to be
15 | * highlighted
16 | *
17 | * * `[data-target]` - the element to render highlighted code into
18 | *
19 | */
20 | const Highlight = {
21 | mounted() {
22 | this.props = this.getProps();
23 |
24 | this.sourceEl = findChildOrThrow(this.el, "[data-source]");
25 | this.targetEl = findChildOrThrow(this.el, "[data-target]");
26 |
27 | this.updateDOM();
28 | },
29 |
30 | updated() {
31 | this.props = this.getProps();
32 | this.updateDOM();
33 | },
34 |
35 | getProps() {
36 | return parseHookProps(this.el, ["language"]);
37 | },
38 |
39 | updateDOM() {
40 | const code = this.sourceEl.innerText;
41 |
42 | const html = highlight(code, this.props.language);
43 | this.targetEl.innerHTML = html;
44 | this.sourceEl.style.display = "none";
45 | },
46 | };
47 |
48 | export default Highlight;
49 |
--------------------------------------------------------------------------------
/assets/js/hooks/scroll_on_update.js:
--------------------------------------------------------------------------------
1 | import { scrollToEnd } from "../lib/utils";
2 |
3 | /**
4 | * A hook used to scroll to the bottom of an element whenever it
5 | * receives LV update.
6 | */
7 | const ScrollOnUpdate = {
8 | mounted() {
9 | this.scroll();
10 | },
11 |
12 | updated() {
13 | this.scroll();
14 | },
15 |
16 | scroll() {
17 | scrollToEnd(this.el);
18 | },
19 | };
20 |
21 | export default ScrollOnUpdate;
22 |
--------------------------------------------------------------------------------
/assets/js/hooks/textarea_autosize.js:
--------------------------------------------------------------------------------
1 | const BORDER_HEIGHT = 1;
2 |
3 | /**
4 | * A hook that automatically matches textarea height to its content.
5 | */
6 | const TextareaAutosize = {
7 | mounted() {
8 | this.autosize();
9 |
10 | this.el.addEventListener("input", (event) => {
11 | this.autosize();
12 | });
13 | },
14 |
15 | updated() {
16 | this.autosize();
17 | },
18 |
19 | autosize() {
20 | this.el.style.height = "0px";
21 | this.el.style.height = `${this.el.scrollHeight + 2 * BORDER_HEIGHT}px`;
22 | },
23 | };
24 |
25 | export default TextareaAutosize;
26 |
--------------------------------------------------------------------------------
/assets/js/hooks/timer.js:
--------------------------------------------------------------------------------
1 | import { parseHookProps } from "../lib/attribute";
2 |
3 | const UPDATE_INTERVAL_MS = 100;
4 |
5 | /**
6 | * A hook used to display a counting timer.
7 | *
8 | * ## Props
9 | *
10 | * * `start` - the timestamp to count from
11 | *
12 | */
13 | const Timer = {
14 | mounted() {
15 | this.props = this.getProps();
16 |
17 | this.interval = setInterval(() => this.updateDOM(), UPDATE_INTERVAL_MS);
18 | },
19 |
20 | updated() {
21 | this.props = this.getProps();
22 | this.updateDOM();
23 | },
24 |
25 | destroyed() {
26 | clearInterval(this.interval);
27 | },
28 |
29 | getProps() {
30 | return parseHookProps(this.el, ["start"]);
31 | },
32 |
33 | updateDOM() {
34 | const elapsedMs = Date.now() - new Date(this.props.start);
35 | const elapsedSeconds = elapsedMs / 1_000;
36 |
37 | this.el.innerHTML = `${elapsedSeconds.toFixed(1)}s`;
38 | },
39 | };
40 |
41 | export default Timer;
42 |
--------------------------------------------------------------------------------
/assets/js/hooks/user_form.js:
--------------------------------------------------------------------------------
1 | import { storeUserData } from "../lib/user";
2 |
3 | /**
4 | * A hook for the user profile form.
5 | *
6 | * On submit this hook saves the new data into cookie. This cookie
7 | * serves as a backup and can be used to restore user data if the
8 | * server is restarted.
9 | */
10 | const UserForm = {
11 | mounted() {
12 | this.el.addEventListener("submit", (event) => {
13 | const name = this.el.user_form_name.value;
14 | const hex_color = this.el.user_form_hex_color.value;
15 | storeUserData({ name, hex_color });
16 | });
17 | },
18 | };
19 |
20 | export default UserForm;
21 |
--------------------------------------------------------------------------------
/assets/js/lib/app.js:
--------------------------------------------------------------------------------
1 | import { load, store } from "./storage";
2 |
3 | const APP_AUTH_TOKEN_PREFIX = "app_auth_token:";
4 |
5 | export function storeAppAuthToken(slug, token) {
6 | store(APP_AUTH_TOKEN_PREFIX + slug, token);
7 | }
8 |
9 | export function loadAppAuthToken() {
10 | const path = window.location.pathname;
11 |
12 | if (path.startsWith("/apps/")) {
13 | const slug = path.split("/")[2];
14 | const token = load(APP_AUTH_TOKEN_PREFIX + slug);
15 |
16 | if (token) {
17 | return token;
18 | }
19 | }
20 |
21 | return null;
22 | }
23 |
--------------------------------------------------------------------------------
/assets/js/lib/attribute.js:
--------------------------------------------------------------------------------
1 | export function parseHookProps(element, names) {
2 | const props = {};
3 |
4 | for (const name of names) {
5 | const attr = `data-p-${name}`;
6 |
7 | if (!element.hasAttribute(attr)) {
8 | throw new Error(
9 | `Missing attribute "${attr}" on element <${element.tagName}:${element.id}>`,
10 | );
11 | }
12 |
13 | const value = element.getAttribute(attr);
14 | props[kebabToCamelCase(name)] = JSON.parse(value);
15 | }
16 |
17 | return props;
18 | }
19 |
20 | function kebabToCamelCase(name) {
21 | const [part, ...parts] = name.split("-");
22 |
23 | return [
24 | part,
25 | ...parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)),
26 | ].join("");
27 | }
28 |
--------------------------------------------------------------------------------
/assets/js/lib/cache_lru.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A Map-based LRU cache.
3 | */
4 | export default class CacheLRU {
5 | constructor(size) {
6 | this.size = size;
7 | this.cache = new Map();
8 | }
9 |
10 | get(key) {
11 | if (this.cache.has(key)) {
12 | const value = this.cache.get(key);
13 | // Map keys are stored and iterated in insertion order,
14 | // so we reinsert on every access
15 | this.cache.delete(key);
16 | this.cache.set(key, value);
17 | return value;
18 | } else {
19 | return undefined;
20 | }
21 | }
22 |
23 | set(key, value) {
24 | if (this.cache.has(key)) {
25 | this.cache.delete(key);
26 | } else if (this.cache.size === this.size) {
27 | const oldestKey = this.cache.keys().next().value;
28 | this.cache.delete(oldestKey);
29 | }
30 |
31 | this.cache.set(key, value);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/assets/js/lib/emitter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * An abstraction for registering and dispatching callbacks.
3 | */
4 | export default class Emitter {
5 | constructor() {
6 | /** @private */
7 | this.callbacks = [];
8 | }
9 |
10 | /**
11 | * A function used to register new listener in the emitter.
12 | *
13 | * This is a shorthand for `addListener`.
14 | */
15 | get event() {
16 | return this.addListener.bind(this);
17 | }
18 |
19 | /**
20 | * Adds new listener to the emitter.
21 | *
22 | * Returns a subscription object that you can destroy in order to
23 | * unsubscribe.
24 | */
25 | addListener(callback) {
26 | this.callbacks.push(callback);
27 |
28 | return {
29 | destroy: () => {
30 | this.removeListener(callback);
31 | },
32 | };
33 | }
34 |
35 | /**
36 | * Removes a listener from the emitter.
37 | */
38 | removeListener(callback) {
39 | const idx = this.callbacks.indexOf(callback);
40 |
41 | if (idx !== -1) {
42 | this.callbacks.splice(idx, 1);
43 | }
44 | }
45 |
46 | /**
47 | * Dispatches all listeners with the given arguments.
48 | */
49 | dispatch(...args) {
50 | this.callbacks.forEach((callback) => {
51 | callback(...args);
52 | });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/assets/js/lib/notebook.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Checks if the given cell type is eligible for evaluation.
3 | */
4 | export function isEvaluable(cellType) {
5 | return ["code", "smart"].includes(cellType);
6 | }
7 |
8 | /**
9 | * Checks if the given cell type has primary editable editor.
10 | */
11 | export function isDirectlyEditable(cellType) {
12 | return ["markdown", "code"].includes(cellType);
13 | }
14 |
--------------------------------------------------------------------------------
/assets/js/lib/storage.js:
--------------------------------------------------------------------------------
1 | const PREFIX = "livebook:";
2 |
3 | /**
4 | * Loads value from local storage.
5 | */
6 | export function load(key) {
7 | try {
8 | const json = localStorage.getItem(PREFIX + key);
9 |
10 | if (json) {
11 | return JSON.parse(json);
12 | }
13 | } catch (error) {
14 | console.error(
15 | `Failed to load from local storage, reason: ${error.message}`,
16 | );
17 | }
18 |
19 | return undefined;
20 | }
21 |
22 | /**
23 | * Stores value in local storage.
24 | *
25 | * The value is serialized as JSON.
26 | */
27 | export function store(key, value) {
28 | try {
29 | const json = JSON.stringify(value);
30 | localStorage.setItem(PREFIX + key, json);
31 | } catch (error) {
32 | console.error(`Failed to write to local storage, reason: ${error.message}`);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/assets/js/lib/text_utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns length of suffix in `string` that should be replaced
3 | * with `newSuffix` to avoid duplication.
4 | */
5 | export function replacedSuffixLength(string, newSuffix) {
6 | let suffix = newSuffix;
7 |
8 | while (!string.endsWith(suffix)) {
9 | suffix = suffix.slice(0, -1);
10 | }
11 |
12 | return suffix.length;
13 | }
14 |
--------------------------------------------------------------------------------
/assets/js/lib/user.js:
--------------------------------------------------------------------------------
1 | import { cookieOptions, decodeBase64, encodeBase64 } from "./utils";
2 |
3 | const USER_DATA_COOKIE = "lb_user_data";
4 |
5 | /**
6 | * Stores user data in the `"lb_user_data"` cookie.
7 | */
8 | export function storeUserData(userData) {
9 | const json = JSON.stringify(userData);
10 | const encoded = encodeBase64(json);
11 | setCookie(USER_DATA_COOKIE, encoded, 157_680_000); // 5 years
12 | }
13 |
14 | /**
15 | * Loads user data from the `"lb_user_data"` cookie.
16 | */
17 | export function loadUserData() {
18 | const encoded = getCookieValue(USER_DATA_COOKIE);
19 | if (encoded) {
20 | const json = decodeBase64(encoded);
21 | return JSON.parse(json);
22 | } else {
23 | return null;
24 | }
25 | }
26 |
27 | function getCookieValue(key) {
28 | const cookie = document.cookie
29 | .split("; ")
30 | .find((cookie) => cookie.startsWith(`${key}=`));
31 |
32 | if (cookie) {
33 | const value = cookie.replace(`${key}=`, "");
34 | return value;
35 | } else {
36 | return null;
37 | }
38 | }
39 |
40 | function setCookie(key, value, maxAge) {
41 | const cookie = `${key}=${value};max-age=${maxAge};path=/${cookieOptions()}`;
42 | document.cookie = cookie;
43 | }
44 |
--------------------------------------------------------------------------------
/assets/test/lib/emitter.test.js:
--------------------------------------------------------------------------------
1 | import Emitter from "../../js/lib/emitter";
2 |
3 | test("listener callbacks are called on dispatch", () => {
4 | const emitter = new Emitter();
5 | const callback1 = jest.fn();
6 | const callback2 = jest.fn();
7 |
8 | emitter.addListener(callback1);
9 | emitter.addListener(callback2);
10 | emitter.dispatch({ data: 1 });
11 |
12 | expect(callback1).toHaveBeenCalledWith({ data: 1 });
13 | expect(callback2).toHaveBeenCalledWith({ data: 1 });
14 | });
15 |
16 | test("addListener returns a subscription object that can be destroyed", () => {
17 | const emitter = new Emitter();
18 | const callback1 = jest.fn();
19 |
20 | const subscription = emitter.addListener(callback1);
21 | subscription.destroy();
22 | emitter.dispatch({});
23 |
24 | expect(callback1).not.toHaveBeenCalled();
25 | });
26 |
--------------------------------------------------------------------------------
/assets/test/lib/pubsub.test.js:
--------------------------------------------------------------------------------
1 | import PubSub from "../../js/lib/pubsub";
2 |
3 | test("subscribed callback is called on the specified topic", () => {
4 | const pubsub = new PubSub();
5 | const callback1 = jest.fn();
6 | const callback2 = jest.fn();
7 |
8 | pubsub.subscribe("topic1", callback1);
9 | pubsub.subscribe("topic2", callback2);
10 | pubsub.broadcast("topic1", { data: 1 });
11 |
12 | expect(callback1).toHaveBeenCalledWith({ data: 1 });
13 | expect(callback2).not.toHaveBeenCalled();
14 | });
15 |
16 | test("subscribe returns a subscription object that can be destroyed", () => {
17 | const pubsub = new PubSub();
18 | const callback1 = jest.fn();
19 |
20 | const subscription = pubsub.subscribe("topic1", callback1);
21 | subscription.destroy();
22 | pubsub.broadcast("topic1", {});
23 |
24 | expect(callback1).not.toHaveBeenCalled();
25 | });
26 |
--------------------------------------------------------------------------------
/assets/test/lib/text_utils.test.js:
--------------------------------------------------------------------------------
1 | import { replacedSuffixLength } from "../../js/lib/text_utils";
2 |
3 | test("replacedSuffixLength", () => {
4 | expect(replacedSuffixLength("to_string(", "")).toEqual(0);
5 | expect(replacedSuffixLength("to_string(", "length")).toEqual(0);
6 | expect(replacedSuffixLength("length", "length")).toEqual(6);
7 | expect(replacedSuffixLength("x = ~", "~r")).toEqual(1);
8 | expect(replacedSuffixLength("Enum.ma", "map")).toEqual(2);
9 | expect(replacedSuffixLength("Enum.ma", "map_reduce")).toEqual(2);
10 | });
11 |
--------------------------------------------------------------------------------
/config/runtime.exs:
--------------------------------------------------------------------------------
1 | import Config
2 | Livebook.config_runtime()
3 |
--------------------------------------------------------------------------------
/config/test.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | # We don't run a server during test. If one is required,
4 | # you can enable the server option below.
5 | config :livebook, LivebookWeb.Endpoint,
6 | http: [port: 4002],
7 | server: false
8 |
9 | # Print only warnings and errors during test
10 | config :logger, level: :warning
11 |
12 | # Disable authentication in tests
13 | config :livebook,
14 | authentication: :disabled,
15 | check_completion_data_interval: 300,
16 | iframe_port: 4003
17 |
18 | data_path = Path.expand("tmp/livebook_data/test")
19 |
20 | # Clear data path for tests
21 | if File.exists?(data_path) do
22 | File.rm_rf!(data_path)
23 | end
24 |
25 | config :livebook,
26 | data_path: data_path,
27 | agent_name: "chonky-cat",
28 | k8s_kubeconfig_pipeline:
29 | {Kubereq.Kubeconfig.Stub,
30 | plugs: %{
31 | "default" => {Req.Test, :k8s_cluster},
32 | "no-permission" => {Req.Test, :k8s_cluster}
33 | }}
34 |
35 | config :livebook, Livebook.Apps.Manager, retry_backoff_base_ms: 0
36 |
--------------------------------------------------------------------------------
/docs/images/add_app_server_to_deployment_group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_app_server_to_deployment_group.png
--------------------------------------------------------------------------------
/docs/images/add_shared_file_storage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_shared_file_storage.png
--------------------------------------------------------------------------------
/docs/images/add_shared_secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_shared_secret.png
--------------------------------------------------------------------------------
/docs/images/add_shared_secret_from_notebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_shared_secret_from_notebook.png
--------------------------------------------------------------------------------
/docs/images/app_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_preview.png
--------------------------------------------------------------------------------
/docs/images/app_server_authentication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_authentication.png
--------------------------------------------------------------------------------
/docs/images/app_server_docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_docker.png
--------------------------------------------------------------------------------
/docs/images/app_server_setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_setup.png
--------------------------------------------------------------------------------
/docs/images/app_server_setup_message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_setup_message.png
--------------------------------------------------------------------------------
/docs/images/auth_via_teams.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/auth_via_teams.png
--------------------------------------------------------------------------------
/docs/images/deploy_button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deploy_button.png
--------------------------------------------------------------------------------
/docs/images/deployed_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployed_app.png
--------------------------------------------------------------------------------
/docs/images/deployed_apps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployed_apps.png
--------------------------------------------------------------------------------
/docs/images/deployment_group_form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployment_group_form.png
--------------------------------------------------------------------------------
/docs/images/deployment_groups_inside_workspace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployment_groups_inside_workspace.png
--------------------------------------------------------------------------------
/docs/images/email_domain_auth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/email_domain_auth.png
--------------------------------------------------------------------------------
/docs/images/github_stars_notebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/github_stars_notebook.png
--------------------------------------------------------------------------------
/docs/images/instructions_setup_app_server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/instructions_setup_app_server.png
--------------------------------------------------------------------------------
/docs/images/oidc_groups_auth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/oidc_groups_auth.png
--------------------------------------------------------------------------------
/docs/teams/intro_to_teams.md:
--------------------------------------------------------------------------------
1 | # Intro to Livebook Teams
2 |
3 | [Livebook Teams](https://livebook.dev/teams/?ref=docs) enables you to deploy Livebook apps to your own infrastructure.
4 |
5 |
6 |
7 | Besides deployment automation, it also offers the following features:
8 |
9 | - Authentication via multiple identity providers:
10 | - Livebook Teams
11 | - [Email-domain (Google, Microsoft, etc)](email_domain.md)
12 | - [OIDC (Okta, Microsoft Entra, etc)](oidc_sso.md)
13 | - Authorization
14 | - [based on groups from an OIDC identity provider (Okta, Microsoft Entra, etc)](oidc_groups.md)
15 | - [Shared Livebook secrets](shared_secrets.md)
16 | - [Shared Livebook file storages](shared_file_storages.md)
17 |
18 | You can [start a free trial here](https://livebook.dev/teams/?ref=docs).
19 |
--------------------------------------------------------------------------------
/docs/teams/shared_file_storages.md:
--------------------------------------------------------------------------------
1 | # Shared file storages
2 |
3 | ## Overview
4 |
5 | This feature allows your team to share the configuration of S3-compatible storages.
6 |
7 | Livebook file storages are used to store notebooks and their files.
8 |
9 | Whenever you add (update or detach) a file storage to your organization workspace, Livebook Teams will synchronize that with the Livebook of every member of your organization.
10 |
11 | 
12 |
13 | ## How it works
14 |
15 | Here's a video showing how that feature works.
16 |
17 |
18 |
19 | ## Security strengths
20 |
21 | Livebook Teams cannot access the credentials of your S3 (compatible) account.
22 |
23 | Livebook encrypts your S3 credentials locally in your machine using your Teams key. Then, they're sent encrypted to Livebook Teams servers.
24 |
25 | When a new synchronization is needed, Livebook Teams sends the encrypted credentials to the Livebook of team members, and their Livebook decrypts that in their local machines using the same Teams key.
26 |
--------------------------------------------------------------------------------
/elixirkit/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4 | ]
5 |
--------------------------------------------------------------------------------
/elixirkit/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | elixirkit-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/elixirkit/demo/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4 | ]
5 |
--------------------------------------------------------------------------------
/elixirkit/demo/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | demo-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/elixirkit/demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | This is an example ElixirKit application.
4 |
5 | See:
6 |
7 | * [Elixir Server](lib/demo.ex)
8 | * [Swift Client](rel/swift)
9 | * [AppKit Client](rel/appkit)
10 | * [C# Client](rel/dotnet)
11 | * [Windows Forms Client](rel/winforms)
12 |
--------------------------------------------------------------------------------
/elixirkit/demo/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Demo.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :demo,
7 | version: "0.1.0",
8 | elixir: "~> 1.14",
9 | start_permanent: Mix.env() == :prod,
10 | deps: deps()
11 | ]
12 | end
13 |
14 | def application do
15 | [
16 | extra_applications: [:logger],
17 | mod: {Demo.Application, []}
18 | ]
19 | end
20 |
21 | defp deps do
22 | [
23 | {:elixirkit, path: ".."}
24 | ]
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/.gitignore:
--------------------------------------------------------------------------------
1 | /.build
2 | /Packages
3 | /*.xcodeproj
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/config/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/App.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.allow-jit
6 |
7 | com.apple.security.cs.allow-unsigned-executable-memory
8 |
9 | com.apple.security.cs.allow-dyld-environment-variables
10 |
11 | com.apple.security.cs.disable-library-validation
12 |
13 |
14 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Demo",
7 | platforms: [
8 | .macOS(.v11)
9 | ],
10 | dependencies: [
11 | .package(name: "ElixirKit", path: "../../../elixirkit_swift")
12 | ],
13 | targets: [
14 | .executableTarget(
15 | name: "Demo",
16 | dependencies: ["ElixirKit"]
17 | )
18 | ]
19 | )
20 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | Run the app, just the executable:
4 |
5 | $ ./run.sh
6 |
7 | Run the app bundle:
8 |
9 | $ ./run_app.sh
10 |
11 | Build the .dmg:
12 |
13 | $ ./build_dmg.sh
14 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/build_app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | export MIX_ENV=prod
5 | export ELIXIRKIT_APP_NAME=Demo
6 | export ELIXIRKIT_PROJECT_DIR=$PWD/../../..
7 |
8 | . ../../../elixirkit_swift/Scripts/build_macos_app.sh
9 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/build_dmg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | . `dirname $0`/build_app.sh
5 | . ../../../elixirkit_swift/Scripts/build_macos_dmg.sh
6 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | swift build
5 | target_dir=`swift build --show-bin-path`
6 | (cd ../.. && mix release --overwrite --path=$target_dir/rel)
7 | $target_dir/Demo
8 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/appkit/run_app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | export ELIXIRKIT_APP_NAME=Demo
5 | export ELIXIRKIT_PROJECT_DIR=$PWD/../..
6 |
7 | . ../../../../elixirkit/elixirkit_swift/Scripts/build_macos_app.sh
8 | open -W --stdout `tty` --stderr `tty` .build/Demo.app
9 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/dotnet/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /obj
3 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/dotnet/Demo.cs:
--------------------------------------------------------------------------------
1 | class Demo
2 | {
3 | public static void Main()
4 | {
5 | ElixirKit.API.Start(
6 | name: "demo",
7 | ready: () =>
8 | {
9 | ElixirKit.API.Publish("log", "Hello from C#!");
10 |
11 | ElixirKit.API.Subscribe((name, data) =>
12 | {
13 | switch (name)
14 | {
15 | case "log":
16 | Console.WriteLine($"[client] {data}");
17 | break;
18 |
19 | default:
20 | throw new Exception($"unknown event {name}");
21 | }
22 | });
23 | }
24 | );
25 |
26 | System.Console.WriteLine($"{ElixirKit.API.WaitForExit()}");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/dotnet/Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/dotnet/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | Run the app:
4 |
5 | $ ./run.sh
6 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/dotnet/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | dotnet build
5 | target_dir="$PWD/bin/Debug/net6.0"
6 | (cd ../.. && mix release --overwrite --path=${target_dir}/rel)
7 | dotnet run --no-build
8 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/swift/.gitignore:
--------------------------------------------------------------------------------
1 | /.build
2 | /Packages
3 | /*.xcodeproj
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/config/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/swift/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Demo",
7 | platforms: [
8 | .macOS(.v11)
9 | ],
10 | dependencies: [
11 | .package(name: "ElixirKit", path: "../../../elixirkit_swift")
12 | ],
13 | targets: [
14 | .executableTarget(
15 | name: "Demo",
16 | dependencies: ["ElixirKit"]
17 | )
18 | ]
19 | )
20 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/swift/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | Run the app:
4 |
5 | $ ./run.sh
6 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/swift/Sources/Demo/Demo.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import ElixirKit
3 |
4 | @main
5 | struct Demo {
6 | public static func main() {
7 | // Capture ctrl+c
8 | signal(SIGINT) { signal in
9 | ElixirKit.API.stop()
10 | exit(signal)
11 | }
12 |
13 | ElixirKit.API.start(
14 | name: "demo",
15 | readyHandler: {
16 | ElixirKit.API.publish("log", "Hello from Swift!")
17 |
18 | ElixirKit.API.addObserver(queue: .main) { (name, data) in
19 | switch name {
20 | case "log":
21 | print("[client] " + data)
22 | default:
23 | fatalError("unknown event \(name)")
24 | }
25 | }
26 | }
27 | )
28 |
29 | ElixirKit.API.waitUntilExit()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/swift/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | swift build
5 | target_dir=`swift build --show-bin-path`
6 | (cd ../.. && mix release --overwrite --path=$target_dir/rel)
7 | $target_dir/Demo
8 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/winforms/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /obj
3 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/winforms/Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | WinExe
6 | net6.0-windows
7 | enable
8 | true
9 | enable
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/winforms/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | Run the app:
4 |
5 | $ ./run.sh
6 |
--------------------------------------------------------------------------------
/elixirkit/demo/rel/winforms/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | dotnet build
5 | target_dir="$PWD/bin/Debug/net6.0-windows"
6 | (cd ../.. && mix release --overwrite --path=${target_dir}/rel)
7 | dotnet run --no-build
8 |
--------------------------------------------------------------------------------
/elixirkit/demo/test/demo_test.exs:
--------------------------------------------------------------------------------
1 | defmodule DemoTest do
2 | use ExUnit.Case, async: true
3 | end
4 |
--------------------------------------------------------------------------------
/elixirkit/demo/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/elixirkit/elixirkit_dotnet/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /obj
3 |
--------------------------------------------------------------------------------
/elixirkit/elixirkit_dotnet/ElixirKit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net46
5 | 10.0
6 | enable
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/elixirkit/elixirkit_swift/.gitignore:
--------------------------------------------------------------------------------
1 | /.build
2 | /Packages
3 | /*.xcodeproj
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/config/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/elixirkit/elixirkit_swift/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "ElixirKit",
7 | platforms: [
8 | .macOS(.v11)
9 | ],
10 | products: [
11 | .library(
12 | name: "ElixirKit",
13 | targets: ["ElixirKit"]
14 | ),
15 | ],
16 | dependencies: [],
17 | targets: [
18 | .target(
19 | name: "ElixirKit",
20 | dependencies: []
21 | ),
22 | .testTarget(
23 | name: "ElixirKitTests",
24 | dependencies: ["ElixirKit"]
25 | )
26 | ]
27 | )
28 |
--------------------------------------------------------------------------------
/elixirkit/elixirkit_swift/README.md:
--------------------------------------------------------------------------------
1 | # ElixirKit
2 |
--------------------------------------------------------------------------------
/elixirkit/elixirkit_swift/Tests/ElixirKitTests/ElixirKitTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import ElixirKit
3 |
4 | final class ElixirKitTests: XCTestCase {
5 | func testExample() throws {
6 | XCTAssertEqual(1 + 2, 3)
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/elixirkit/lib/elixirkit.ex:
--------------------------------------------------------------------------------
1 | defmodule ElixirKit do
2 | def start do
3 | Supervisor.start_child(ElixirKit.Supervisor, {ElixirKit.Server, self()})
4 | end
5 |
6 | def publish(name, data) do
7 | GenServer.call(ElixirKit.Server, {:send_event, name, data})
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/elixirkit/lib/elixirkit/application.ex:
--------------------------------------------------------------------------------
1 | defmodule ElixirKit.Application do
2 | @moduledoc false
3 |
4 | use Application
5 |
6 | @impl true
7 | def start(_type, _args) do
8 | children = []
9 | opts = [strategy: :one_for_one, name: ElixirKit.Supervisor]
10 | Supervisor.start_link(children, opts)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/elixirkit/lib/elixirkit/server.ex:
--------------------------------------------------------------------------------
1 | defmodule ElixirKit.Server do
2 | @moduledoc false
3 |
4 | use GenServer
5 |
6 | def start_link(arg) do
7 | GenServer.start_link(__MODULE__, arg, name: __MODULE__)
8 | end
9 |
10 | @impl true
11 | def init(pid) do
12 | port = System.fetch_env!("ELIXIRKIT_PORT") |> String.to_integer()
13 | {:ok, socket} = :gen_tcp.connect(~c"localhost", port, mode: :binary, packet: 4)
14 | {:ok, %{pid: pid, socket: socket}}
15 | end
16 |
17 | @impl true
18 | def handle_call({:send_event, name, data}, _from, state) do
19 | payload = [name, ?:, data]
20 | :ok = :gen_tcp.send(state.socket, payload)
21 | {:reply, :ok, state}
22 | end
23 |
24 | @impl true
25 | def handle_info({:tcp, socket, payload}, state) when socket == state.socket do
26 | [name, data] = :binary.split(payload, ":")
27 | send(state.pid, {:event, name, data})
28 | {:noreply, state}
29 | end
30 |
31 | @impl true
32 | def handle_info({:tcp_closed, socket}, state) when socket == state.socket do
33 | {:stop, :shutdown, state}
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/elixirkit/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule ElixirKit.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :elixirkit,
7 | version: "0.1.0",
8 | elixir: "~> 1.14",
9 | start_permanent: Mix.env() == :prod,
10 | package: package(),
11 | deps: deps()
12 | ]
13 | end
14 |
15 | def application do
16 | [
17 | extra_applications: [:logger],
18 | mod: {ElixirKit.Application, []}
19 | ]
20 | end
21 |
22 | def package do
23 | [
24 | files: [
25 | "lib",
26 | "elixirkit_swift/Package.swift",
27 | "elixirkit_swift/Sources",
28 | "elixirkit_dotnet/ElixirKit.csproj",
29 | "elixirkit_dotnet/ElixirKit.cs",
30 | "mix.exs",
31 | "README.md"
32 | ]
33 | ]
34 | end
35 |
36 | defp deps do
37 | []
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/elixirkit/otp_bootstrap/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 |
--------------------------------------------------------------------------------
/elixirkit/test/elixirkit_test.exs:
--------------------------------------------------------------------------------
1 | defmodule ElixirKitTest do
2 | use ExUnit.Case, async: true
3 | end
4 |
--------------------------------------------------------------------------------
/elixirkit/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/iframe/.formatter.exs:
--------------------------------------------------------------------------------
1 | [
2 | import_deps: [:plug],
3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4 | ]
5 |
--------------------------------------------------------------------------------
/iframe/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | livebook_space-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/iframe/Dockerfile:
--------------------------------------------------------------------------------
1 | # Stage 1: build
2 | FROM hexpm/elixir:1.13.2-erlang-24.1.7-alpine-3.15.0 AS build
3 |
4 | # Install build dependencies
5 | RUN apk add --no-cache build-base git
6 |
7 | WORKDIR /app
8 |
9 | # Install hex and rebar
10 | RUN mix local.hex --force && \
11 | mix local.rebar --force
12 |
13 | # Set build ENV
14 | ENV MIX_ENV=prod
15 |
16 | # Install mix dependencies
17 | COPY mix.exs mix.lock ./
18 | RUN mix do deps.get, deps.compile
19 |
20 | # Compile and build release
21 | COPY priv priv
22 | COPY lib lib
23 | RUN mix do compile, release
24 |
25 | # Stage 2: release image
26 | FROM alpine:3.15.0
27 |
28 | # Install runtime dependencies
29 | RUN apk add --no-cache openssl ncurses-libs libstdc++
30 |
31 | WORKDIR /app
32 |
33 | # Copy the release build from the previous stage.
34 | COPY --from=build /app/_build/prod/rel/livebook_space ./
35 |
36 | ENV HOME=/app
37 |
38 | CMD [ "/app/bin/livebook_space", "start" ]
39 |
--------------------------------------------------------------------------------
/iframe/README.md:
--------------------------------------------------------------------------------
1 | # Livebook iframes
2 |
3 | Livebook executes custom JavaScript inside iframes. When running on http,
4 | they are served on a separate port. For https, they are safely served by
5 | [livebookusercontent.com](https://livebookusercontent.com), which runs this application.
6 |
--------------------------------------------------------------------------------
/iframe/fly.toml:
--------------------------------------------------------------------------------
1 | app = "livebook-space"
2 |
3 | kill_signal = "SIGTERM"
4 | kill_timeout = 5
5 |
6 | [env]
7 |
8 | [[services]]
9 | internal_port = 4000
10 | protocol = "tcp"
11 |
12 | [[services.ports]]
13 | handlers = ["http"]
14 | port = 80
15 |
16 | [[services.ports]]
17 | handlers = ["tls", "http"]
18 | port = 443
19 |
20 | [[services.tcp_checks]]
21 | grace_period = "30s"
22 | interval = "15s"
23 | restart_limit = 6
24 | timeout = "2s"
25 |
--------------------------------------------------------------------------------
/iframe/lib/livebook_space/application.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookSpace.Application do
2 | use Application
3 |
4 | def start(_type, _args) do
5 | children = [
6 | {Bandit, scheme: :http, plug: LivebookSpaceWeb.Plug, port: 4000}
7 | ]
8 |
9 | opts = [strategy: :one_for_one, name: LivebookSpace.Supervisor]
10 | Supervisor.start_link(children, opts)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/iframe/lib/livebook_space_web/plug.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookSpaceWeb.Plug do
2 | use Plug.Builder
3 |
4 | plug Plug.Static,
5 | from: {:livebook_space, "priv/static/iframe"},
6 | at: "/iframe",
7 | # Iframes are versioned, so we cache them for long
8 | cache_control_for_etags: "public, max-age=31536000",
9 | headers: [
10 | # Enable CORS to allow Livebook fetch the content and verify its integrity
11 | {"access-control-allow-origin", "*"},
12 | {"content-type", "text/html; charset=utf-8"}
13 | ]
14 |
15 | plug Plug.Static, from: :livebook_space, at: "/"
16 |
17 | plug :not_found
18 |
19 | defp not_found(%{path_info: [], method: "GET"} = conn, _) do
20 | call(%{conn | path_info: ["index.html"], request_path: "/index.html"}, [])
21 | end
22 |
23 | defp not_found(conn, _) do
24 | send_resp(conn, 404, "not found")
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/iframe/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookSpace.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :livebook_space,
7 | version: "0.1.0",
8 | elixir: "~> 1.13",
9 | start_permanent: Mix.env() == :prod,
10 | deps: deps()
11 | ]
12 | end
13 |
14 | def application do
15 | [
16 | extra_applications: [:logger],
17 | mod: {LivebookSpace.Application, []}
18 | ]
19 | end
20 |
21 | defp deps do
22 | [
23 | {:bandit, "~> 1.0"}
24 | ]
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/iframe/priv/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/iframe/priv/static/images/logo.png
--------------------------------------------------------------------------------
/iframe/priv/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/lib/livebook/apps/deployment_supervisor.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Apps.DeploymentSupervisor do
2 | # Supervision tree related to orchestrating app deployments in the
3 | # cluster.
4 |
5 | use Supervisor
6 |
7 | @name __MODULE__
8 |
9 | @doc """
10 | Starts the supervisor.
11 | """
12 | @spec start_link(keyword()) :: GenServer.on_start()
13 | def start_link(_opts) do
14 | Supervisor.start_link(__MODULE__, {}, name: @name)
15 | end
16 |
17 | @doc """
18 | Starts a node-local instance of `Livebook.Apps.Manager`.
19 | """
20 | @spec start_manager() :: Supervisor.on_start_child()
21 | def start_manager() do
22 | Supervisor.start_child(@name, Livebook.Apps.Manager)
23 | end
24 |
25 | @impl true
26 | def init({}) do
27 | children = [
28 | # Start the supervisor dynamically managing apps
29 | {DynamicSupervisor, name: Livebook.AppSupervisor, strategy: :one_for_one},
30 | # Process group for app deployers
31 | %{id: Livebook.Apps.Deployer.PG, start: {:pg, :start_link, [Livebook.Apps.Deployer.PG]}},
32 | # Node-local app deployer
33 | Livebook.Apps.Deployer,
34 | # Node-local app manager watcher
35 | Livebook.Apps.ManagerWatcher
36 | ]
37 |
38 | Supervisor.init(children, strategy: :one_for_one)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/livebook/hubs/metadata.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Hubs.Metadata do
2 | defstruct [:id, :name, :provider, :emoji, connected?: false]
3 |
4 | @type t :: %__MODULE__{
5 | id: String.t(),
6 | name: String.t(),
7 | provider: struct(),
8 | emoji: String.t(),
9 | connected?: boolean()
10 | }
11 | end
12 |
--------------------------------------------------------------------------------
/lib/livebook/notebook/cell/markdown.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Notebook.Cell.Markdown do
2 | # Notebook cell with Markdown text content.
3 | #
4 | # It consists of text content that the user can edit and which is
5 | # rendered on the page.
6 |
7 | defstruct [:id, :source]
8 |
9 | alias Livebook.Utils
10 | alias Livebook.Notebook.Cell
11 |
12 | @type t :: %__MODULE__{
13 | id: Cell.id(),
14 | source: String.t() | :__pruned__
15 | }
16 |
17 | @doc """
18 | Returns an empty cell.
19 | """
20 | @spec new() :: t()
21 | def new() do
22 | %__MODULE__{
23 | id: Utils.random_id(),
24 | source: ""
25 | }
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/livebook/notebook/learn/files/portal-drop.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/lib/livebook/notebook/learn/files/portal-drop.jpeg
--------------------------------------------------------------------------------
/lib/livebook/notebook/learn/files/portal-list.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/lib/livebook/notebook/learn/files/portal-list.jpeg
--------------------------------------------------------------------------------
/lib/livebook/notebook/section.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Notebook.Section do
2 | # Data structure representing a single section in a notebook.
3 | #
4 | # Section can contains a number of cells and serves as a way of
5 | # grouping related cells.
6 | #
7 | # A section may optionally have a parent, in which case it is a
8 | # **branching section**. Such section logically follows its parent
9 | # section and has no impact on any further sections.
10 |
11 | defstruct [:id, :name, :cells, :parent_id]
12 |
13 | alias Livebook.Notebook.Cell
14 | alias Livebook.Utils
15 |
16 | @type id :: Utils.id()
17 |
18 | @type t :: %__MODULE__{
19 | id: id(),
20 | name: String.t(),
21 | cells: list(Cell.t()),
22 | parent_id: id() | nil
23 | }
24 |
25 | @doc """
26 | Returns a blank section.
27 | """
28 | @spec new() :: t()
29 | def new() do
30 | %__MODULE__{
31 | id: Utils.random_id(),
32 | name: "Section",
33 | cells: [],
34 | parent_id: nil
35 | }
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/livebook/runtime/erl_dist/evaluator_supervisor.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Runtime.ErlDist.EvaluatorSupervisor do
2 | # Supervisor responsible for dynamically spawning
3 | # and terminating evaluator server processes.
4 |
5 | use DynamicSupervisor
6 |
7 | alias Livebook.Runtime.Evaluator
8 |
9 | def start_link(opts \\ []) do
10 | DynamicSupervisor.start_link(__MODULE__, opts)
11 | end
12 |
13 | @impl true
14 | def init(_opts) do
15 | DynamicSupervisor.init(strategy: :one_for_one)
16 | end
17 |
18 | @doc """
19 | Spawns a new evaluator.
20 | """
21 | @spec start_evaluator(pid(), keyword()) :: {:ok, Evaluator.t()} | {:error, any()}
22 | def start_evaluator(supervisor, opts) do
23 | case DynamicSupervisor.start_child(supervisor, {Evaluator, opts}) do
24 | {:ok, _pid, evaluator} -> {:ok, evaluator}
25 | {:error, reason} -> {:error, reason}
26 | end
27 | end
28 |
29 | @doc """
30 | Terminates the given evaluator.
31 | """
32 | @spec terminate_evaluator(pid(), Evaluator.t()) :: :ok
33 | def terminate_evaluator(supervisor, evaluator) do
34 | DynamicSupervisor.terminate_child(supervisor, evaluator.pid)
35 | :ok
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/livebook/runtime/erl_dist/smart_cell_gl.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Runtime.ErlDist.SmartCellGL do
2 | use GenServer
3 |
4 | @spec start_link(pid()) :: GenServer.on_start()
5 | def start_link(runtime_broadcast_to) do
6 | GenServer.start_link(__MODULE__, {runtime_broadcast_to})
7 | end
8 |
9 | @impl true
10 | def init({runtime_broadcast_to}) do
11 | {:ok, %{runtime_broadcast_to: runtime_broadcast_to}}
12 | end
13 |
14 | @impl true
15 | def handle_info({:io_request, from, reply_as, req}, state) do
16 | case io_request(req, state) do
17 | :forward ->
18 | # Forward the request to own group leader
19 | gl = Process.group_leader()
20 | send(gl, {:io_request, from, reply_as, req})
21 |
22 | {:reply, reply} ->
23 | send(from, {:io_reply, reply_as, reply})
24 | end
25 |
26 | {:noreply, state}
27 | end
28 |
29 | defp io_request(:livebook_get_broadcast_target, state) do
30 | {:reply, {:ok, state.runtime_broadcast_to}}
31 | end
32 |
33 | defp io_request(_req, _state) do
34 | :forward
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/livebook/secrets.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Secrets do
2 | # Shared secret functionality across all hubs.
3 |
4 | alias Livebook.Secrets.Secret
5 |
6 | @doc """
7 | Returns an `%Ecto.Changeset{}` for tracking secret changes.
8 | """
9 | @spec change_secret(Secret.t(), map()) :: Ecto.Changeset.t()
10 | def change_secret(%Secret{} = secret, attrs) do
11 | Secret.changeset(secret, attrs)
12 | end
13 |
14 | @doc """
15 | Updates secret with the given changes.
16 | """
17 | @spec update_secret(Secret.t(), map()) :: {:ok, Secret.t()} | {:error, Ecto.Changeset.t()}
18 | def update_secret(%Secret{} = secret, attrs) do
19 | secret
20 | |> Secret.changeset(attrs)
21 | |> Ecto.Changeset.apply_action(:update)
22 | end
23 |
24 | @secret_startup_key :livebook_startup_secrets
25 |
26 | @doc """
27 | Get the startup secrets list from persistent term.
28 | """
29 | @spec get_startup_secrets() :: list(Secret.t())
30 | def get_startup_secrets do
31 | :persistent_term.get(@secret_startup_key, [])
32 | end
33 |
34 | @doc """
35 | Sets additional secrets that are kept only in memory.
36 | """
37 | @spec set_startup_secrets(list(Secret.t())) :: :ok
38 | def set_startup_secrets(secrets) do
39 | :persistent_term.put(@secret_startup_key, secrets)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/livebook/secrets/secret.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Secrets.Secret do
2 | use Ecto.Schema
3 | import Ecto.Changeset
4 |
5 | @type t :: %__MODULE__{
6 | name: String.t() | nil,
7 | value: String.t() | nil,
8 | hub_id: String.t() | nil,
9 | deployment_group_id: String.t() | nil
10 | }
11 |
12 | @primary_key {:name, :string, autogenerate: false}
13 | embedded_schema do
14 | field :value, :string
15 | field :hub_id, :string
16 | field :deployment_group_id, :string
17 | end
18 |
19 | def changeset(secret, attrs \\ %{}) do
20 | secret
21 | |> cast(attrs, [:name, :value, :hub_id, :deployment_group_id])
22 | |> update_change(:name, &String.upcase/1)
23 | |> validate_format(:name, ~r/^\w+$/,
24 | message: "should contain only alphanumeric characters and underscore"
25 | )
26 | |> validate_required([:name, :value])
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/livebook/session/worker.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Session.Worker do
2 | # A dedicated process for offloading the session process,
3 | # when the session state is not necessary.
4 | #
5 | # In particular, this process handles broadcast messages
6 | # sent from within the runtime and distributes them to the
7 | # actual subscribers via pubsub.
8 |
9 | use GenServer
10 |
11 | def start_link(session_id) do
12 | GenServer.start_link(__MODULE__, {session_id})
13 | end
14 |
15 | @impl true
16 | def init({session_id}) do
17 | {:ok, %{session_id: session_id}}
18 | end
19 |
20 | @impl true
21 | def handle_info({:runtime_broadcast, topic, subtopic, message}, state) do
22 | Livebook.Session.broadcast_runtime_event(state.session_id, topic, subtopic, message)
23 | {:noreply, state}
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/livebook/settings/env_var.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Settings.EnvVar do
2 | use Ecto.Schema
3 | import Ecto.Changeset
4 |
5 | @type t :: %__MODULE__{
6 | name: String.t() | nil,
7 | value: String.t() | nil
8 | }
9 |
10 | @primary_key {:name, :string, autogenerate: false}
11 | embedded_schema do
12 | field :value, :string
13 | end
14 |
15 | def changeset(env_var, attrs \\ %{}) do
16 | env_var
17 | |> cast(attrs, [:name, :value])
18 | |> update_change(:name, &String.upcase/1)
19 | |> validate_format(:name, ~r/^(?!LB_)\w+$/, message: "cannot start with the LB_ prefix")
20 | |> validate_required([:name, :value])
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/livebook/teams/agent.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Teams.Agent do
2 | use Ecto.Schema
3 |
4 | @type t :: %__MODULE__{
5 | id: String.t() | nil,
6 | name: String.t() | nil,
7 | hub_id: String.t() | nil,
8 | org_id: String.t() | nil,
9 | deployment_group_id: String.t() | nil
10 | }
11 |
12 | @primary_key {:id, :string, autogenerate: false}
13 | embedded_schema do
14 | field :name, :string
15 | field :hub_id, :string
16 | field :org_id, :string
17 | field :deployment_group_id, :string
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/livebook/teams/agent_key.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Teams.AgentKey do
2 | use Ecto.Schema
3 |
4 | @type t :: %__MODULE__{
5 | id: String.t() | nil,
6 | key: String.t() | nil,
7 | deployment_group_id: String.t() | nil
8 | }
9 |
10 | @primary_key {:id, :string, autogenerate: false}
11 | embedded_schema do
12 | field :key, :string
13 | field :deployment_group_id, :string
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/livebook/teams/authorization_group.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Teams.AuthorizationGroup do
2 | use Ecto.Schema
3 |
4 | @type t :: %__MODULE__{
5 | provider_id: String.t() | nil,
6 | group_name: String.t() | nil
7 | }
8 |
9 | @primary_key false
10 | embedded_schema do
11 | field :provider_id, :string
12 | field :group_name, :string
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/livebook/teams/environment_variable.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Teams.EnvironmentVariable do
2 | use Ecto.Schema
3 |
4 | @type t :: %__MODULE__{
5 | name: String.t(),
6 | value: String.t(),
7 | hub_id: String.t(),
8 | deployment_group_id: String.t()
9 | }
10 |
11 | @enforce_keys [:name, :value, :hub_id, :deployment_group_id]
12 |
13 | @primary_key false
14 | embedded_schema do
15 | field :name, :string
16 | field :value, :string
17 | field :hub_id, :string
18 | field :deployment_group_id, :string
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/livebook/utils/supervision_step.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Utils.SupervisionStep do
2 | use GenServer, restart: :temporary
3 |
4 | @doc """
5 | Synchronously runs the given function as part of supervision tree
6 | startup.
7 | """
8 | @spec start_link({atom(), function()}) :: :ignore
9 | def start_link({_id, fun}) do
10 | GenServer.start_link(__MODULE__, fun)
11 | end
12 |
13 | @doc false
14 | def child_spec({id, fun}) do
15 | child_spec = super({id, fun})
16 | update_in(child_spec.id, &{&1, id})
17 | end
18 |
19 | @impl true
20 | def init(fun) do
21 | fun.()
22 | :ignore
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/livebook/zta/basic_auth.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.ZTA.BasicAuth do
2 | @behaviour Livebook.ZTA
3 |
4 | @impl true
5 | def child_spec(opts) do
6 | %{id: __MODULE__, start: {__MODULE__, :start_link, [opts]}}
7 | end
8 |
9 | def start_link(options) do
10 | name = Keyword.fetch!(options, :name)
11 | identity_key = Keyword.fetch!(options, :identity_key)
12 | [username, password] = String.split(identity_key, ":", parts: 2)
13 |
14 | Livebook.ZTA.put(name, {username, password})
15 | :ignore
16 | end
17 |
18 | @impl true
19 | def authenticate(name, conn, _opts) do
20 | {username, password} = Livebook.ZTA.get(name)
21 | conn = Plug.BasicAuth.basic_auth(conn, username: username, password: password)
22 |
23 | if conn.halted do
24 | {conn, nil}
25 | else
26 | {conn, %{}}
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/livebook/zta/pass_through.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.ZTA.PassThrough do
2 | @behaviour Livebook.ZTA
3 |
4 | @impl true
5 | def child_spec(_opts) do
6 | %{id: __MODULE__, start: {Function, :identity, [:ignore]}}
7 | end
8 |
9 | @impl true
10 | def authenticate(_name, conn, _opts) do
11 | {conn, %{}}
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/livebook_app.ex:
--------------------------------------------------------------------------------
1 | if Mix.target() == :app do
2 | defmodule LivebookApp do
3 | use GenServer
4 |
5 | def start_link(arg) do
6 | GenServer.start_link(__MODULE__, arg, name: __MODULE__)
7 | end
8 |
9 | @impl true
10 | def init(_) do
11 | {:ok, pid} = ElixirKit.start()
12 | ref = Process.monitor(pid)
13 |
14 | ElixirKit.publish("url", LivebookWeb.Endpoint.access_url())
15 |
16 | {:ok, %{ref: ref}}
17 | end
18 |
19 | @impl true
20 | def handle_info({:event, "open", url}, state) do
21 | url
22 | |> Livebook.Utils.expand_desktop_url()
23 | |> Livebook.Utils.browser_open()
24 |
25 | {:noreply, state}
26 | end
27 |
28 | @impl true
29 | def handle_info({:DOWN, ref, :process, _, :shutdown}, state) when ref == state.ref do
30 | Livebook.Config.shutdown()
31 | {:noreply, state}
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/livebook_cli/task.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookCLI.Task do
2 | @doc """
3 | Returns a description of the task usage.
4 | """
5 | @callback usage() :: String.t()
6 |
7 | @doc """
8 | Runs the task with the given list of command line arguments.
9 | """
10 | @callback call(args :: list(String.t())) :: :ok
11 | end
12 |
--------------------------------------------------------------------------------
/lib/livebook_web/channels/socket.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.Socket do
2 | use Phoenix.Socket
3 |
4 | channel "js_view", LivebookWeb.JSViewChannel
5 |
6 | @impl true
7 | def connect(_params, socket, info) do
8 | # The session is present only if the CSRF token is valid. We rely
9 | # on CSRF token, because we don't check connection origin as noted
10 | # in LivebookWeb.Endpoint.
11 | if info.session do
12 | {:ok, socket}
13 | else
14 | :error
15 | end
16 | end
17 |
18 | @impl true
19 | def id(_socket), do: nil
20 | end
21 |
--------------------------------------------------------------------------------
/lib/livebook_web/components/file_system_components.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.FileSystemComponents do
2 | use LivebookWeb, :html
3 |
4 | alias Livebook.FileSystem
5 |
6 | @doc """
7 | Formats the given file system into a short name.
8 | """
9 | def file_system_name(file_system_module)
10 |
11 | def file_system_name(FileSystem.Local), do: "Disk"
12 | def file_system_name(FileSystem.S3), do: "S3"
13 |
14 | @doc """
15 | Formats the given file system into a descriptive label.
16 | """
17 | def file_system_label(file_system)
18 |
19 | def file_system_label(%FileSystem.Local{}), do: "Disk"
20 | def file_system_label(%FileSystem.S3{} = fs), do: fs.bucket_url
21 |
22 | @doc """
23 | Renders an icon representing the given file system.
24 | """
25 | def file_system_icon(assigns)
26 |
27 | def file_system_icon(%{file_system: %FileSystem.Local{}} = assigns) do
28 | ~H"""
29 | <.remix_icon icon="hard-drive-2-line leading-none" />
30 | """
31 | end
32 |
33 | def file_system_icon(%{file_system: %FileSystem.S3{}} = assigns) do
34 | ~H"""
35 |
36 | S3
37 |
38 | """
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/livebook_web/components/layouts.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.Layouts do
2 | use LivebookWeb, :html
3 |
4 | embed_templates "layouts/*"
5 | end
6 |
--------------------------------------------------------------------------------
/lib/livebook_web/components/layouts/app.html.heex:
--------------------------------------------------------------------------------
1 |
2 | {@inner_content}
3 |
4 |
--------------------------------------------------------------------------------
/lib/livebook_web/components/layouts/live.html.heex:
--------------------------------------------------------------------------------
1 |
2 | <.flash_group flash={@flash} />
3 | {@inner_content}
4 |
5 |
6 | <.confirm_root confirm_state={@confirm_state} />
7 |
--------------------------------------------------------------------------------
/lib/livebook_web/controllers/auth_html.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.AuthHTML do
2 | use LivebookWeb, :html
3 |
4 | embed_templates "auth_html/*"
5 | end
6 |
--------------------------------------------------------------------------------
/lib/livebook_web/controllers/error_json.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.ErrorJSON do
2 | # By default, Phoenix returns the status message from
3 | # the template name. For example, "404.json" becomes
4 | # "Not Found".
5 | def render(template, _assigns) do
6 | %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/livebook_web/controllers/health_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.HealthController do
2 | use LivebookWeb, :controller
3 |
4 | def index(conn, _params) do
5 | conn
6 | |> put_resp_header("access-control-allow-origin", "*")
7 | |> json(%{
8 | "application" => "livebook"
9 | })
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/livebook_web/controllers/user_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.UserController do
2 | use LivebookWeb, :controller
3 |
4 | def logout(conn, _params) do
5 | if get_session(conn, :user_id) do
6 | if Livebook.Config.logout_enabled?() do
7 | do_zta_logout(conn)
8 | else
9 | do_logout(conn)
10 | end
11 | else
12 | redirect(conn, to: ~p"/")
13 | end
14 | end
15 |
16 | defp do_logout(conn) do
17 | conn
18 | |> configure_session(renew: true)
19 | |> clear_session()
20 | |> redirect(to: ~p"/")
21 | end
22 |
23 | defp do_zta_logout(conn) do
24 | {_type, module, _key} = Livebook.Config.identity_provider()
25 | module.logout(LivebookWeb.ZTA, conn)
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/livebook_web/controllers/user_html.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.UserHTML do
2 | use LivebookWeb, :html
3 |
4 | embed_templates "user_html/*"
5 | end
6 |
--------------------------------------------------------------------------------
/lib/livebook_web/errors.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.NotFoundError do
2 | defexception [:message, plug_status: 404]
3 | end
4 |
5 | defmodule LivebookWeb.BadRequestError do
6 | defexception [:message, plug_status: 400]
7 | end
8 |
--------------------------------------------------------------------------------
/lib/livebook_web/live/app_session_live/cell_outputs.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.AppSessionLive.CellOutputsComponent do
2 | use LivebookWeb, :live_component
3 |
4 | @impl true
5 | def mount(socket) do
6 | {:ok, stream(socket, :outputs, [])}
7 | end
8 |
9 | @impl true
10 | def update(assigns, socket) do
11 | socket = assign(socket, assigns)
12 |
13 | stream_items =
14 | for {idx, output} <- Enum.reverse(assigns.cell_view.outputs) do
15 | %{id: Integer.to_string(idx), idx: idx, output: output}
16 | end
17 |
18 | socket = stream(socket, :outputs, stream_items)
19 |
20 | {:ok, socket}
21 | end
22 |
23 | @impl true
24 | def render(assigns) do
25 | ~H"""
26 |
41 | """
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/livebook_web/live/hooks/auth_hook.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.AuthHook do
2 | import Phoenix.LiveView
3 |
4 | use LivebookWeb, :verified_routes
5 |
6 | def on_mount(:default, _params, session, socket) do
7 | uri = get_connect_info(socket, :uri)
8 | socket = attach_hook(socket, :authorization_subscription, :handle_info, &handle_info/2)
9 |
10 | if LivebookWeb.AuthPlug.authorized?(session || %{}, uri.port) do
11 | {:cont, socket}
12 | else
13 | {:halt, redirect(socket, to: ~p"/")}
14 | end
15 | end
16 |
17 | defp handle_info({:server_authorization_updated, %{hub_id: hub_id}}, socket) do
18 | # We already updated the current user, so we just need to force the redirection.
19 | # But, for apps, we redirect directly from the app session
20 | current_user = socket.assigns.current_user
21 |
22 | if current_user.payload do
23 | if current_user.access_type == :full and
24 | Livebook.Hubs.TeamClient.user_full_access?(hub_id, current_user.groups) do
25 | {:halt, socket}
26 | else
27 | {:halt, redirect(socket, to: ~p"/")}
28 | end
29 | else
30 | {:halt, socket}
31 | end
32 | end
33 |
34 | defp handle_info(_message, socket), do: {:cont, socket}
35 | end
36 |
--------------------------------------------------------------------------------
/lib/livebook_web/live/output/image_component.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.Output.ImageComponent do
2 | use LivebookWeb, :live_component
3 |
4 | @impl true
5 | def render(assigns) do
6 | ~H"""
7 |
14 | """
15 | end
16 |
17 | defp data_url(content, mime_type) do
18 | image_base64 = Base.encode64(content)
19 | ["data:", mime_type, ";base64,", image_base64]
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/livebook_web/live/session_live/insert_file_component.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.SessionLive.InsertFileComponent do
2 | use LivebookWeb, :live_component
3 |
4 | @impl true
5 | def render(assigns) do
6 | ~H"""
7 |
8 |
9 | Suggested actions
10 |
11 |
12 | What do you want to do with the file?
13 |
14 |
15 |
20 |
21 | {handler.definition.description}
22 |
23 |
24 |
25 |
26 | """
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/livebook_web/plugs/configured_plug.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.ConfiguredPlug do
2 | # Runs plugs configured for the :livebook application
3 |
4 | @behaviour Plug
5 |
6 | @impl true
7 | def init(opts), do: opts
8 |
9 | @impl true
10 | def call(conn, _opts) do
11 | case Application.fetch_env!(:livebook, :plugs) do
12 | [] -> conn
13 | plugs -> Plug.run(conn, plugs)
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/proto/.formatter.exs:
--------------------------------------------------------------------------------
1 | # Used by "mix format"
2 | [
3 | import_deps: [:protobuf],
4 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
5 | ]
6 |
--------------------------------------------------------------------------------
/proto/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where third-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | livebook_proto-*.tar
24 |
25 | # Temporary files, for example, from tests.
26 | /tmp/
27 |
--------------------------------------------------------------------------------
/proto/README.md:
--------------------------------------------------------------------------------
1 | # Livebook Protobuf
2 |
3 | Livebook uses Protobuf messages for cross-backend communication over WebSocket.
4 |
5 | ## Generate message
6 |
7 | To generate the message modules, you'll need to run:
8 |
9 | ```sh
10 | mix protobuf.generate
11 | ```
12 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/agent.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.Agent do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :org_id, 3, type: :string, json_name: "orgId"
7 | field :deployment_group_id, 4, type: :string, json_name: "deploymentGroupId"
8 | end
9 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/agent_connected.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AgentConnected do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 2, type: :string
5 | field :public_key, 3, type: :string, json_name: "publicKey"
6 | field :deployment_group_id, 4, type: :int32, json_name: "deploymentGroupId"
7 | field :secrets, 5, repeated: true, type: LivebookProto.Secret
8 | field :file_systems, 6, repeated: true, type: LivebookProto.FileSystem, json_name: "fileSystems"
9 |
10 | field :deployment_groups, 7,
11 | repeated: true,
12 | type: LivebookProto.DeploymentGroup,
13 | json_name: "deploymentGroups"
14 |
15 | field :app_deployments, 8,
16 | repeated: true,
17 | type: LivebookProto.AppDeployment,
18 | json_name: "appDeployments"
19 |
20 | field :agents, 9, repeated: true, type: LivebookProto.Agent
21 | field :billing_status, 10, type: LivebookProto.BillingStatus, json_name: "billingStatus"
22 | end
23 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/agent_joined.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AgentJoined do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :agent, 1, type: LivebookProto.Agent
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/agent_key.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AgentKey do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :key, 2, type: :string
6 | field :deployment_group_id, 3, type: :string, json_name: "deploymentGroupId"
7 | end
8 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/agent_left.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AgentLeft do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeployment do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :title, 2, type: :string
6 | field :sha, 3, type: :string
7 | field :revision_id, 4, type: :string, json_name: "revisionId"
8 | field :slug, 5, type: :string
9 | field :deployment_group_id, 6, type: :string, json_name: "deploymentGroupId"
10 | field :deployed_by, 7, type: :string, json_name: "deployedBy"
11 | field :deployed_at, 8, type: :int64, json_name: "deployedAt"
12 | field :multi_session, 9, type: :bool, json_name: "multiSession"
13 | field :access_type, 10, type: :string, json_name: "accessType"
14 | field :version, 11, type: :string
15 |
16 | field :authorization_groups, 12,
17 | repeated: true,
18 | type: LivebookProto.AuthorizationGroup,
19 | json_name: "authorizationGroups"
20 | end
21 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment_started.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeploymentStarted do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :app_deployment, 1, type: LivebookProto.AppDeployment, json_name: "appDeployment"
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment_status.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeploymentStatus do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :deployment_group_id, 2, type: :string, json_name: "deploymentGroupId"
6 | field :version, 3, type: :string
7 | field :status, 4, type: LivebookProto.AppDeploymentStatusType, enum: true
8 | end
9 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment_status_report.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeploymentStatusReport do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :app_deployment_statuses, 1,
5 | repeated: true,
6 | type: LivebookProto.AppDeploymentStatus,
7 | json_name: "appDeploymentStatuses"
8 | end
9 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment_status_type.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeploymentStatusType do
2 | @moduledoc """
3 | We're only using Enum in this case because
4 | the Client is the source of the information,
5 | and the Server will always be up-to-date.
6 |
7 | Otherwise, it shouldn't be used.
8 | """
9 |
10 | use Protobuf, enum: true, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
11 |
12 | field :preparing, 0
13 | field :available, 1
14 | end
15 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment_stopped.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeploymentStopped do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/app_deployment_updated.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AppDeploymentUpdated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :app_deployment, 1, type: LivebookProto.AppDeployment, json_name: "appDeployment"
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/authorization_group.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.AuthorizationGroup do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :provider_id, 1, type: :string, json_name: "providerId"
5 | field :group_name, 2, type: :string, json_name: "groupName"
6 | end
7 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/billing_status.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.BillingStatus do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | oneof :type, 0
5 |
6 | field :disabled, 1, type: :bool
7 | field :trialing, 2, type: LivebookProto.BillingStatusTrialing, oneof: 0
8 |
9 | field :trial_ended, 3,
10 | type: LivebookProto.BillingStatusTrialEnded,
11 | json_name: "trialEnded",
12 | oneof: 0
13 |
14 | field :canceling, 4, type: LivebookProto.BillingStatusCanceling, oneof: 0
15 | field :canceled, 5, type: LivebookProto.BillingStatusCanceled, oneof: 0
16 | end
17 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/billing_status_canceled.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.BillingStatusCanceled do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 | end
4 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/billing_status_canceling.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.BillingStatusCanceling do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :cancel_at, 1, type: :int64, json_name: "cancelAt"
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/billing_status_trial_ended.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.BillingStatusTrialEnded do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt"
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/billing_status_trialing.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.BillingStatusTrialing do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt"
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/deployment_group.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.DeploymentGroup do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :mode, 3, type: :string
7 | field :secrets, 4, repeated: true, type: LivebookProto.DeploymentGroupSecret
8 | field :clustering, 5, type: :string
9 | field :zta_provider, 6, type: :string, json_name: "ztaProvider", deprecated: true
10 | field :zta_key, 7, type: :string, json_name: "ztaKey", deprecated: true
11 | field :agent_keys, 8, repeated: true, type: LivebookProto.AgentKey, json_name: "agentKeys"
12 | field :url, 9, type: :string
13 |
14 | field :environment_variables, 10,
15 | repeated: true,
16 | type: LivebookProto.EnvironmentVariable,
17 | json_name: "environmentVariables"
18 |
19 | field :teams_auth, 11, type: :bool, json_name: "teamsAuth"
20 |
21 | field :authorization_groups, 12,
22 | repeated: true,
23 | type: LivebookProto.AuthorizationGroup,
24 | json_name: "authorizationGroups"
25 |
26 | field :groups_auth, 13, type: :bool, json_name: "groupsAuth"
27 | end
28 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/deployment_group_created.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.DeploymentGroupCreated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :mode, 3, type: :string
7 | field :clustering, 5, type: :string
8 | field :zta_provider, 6, type: :string, json_name: "ztaProvider", deprecated: true
9 | field :zta_key, 7, type: :string, json_name: "ztaKey", deprecated: true
10 | field :agent_keys, 8, repeated: true, type: LivebookProto.AgentKey, json_name: "agentKeys"
11 | field :url, 9, type: :string
12 | field :teams_auth, 10, type: :bool, json_name: "teamsAuth"
13 | end
14 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/deployment_group_deleted.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.DeploymentGroupDeleted do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/deployment_group_secret.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.DeploymentGroupSecret do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | field :value, 2, type: :string
6 | field :deployment_group_id, 3, type: :string, json_name: "deploymentGroupId"
7 | end
8 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/deployment_group_updated.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.DeploymentGroupUpdated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :secrets, 3, repeated: true, type: LivebookProto.DeploymentGroupSecret
7 | field :clustering, 4, type: :string
8 | field :zta_provider, 5, type: :string, json_name: "ztaProvider", deprecated: true
9 | field :zta_key, 6, type: :string, json_name: "ztaKey", deprecated: true
10 | field :agent_keys, 7, repeated: true, type: LivebookProto.AgentKey, json_name: "agentKeys"
11 | field :url, 8, type: :string
12 |
13 | field :environment_variables, 9,
14 | repeated: true,
15 | type: LivebookProto.EnvironmentVariable,
16 | json_name: "environmentVariables"
17 |
18 | field :teams_auth, 10, type: :bool, json_name: "teamsAuth"
19 |
20 | field :authorization_groups, 11,
21 | repeated: true,
22 | type: LivebookProto.AuthorizationGroup,
23 | json_name: "authorizationGroups"
24 |
25 | field :groups_auth, 12, type: :bool, json_name: "groupsAuth"
26 | end
27 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/environment_variable.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.EnvironmentVariable do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | field :value, 2, type: :string
6 | end
7 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/error.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.Error do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :details, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/file_system.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.FileSystem do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :type, 3, type: :string
7 | field :value, 4, type: :string
8 | end
9 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/file_system_created.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.FileSystemCreated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :type, 3, type: :string
7 | field :value, 4, type: :string
8 | end
9 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/file_system_deleted.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.FileSystemDeleted do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/file_system_updated.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.FileSystemUpdated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :name, 2, type: :string
6 | field :type, 3, type: :string
7 | field :value, 4, type: :string
8 | end
9 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/org_status_cancel.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.OrgStatusCancel do
2 | @moduledoc """
3 | the org is or will be cancelled
4 | """
5 |
6 | use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
7 |
8 | field :cancel_at, 1, type: :int64, json_name: "cancelAt"
9 | end
10 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/org_status_trial.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.OrgStatusTrial do
2 | @moduledoc """
3 | the org is on active or expired trial
4 | """
5 |
6 | use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
7 |
8 | field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt"
9 | end
10 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/org_updated.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.OrgUpdated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | field :billing_status, 3, type: LivebookProto.BillingStatus, json_name: "billingStatus"
6 | end
7 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/secret.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.Secret do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | field :value, 2, type: :string
6 | end
7 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/secret_created.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.SecretCreated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | field :value, 2, type: :string
6 | end
7 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/secret_deleted.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.SecretDeleted do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/secret_updated.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.SecretUpdated do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | field :value, 2, type: :string
6 | end
7 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/user_connected.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.UserConnected do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :name, 1, type: :string
5 | field :secrets, 2, repeated: true, type: LivebookProto.Secret
6 | field :file_systems, 3, repeated: true, type: LivebookProto.FileSystem, json_name: "fileSystems"
7 |
8 | field :deployment_groups, 4,
9 | repeated: true,
10 | type: LivebookProto.DeploymentGroup,
11 | json_name: "deploymentGroups"
12 |
13 | field :app_deployments, 5,
14 | repeated: true,
15 | type: LivebookProto.AppDeployment,
16 | json_name: "appDeployments"
17 |
18 | field :agents, 6, repeated: true, type: LivebookProto.Agent
19 | field :billing_status, 7, type: LivebookProto.BillingStatus, json_name: "billingStatus"
20 | end
21 |
--------------------------------------------------------------------------------
/proto/lib/livebook_proto/user_deleted.pb.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.UserDeleted do
2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3
3 |
4 | field :id, 1, type: :string
5 | end
6 |
--------------------------------------------------------------------------------
/proto/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookProto.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :livebook_proto,
7 | version: "0.1.0",
8 | elixir: "~> 1.14",
9 | start_permanent: Mix.env() == :prod,
10 | deps: deps(),
11 | aliases: aliases()
12 | ]
13 | end
14 |
15 | def application do
16 | [extra_applications: [:logger]]
17 | end
18 |
19 | defp deps do
20 | [{:protobuf, "~> 0.12.0"}]
21 | end
22 |
23 | defp aliases do
24 | [
25 | "protobuf.generate": [
26 | "cmd protoc --elixir_out=one_file_per_module=true:lib --elixir_opt=include_docs=true --elixir_opt=gen_struct=true --elixir_opt=package_prefix=livebook_proto messages.proto",
27 | "format lib/livebook_proto/*.pb.ex",
28 | "deps.get",
29 | "compile"
30 | ]
31 | ]
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/proto/mix.lock:
--------------------------------------------------------------------------------
1 | %{
2 | "protobuf": {:hex, :protobuf, "0.12.0", "58c0dfea5f929b96b5aa54ec02b7130688f09d2de5ddc521d696eec2a015b223", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "75fa6cbf262062073dd51be44dd0ab940500e18386a6c4e87d5819a58964dc45"},
3 | }
4 |
--------------------------------------------------------------------------------
/rel/app/env.bat.eex:
--------------------------------------------------------------------------------
1 | if exist "!USERPROFILE!\.livebookdesktop.bat" (
2 | call "!USERPROFILE!\.livebookdesktop.bat"
3 | )
4 |
5 | set RELEASE_MODE=interactive
6 | if not defined RELEASE_DISTRIBUTION set RELEASE_DISTRIBUTION=none
7 |
8 | set vendor_dir=!RELEASE_ROOT!\vendor\livebook-!RELEASE_VSN!
9 | set MIX_ARCHIVES=!vendor_dir!\archives
10 | set MIX_REBAR3=!vendor_dir!\rebar3
11 | if not defined LIVEBOOK_SHUTDOWN_ENABLED set LIVEBOOK_SHUTDOWN_ENABLED=true
12 | if not defined LIVEBOOK_PORT (set LIVEBOOK_PORT=32123)
13 | set PATH=!vendor_dir!\otp\erts-<%= @release.erts_version%>\bin;!vendor_dir!\otp\bin;!vendor_dir!\elixir\bin;!PATH!
14 |
15 | if defined LIVEBOOK_NODE set RELEASE_NODE=!LIVEBOOK_NODE!
16 | if defined LIVEBOOK_COOKIE set RELEASE_COOKIE=!LIVEBOOK_COOKIE!
17 |
18 | if not defined RELEASE_COOKIE (
19 | REM Create random cookie. Removes spaces using : =%. Do not remove this comment.
20 | set RELEASE_COOKIE=cookie-%DATE: =%_%TIME: =%_%RANDOM%
21 | )
22 |
23 | cd !HOMEDRIVE!!HOMEPATH!
24 |
--------------------------------------------------------------------------------
/rel/app/env.sh.eex:
--------------------------------------------------------------------------------
1 | if [ -f "$HOME/.livebookdesktop.sh" ]; then
2 | . "$HOME/.livebookdesktop.sh"
3 | fi
4 |
5 | export RELEASE_MODE="interactive"
6 | if [ -z "${RELEASE_DISTRIBUTION}" ]; then export RELEASE_DISTRIBUTION="none"; fi
7 |
8 | vendor_dir="${RELEASE_ROOT}/vendor/livebook-${RELEASE_VSN}"
9 | export MIX_ARCHIVES="${vendor_dir}/archives"
10 | export MIX_REBAR3="${vendor_dir}/rebar3"
11 | export LIVEBOOK_SHUTDOWN_ENABLED=${LIVEBOOK_SHUTDOWN_ENABLED:-true}
12 | [ -z "$LIVEBOOK_PORT" ] && export LIVEBOOK_PORT=32123
13 | export PATH="${vendor_dir}/otp/erts-<%= @release.erts_version%>/bin:${vendor_dir}/otp/bin:${vendor_dir}/elixir/bin:$PATH"
14 |
15 | if [ ! -z "${LIVEBOOK_NODE}" ]; then export RELEASE_NODE=${LIVEBOOK_NODE}; fi
16 | if [ ! -z "${LIVEBOOK_COOKIE}" ]; then export RELEASE_COOKIE=${LIVEBOOK_COOKIE}; fi
17 |
18 | export RELEASE_COOKIE="${RELEASE_COOKIE:-$(cat /dev/urandom | env LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)}"
19 |
20 | cd $HOME
21 |
--------------------------------------------------------------------------------
/rel/app/macos/.gitignore:
--------------------------------------------------------------------------------
1 | /.build
2 | /Packages
3 | /*.xcodeproj
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/config/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/rel/app/macos/App.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.allow-jit
6 |
7 | com.apple.security.cs.allow-unsigned-executable-memory
8 |
9 | com.apple.security.cs.allow-dyld-environment-variables
10 |
11 | com.apple.security.cs.disable-library-validation
12 |
13 | com.apple.security.device.camera
14 |
15 | com.apple.security.device.audio-input
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128px.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16px.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-1.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-1.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px-1.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64px.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512.png
--------------------------------------------------------------------------------
/rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/rel/app/macos/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Livebook",
7 | platforms: [
8 | .macOS(.v11)
9 | ],
10 | dependencies: [
11 | .package(name: "ElixirKit", path: "../../../elixirkit/elixirkit_swift")
12 | ],
13 | targets: [
14 | .executableTarget(
15 | name: "Livebook",
16 | dependencies: ["ElixirKit"]
17 | )
18 | ]
19 | )
20 |
--------------------------------------------------------------------------------
/rel/app/macos/README.md:
--------------------------------------------------------------------------------
1 | # Livebook for macOS
2 |
3 | Run the app:
4 |
5 | $ ./run.sh
6 |
--------------------------------------------------------------------------------
/rel/app/macos/build_app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | export MIX_TARGET=app
5 | export MIX_ENV=prod
6 | export ELIXIRKIT_APP_NAME=Livebook
7 | export ELIXIRKIT_PROJECT_DIR=$PWD/../../..
8 | export ELIXIRKIT_RELEASE_NAME=app
9 |
10 | . ../../../elixirkit/elixirkit_swift/Scripts/build_macos_app.sh
11 |
--------------------------------------------------------------------------------
/rel/app/macos/build_dmg.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | . `dirname $0`/build_app.sh
5 | . ../../../elixirkit/elixirkit_swift/Scripts/build_macos_dmg.sh
6 |
--------------------------------------------------------------------------------
/rel/app/macos/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | . `dirname $0`/build_app.sh
5 |
6 | # Deregister /Applications/Livebook.app so that the "dev version" will handle .livemd and livebook://
7 | /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
8 | -u /Applications/Livebook.app \
9 | || true
10 |
11 | /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
12 | "$app_dir"
13 |
14 | open -W --stdout `tty` --stderr `tty` "$app_dir"
15 |
--------------------------------------------------------------------------------
/rel/app/vm.args.eex:
--------------------------------------------------------------------------------
1 | # Disable busy waiting so that we don't waste resources
2 | # Limit the maximal number of ports for the same reason
3 | # Set the custom EPMD module
4 | +sbwt none +sbwtdcpu none +sbwtdio none +Q 65536 -epmd_module Elixir.Livebook.EPMD
5 |
--------------------------------------------------------------------------------
/rel/app/windows/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /obj
3 |
--------------------------------------------------------------------------------
/rel/app/windows/App.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | PerMonitorV2
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/rel/app/windows/Livebook.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | WinExe
6 | bin/Livebook-$(Configuration)
7 | bin/Livebook-$(Configuration)
8 | net4.6-windows
9 | 10.0
10 | win-x64
11 |
12 | false
13 | true
14 | true
15 | Resources/AppIcon.ico
16 | App.manifest
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/rel/app/windows/README.md:
--------------------------------------------------------------------------------
1 | # Livebook for Windows
2 |
3 | Run the app:
4 |
5 | $ ./run.sh
6 |
--------------------------------------------------------------------------------
/rel/app/windows/Resources/AppIcon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/windows/Resources/AppIcon.ico
--------------------------------------------------------------------------------
/rel/app/windows/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | . `dirname $0`/env.sh
5 |
6 | rm -rf $target_dir
7 | dotnet build Livebook.csproj $build_args
8 |
9 | (
10 | cd ../../..
11 | mix release app --overwrite --path=${target_dir}/rel
12 | )
13 |
--------------------------------------------------------------------------------
/rel/app/windows/build_installer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | . `dirname $0`/env.sh
5 |
6 | rm -rf $target_dir
7 | dotnet publish Livebook.csproj $build_args
8 |
9 | (
10 | cd ../../..
11 | mix release app --overwrite --path=${target_dir}/rel
12 | )
13 |
14 | vc_redist_path="bin/vc_redist.x64.exe"
15 | if [ ! -f $vc_redist_path ]; then
16 | url="https://aka.ms/vs/17/release/vc_redist.x64.exe"
17 | echo "downloading $url"
18 | curl -L --fail --output $vc_redist_path $url
19 | fi
20 |
21 | makensis \
22 | //DERTS_VERSION=`elixir -e "IO.puts :erlang.system_info(:version)"` \
23 | //DLIVEBOOK_VERSION=`elixir -e "Mix.start() ; Mix.Project.in_project(:livebook, \"../../..\", fn _ -> IO.puts Mix.Project.config()[:version] end)"` \
24 | Installer.nsi
25 |
--------------------------------------------------------------------------------
/rel/app/windows/env.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | export MIX_ENV=prod
5 | export MIX_TARGET=app
6 | export ELIXIRKIT_APP_NAME=Livebook
7 | export ELIXIRKIT_PROJECT_DIR=$PWD/../../..
8 | export ELIXIRKIT_RELEASE_NAME=app
9 |
10 | configuration=${ELIXIRKIT_CONFIGURATION:-Debug}
11 | target_dir="$PWD/bin/${ELIXIRKIT_APP_NAME}-$configuration"
12 | build_args="--configuration $configuration"
13 |
--------------------------------------------------------------------------------
/rel/app/windows/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euo pipefail
3 |
4 | . `dirname $0`/build.sh
5 |
6 | root="HKEY_CURRENT_USER\\Software\\Classes"
7 |
8 | exe="`cmd //c cd`\\bin\\Livebook-$configuration\\Livebook.exe"
9 |
10 | echo
11 | echo "!!!!!!"
12 | echo "Setting registry to use $exe"
13 | echo "!!!!!!"
14 | echo
15 |
16 | # .livemd
17 | reg add "$root\\.livemd" //d "Livebook.LiveMarkdown" //f
18 | reg add "$root\\Livebook.LiveMarkdown\\DefaultIcon" //d "$exe,1" //f
19 | reg add "$root\\Livebook.LiveMarkdown\\shell\\open\\command" //d "\"$exe\" \"open:%1\"" //f
20 |
21 | # livebook://
22 | reg add "$root\\livebook" //d "URL:Livebook Protocol" //f
23 | reg add "$root\\livebook" //v "URL Protocol" //f
24 | reg add "$root\\livebook\\shell\\open\\command" //d "\"$exe\" \"open:%1\"" //f
25 |
26 | dotnet run --no-build
27 |
--------------------------------------------------------------------------------
/rel/server/env.bat.eex:
--------------------------------------------------------------------------------
1 | if "!RELEASE_COMMAND!"=="rpc" goto remote
2 | if "!RELEASE_COMMAND!"=="remote" goto remote
3 | goto server
4 |
5 | :server
6 | if exist "!RELEASE_ROOT!\user\env.bat" (
7 | call "!RELEASE_ROOT!\user\env.bat"
8 | )
9 |
10 | set RELEASE_MODE=interactive
11 | set RELEASE_DISTRIBUTION=none
12 |
13 | if not defined RELEASE_COOKIE (
14 | REM Create random cookie. Removes spaces using : =%. Do not remove this comment.
15 | set RELEASE_COOKIE=cookie-%DATE: =%_%TIME: =%_%RANDOM%
16 | )
17 |
18 | cd !HOMEDRIVE!!HOMEPATH!
19 | goto end
20 |
21 | :remote
22 | set RELEASE_DISTRIBUTION=name
23 | if defined LIVEBOOK_NODE set RELEASE_NODE=!LIVEBOOK_NODE!
24 | if defined LIVEBOOK_COOKIE set RELEASE_COOKIE=!LIVEBOOK_COOKIE!
25 | goto end
26 |
27 | :end
28 |
--------------------------------------------------------------------------------
/rel/server/overlays/bin/server:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | cd -P -- "$(dirname -- "$0")"
4 |
5 | # Livebook does not start EPMD automatically, but we want to start it
6 | # here, becasue we need it for clustering
7 | epmd -daemon
8 |
9 | if [ -n "${FLAME_PARENT}" ]; then
10 | exec elixir ./start_flame.exs
11 | elif [ -n "${LIVEBOOK_RUNTIME}" ]; then
12 | # Note: keep the flags in sync with the standalone runtime
13 | erl_flags="+sbwt none +sbwtdcpu none +sbwtdio none +sssdio 128 -elixir ansi_enabled true -noinput"
14 |
15 | # We add Livebook modules to the path, so that they are loaded from
16 | # from disk, rather than having module binaries sent from the parent
17 | # node. This cuts down the initialization time.
18 | livebook_beams="$(dirname -- "$(pwd)")/lib/livebook_runtime_ebin"
19 | erl_flags="$erl_flags -pa $livebook_beams"
20 |
21 | exec elixir --erl "$erl_flags" ./start_runtime.exs
22 | else
23 | exec ./livebook start
24 | fi
25 |
--------------------------------------------------------------------------------
/rel/server/overlays/bin/start_flame.exs:
--------------------------------------------------------------------------------
1 | File.cd!(System.user_home!())
2 |
3 | flame_parent = System.fetch_env!("FLAME_PARENT") |> Base.decode64!() |> :erlang.binary_to_term()
4 |
5 | %{
6 | pid: parent_pid,
7 | flame_vsn: flame_parent_vsn,
8 | backend: _backend,
9 | backend_app: backend_app,
10 | backend_vsn: backend_vsn,
11 | node_base: node_base,
12 | host_env: host_env
13 | } = flame_parent
14 |
15 | flame_node_name = :"#{node_base}@#{System.fetch_env!(host_env)}"
16 | flame_node_cookie = String.to_atom(System.fetch_env!("LIVEBOOK_COOKIE"))
17 |
18 | flame_dep =
19 | if git_ref = System.get_env("FLAME_GIT_REF") do
20 | {:flame, github: "phoenixframework/flame", ref: git_ref}
21 | else
22 | {:flame, flame_parent_vsn}
23 | end
24 |
25 | flame_backend_deps =
26 | case backend_app do
27 | :flame -> []
28 | _ -> [{backend_app, backend_vsn}]
29 | end
30 |
31 | {:ok, _} = :net_kernel.start(flame_node_name, %{name_domain: :longnames})
32 | Node.set_cookie(flame_node_cookie)
33 |
34 | Mix.install([flame_dep | flame_backend_deps], consolidate_protocols: false)
35 |
36 | IO.puts(
37 | "[Livebook] starting #{inspect(flame_node_name)} in FLAME mode with parent: #{inspect(parent_pid)}, backend: #{inspect(backend_app)}"
38 | )
39 |
40 | System.no_halt(true)
41 |
--------------------------------------------------------------------------------
/rel/server/overlays/bin/warmup_apps:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd -P -- "$(dirname -- "$0")"
4 | exec ./livebook eval Livebook.Release.warmup_apps
5 |
--------------------------------------------------------------------------------
/rel/server/overlays/bin/warmup_apps.bat:
--------------------------------------------------------------------------------
1 | call "%~dp0\livebook" eval Livebook.Release.warmup_apps
2 |
--------------------------------------------------------------------------------
/rel/server/overlays/user/extensions/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/server/overlays/user/extensions/.gitkeep
--------------------------------------------------------------------------------
/rel/server/remote.vm.args.eex:
--------------------------------------------------------------------------------
1 | # Disable busy waiting so that we don't waste resources
2 | # Limit the maximal number of ports for the same reason
3 | +sbwt none +sbwtdcpu none +sbwtdio none +Q 65536
4 |
5 | # Set custom EPMD module and port
6 | -epmd_module Elixir.Livebook.EPMD -erl_epmd_port 13825
7 |
8 | # Disable listening and does not start epmd (due to RELEASE_DISTRIBUTION=name)
9 | -dist_listen false -start_epmd false
10 |
--------------------------------------------------------------------------------
/rel/server/vm.args.eex:
--------------------------------------------------------------------------------
1 | # Disable busy waiting so that we don't waste resources
2 | # Limit the maximal number of ports for the same reason
3 | +sbwt none +sbwtdcpu none +sbwtdio none +Q 65536
4 |
5 | # Set custom EPMD module and port
6 | #
7 | # Note we don't use -erl_epmd_port here on purpose,
8 | # because that also assumes all nodes we connect to
9 | # run on erl_epmd_port and Livebook's attached node
10 | # needs to be able to connect regurlarly.
11 | -epmd_module Elixir.Livebook.EPMD -kernel inet_dist_listen_min 13825
12 |
--------------------------------------------------------------------------------
/static/assets/KaTeX_AMS-Regular-CYEKBG2K.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_AMS-Regular-CYEKBG2K.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_AMS-Regular-JKX5W2C4.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_AMS-Regular-JKX5W2C4.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_AMS-Regular-U6PRYMIZ.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_AMS-Regular-U6PRYMIZ.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Caligraphic-Regular-3LKEU76G.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Regular-3LKEU76G.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Fraktur-Bold-T4SWXBMT.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Bold-T4SWXBMT.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Fraktur-Bold-WGHVTYOR.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Bold-WGHVTYOR.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Fraktur-Regular-5U4OPH2X.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Regular-5U4OPH2X.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Fraktur-Regular-PQMHCIK6.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Regular-PQMHCIK6.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Bold-2GA4IZIN.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Bold-2GA4IZIN.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Bold-W5FBVCZM.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Bold-W5FBVCZM.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Bold-YP5VVQRP.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Bold-YP5VVQRP.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-BoldItalic-4P4C7HJH.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-BoldItalic-4P4C7HJH.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-BoldItalic-N4V3DX7S.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-BoldItalic-N4V3DX7S.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Italic-I43T2HSR.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Italic-I43T2HSR.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Italic-RELBIK7M.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Italic-RELBIK7M.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Italic-SASNQFN2.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Italic-SASNQFN2.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Regular-ARRPAO67.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Regular-ARRPAO67.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Regular-P5I74A2A.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Regular-P5I74A2A.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Main-Regular-W74P5G27.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Regular-W74P5G27.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Math-BoldItalic-6EBV3DK5.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-BoldItalic-6EBV3DK5.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Math-BoldItalic-K4WTGH3J.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-BoldItalic-K4WTGH3J.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Math-BoldItalic-VB447A4D.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-BoldItalic-VB447A4D.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Math-Italic-6KGCHLFN.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-Italic-6KGCHLFN.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Math-Italic-KKK3USB2.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-Italic-KKK3USB2.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Math-Italic-SON4MRCA.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-Italic-SON4MRCA.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Bold-RRNVJFFW.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Bold-RRNVJFFW.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Bold-STQ6RXC7.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Bold-STQ6RXC7.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Bold-X5M5EMOD.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Bold-X5M5EMOD.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Italic-HMPFTM52.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Italic-HMPFTM52.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Italic-PSN4QKYX.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Italic-PSN4QKYX.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Italic-WTBAZBGY.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Italic-WTBAZBGY.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Regular-2TL3USAE.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Regular-2TL3USAE.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Regular-OQCII6EP.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Regular-OQCII6EP.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_SansSerif-Regular-XIQ62X4E.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Regular-XIQ62X4E.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Script-Regular-72OLXYNA.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Script-Regular-72OLXYNA.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Script-Regular-A5IFOEBS.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Script-Regular-A5IFOEBS.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Script-Regular-APUWIHLP.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Script-Regular-APUWIHLP.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size1-Regular-4HRHTS65.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size1-Regular-4HRHTS65.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size1-Regular-5LRUTBFT.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size1-Regular-5LRUTBFT.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size1-Regular-7K6AASVL.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size1-Regular-7K6AASVL.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size2-Regular-222HN3GT.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size2-Regular-222HN3GT.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size2-Regular-K5ZHAIS6.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size2-Regular-K5ZHAIS6.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size2-Regular-LELKET5D.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size2-Regular-LELKET5D.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size3-Regular-TLFPAHDE.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size3-Regular-TLFPAHDE.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size3-Regular-UFCO6WCA.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size3-Regular-UFCO6WCA.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size3-Regular-WQRQ47UD.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size3-Regular-WQRQ47UD.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size4-Regular-7PGNVPQK.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size4-Regular-7PGNVPQK.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size4-Regular-CDMV7U5C.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size4-Regular-CDMV7U5C.woff2
--------------------------------------------------------------------------------
/static/assets/KaTeX_Size4-Regular-PKMWZHNC.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size4-Regular-PKMWZHNC.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf
--------------------------------------------------------------------------------
/static/assets/KaTeX_Typewriter-Regular-MJMFSK64.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Typewriter-Regular-MJMFSK64.woff
--------------------------------------------------------------------------------
/static/assets/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2
--------------------------------------------------------------------------------
/static/assets/architecture-I3QFYML2-6IR3LVRE.js:
--------------------------------------------------------------------------------
1 | import{a as e,b as r}from"./chunk-WNVR7S66.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{e as ArchitectureModule,r as createArchitectureServices};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-5JLOMFCN.js:
--------------------------------------------------------------------------------
1 | import{b as u,c as y,d as f,e as h}from"./chunk-IDH2HD35.js";import{b as g,e as m,h as d}from"./chunk-FIA6NTGC.js";import{c as l}from"./chunk-47P5NBBB.js";import{D as n,N as s,h as o,j as a}from"./chunk-KRX7QNR4.js";var p={common:s,getConfig:n,insertCluster:m,insertEdge:f,insertEdgeLabel:u,insertMarkers:h,insertNode:d,interpolateToCurve:l,labelHelper:g,log:a,positionEdgeLabel:y},t={},L=o(r=>{for(let e of r)t[e.name]=e},"registerLayoutLoaders"),w=o(()=>{L([{name:"dagre",loader:o(async()=>await import("./dagre-4EVJKHTY-TI3ZPXZV.js"),"loader")}])},"registerDefaultLayoutLoaders");w();var R=o(async(r,e)=>{if(!(r.layoutAlgorithm in t))throw new Error(`Unknown layout algorithm: ${r.layoutAlgorithm}`);let i=t[r.layoutAlgorithm];return(await i.loader()).render(r,e,p,{algorithm:i.algorithm})},"render"),_=o((r="",{fallback:e="dagre"}={})=>{if(r in t)return r;if(e in t)return a.warn(`Layout algorithm ${r} is not registered. Using ${e} as fallback.`),e;throw new Error(`Both layout algorithms ${r} and ${e} are not registered.`)},"getRegisteredLayoutAlgorithm");export{L as a,R as b,_ as c};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-5XJO6U7A.js:
--------------------------------------------------------------------------------
1 | import{Z as m,h as c,ia as e}from"./chunk-KRX7QNR4.js";var v=c(t=>{var n,s;let{securityLevel:l}=m(),o=e("body");if(l==="sandbox"){let r=(s=(n=e(`#i${t}`).node())==null?void 0:n.contentDocument)!=null?s:document;o=e(r.body)}return o.select(`#${t}`)},"selectSvgElement");export{v as a};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-BQJTSJOB.js:
--------------------------------------------------------------------------------
1 | import{O as x,h as n,ia as a,j as h}from"./chunk-KRX7QNR4.js";var b=n((t,e)=>{let o;return e==="sandbox"&&(o=a("#i"+t)),(e==="sandbox"?a(o.nodes()[0].contentDocument.body):a("body")).select(`[id="${t}"]`)},"getDiagramElement"),B=n((t,e,o,i)=>{t.attr("class",o);let{width:r,height:s,x:m,y:d}=g(t,e);x(t,s,r,i);let c=w(m,d,r,s,e);t.attr("viewBox",c),h.debug(`viewBox configured: ${c} with padding: ${e}`)},"setupViewPortForSVG"),g=n((t,e)=>{var i;let o=((i=t.node())==null?void 0:i.getBBox())||{width:0,height:0,x:0,y:0};return{width:o.width+e*2,height:o.height+e*2,x:o.x,y:o.y}},"calculateDimensionsWithPadding"),w=n((t,e,o,i,r)=>`${t-r} ${e-r} ${o} ${i}`,"createViewBox");export{b as a,B as b};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-E4RBDSW5.js:
--------------------------------------------------------------------------------
1 | import{f as a}from"./chunk-SA2EGKJT.js";var i={},n={info:a(async()=>{let{createInfoServices:r}=await import("./info-46DW6VJ7-VCNA5FQQ.js"),e=r().Info.parser.LangiumParser;i.info=e},"info"),packet:a(async()=>{let{createPacketServices:r}=await import("./packet-W2GHVCYJ-FC4ERNLH.js"),e=r().Packet.parser.LangiumParser;i.packet=e},"packet"),pie:a(async()=>{let{createPieServices:r}=await import("./pie-BEWT4RHE-5KCD7UAH.js"),e=r().Pie.parser.LangiumParser;i.pie=e},"pie"),architecture:a(async()=>{let{createArchitectureServices:r}=await import("./architecture-I3QFYML2-6IR3LVRE.js"),e=r().Architecture.parser.LangiumParser;i.architecture=e},"architecture"),gitGraph:a(async()=>{let{createGitGraphServices:r}=await import("./gitGraph-YCYPL57B-J4JOQTHP.js"),e=r().GitGraph.parser.LangiumParser;i.gitGraph=e},"gitGraph")};async function p(r,e){let s=n[r];if(!s)throw new Error(`Unknown diagram type: ${r}`);i[r]||await s();let t=i[r].parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new m(t);return t.value}a(p,"parse");var c,m=(c=class extends Error{constructor(e){let s=e.lexerErrors.map(t=>t.message).join(`
2 | `),o=e.parserErrors.map(t=>t.message).join(`
3 | `);super(`Parsing failed: ${s} ${o}`),this.result=e}},a(c,"MermaidParseError"),c);export{p as a};
4 |
--------------------------------------------------------------------------------
/static/assets/chunk-HTMW6R2V.js:
--------------------------------------------------------------------------------
1 | import{h as i}from"./chunk-KRX7QNR4.js";var t,e=(t=class{constructor(r){this.init=r,this.records=this.init()}reset(){this.records=this.init()}},i(t,"ImperativeState"),t);export{e as a};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-MF5TUIBL.js:
--------------------------------------------------------------------------------
1 | import{h as l}from"./chunk-KRX7QNR4.js";function m(e,c){var i,t,o;e.accDescr&&((i=c.setAccDescription)==null||i.call(c,e.accDescr)),e.accTitle&&((t=c.setAccTitle)==null||t.call(c,e.accTitle)),e.title&&((o=c.setDiagramTitle)==null||o.call(c,e.title))}l(m,"populateCommonDb");export{m as a};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-NKCQAART.js:
--------------------------------------------------------------------------------
1 | import{a as n,b as s,c as o,d as u,e as C,f as e,g as d,j as c,m as l,o as m}from"./chunk-SA2EGKJT.js";var r,P=(r=class extends m{constructor(){super(["pie","showData"])}},e(r,"PieTokenBuilder"),r),t,p=(t=class extends l{runCustomConverter(i,a,f){if(i.name==="PIE_SECTION_LABEL")return a.replace(/"/g,"").trim()}},e(t,"PieValueConverter"),t),M={parser:{TokenBuilder:e(()=>new P,"TokenBuilder"),ValueConverter:e(()=>new p,"ValueConverter")}};function S(v=u){let i=o(s(v),d),a=o(n({shared:i}),c,M);return i.ServiceRegistry.register(a),{shared:i,Pie:a}}e(S,"createPieServices");export{M as a,S as b};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-SISR4MA5.js:
--------------------------------------------------------------------------------
1 | import{B as d,D as v,G as P,a as e,c as u,f as i,g as a,i as m,t as w,v as f,y as b,z as j}from"./chunk-24JW6VB3.js";var A=w(Object.keys,Object),S=A;var C=Object.prototype,V=C.hasOwnProperty;function D(r){if(!f(r))return S(r);var t=[];for(var o in Object(r))V.call(r,o)&&o!="constructor"&&t.push(o);return t}var T=D;var K=a(e,"DataView"),n=K;var N=a(e,"Promise"),s=N;var W=a(e,"Set"),c=W;var B=a(e,"WeakMap"),g=B;var O="[object Map]",z="[object Object]",M="[object Promise]",h="[object Set]",x="[object WeakMap]",k="[object DataView]",E=i(n),G=i(m),L=i(s),q=i(c),F=i(g),p=u;(n&&p(new n(new ArrayBuffer(1)))!=k||m&&p(new m)!=O||s&&p(s.resolve())!=M||c&&p(new c)!=h||g&&p(new g)!=x)&&(p=function(r){var t=u(r),o=t==z?r.constructor:void 0,y=o?i(o):"";if(y)switch(y){case E:return k;case G:return O;case L:return M;case q:return h;case F:return x}return t});var l=p;var H="[object Map]",I="[object Set]",J=Object.prototype,Q=J.hasOwnProperty;function R(r){if(r==null)return!0;if(d(r)&&(j(r)||typeof r=="string"||typeof r.splice=="function"||v(r)||P(r)||b(r)))return!r.length;var t=l(r);if(t==H||t==I)return!r.size;if(f(r))return!T(r).length;for(var o in r)if(Q.call(r,o))return!1;return!0}var kr=R;export{T as a,c as b,l as c,kr as d};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-TZMIYTTY.js:
--------------------------------------------------------------------------------
1 | import{a as c,b as n,c as a,d as i,e as m,f as r,g as u,i as d,n as l,o as s}from"./chunk-SA2EGKJT.js";var e,v=(e=class extends s{constructor(){super(["packet-beta"])}},r(e,"PacketTokenBuilder"),e),p={parser:{TokenBuilder:r(()=>new v,"TokenBuilder"),ValueConverter:r(()=>new l,"ValueConverter")}};function M(k=i){let t=a(n(k),u),o=a(c({shared:t}),d,p);return t.ServiceRegistry.register(o),{shared:t,Packet:o}}r(M,"createPacketServices");export{p as a,M as b};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-UY4K4Z6W.js:
--------------------------------------------------------------------------------
1 | import{a,b as i,c as n,d as u,e as m,f as r,g as d,h as s,n as l,o as c}from"./chunk-SA2EGKJT.js";var e,v=(e=class extends c{constructor(){super(["info","showInfo"])}},r(e,"InfoTokenBuilder"),e),I={parser:{TokenBuilder:r(()=>new v,"TokenBuilder"),ValueConverter:r(()=>new l,"ValueConverter")}};function M(f=u){let o=n(i(f),d),t=n(a({shared:o}),s,I);return o.ServiceRegistry.register(t),{shared:o,Info:t}}r(M,"createInfoServices");export{I as a,M as b};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-VW6A6SAT.js:
--------------------------------------------------------------------------------
1 | import{h as g}from"./chunk-KRX7QNR4.js";var b=g(({flowchart:i})=>{var t,p,r,s;let e=(p=(t=i==null?void 0:i.subGraphTitleMargin)==null?void 0:t.top)!=null?p:0,n=(s=(r=i==null?void 0:i.subGraphTitleMargin)==null?void 0:r.bottom)!=null?s:0,T=e+n;return{subGraphTitleTopMargin:e,subGraphTitleBottomMargin:n,subGraphTitleTotalMargin:T}},"getSubGraphTitleMargins");export{b as a};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-W7LTUAGZ.js:
--------------------------------------------------------------------------------
1 | var r="11.4.1";export{r as a};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-WNVR7S66.js:
--------------------------------------------------------------------------------
1 | import{a as u,b as n,c as i,d as o,e as A,f as t,g as s,k as l,m as d,o as m}from"./chunk-SA2EGKJT.js";var c,C=(c=class extends m{constructor(){super(["architecture"])}},t(c,"ArchitectureTokenBuilder"),c),a,v=(a=class extends d{runCustomConverter(e,r,M){if(e.name==="ARCH_ICON")return r.replace(/[()]/g,"").trim();if(e.name==="ARCH_TEXT_ICON")return r.replace(/["()]/g,"");if(e.name==="ARCH_TITLE")return r.replace(/[[\]]/g,"").trim()}},t(a,"ArchitectureValueConverter"),a),T={parser:{TokenBuilder:t(()=>new C,"TokenBuilder"),ValueConverter:t(()=>new v,"ValueConverter")}};function f(h=o){let e=i(n(h),s),r=i(u({shared:e}),l,T);return e.ServiceRegistry.register(r),{shared:e,Architecture:r}}t(f,"createArchitectureServices");export{T as a,f as b};
2 |
--------------------------------------------------------------------------------
/static/assets/chunk-ZP5QXAVO.js:
--------------------------------------------------------------------------------
1 | import{a as o,b as n,c as a,d as u,e as p,f as r,g as d,l,n as s,o as G}from"./chunk-SA2EGKJT.js";var e,h=(e=class extends G{constructor(){super(["gitGraph"])}},r(e,"GitGraphTokenBuilder"),e),m={parser:{TokenBuilder:r(()=>new h,"TokenBuilder"),ValueConverter:r(()=>new s,"ValueConverter")}};function v(c=u){let t=a(n(c),d),i=a(o({shared:t}),l,m);return t.ServiceRegistry.register(i),{shared:t,GitGraph:i}}r(v,"createGitGraphServices");export{m as a,v as b};
2 |
--------------------------------------------------------------------------------
/static/assets/classDiagram-LNE6IOMH-QE5QBIDP.js:
--------------------------------------------------------------------------------
1 | import{a as e,b as a,c as i,d as s}from"./chunk-IJWB56EA.js";import"./chunk-BQJTSJOB.js";import"./chunk-5JLOMFCN.js";import"./chunk-IDH2HD35.js";import"./chunk-FIA6NTGC.js";import"./chunk-JVEBZTDG.js";import"./chunk-VW6A6SAT.js";import"./chunk-NCYNIMJ4.js";import"./chunk-ZPWCXWQI.js";import"./chunk-47P5NBBB.js";import"./chunk-XHGORZV2.js";import{h as t}from"./chunk-KRX7QNR4.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var g={parser:e,db:a,renderer:s,styles:i,init:t(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute,a.clear()},"init")};export{g as diagram};
2 |
--------------------------------------------------------------------------------
/static/assets/classDiagram-v2-MQ7JQ4JX-6A4BS74I.js:
--------------------------------------------------------------------------------
1 | import{a as e,b as a,c as i,d as s}from"./chunk-IJWB56EA.js";import"./chunk-BQJTSJOB.js";import"./chunk-5JLOMFCN.js";import"./chunk-IDH2HD35.js";import"./chunk-FIA6NTGC.js";import"./chunk-JVEBZTDG.js";import"./chunk-VW6A6SAT.js";import"./chunk-NCYNIMJ4.js";import"./chunk-ZPWCXWQI.js";import"./chunk-47P5NBBB.js";import"./chunk-XHGORZV2.js";import{h as t}from"./chunk-KRX7QNR4.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var g={parser:e,db:a,renderer:s,styles:i,init:t(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute,a.clear()},"init")};export{g as diagram};
2 |
--------------------------------------------------------------------------------
/static/assets/gitGraph-YCYPL57B-J4JOQTHP.js:
--------------------------------------------------------------------------------
1 | import{a as r,b as e}from"./chunk-ZP5QXAVO.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{r as GitGraphModule,e as createGitGraphServices};
2 |
--------------------------------------------------------------------------------
/static/assets/info-46DW6VJ7-VCNA5FQQ.js:
--------------------------------------------------------------------------------
1 | import{a as o,b as e}from"./chunk-UY4K4Z6W.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{o as InfoModule,e as createInfoServices};
2 |
--------------------------------------------------------------------------------
/static/assets/infoDiagram-A4XQUW5V-2UH4OEMN.js:
--------------------------------------------------------------------------------
1 | import{a as s}from"./chunk-W7LTUAGZ.js";import{a as i}from"./chunk-5XJO6U7A.js";import{a as g}from"./chunk-E4RBDSW5.js";import"./chunk-NKCQAART.js";import"./chunk-WNVR7S66.js";import"./chunk-ZP5QXAVO.js";import{O as n,h as r,j as a}from"./chunk-KRX7QNR4.js";import"./chunk-UY4K4Z6W.js";import"./chunk-TZMIYTTY.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var v={parse:r(async e=>{let t=await g("info",e);a.debug(t)},"parse")},d={version:s},m=r(()=>d.version,"getVersion"),c={getVersion:m},f=r((e,t,p)=>{a.debug(`rendering info diagram
2 | `+e);let o=i(t);n(o,100,400,!0),o.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${p}`)},"draw"),l={draw:f},y={parser:v,db:c,renderer:l};export{y as diagram};
3 |
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-400-normal-JTDOFDEL.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-400-normal-JTDOFDEL.woff
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-400-normal-KR3WP37A.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-400-normal-KR3WP37A.woff2
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-500-normal-L3ERAO2A.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-500-normal-L3ERAO2A.woff2
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-500-normal-YGY63224.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-500-normal-YGY63224.woff
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-600-normal-RRU6DOYW.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-600-normal-RRU6DOYW.woff2
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-600-normal-YAIFIQFF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-600-normal-YAIFIQFF.woff
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-ext-400-normal-ERANL6CF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-400-normal-ERANL6CF.woff2
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-ext-400-normal-NYJ3VVCY.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-400-normal-NYJ3VVCY.woff
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-ext-500-normal-3D2TL7BV.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-500-normal-3D2TL7BV.woff2
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-ext-500-normal-MVWVUDWY.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-500-normal-MVWVUDWY.woff
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-ext-600-normal-3WK3AIAE.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-600-normal-3WK3AIAE.woff2
--------------------------------------------------------------------------------
/static/assets/inter-cyrillic-ext-600-normal-ML7GDJ53.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-600-normal-ML7GDJ53.woff
--------------------------------------------------------------------------------
/static/assets/inter-greek-400-normal-22W3W6EP.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-400-normal-22W3W6EP.woff
--------------------------------------------------------------------------------
/static/assets/inter-greek-400-normal-K452ZUNI.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-400-normal-K452ZUNI.woff2
--------------------------------------------------------------------------------
/static/assets/inter-greek-500-normal-SR6UVMZM.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-500-normal-SR6UVMZM.woff2
--------------------------------------------------------------------------------
/static/assets/inter-greek-500-normal-V5MWXBXZ.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-500-normal-V5MWXBXZ.woff
--------------------------------------------------------------------------------
/static/assets/inter-greek-600-normal-52AI77RO.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-600-normal-52AI77RO.woff2
--------------------------------------------------------------------------------
/static/assets/inter-greek-600-normal-MHDVNT3B.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-600-normal-MHDVNT3B.woff
--------------------------------------------------------------------------------
/static/assets/inter-greek-ext-400-normal-6GLOS2AC.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-400-normal-6GLOS2AC.woff
--------------------------------------------------------------------------------
/static/assets/inter-greek-ext-400-normal-7CMSSAC3.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-400-normal-7CMSSAC3.woff2
--------------------------------------------------------------------------------
/static/assets/inter-greek-ext-500-normal-GTVYGHNV.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-500-normal-GTVYGHNV.woff
--------------------------------------------------------------------------------
/static/assets/inter-greek-ext-500-normal-L7SAHJMO.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-500-normal-L7SAHJMO.woff2
--------------------------------------------------------------------------------
/static/assets/inter-greek-ext-600-normal-2BPZID6V.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-600-normal-2BPZID6V.woff2
--------------------------------------------------------------------------------
/static/assets/inter-greek-ext-600-normal-URVVZOUP.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-600-normal-URVVZOUP.woff
--------------------------------------------------------------------------------
/static/assets/inter-latin-400-normal-GYFJJKJF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-400-normal-GYFJJKJF.woff2
--------------------------------------------------------------------------------
/static/assets/inter-latin-400-normal-HCV22YHT.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-400-normal-HCV22YHT.woff
--------------------------------------------------------------------------------
/static/assets/inter-latin-500-normal-5GHDSDAY.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-500-normal-5GHDSDAY.woff2
--------------------------------------------------------------------------------
/static/assets/inter-latin-500-normal-PGDKKSLY.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-500-normal-PGDKKSLY.woff
--------------------------------------------------------------------------------
/static/assets/inter-latin-600-normal-56WZKGNJ.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-600-normal-56WZKGNJ.woff2
--------------------------------------------------------------------------------
/static/assets/inter-latin-600-normal-RLL3LKKR.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-600-normal-RLL3LKKR.woff
--------------------------------------------------------------------------------
/static/assets/inter-latin-ext-400-normal-2H3KLIXK.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-400-normal-2H3KLIXK.woff
--------------------------------------------------------------------------------
/static/assets/inter-latin-ext-400-normal-OMTSHYQS.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-400-normal-OMTSHYQS.woff2
--------------------------------------------------------------------------------
/static/assets/inter-latin-ext-500-normal-R6CV3WNH.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-500-normal-R6CV3WNH.woff2
--------------------------------------------------------------------------------
/static/assets/inter-latin-ext-500-normal-WO7CCPI4.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-500-normal-WO7CCPI4.woff
--------------------------------------------------------------------------------
/static/assets/inter-latin-ext-600-normal-IPOBZ3TF.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-600-normal-IPOBZ3TF.woff2
--------------------------------------------------------------------------------
/static/assets/inter-latin-ext-600-normal-Q5LOCDKP.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-600-normal-Q5LOCDKP.woff
--------------------------------------------------------------------------------
/static/assets/inter-vietnamese-400-normal-3ZH4IT4J.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-400-normal-3ZH4IT4J.woff2
--------------------------------------------------------------------------------
/static/assets/inter-vietnamese-400-normal-CQIZECWR.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-400-normal-CQIZECWR.woff
--------------------------------------------------------------------------------
/static/assets/inter-vietnamese-500-normal-PQFGSX3P.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-500-normal-PQFGSX3P.woff2
--------------------------------------------------------------------------------
/static/assets/inter-vietnamese-500-normal-YWCA5IC5.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-500-normal-YWCA5IC5.woff
--------------------------------------------------------------------------------
/static/assets/inter-vietnamese-600-normal-RMFHJUBF.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-600-normal-RMFHJUBF.woff
--------------------------------------------------------------------------------
/static/assets/inter-vietnamese-600-normal-TVIYLGI7.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-600-normal-TVIYLGI7.woff2
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-cyrillic-400-normal-C5JECUCT.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-400-normal-C5JECUCT.woff2
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-cyrillic-400-normal-ZHDMAAUB.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-400-normal-ZHDMAAUB.woff
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-cyrillic-ext-400-normal-C5DDHB7N.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-ext-400-normal-C5DDHB7N.woff
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-cyrillic-ext-400-normal-C7IFWGF6.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-ext-400-normal-C7IFWGF6.woff2
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-greek-400-normal-KF6EKC2J.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-greek-400-normal-KF6EKC2J.woff
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-greek-400-normal-O7JTTR3P.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-greek-400-normal-O7JTTR3P.woff2
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-latin-400-normal-3OOWLGQ2.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-400-normal-3OOWLGQ2.woff2
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-latin-400-normal-4JODGGIE.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-400-normal-4JODGGIE.woff
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-latin-ext-400-normal-3RACNWYK.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-ext-400-normal-3RACNWYK.woff
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-latin-ext-400-normal-JVR3IR4Z.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-ext-400-normal-JVR3IR4Z.woff2
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-vietnamese-400-normal-7A56GUVN.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-vietnamese-400-normal-7A56GUVN.woff
--------------------------------------------------------------------------------
/static/assets/jetbrains-mono-vietnamese-400-normal-KU7YLUPA.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-vietnamese-400-normal-KU7YLUPA.woff2
--------------------------------------------------------------------------------
/static/assets/katex-R4V72ZOM.js:
--------------------------------------------------------------------------------
1 | import{a}from"./chunk-GJ6CDUB5.js";import"./chunk-MGYUK2XN.js";export{a as default};
2 |
--------------------------------------------------------------------------------
/static/assets/packet-W2GHVCYJ-FC4ERNLH.js:
--------------------------------------------------------------------------------
1 | import{a as e,b as r}from"./chunk-TZMIYTTY.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{e as PacketModule,r as createPacketServices};
2 |
--------------------------------------------------------------------------------
/static/assets/pie-BEWT4RHE-5KCD7UAH.js:
--------------------------------------------------------------------------------
1 | import{a as e,b as r}from"./chunk-NKCQAART.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{e as PieModule,r as createPieServices};
2 |
--------------------------------------------------------------------------------
/static/assets/red-hat-text-latin-400-normal-OV4KODUR.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-400-normal-OV4KODUR.woff2
--------------------------------------------------------------------------------
/static/assets/red-hat-text-latin-400-normal-YWAZCVDO.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-400-normal-YWAZCVDO.woff
--------------------------------------------------------------------------------
/static/assets/red-hat-text-latin-ext-400-normal-H2UN6E6K.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-ext-400-normal-H2UN6E6K.woff
--------------------------------------------------------------------------------
/static/assets/red-hat-text-latin-ext-400-normal-PD2NZNLC.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-ext-400-normal-PD2NZNLC.woff2
--------------------------------------------------------------------------------
/static/assets/remixicon-6QPK2BSM.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-6QPK2BSM.ttf
--------------------------------------------------------------------------------
/static/assets/remixicon-7463F4HZ.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-7463F4HZ.woff2
--------------------------------------------------------------------------------
/static/assets/remixicon-DH65ADK5.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-DH65ADK5.woff
--------------------------------------------------------------------------------
/static/assets/remixicon-OJBB7CW7.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-OJBB7CW7.eot
--------------------------------------------------------------------------------
/static/assets/stateDiagram-v2-4JROLMXI-VMRUNBYI.js:
--------------------------------------------------------------------------------
1 | import{a,b as i,c as r,d as o}from"./chunk-6KI6TUY3.js";import"./chunk-BQJTSJOB.js";import"./chunk-5JLOMFCN.js";import"./chunk-IDH2HD35.js";import"./chunk-FIA6NTGC.js";import"./chunk-JVEBZTDG.js";import"./chunk-VW6A6SAT.js";import"./chunk-NCYNIMJ4.js";import"./chunk-ZPWCXWQI.js";import"./chunk-47P5NBBB.js";import"./chunk-XHGORZV2.js";import{h as e}from"./chunk-KRX7QNR4.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var k={parser:a,db:r,renderer:i,styles:o,init:e(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute,r.clear()},"init")};export{k as diagram};
2 |
--------------------------------------------------------------------------------
/static/favicons/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/favicons/favicon.png
--------------------------------------------------------------------------------
/static/images/elixir-portal.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/elixir-portal.jpeg
--------------------------------------------------------------------------------
/static/images/elixir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/elixir.png
--------------------------------------------------------------------------------
/static/images/explorer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/explorer.png
--------------------------------------------------------------------------------
/static/images/github-stars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/github-stars.png
--------------------------------------------------------------------------------
/static/images/kino.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/kino.png
--------------------------------------------------------------------------------
/static/images/logo-with-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/logo-with-text.png
--------------------------------------------------------------------------------
/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/logo.png
--------------------------------------------------------------------------------
/static/images/maplibre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/maplibre.png
--------------------------------------------------------------------------------
/static/images/teams.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/teams.png
--------------------------------------------------------------------------------
/static/images/vega_lite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/vega_lite.png
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | ## Running Livebook test suite
2 |
3 | ### Unit tests
4 |
5 | ```
6 | $ mix test
7 | ```
8 |
9 | ### Teams tests
10 |
11 | Integration with Livebook Teams requires cloning the private Livebook Teams repository. By default it is expected to be in sibling directory to Livebook itself. But you can set `TEAMS_PATH` to a custom directory.
12 |
13 | Livebook should take care of automatically setting up the Teams repository for you. However, if you run into issues, you can run the following in the Teams repository:
14 |
15 | ```
16 | $ MIX_ENV=livebook mix setup
17 | ```
18 |
--------------------------------------------------------------------------------
/test/livebook/ecto_types/hex_color_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.EctoTypes.HexColorTest do
2 | use ExUnit.Case, async: true
3 | doctest Livebook.EctoTypes.HexColor
4 | end
5 |
--------------------------------------------------------------------------------
/test/livebook/epmd_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.EPMDTest do
2 | use ExUnit.Case, async: true
3 |
4 | test "has a custom dist port" do
5 | assert Livebook.EPMD.dist_port() != 0
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/livebook/hubs/personal_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Hubs.PersonalTest do
2 | use Livebook.DataCase
3 |
4 | alias Livebook.Hubs.Personal
5 |
6 | test "get_secrets/1 returns a list of secrets from storage" do
7 | secret = build(:secret)
8 |
9 | Personal.set_secret(secret)
10 | assert secret in Personal.get_secrets()
11 |
12 | Personal.unset_secret(secret.name)
13 | refute secret in Personal.get_secrets()
14 | end
15 |
16 | test "fetch an specific secret" do
17 | secret = insert_secret()
18 |
19 | assert_raise Livebook.Storage.NotFoundError,
20 | ~s(could not find entry in "hub_secrets" with ID "NOT_HERE"),
21 | fn ->
22 | Personal.fetch_secret!("NOT_HERE")
23 | end
24 |
25 | assert Personal.fetch_secret!(secret.name) == secret
26 | Personal.unset_secret(secret.name)
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/livebook/learn_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.LearnTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Notebook
5 | alias Livebook.Notebook.Learn
6 |
7 | describe "notebook_by_slug!/1" do
8 | test "returns notebook structure and images if found" do
9 | assert {%Notebook{}, _imaegs} = Learn.notebook_by_slug!("intro-to-livebook")
10 | end
11 |
12 | test "raises an error if no matching notebook if found" do
13 | assert_raise Learn.NotFoundError, fn ->
14 | Learn.notebook_by_slug!("invalid-slug")
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/test/livebook/runtime/attached_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Runtime.AttachedTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Runtime
5 |
6 | describe "Runtime.connect/1" do
7 | test "given an invalid node returns an error" do
8 | runtime = Runtime.Attached.new(:nonexistent@node)
9 | pid = Runtime.connect(runtime)
10 |
11 | assert_receive {:runtime_connect_done, ^pid,
12 | {:error, "node :nonexistent@node is unreachable"}}
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/test/livebook/runtime/erl_dist/io_forward_gl_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Runtime.ErlDist.IOForwardGLTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Runtime.ErlDist.IOForwardGL
5 |
6 | test "forwards requests to sender's group leader" do
7 | pid = start_supervised!(IOForwardGL)
8 |
9 | group_leader_io =
10 | ExUnit.CaptureIO.capture_io(:stdio, fn ->
11 | # This sends an IO request to the IOForwardGL process.
12 | # Our group leader is :stdio (by default) so we expect
13 | # it to receive the string.
14 | IO.puts(pid, "hey")
15 | end)
16 |
17 | assert group_leader_io == "hey\n"
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/test/livebook/runtime/erl_dist/node_manager_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Runtime.ErlDist.NodeManagerTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Runtime
5 | alias Livebook.Runtime.ErlDist.NodeManager
6 | alias Livebook.Runtime.ErlDist.RuntimeServer
7 |
8 | test "terminates when the last runtime server terminates" do
9 | # We use a standalone runtime, so that we have an isolated node
10 | # with its own node manager
11 | pid = Runtime.Standalone.new() |> Runtime.connect()
12 | assert_receive {:runtime_connect_done, ^pid, {:ok, runtime}}
13 | %{node: node, server_pid: server1} = runtime
14 |
15 | Runtime.take_ownership(runtime)
16 |
17 | manager_pid = :erpc.call(node, Process, :whereis, [Livebook.Runtime.ErlDist.NodeManager])
18 | ref = Process.monitor(manager_pid)
19 |
20 | {:ok, server2} = NodeManager.start_runtime_server(node)
21 |
22 | RuntimeServer.stop(server1)
23 | RuntimeServer.stop(server2)
24 |
25 | assert_receive {:DOWN, ^ref, :process, _, _}
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/test/livebook/runtime/erl_dist/smart_cell_gl_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Runtime.ErlDist.SmartCellGLTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Runtime.ErlDist.SmartCellGL
5 |
6 | test "forwards regular IO requests to its group leader" do
7 | assert ExUnit.CaptureIO.capture_io(:stdio, fn ->
8 | runtime_broadcast_to = self()
9 | pid = start_supervised!({SmartCellGL, runtime_broadcast_to})
10 |
11 | IO.puts(pid, "hey")
12 | end) == "hey\n"
13 | end
14 |
15 | test "supports :livebook_get_broadcast_target request" do
16 | runtime_broadcast_to = self()
17 | pid = start_supervised!({SmartCellGL, runtime_broadcast_to})
18 |
19 | assert {:ok, ^runtime_broadcast_to} = io_request(pid, :livebook_get_broadcast_target)
20 | end
21 |
22 | defp io_request(io, request) do
23 | ref = make_ref()
24 | send(io, {:io_request, self(), ref, request})
25 | assert_receive {:io_reply, ^ref, reply}
26 | reply
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/livebook/secrets_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.SecretsTest do
2 | use Livebook.DataCase, async: true
3 |
4 | alias Livebook.Secrets
5 | alias Livebook.Secrets.Secret
6 |
7 | describe "update_secret/2" do
8 | test "returns a valid secret" do
9 | attrs = params_for(:secret)
10 |
11 | assert {:ok, secret} = Secrets.update_secret(%Secret{}, attrs)
12 | assert attrs.name == secret.name
13 | assert attrs.value == secret.value
14 | assert attrs.hub_id == secret.hub_id
15 | end
16 |
17 | test "returns changeset error" do
18 | attrs = params_for(:secret, name: nil, value: "111")
19 | assert {:error, changeset} = Secrets.update_secret(%Secret{}, attrs)
20 | assert "can't be blank" in errors_on(changeset).name
21 |
22 | attrs = params_for(:secret, name: "@inavalid", value: "111")
23 | assert {:error, changeset} = Secrets.update_secret(%Secret{}, attrs)
24 |
25 | assert "should contain only alphanumeric characters and underscore" in errors_on(changeset).name
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/livebook/users/user_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Users.UserTest do
2 | use Livebook.DataCase, async: true
3 |
4 | alias Livebook.Users.User
5 |
6 | describe "change/2" do
7 | test "given valid attributes returns and updated user" do
8 | user = build(:user)
9 |
10 | attrs = %{
11 | "name" => "Jake Peralta",
12 | "hex_color" => "#000000",
13 | "payload" => %{"country" => "US"}
14 | }
15 |
16 | changeset = User.changeset(user, attrs)
17 |
18 | assert changeset.valid?
19 | assert get_field(changeset, :name) == "Jake Peralta"
20 | assert get_field(changeset, :hex_color) == "#000000"
21 | assert get_field(changeset, :payload) == %{"country" => "US"}
22 | end
23 |
24 | test "given invalid color returns an error" do
25 | user = build(:user)
26 | attrs = %{"hex_color" => "#invalid"}
27 | changeset = User.changeset(user, attrs)
28 |
29 | refute changeset.valid?
30 | assert "not a valid color" in errors_on(changeset).hex_color
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/test/livebook/users_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.UsersTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Users
5 | alias Livebook.Users.User
6 |
7 | describe "broadcast_change/1" do
8 | test "notifies subscribers of user change" do
9 | user = User.new()
10 | Users.subscribe(user.id)
11 |
12 | Users.broadcast_change(user)
13 |
14 | assert_received {:user_change, ^user}
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/test/livebook/utils/time_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Utils.TimeTest do
2 | use ExUnit.Case, async: true
3 |
4 | alias Livebook.Utils
5 |
6 | doctest Utils.Time
7 | end
8 |
--------------------------------------------------------------------------------
/test/livebook_test.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookTest do
2 | use ExUnit.Case, async: true
3 |
4 | test "live_markdown_to_elixir/1" do
5 | markdown = """
6 | # Lists
7 |
8 | ## Introduction
9 |
10 | Let's generate a list of numbers:
11 |
12 | ```elixir
13 | Enum.to_list(1..10)
14 | ```
15 | """
16 |
17 | assert Livebook.live_markdown_to_elixir(markdown) == """
18 | # Run as: iex --dot-iex path/to/notebook.exs
19 |
20 | # Title: Lists
21 |
22 | # ── Introduction ──
23 |
24 | # Let's generate a list of numbers:
25 |
26 | Enum.to_list(1..10)
27 | """
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/test/livebook_web/controllers/endpoint_test.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.EndpointTest do
2 | use LivebookWeb.ConnCase, async: true
3 |
4 | test "delete cookies once they go over a certain limit", %{conn: conn} do
5 | cookies =
6 | Enum.map(1..5, &"c#{&1}=#{String.duplicate("a", 4096)}") ++
7 | Enum.map(1..5, &"lb_#{&1}=#{String.duplicate("a", 4096)}")
8 |
9 | assert [
10 | "c1=;" <> _,
11 | "c2=;" <> _,
12 | "c3=;" <> _,
13 | "c4=;" <> _,
14 | "c5=;" <> _,
15 | "lb_session" <> _,
16 | "lb_user_data" <> _
17 | ] =
18 | conn
19 | |> put_req_header("cookie", Enum.join(cookies, "; "))
20 | |> get(~p"/")
21 | |> get_resp_header("set-cookie")
22 | |> Enum.sort()
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/livebook_web/controllers/errors_test.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.ErrorsTest do
2 | use LivebookWeb.ConnCase, async: true
3 |
4 | test "renders 404", %{conn: conn} do
5 | conn = get(conn, "/this/does/not/exist")
6 |
7 | assert conn.status == 404
8 | assert conn.resp_body =~ "No Numbats here!"
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/test/livebook_web/controllers/user_controller_test.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.UserControllerTest do
2 | use LivebookWeb.ConnCase, async: true
3 |
4 | describe "GET /logout" do
5 | test "redirects when already logged out", %{conn: conn} do
6 | assert conn
7 | |> Phoenix.ConnTest.init_test_session(%{})
8 | |> get(~p"/logout")
9 | |> redirected_to() == ~p"/"
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/test/livebook_web/live/learn_live_test.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.LearnLiveTest do
2 | use LivebookWeb.ConnCase, async: true
3 |
4 | import Phoenix.LiveViewTest
5 |
6 | test "link to introductory notebook correctly creates a new session", %{conn: conn} do
7 | {:ok, view, _} = live(conn, ~p"/learn")
8 |
9 | assert {:error, {:live_redirect, %{to: to}}} =
10 | view
11 | |> element(~s{#welcome-to-livebook a}, "Open notebook")
12 | |> render_click()
13 |
14 | assert to =~ "/sessions/"
15 |
16 | # Note that this LV page is huge and the simulated rendering in
17 | # LV tests is not heavily optimized. This LV receives events from
18 | # concurrent tests (such as hub creation) and rendering is a big
19 | # bottleneck, to the point where calling render(view) times out.
20 | # That's why we only assert on the dead render HTML.
21 | {:ok, _view, html} = live(conn, to)
22 | assert html =~ "Welcome to Livebook"
23 |
24 | close_session_by_path(to)
25 | end
26 |
27 | defp close_session_by_path("/sessions/" <> session_id) do
28 | {:ok, session} = Livebook.Sessions.fetch_session(session_id)
29 | Livebook.Session.close(session.pid)
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/test/livebook_web/live/output/plain_text_component_test.exs:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.Output.PlainTextComponentTest do
2 | use LivebookWeb.ConnCase, async: true
3 | import Phoenix.LiveViewTest
4 |
5 | alias LivebookWeb.Output.PlainTextComponent
6 |
7 | test "renders chunks with styles" do
8 | assigns = %{
9 | id: "test-id",
10 | output: %{
11 | text: "Hello",
12 | style: [color: "red", unknown: "discarded", font_size: "invalid;discarded"]
13 | }
14 | }
15 |
16 | content = render_component(PlainTextComponent, assigns)
17 | assert content =~ ~s|Hello|
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/test/support/app_helpers.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.AppHelpers do
2 | def deploy_notebook_sync(notebook) do
3 | app_spec = Livebook.Apps.NotebookAppSpec.new(notebook)
4 |
5 | deployer_pid = Livebook.Apps.Deployer.local_deployer()
6 | ref = Livebook.Apps.Deployer.deploy_monitor(deployer_pid, app_spec)
7 |
8 | receive do
9 | {:deploy_result, ^ref, {:ok, pid}} ->
10 | Process.demonitor(ref, [:flush])
11 |
12 | ExUnit.Callbacks.on_exit(fn ->
13 | if Process.alive?(pid) do
14 | Livebook.App.close(pid)
15 | end
16 | end)
17 |
18 | pid
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/test/support/assets.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/test/support/assets.tar.gz
--------------------------------------------------------------------------------
/test/support/channel_case.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.ChannelCase do
2 | use ExUnit.CaseTemplate
3 |
4 | using do
5 | quote do
6 | # Import conveniences for testing with channels
7 | import Phoenix.ChannelTest
8 |
9 | # The default endpoint for testing
10 | @endpoint LivebookWeb.Endpoint
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/support/conn_case.ex:
--------------------------------------------------------------------------------
1 | defmodule LivebookWeb.ConnCase do
2 | use ExUnit.CaseTemplate
3 |
4 | using do
5 | quote do
6 | # Import conveniences for testing with connections
7 | import Plug.Conn
8 | import Phoenix.ConnTest
9 | import Livebook.Factory
10 | import LivebookWeb.ConnCase
11 |
12 | use LivebookWeb, :verified_routes
13 |
14 | # The default endpoint for testing
15 | @endpoint LivebookWeb.Endpoint
16 | end
17 | end
18 |
19 | setup tags do
20 | conn = Phoenix.ConnTest.build_conn()
21 |
22 | conn =
23 | if authentication = tags[:authentication] do
24 | with_authentication(conn, authentication)
25 | else
26 | conn
27 | end
28 |
29 | [conn: conn]
30 | end
31 |
32 | def with_authentication(conn, authentication) do
33 | Plug.Test.init_test_session(conn, %{authentication_test_override: authentication})
34 | end
35 |
36 | def with_authorization(conn, id) do
37 | Plug.Test.init_test_session(conn, %{
38 | identity_provider_test_override: {:zta, Livebook.ZTA.LivebookTeams, id}
39 | })
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/test/support/notebook_app_spec.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.Apps.NotebookAppSpec do
2 | # App spec carrying an in-memory notebook.
3 |
4 | defstruct [:slug, :notebook, :load_failures, :should_warmup, version: "1"]
5 |
6 | def new(notebook, opts \\ []) do
7 | opts = Keyword.validate!(opts, load_failures: 0, should_warmup: false)
8 |
9 | slug = notebook.app_settings.slug
10 |
11 | :persistent_term.put({slug, :failures}, 0)
12 |
13 | %__MODULE__{
14 | slug: slug,
15 | notebook: notebook,
16 | load_failures: opts[:load_failures],
17 | should_warmup: opts[:should_warmup]
18 | }
19 | end
20 | end
21 |
22 | defimpl Livebook.Apps.AppSpec, for: Livebook.Apps.NotebookAppSpec do
23 | def load(app_spec, _files_tmp_path) do
24 | key = {app_spec.slug, :failures}
25 | num_failures = :persistent_term.get(key)
26 |
27 | if num_failures < app_spec.load_failures do
28 | :persistent_term.put(key, num_failures + 1)
29 | {:error, "failed to load"}
30 | else
31 | {:ok, %{notebook: app_spec.notebook, warnings: []}}
32 | end
33 | end
34 |
35 | def should_warmup?(app_spec) do
36 | app_spec.should_warmup
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/test/support/notebooks/basic.livemd:
--------------------------------------------------------------------------------
1 | # Notebook
2 |
3 | ## First section
4 |
5 | One Code cell below.
6 |
7 | ```elixir
8 | length([1, 2, 3])
9 | ```
10 |
--------------------------------------------------------------------------------
/test/support/notebooks/with_two_sections.livemd:
--------------------------------------------------------------------------------
1 | # Notebook
2 |
3 | ## First section
4 |
5 | This is the first section.
6 |
7 | ```elixir
8 | length([1, 2, 3])
9 | ```
10 |
11 | ## Second section
12 |
13 | This is the second section.
14 |
--------------------------------------------------------------------------------
/test/support/static/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/test/support/static/icon.ico
--------------------------------------------------------------------------------
/test/support/static/js/app.js:
--------------------------------------------------------------------------------
1 | console.log("Hello");
2 |
--------------------------------------------------------------------------------
/test/support/teams_integration_case.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.TeamsIntegrationCase do
2 | use ExUnit.CaseTemplate
3 |
4 | alias Livebook.TeamsServer
5 |
6 | using do
7 | quote do
8 | use Livebook.DataCase
9 | use LivebookWeb.ConnCase
10 |
11 | @moduletag :teams_integration
12 |
13 | alias Livebook.TeamsServer
14 | alias Livebook.TeamsRPC
15 |
16 | import Livebook.HubHelpers
17 | import Livebook.TeamsIntegrationHelper
18 | end
19 | end
20 |
21 | setup_all do
22 | case TeamsServer.start() do
23 | {:ok, _} -> :ok
24 | {:error, {:already_started, _}} -> :ok
25 | end
26 |
27 | url = TeamsServer.url()
28 | node = TeamsServer.get_node()
29 |
30 | Application.put_env(:livebook, :teams_url, url, persistent: true)
31 |
32 | {:ok, node: node}
33 | end
34 |
35 | setup context do
36 | if topics = context[:subscribe_to_hubs_topics] do
37 | Livebook.Hubs.Broadcasts.subscribe(topics)
38 | end
39 |
40 | if topics = context[:subscribe_to_teams_topics] do
41 | Livebook.Teams.Broadcasts.subscribe(topics)
42 | end
43 |
44 | :ok
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/test/support/test_modules/bad_inspect.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.TestModules.BadInspect do
2 | defstruct []
3 |
4 | defimpl Inspect do
5 | @dialyzer {:nowarn_function, inspect: 2}
6 |
7 | def inspect(%Livebook.TestModules.BadInspect{}, _opts) do
8 | :bad_return
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/test/support/test_modules/hidden.ex:
--------------------------------------------------------------------------------
1 | defmodule Livebook.TestModules.Hidden do
2 | @moduledoc false
3 | def visible, do: :ok
4 |
5 | @doc false
6 | def hidden, do: :ok
7 | end
8 |
--------------------------------------------------------------------------------
/versions:
--------------------------------------------------------------------------------
1 | elixir="1.18.3"
2 | otp="27.3.3"
3 | openssl="1.1.1s"
4 | rebar3="3.22.0"
5 | ubuntu="noble-20250404"
6 |
--------------------------------------------------------------------------------