├── .cargo └── config.toml ├── .config └── nextest.toml ├── .envrc-example ├── .github ├── dependabot.yml └── workflows │ ├── audit.yml │ ├── bot-rebase.yml │ ├── check-build-webui.yml │ ├── check-run-cargo-build-verify-toml-schema.yml │ ├── check-test.yml │ ├── check.yml │ ├── push-test-components.yml │ ├── push-webui.yml │ ├── release-2-cargo-publish.yml │ ├── release-3-create-github-release.yml │ ├── release-3.1-upload-artifacts.yml │ ├── release-4-push-docker-image.yml │ ├── release-5-verify-cargo-binstall.yml │ ├── release-5-verify-cargo-install.yml │ ├── release-5-verify-download-sh.yml │ ├── release-5-verify-github-release.yml │ ├── release-5-verify-run-nix.yml │ ├── release │ └── docker-image │ │ ├── alpine.Dockerfile │ │ └── ubuntu-24.04.Dockerfile │ ├── scheduled-bump-cargo-lock.yml │ └── scheduled-bump-flake-lock.yml ├── .gitignore ├── .ignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-AGPL ├── LICENSE.md ├── README.md ├── ROADMAP.md ├── assets ├── images │ ├── logo-small.png │ └── screencast.gif ├── unpublishable-packages.txt └── webui-version.txt ├── build.rs ├── crates ├── component-builder │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── concepts │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── rusqlite_ext.rs │ │ ├── snapshots │ │ ├── obeli_sk_concepts__storage__tests__verify_pending_state_finished_result_kind_serde.snap │ │ └── obeli_sk_concepts__tests__execution_id_hash_should_be_stable.snap │ │ ├── storage.rs │ │ └── time.rs ├── db-mem │ ├── Cargo.toml │ └── src │ │ ├── inmemory_dao.rs │ │ ├── journal.rs │ │ └── lib.rs ├── db-sqlite │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── sqlite_dao.rs ├── executor │ ├── Cargo.toml │ └── src │ │ ├── executor.rs │ │ ├── expired_timers_watcher.rs │ │ ├── lib.rs │ │ └── worker.rs ├── testing │ ├── db-tests │ │ ├── Cargo.toml │ │ ├── src │ │ │ ├── db_proxy.rs │ │ │ └── lib.rs │ │ └── tests │ │ │ ├── diff-tests.rs │ │ │ └── lifecycle.rs │ ├── test-programs │ │ ├── dir │ │ │ └── activity │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ ├── Cargo.toml │ │ │ │ ├── build.rs │ │ │ │ └── src │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ ├── deps │ │ │ │ └── testing_dir │ │ │ │ │ └── dir.wit │ │ │ │ └── impl.wit │ │ ├── fibo │ │ │ ├── activity │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ │ ├── Cargo.toml │ │ │ │ │ ├── build.rs │ │ │ │ │ └── src │ │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ │ ├── deps │ │ │ │ │ └── testing_fibo │ │ │ │ │ ├── impl.wit │ │ │ │ │ ├── testing_fibo-obelisk-ext │ │ │ │ │ └── fibo-ext.wit │ │ │ │ │ └── testing_fibo │ │ │ │ │ └── fibo.wit │ │ │ ├── webhook │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ │ ├── Cargo.toml │ │ │ │ │ ├── build.rs │ │ │ │ │ └── src │ │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ │ ├── deps │ │ │ │ │ ├── obelisk_log@1.0.0 │ │ │ │ │ ├── obelisk_types@1.1.0 │ │ │ │ │ ├── testing_fibo-workflow │ │ │ │ │ └── testing_fibo-workflow-obelisk-ext │ │ │ │ │ └── impl.wit │ │ │ └── workflow │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ ├── Cargo.toml │ │ │ │ ├── build.rs │ │ │ │ └── src │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ ├── deps │ │ │ │ ├── obelisk_log@1.0.0 │ │ │ │ ├── obelisk_types@1.1.0 │ │ │ │ ├── obelisk_workflow@1.1.0 │ │ │ │ ├── testing_fibo │ │ │ │ ├── testing_fibo-obelisk-ext │ │ │ │ ├── testing_fibo-workflow │ │ │ │ └── testing_fibo-workflow-obelisk-ext │ │ │ │ ├── impl.wit │ │ │ │ ├── testing_fibo-workflow-obelisk-ext │ │ │ │ └── workflow-ext.wit │ │ │ │ └── testing_fibo-workflow │ │ │ │ └── ifc.wit │ │ ├── http │ │ │ ├── activity │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ │ ├── Cargo.toml │ │ │ │ │ ├── build.rs │ │ │ │ │ └── src │ │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ │ ├── deps │ │ │ │ │ ├── obelisk_log@1.0.0 │ │ │ │ │ └── testing_http │ │ │ │ │ ├── impl.wit │ │ │ │ │ ├── testing_http-obelisk-ext │ │ │ │ │ └── http-get-ext.wit │ │ │ │ │ └── testing_http │ │ │ │ │ └── http-get.wit │ │ │ └── workflow │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ ├── Cargo.toml │ │ │ │ ├── build.rs │ │ │ │ └── src │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ ├── deps │ │ │ │ ├── obelisk_types@1.1.0 │ │ │ │ ├── obelisk_workflow@1.1.0 │ │ │ │ ├── testing_http │ │ │ │ └── testing_http-obelisk-ext │ │ │ │ └── impl.wit │ │ ├── process │ │ │ └── activity │ │ │ │ ├── Cargo.toml │ │ │ │ ├── builder │ │ │ │ ├── Cargo.toml │ │ │ │ ├── build.rs │ │ │ │ └── src │ │ │ │ │ └── lib.rs │ │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ │ └── wit │ │ │ │ ├── deps │ │ │ │ ├── obelisk_activity@1.0.0 │ │ │ │ ├── testing_process │ │ │ │ │ └── process.wit │ │ │ │ └── wasi_io@0.2.3 │ │ │ │ └── impl.wit │ │ └── sleep │ │ │ ├── activity │ │ │ ├── Cargo.toml │ │ │ ├── builder │ │ │ │ ├── Cargo.toml │ │ │ │ ├── build.rs │ │ │ │ └── src │ │ │ │ │ └── lib.rs │ │ │ ├── src │ │ │ │ └── lib.rs │ │ │ └── wit │ │ │ │ ├── deps │ │ │ │ ├── obelisk_types@1.1.0 │ │ │ │ └── testing_sleep │ │ │ │ ├── impl.wit │ │ │ │ ├── testing_sleep-obelisk-ext │ │ │ │ └── sleep-ext.wit │ │ │ │ └── testing_sleep │ │ │ │ └── sleep.wit │ │ │ └── workflow │ │ │ ├── Cargo.toml │ │ │ ├── builder │ │ │ ├── Cargo.toml │ │ │ ├── build.rs │ │ │ └── src │ │ │ │ └── lib.rs │ │ │ ├── src │ │ │ └── lib.rs │ │ │ └── wit │ │ │ ├── deps │ │ │ ├── obelisk_types@1.1.0 │ │ │ ├── obelisk_workflow@1.1.0 │ │ │ ├── testing_sleep │ │ │ ├── testing_sleep-obelisk-ext │ │ │ ├── testing_sleep-workflow │ │ │ └── testing_sleep-workflow-obelisk-ext │ │ │ ├── impl.wit │ │ │ ├── testing_sleep-workflow-obelisk-ext │ │ │ └── sleep-ext.wit │ │ │ └── testing_sleep-workflow │ │ │ └── sleep.wit │ └── test-utils │ │ ├── Cargo.toml │ │ └── src │ │ ├── arbitrary.rs │ │ ├── lib.rs │ │ └── sim_clock.rs ├── utils │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ ├── sha256sum.rs │ │ ├── snapshots │ │ │ ├── obeli_sk_utils__wasm_tools__tests__exports_imports@test_programs_fibo_workflow.wasm_exports_ext.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__exports_imports@test_programs_fibo_workflow.wasm_exports_noext.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__exports_imports@test_programs_fibo_workflow.wasm_imports.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__exports_imports@test_programs_http_get_workflow.wasm_exports_ext.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__exports_imports@test_programs_http_get_workflow.wasm_exports_noext.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__exports_imports@test_programs_http_get_workflow.wasm_imports.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__params@test_programs_fibo_workflow.wasm_exports.snap │ │ │ ├── obeli_sk_utils__wasm_tools__tests__params@test_programs_fibo_workflow.wasm_imports.snap │ │ │ ├── obeli_sk_utils__wit__tests__wit_should_contain_extensions@test_programs_fibo_activity.wasm_wit.snap │ │ │ ├── obeli_sk_utils__wit__tests__wit_should_contain_extensions@test_programs_fibo_webhook.wasm_wit.snap │ │ │ ├── obeli_sk_utils__wit__tests__wit_should_contain_extensions@test_programs_fibo_workflow.wasm_wit.snap │ │ │ └── obeli_sk_utils__wit__tests__wit_should_contain_extensions@test_programs_http_get_activity.wasm_wit.snap │ │ ├── wasm_tools.rs │ │ └── wit.rs │ └── wit ├── val-json │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── snapshots │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_bool.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_enum.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_flags.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_list.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_option_none.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_option_some.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_record.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_result_err_none.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_result_err_some.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_result_ok_none.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_result_ok_option_none.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_result_ok_some.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_tuple.snap │ │ ├── obeli_sk_val_json__wast_val_ser__tests__serde_variant_with_field.snap │ │ └── obeli_sk_val_json__wast_val_ser__tests__serde_variant_without_field.snap │ │ ├── type_wrapper.rs │ │ ├── wast_val.rs │ │ └── wast_val_ser.rs ├── wasm-workers │ ├── Cargo.toml │ ├── host-wit-activity │ │ ├── deps │ │ │ ├── obelisk_activity@1.0.0 │ │ │ └── wasi_io@0.2.3 │ │ └── dummy.wit │ ├── host-wit-log │ │ ├── deps │ │ │ └── obelisk_log@1.0.0 │ │ └── dummy.wit │ ├── host-wit-webhook │ │ ├── deps │ │ │ └── obelisk_types@1.1.0 │ │ └── dummy.wit │ ├── host-wit-workflow-wasi │ │ ├── deps │ │ │ ├── wasi_cli@0.2.3 │ │ │ ├── wasi_clocks@0.2.3 │ │ │ ├── wasi_filesystem@0.2.3 │ │ │ ├── wasi_io@0.2.3 │ │ │ ├── wasi_random@0.2.3 │ │ │ └── wasi_sockets@0.2.3 │ │ └── dummy.wit │ ├── host-wit-workflow │ │ ├── deps │ │ │ ├── obelisk_types@1.1.0 │ │ │ └── obelisk_workflow@1.1.0 │ │ └── dummy.wit │ └── src │ │ ├── activity │ │ ├── activity_ctx.rs │ │ ├── activity_ctx_process.rs │ │ ├── activity_worker.rs │ │ ├── mod.rs │ │ └── process.rs │ │ ├── component_logger.rs │ │ ├── engines.rs │ │ ├── epoch_ticker.rs │ │ ├── lib.rs │ │ ├── preopens_cleaner.rs │ │ ├── std_output_stream.rs │ │ ├── webhook │ │ ├── mod.rs │ │ └── webhook_trigger.rs │ │ └── workflow │ │ ├── event_history.rs │ │ ├── host_exports.rs │ │ ├── mod.rs │ │ ├── wasi │ │ ├── cli.rs │ │ ├── clocks.rs │ │ ├── filesystem.rs │ │ ├── mod.rs │ │ └── random.rs │ │ ├── workflow_ctx.rs │ │ └── workflow_worker.rs ├── webui-proxy │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ ├── src │ │ └── lib.rs │ └── wit │ │ ├── deps │ │ ├── cli │ │ │ ├── command.wit │ │ │ ├── environment.wit │ │ │ ├── exit.wit │ │ │ ├── imports.wit │ │ │ ├── run.wit │ │ │ ├── stdio.wit │ │ │ └── terminal.wit │ │ ├── clocks │ │ │ ├── monotonic-clock.wit │ │ │ ├── wall-clock.wit │ │ │ └── world.wit │ │ ├── filesystem │ │ │ ├── preopens.wit │ │ │ ├── types.wit │ │ │ └── world.wit │ │ ├── http │ │ │ ├── handler.wit │ │ │ ├── proxy.wit │ │ │ └── types.wit │ │ ├── io │ │ │ ├── error.wit │ │ │ ├── poll.wit │ │ │ ├── streams.wit │ │ │ └── world.wit │ │ ├── random │ │ │ ├── insecure-seed.wit │ │ │ ├── insecure.wit │ │ │ ├── random.wit │ │ │ └── world.wit │ │ └── sockets │ │ │ ├── instance-network.wit │ │ │ ├── ip-name-lookup.wit │ │ │ ├── network.wit │ │ │ ├── tcp-create-socket.wit │ │ │ ├── tcp.wit │ │ │ ├── udp-create-socket.wit │ │ │ ├── udp.wit │ │ │ └── world.wit │ │ └── impl.wit └── webui │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── Trunk-dev.toml │ ├── Trunk.toml │ ├── build.rs │ ├── builder │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── lib.rs │ ├── index.html │ ├── src │ ├── app.rs │ ├── components │ │ ├── code │ │ │ ├── code_block.rs │ │ │ ├── mod.rs │ │ │ └── syntect_code_block.rs │ │ ├── component_list_page.rs │ │ ├── component_tree.rs │ │ ├── debugger │ │ │ ├── debugger_view.rs │ │ │ └── mod.rs │ │ ├── execution_detail │ │ │ ├── created.rs │ │ │ ├── finished.rs │ │ │ ├── history │ │ │ │ ├── join_next.rs │ │ │ │ ├── join_set_created.rs │ │ │ │ ├── join_set_request.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── persist.rs │ │ │ │ └── schedule.rs │ │ │ ├── http_trace.rs │ │ │ ├── locked.rs │ │ │ ├── mod.rs │ │ │ ├── temporarily_failed.rs │ │ │ ├── timed_out.rs │ │ │ ├── tree_component.rs │ │ │ ├── unlocked.rs │ │ │ └── utils.rs │ │ ├── execution_detail_page.rs │ │ ├── execution_header.rs │ │ ├── execution_list_page.rs │ │ ├── execution_status.rs │ │ ├── execution_submit.rs │ │ ├── execution_submit_page.rs │ │ ├── ffqn_with_links.rs │ │ ├── function_signature.rs │ │ ├── json_tree.rs │ │ ├── mod.rs │ │ ├── not_found.rs │ │ └── trace │ │ │ ├── data.rs │ │ │ ├── execution_trace.rs │ │ │ ├── mod.rs │ │ │ └── trace_view.rs │ ├── grpc │ │ ├── execution_id.rs │ │ ├── ffqn.rs │ │ ├── function_detail.rs │ │ ├── grpc_client.rs │ │ ├── ifc_fqn.rs │ │ ├── join_set_id.rs │ │ ├── mod.rs │ │ ├── pkg_fqn.rs │ │ └── version.rs │ ├── lib.rs │ ├── main.rs │ └── util │ │ ├── mod.rs │ │ └── wit_highlighter.rs │ └── styles.css ├── dev-deps.txt ├── download.sh ├── flake.lock ├── flake.nix ├── garnix.yaml ├── obelisk-local.toml ├── obelisk.toml ├── proto ├── LICENSE-MIT └── obelisk.proto ├── release-plz.toml ├── rust-toolchain-cross.toml ├── rust-toolchain.toml ├── scripts ├── cargo-publish-workspace.sh ├── clippy.sh ├── dev-deps.sh ├── extract-release-info.sh ├── push-test-components.sh ├── push-webui.sh ├── test-madsim.sh ├── test.sh ├── unpublishable-packages.sh └── update-toml-schema.sh ├── src ├── args.rs ├── command │ ├── component.rs │ ├── execution.rs │ ├── generate.rs │ ├── mod.rs │ └── server.rs ├── config.rs ├── config │ ├── config_holder.rs │ ├── env_var.rs │ └── toml.rs ├── env_vars.rs ├── grpc_util │ ├── grpc_mapping.rs │ └── mod.rs ├── init.rs ├── main.rs └── oci.rs ├── toml ├── LICENSE-MIT └── schema.json └── wit ├── LICENSE-MIT ├── obelisk_activity@1.0.0 └── process.wit ├── obelisk_log@1.0.0 └── log@1.0.0.wit ├── obelisk_types@1.1.0 └── types@1.1.0.wit ├── obelisk_workflow@1.1.0 └── workflow-support@1.1.0.wit ├── wasi_cli@0.2.3 ├── command.wit ├── environment.wit ├── exit.wit ├── imports.wit ├── run.wit ├── stdio.wit └── terminal.wit ├── wasi_clocks@0.2.3 ├── monotonic-clock.wit ├── timezone.wit ├── wall-clock.wit └── world.wit ├── wasi_filesystem@0.2.3 ├── preopens.wit ├── types.wit └── world.wit ├── wasi_io@0.2.3 ├── error.wit ├── poll.wit ├── streams.wit └── world.wit ├── wasi_random@0.2.3 ├── insecure-seed.wit ├── insecure.wit ├── random.wit └── world.wit └── wasi_sockets@0.2.3 ├── instance-network.wit ├── ip-name-lookup.wit ├── network.wit ├── tcp-create-socket.wit ├── tcp.wit ├── udp-create-socket.wit ├── udp.wit └── world.wit /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # rustflags = ["--cfg", "madsim"] 3 | 4 | [env] 5 | CARGO_WORKSPACE_DIR = { value = "", relative = true } # remove once stable https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads 6 | -------------------------------------------------------------------------------- /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [profile.ci-test-madsim] 2 | failure-output = "immediate" 3 | fail-fast = false 4 | status-level = "all" 5 | slow-timeout = { period = "10s", terminate-after = 4 } 6 | junit.path = "junit.xml" 7 | junit.report-name = "madsim" 8 | junit.store-success-output = true 9 | junit.store-failure-output = true 10 | 11 | [profile.ci-test-nosim] 12 | failure-output = "immediate" 13 | fail-fast = false 14 | status-level = "all" 15 | slow-timeout = { period = "10s", terminate-after = 4 } 16 | junit.path = "junit.xml" 17 | junit.report-name = "nosim" 18 | junit.store-success-output = true 19 | junit.store-failure-output = true 20 | 21 | [profile.ci-test-populate-codegen-cache] 22 | failure-output = "immediate" 23 | fail-fast = true 24 | status-level = "all" 25 | slow-timeout = { period = "10s", terminate-after = 4 } 26 | -------------------------------------------------------------------------------- /.envrc-example: -------------------------------------------------------------------------------- 1 | use flake . 2 | watch_file ./rust-toolchain.toml ./flake.nix 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: cargo 8 | directory: / 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - "**/Cargo.toml" 8 | - "**/Cargo.lock" 9 | - ".github/workflows/audit.yml" 10 | schedule: 11 | - cron: "0 0 * * *" 12 | 13 | jobs: 14 | security_audit: 15 | name: Audit check 16 | runs-on: ubuntu-24.04 17 | permissions: 18 | checks: write 19 | contents: read 20 | issues: write 21 | steps: 22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 23 | - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 24 | with: 25 | token: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/bot-rebase.yml: -------------------------------------------------------------------------------- 1 | name: Bot Rebase PR 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | jobs: 8 | rebase: 9 | if: github.event.comment.body == '/rebase' 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | pull-requests: write 14 | steps: 15 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 16 | with: 17 | fetch-depth: 0 18 | - name: Rebase PR 19 | run: | 20 | PR_NUMBER=$(jq --raw-output .issue.number "$GITHUB_EVENT_PATH") 21 | BRANCH=$(gh pr view $PR_NUMBER --json headRefName -q .headRefName) 22 | BASE=$(gh pr view $PR_NUMBER --json baseRefName -q .baseRefName) 23 | git config user.name "github-actions" 24 | git config user.email "github-actions@github.com" 25 | git fetch origin $BRANCH:$BRANCH 26 | git checkout $BRANCH 27 | git rebase origin/$BASE 28 | git push --force-with-lease origin $BRANCH 29 | env: 30 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /.github/workflows/check-build-webui.yml: -------------------------------------------------------------------------------- 1 | name: check-build-webui 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - latest 8 | pull_request: 9 | 10 | # If new code is pushed to a PR branch, then cancel in progress workflows for that PR. 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 | cancel-in-progress: true 14 | 15 | 16 | defaults: 17 | run: 18 | shell: bash -xe {0} 19 | 20 | jobs: 21 | check-build-webui: 22 | runs-on: ubuntu-24.04 23 | steps: 24 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 25 | 26 | - uses: nixbuild/nix-quick-install-action@8505cd40ae3d4791ca658f2697c5767212e5ce71 27 | with: 28 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Populate the nix store 31 | run: nix develop .#web --command echo 32 | 33 | - name: Build webui + proxy 34 | run: nix develop .#web --command ./scripts/push-webui.sh dry-run 35 | -------------------------------------------------------------------------------- /.github/workflows/check-run-cargo-build-verify-toml-schema.yml: -------------------------------------------------------------------------------- 1 | name: check-run-cargo-build-verify-toml-schema 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - latest 8 | pull_request: 9 | 10 | # If new code is pushed to a PR branch, then cancel in progress workflows for that PR. 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 | cancel-in-progress: true 14 | 15 | 16 | defaults: 17 | run: 18 | shell: bash -xe {0} 19 | 20 | jobs: 21 | check-build-verify-toml-schema: 22 | runs-on: ubuntu-24.04 23 | steps: 24 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 25 | 26 | - uses: nixbuild/nix-quick-install-action@8505cd40ae3d4791ca658f2697c5767212e5ce71 27 | with: 28 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Populate the nix store 31 | run: nix develop --command echo 32 | 33 | - name: Run `cargo build` 34 | run: nix develop --command cargo build 35 | 36 | - name: Run `obelisk server verify` 37 | run: | 38 | target/debug/obelisk server verify 39 | 40 | - name: Run `obelisk generate config-schema` 41 | run: | 42 | scripts/update-toml-schema.sh 43 | if [ -n "$(git status --porcelain)" ]; then 44 | echo "::error::Uncommitted changes after running 'obelisk generate config-schema toml/schema.json'" 45 | exit 1 46 | fi 47 | -------------------------------------------------------------------------------- /.github/workflows/push-webui.yml: -------------------------------------------------------------------------------- 1 | name: push-webui 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | description: "The tag to be used when pushing components to the Docker Hub." 11 | required: true 12 | type: string 13 | ref: 14 | description: "The ref (branch or SHA) to process" 15 | required: false 16 | type: string 17 | 18 | defaults: 19 | run: 20 | shell: bash -xe {0} 21 | 22 | jobs: 23 | push-webui: 24 | runs-on: ubuntu-24.04 25 | steps: 26 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 27 | with: 28 | ref: ${{ github.event.inputs.ref }} # Use the ref if provided, otherwise defaults to the current branch/commit 29 | 30 | - uses: nixbuild/nix-quick-install-action@8505cd40ae3d4791ca658f2697c5767212e5ce71 31 | with: 32 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | - name: Download obelisk 35 | run: | 36 | curl -L --tlsv1.2 -sSf https://raw.githubusercontent.com/obeli-sk/obelisk/main/download.sh | bash 37 | echo $(pwd) >> $GITHUB_PATH 38 | - name: Log in to Docker Hub 39 | run: | 40 | echo "$DOCKER_HUB_TOKEN" | docker login -u "$DOCKER_HUB_USERNAME" --password-stdin 41 | env: 42 | DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }} 43 | DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }} 44 | - name: Populate the nix store 45 | run: nix develop .#web --command echo 46 | - name: Push webui 47 | run: | 48 | which obelisk 49 | nix develop .#web --command ./scripts/push-webui.sh $TAG 50 | env: 51 | TAG: ${{ github.event.inputs.tag }} 52 | - name: Configure git before push 53 | run: | 54 | git config user.name "github-actions[bot]" 55 | git config user.email "github-actions[bot]@users.noreply.github.com" 56 | - name: Push webui-version.txt 57 | run: | 58 | git add assets/webui-version.txt 59 | git commit -m "chore: Bump webui to $TAG" 60 | git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git 61 | git push origin main 62 | env: 63 | TAG: ${{ github.event.inputs.tag }} 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | -------------------------------------------------------------------------------- /.github/workflows/release-2-cargo-publish.yml: -------------------------------------------------------------------------------- 1 | name: release-2-cargo-publish 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | ref: 10 | description: "The ref (branch or SHA) to process" 11 | required: false 12 | type: string 13 | default: "latest" 14 | update-main: 15 | description: "Should the branch `main` be updated?" 16 | required: false 17 | type: boolean 18 | default: true 19 | extra-args: 20 | description: "Extra args to be used with cargo publish e.g. --no-verify" 21 | type: string 22 | required: false 23 | default: "" 24 | 25 | defaults: 26 | run: 27 | shell: bash -xe {0} 28 | 29 | jobs: 30 | cargo-publish: 31 | runs-on: ubuntu-24.04 32 | steps: 33 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 34 | with: 35 | ref: ${{ github.event.inputs.ref }} # Use the ref if provided, otherwise defaults to the current branch/commit 36 | fetch-depth: 0 37 | 38 | - uses: nixbuild/nix-quick-install-action@8505cd40ae3d4791ca658f2697c5767212e5ce71 39 | with: 40 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 41 | 42 | - name: Configure git before push 43 | run: | 44 | git config user.name "github-actions[bot]" 45 | git config user.email "github-actions[bot]@users.noreply.github.com" 46 | 47 | - name: Run cargo publish 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }} 51 | run: | 52 | nix develop '.#publish' --command ./scripts/cargo-publish-workspace.sh --locked ${{ github.event.inputs.extra-args }} 53 | 54 | - name: Update `main` branch 55 | if: ${{ github.event.inputs.update-main }} 56 | run: | 57 | git push origin HEAD:main 58 | -------------------------------------------------------------------------------- /.github/workflows/release-3-create-github-release.yml: -------------------------------------------------------------------------------- 1 | name: release-3-create-github-release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | ref: 10 | description: "The ref (branch or SHA) to process" 11 | required: false 12 | type: string 13 | workflow_run: 14 | workflows: 15 | - release-2-cargo-publish 16 | types: 17 | - completed 18 | 19 | defaults: 20 | run: 21 | shell: bash -xe {0} 22 | 23 | jobs: 24 | create-github-release: 25 | runs-on: ubuntu-24.04 26 | if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} 27 | steps: 28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 29 | with: 30 | # Use the ref if provided, otherwise obtain the parent workflow branch, defaults to main. 31 | ref: ${{ github.event.inputs.ref || github.event.workflow_run.head_branch }} 32 | 33 | - id: git-info 34 | run: | 35 | VERSION="$(grep -m1 '^version = "' Cargo.toml | cut -d'"' -f2)" 36 | echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT 37 | echo "tag=v$VERSION" >> $GITHUB_OUTPUT 38 | echo "version=$VERSION" >> $GITHUB_OUTPUT 39 | 40 | - name: Create GitHub Release 41 | run: | 42 | echo "Releasing $TAG $SHA" 43 | body=$(./scripts/extract-release-info.sh "$VERSION" | jq -R -s .) 44 | curl -v --fail -X POST \ 45 | -H "Content-Type: application/json" \ 46 | -H "Authorization: Bearer $GITHUB_TOKEN" \ 47 | -d '{ 48 | "tag_name": "'"$TAG"'", 49 | "target_commitish": "'"$SHA"'", 50 | "name": "obelisk-'"$TAG"'", 51 | "body": '"$body"', 52 | "draft": false, 53 | "prerelease": false 54 | }' \ 55 | https://api.github.com/repos/obeli-sk/obelisk/releases 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | SHA: ${{ steps.git-info.outputs.sha }} 59 | TAG: ${{ steps.git-info.outputs.tag }} 60 | VERSION: ${{ steps.git-info.outputs.version }} 61 | -------------------------------------------------------------------------------- /.github/workflows/release-5-verify-cargo-install.yml: -------------------------------------------------------------------------------- 1 | name: release-5-verify-cargo-install 2 | 3 | # Contrary to `check-run-cargo-install`, install latest Rust and run cargo install using sources from crates.io . 4 | 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | ref: 9 | description: "The ref (branch or SHA) to obtain the tag" 10 | required: false 11 | type: string 12 | workflow_run: 13 | workflows: 14 | - release-3.1-upload-artifacts 15 | types: 16 | - completed 17 | 18 | defaults: 19 | run: 20 | shell: bash -xe {0} 21 | 22 | jobs: 23 | verify-assets: 24 | runs-on: ubuntu-24.04 25 | if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} 26 | strategy: 27 | fail-fast: false 28 | steps: 29 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 30 | with: 31 | # Use the ref if provided, otherwise obtain the parent workflow branch, defaults to main. 32 | ref: ${{ github.event.inputs.ref || github.event.workflow_run.head_branch }} 33 | 34 | - id: git-info 35 | run: | 36 | VERSION="$(grep -m1 '^version = "' Cargo.toml | cut -d'"' -f2)" 37 | echo "version=$VERSION" >> $GITHUB_OUTPUT 38 | echo "tag=v$VERSION" >> $GITHUB_OUTPUT 39 | echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT 40 | 41 | - name: Install latest Rust 42 | run: | 43 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 44 | 45 | - name: Install dependencies 46 | run: | 47 | sudo apt update 48 | sudo apt install protobuf-compiler -y 49 | 50 | - name: Run cargo install 51 | run: | 52 | source "$HOME/.cargo/env" 53 | cargo install obelisk@$VERSION --locked 54 | env: 55 | VERSION: ${{ steps.git-info.outputs.version }} 56 | 57 | - name: Verify Obelisk 58 | run: | 59 | source "$HOME/.cargo/env" 60 | mkdir tmp # Avoid conflict with existing obelisk.toml 61 | cd tmp 62 | obelisk --version 63 | obelisk server generate-config 64 | obelisk server verify 65 | -------------------------------------------------------------------------------- /.github/workflows/release-5-verify-github-release.yml: -------------------------------------------------------------------------------- 1 | name: release-5-verify-github-release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | ref: 7 | description: "The ref (branch or SHA) to obtain the tag" 8 | required: false 9 | type: string 10 | workflow_run: 11 | workflows: 12 | - release-3.1-upload-artifacts 13 | types: 14 | - completed 15 | 16 | defaults: 17 | run: 18 | shell: bash -xe {0} 19 | 20 | jobs: 21 | verify-assets: 22 | name: ${{ matrix.runner }} 23 | runs-on: ${{ matrix.runner }} 24 | if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | include: 29 | - runner: ubuntu-22.04 30 | file: obelisk-x86_64-unknown-linux-gnu.tar.gz 31 | - runner: ubuntu-22.04 32 | file: obelisk-x86_64-unknown-linux-musl.tar.gz 33 | - runner: macos-13 34 | file: obelisk-x86_64-apple-darwin.tar.gz 35 | - runner: macos-14 36 | file: obelisk-aarch64-apple-darwin.tar.gz 37 | steps: 38 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 39 | with: 40 | # Use the ref if provided, otherwise obtain the parent workflow branch, defaults to main. 41 | ref: ${{ github.event.inputs.ref || github.event.workflow_run.head_branch }} 42 | 43 | - id: git-info 44 | run: | 45 | VERSION="$(grep -m1 '^version = "' Cargo.toml | cut -d'"' -f2)" 46 | echo "version=$VERSION" >> $GITHUB_OUTPUT 47 | echo "tag=v$VERSION" >> $GITHUB_OUTPUT 48 | echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT 49 | 50 | - run: | 51 | mkdir temp 52 | cd temp 53 | # download the binary from GitHub Releases 54 | FILE="${{ matrix.file }}" 55 | curl --fail -L -o "$FILE" \ 56 | https://github.com/obeli-sk/obelisk/releases/download/$TAG/$FILE 57 | tar xzfv "$FILE" 58 | ./obelisk --version | grep $VERSION 59 | ./obelisk server generate-config 60 | ./obelisk server verify 61 | env: 62 | TAG: ${{ steps.git-info.outputs.tag }} 63 | VERSION: ${{ steps.git-info.outputs.version }} 64 | -------------------------------------------------------------------------------- /.github/workflows/release/docker-image/alpine.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | WORKDIR /obelisk 3 | ADD obelisk . 4 | ADD obelisk.toml /etc/obelisk/obelisk.toml 5 | ENV PATH="/obelisk:${PATH}" 6 | ENTRYPOINT ["obelisk"] 7 | CMD ["server", "run"] 8 | -------------------------------------------------------------------------------- /.github/workflows/release/docker-image/ubuntu-24.04.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | WORKDIR /obelisk 3 | ADD obelisk . 4 | ADD obelisk.toml /etc/obelisk/obelisk.toml 5 | ENV PATH="/obelisk:${PATH}" 6 | ENTRYPOINT ["obelisk"] 7 | CMD ["server", "run"] 8 | -------------------------------------------------------------------------------- /.github/workflows/scheduled-bump-flake-lock.yml: -------------------------------------------------------------------------------- 1 | name: scheduled-bump-flake-lock 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0' # Runs every Sunday at 00:00 UTC 6 | workflow_dispatch: 7 | 8 | defaults: 9 | run: 10 | shell: bash -xe {0} 11 | 12 | permissions: 13 | contents: write 14 | pull-requests: write 15 | 16 | jobs: 17 | bump-cargo-lock: 18 | runs-on: ubuntu-24.04 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 21 | with: 22 | fetch-depth: 1 23 | 24 | - name: Generate Unique Branch Name 25 | id: branch-name 26 | run: echo "branch_name=bump-cargo-lock-$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT 27 | 28 | - uses: nixbuild/nix-quick-install-action@8505cd40ae3d4791ca658f2697c5767212e5ce71 29 | with: 30 | github_access_token: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | - name: Bump flake.lock 33 | run: | 34 | nix flake update 35 | nix develop --command ./scripts/dev-deps.sh 36 | 37 | - name: Configure Git 38 | run: | 39 | git config user.name "github-actions[bot]" 40 | git config user.email "github-actions[bot]@users.noreply.github.com" 41 | 42 | - name: Commit Changes 43 | run: | 44 | git checkout -b ${{ steps.branch-name.outputs.branch_name }} 45 | git add . 46 | if [ -n "$(git status --porcelain)" ]; then 47 | git commit -m 'chore: Bump `flake.lock`' 48 | git push origin ${{ steps.branch-name.outputs.branch_name }} 49 | 50 | OWNER=$(echo "${{ github.repository }}" | cut -d'/' -f1) 51 | REPO=$(echo "${{ github.repository }}" | cut -d'/' -f2) 52 | curl -v --fail -X POST \ 53 | -H "Content-Type: application/json" \ 54 | -H "Authorization: Bearer $GITHUB_TOKEN" \ 55 | https://api.github.com/repos/$OWNER/$REPO/pulls \ 56 | -d '{ 57 | "title": "Bump `flake.lock`", 58 | "head": "'${{ steps.branch-name.outputs.branch_name }}'", 59 | "base": "main", 60 | "body": "" 61 | }' 62 | else 63 | echo "No changes to commit." 64 | fi 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PR_RW }} 67 | 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.direnv/ 3 | /.envrc 4 | test-codegen-cache 5 | /crates/webui/dist/ 6 | /crates/webui/dist-dev/ 7 | -------------------------------------------------------------------------------- /.ignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .direnv/ 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Project Licensing Information 2 | 3 | This project (all files and folders except as noted below) is licensed 4 | under the [GNU Affero General Public License version 3](LICENSE-AGPL). 5 | 6 | ## Subfolder Exceptions 7 | 8 | The following subfolders are licensed under the MIT License: 9 | 10 | * `wit/` – see [LICENSE-MIT](wit/LICENSE-MIT) 11 | * `toml/` – see [LICENSE-MIT](toml/LICENSE-MIT) 12 | * `proto/` – see [LICENSE-MIT](proto/LICENSE-MIT) 13 | 14 | ## Generated WIT Files 15 | 16 | The runtime includes functionality that may generate new "extension" WIT files. These generated files are typically based on a combination of user-provided WIT files and the base WIT definitions found in the MIT-licensed `wit/` directory. 17 | 18 | **Clarification:** Any WIT files generated by this runtime **are NOT covered by the AGPL**. These generated files primarily derive from their inputs (user WIT files and the base MIT-licensed WIT files). To ensure clarity and maintain the intended separation between the runtime and user components, any WIT files generated by the runtime are provided under the terms of the **MIT License**, consistent with the base WIT files they often depend upon. 19 | 20 | Users are free to use, modify, and distribute these generated WIT files under the terms of the MIT License, for example, to allow other WASM components to interact via these extended interfaces. 21 | -------------------------------------------------------------------------------- /assets/images/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/obeli-sk/obelisk/ece7de7e24753418a09ecea0b0fb97a3dfb0abb1/assets/images/logo-small.png -------------------------------------------------------------------------------- /assets/images/screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/obeli-sk/obelisk/ece7de7e24753418a09ecea0b0fb97a3dfb0abb1/assets/images/screencast.gif -------------------------------------------------------------------------------- /assets/unpublishable-packages.txt: -------------------------------------------------------------------------------- 1 | db-mem 2 | db-tests 3 | test-programs-dir-activity 4 | test-programs-dir-activity-builder 5 | test-programs-fibo-activity 6 | test-programs-fibo-activity-builder 7 | test-programs-fibo-webhook 8 | test-programs-fibo-webhook-builder 9 | test-programs-fibo-workflow 10 | test-programs-fibo-workflow-builder 11 | test-programs-http-get-activity 12 | test-programs-http-get-activity-builder 13 | test-programs-http-get-workflow 14 | test-programs-http-get-workflow-builder 15 | test-programs-process-activity 16 | test-programs-process-activity-builder 17 | test-programs-sleep-activity 18 | test-programs-sleep-activity-builder 19 | test-programs-sleep-workflow 20 | test-programs-sleep-workflow-builder 21 | test-utils 22 | webui 23 | webui-builder 24 | webui-proxy 25 | -------------------------------------------------------------------------------- /assets/webui-version.txt: -------------------------------------------------------------------------------- 1 | docker.io/getobelisk/webui:2025-05-26@sha256:48a5730a7e88d1c4754fc663822f9947c6fe447229f864256e9b9132a7fadb08 -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let pkg_version = std::env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION must be set"); 3 | println!("cargo:rustc-env=PKG_VERSION={pkg_version}"); 4 | 5 | tonic_build::configure() 6 | .protoc_arg("--experimental_allow_proto3_optional") // not needed anymore with protoc 25.3 7 | .compile_well_known_types(true) 8 | .extern_path(".google.protobuf.Timestamp", "::prost_wkt_types::Timestamp") 9 | .extern_path(".google.protobuf.Duration", "::prost_wkt_types::Duration") 10 | .extern_path(".google.protobuf.Any", "::prost_wkt_types::Any") 11 | .type_attribute(".", "#[derive(serde::Serialize)]") // for CLI only 12 | .type_attribute(".", "#[serde(rename_all=\"snake_case\")]") // for CLI only 13 | .build_server(true) 14 | .build_client(true) 15 | .compile_protos(&["proto/obelisk.proto"], &[] as &[&str]) 16 | .unwrap(); 17 | } 18 | -------------------------------------------------------------------------------- /crates/component-builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "obelisk-component-builder" 3 | description = "Builder of Obelisk components" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | concepts = { workspace = true, optional = true } 14 | utils = { workspace = true, optional = true } 15 | 16 | cargo_metadata.workspace = true 17 | indexmap = { workspace = true, optional = true } 18 | wasmtime = { workspace = true, optional = true } 19 | 20 | [features] 21 | genrs = ["dep:concepts", "dep:utils", "dep:indexmap", "dep:wasmtime"] 22 | 23 | [lints] 24 | workspace = true 25 | -------------------------------------------------------------------------------- /crates/concepts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "obeli-sk-concepts" 3 | description = "Internal package of obeli-sk" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | val-json = { workspace = true, features = ["wasmtime"] } 14 | 15 | arbitrary.workspace = true 16 | assert_matches.workspace = true 17 | async-trait.workspace = true 18 | cfg-if.workspace = true 19 | chrono.workspace = true 20 | const_format.workspace = true 21 | derive-where.workspace = true 22 | derive_more.workspace = true 23 | fxhash.workspace = true 24 | getrandom.workspace = true # Only needed for the madsim patch 25 | hashbrown.workspace = true 26 | indexmap.workspace = true 27 | opentelemetry.workspace = true 28 | rand.workspace = true 29 | rusqlite = { optional = true, workspace = true } 30 | serde.workspace = true 31 | serde_json.workspace = true 32 | serde_with.workspace = true 33 | strum.workspace = true 34 | thiserror.workspace = true 35 | tokio.workspace = true 36 | tracing-opentelemetry.workspace = true 37 | tracing.workspace = true 38 | ulid.workspace = true 39 | wasmtime.workspace = true 40 | wasmtime-environ.workspace = true 41 | 42 | [dev-dependencies] 43 | insta.workspace = true 44 | madsim.workspace = true 45 | rstest.workspace = true 46 | 47 | [features] 48 | test = [] 49 | rusqlite = ["dep:rusqlite"] 50 | 51 | [lints] 52 | workspace = true 53 | -------------------------------------------------------------------------------- /crates/concepts/src/rusqlite_ext.rs: -------------------------------------------------------------------------------- 1 | use crate::{ExecutionId, JoinSetId, prefixed_ulid::ExecutionIdDerived}; 2 | use rusqlite::{ 3 | ToSql, 4 | types::{FromSql, FromSqlError, ToSqlOutput}, 5 | }; 6 | use tracing::error; 7 | 8 | impl ToSql for ExecutionId { 9 | fn to_sql(&self) -> rusqlite::Result> { 10 | Ok(ToSqlOutput::from(self.to_string())) 11 | } 12 | } 13 | impl FromSql for ExecutionId { 14 | fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { 15 | let str = value.as_str()?; 16 | str.parse::().map_err(|err| { 17 | error!( 18 | backtrace = %std::backtrace::Backtrace::capture(), 19 | "Cannot convert to ExecutionId value:`{str}` - {err:?}" 20 | ); 21 | FromSqlError::InvalidType 22 | }) 23 | } 24 | } 25 | 26 | impl ToSql for ExecutionIdDerived { 27 | fn to_sql(&self) -> rusqlite::Result> { 28 | Ok(ToSqlOutput::from(self.to_string())) 29 | } 30 | } 31 | impl FromSql for ExecutionIdDerived { 32 | fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { 33 | let str = value.as_str()?; 34 | str.parse::().map_err(|err| { 35 | error!( 36 | backtrace = %std::backtrace::Backtrace::capture(), 37 | "Cannot convert to ExecutionIdDerived value:`{str}` - {err:?}" 38 | ); 39 | FromSqlError::InvalidType 40 | }) 41 | } 42 | } 43 | 44 | impl ToSql for JoinSetId { 45 | fn to_sql(&self) -> rusqlite::Result> { 46 | Ok(ToSqlOutput::from(self.to_string())) 47 | } 48 | } 49 | impl FromSql for JoinSetId { 50 | fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult { 51 | let str = value.as_str()?; 52 | str.parse::().map_err(|err| { 53 | error!( 54 | backtrace = %std::backtrace::Backtrace::capture(), 55 | "Cannot convert to JoinSetId value:`{str}` - {err:?}" 56 | ); 57 | FromSqlError::InvalidType 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/concepts/src/snapshots/obeli_sk_concepts__storage__tests__verify_pending_state_finished_result_kind_serde.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/concepts/src/storage.rs 3 | expression: ser 4 | --- 5 | [ 6 | "timeout", 7 | "unhandled_child_execution_error", 8 | "execution_failure", 9 | "fallible_error", 10 | "ok" 11 | ] 12 | -------------------------------------------------------------------------------- /crates/concepts/src/snapshots/obeli_sk_concepts__tests__execution_id_hash_should_be_stable.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/concepts/src/lib.rs 3 | expression: vec 4 | --- 5 | [ 6 | 7784354942592584825, 7 | 11723254811987876837, 8 | 17595035818551879290, 9 | 5639045558307740638, 10 | ] 11 | -------------------------------------------------------------------------------- /crates/concepts/src/time.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use chrono::DateTime; 3 | use chrono::Utc; 4 | use std::time::Duration; 5 | 6 | pub trait ClockFn: Send + Sync + Clone { 7 | fn now(&self) -> DateTime; 8 | } 9 | 10 | #[async_trait] 11 | pub trait Sleep: Send + Sync + Clone { 12 | async fn sleep(&self, duration: Duration); 13 | } 14 | 15 | #[derive(Clone)] 16 | pub struct TokioSleep; 17 | 18 | #[async_trait] 19 | impl Sleep for TokioSleep { 20 | async fn sleep(&self, duration: Duration) { 21 | tokio::time::sleep(duration).await; 22 | } 23 | } 24 | 25 | cfg_if::cfg_if! { 26 | if #[cfg(all(test, madsim))] { 27 | #[must_use] 28 | pub fn now_tokio_instant() -> tokio::time::Instant { 29 | if madsim::rand::random() { 30 | madsim::time::advance(std::time::Duration::from_millis(madsim::rand::random())); 31 | } 32 | madsim::time::Instant::now() 33 | } 34 | } else { 35 | #[must_use] 36 | pub fn now_tokio_instant() -> tokio::time::Instant { 37 | tokio::time::Instant::now() 38 | } 39 | } 40 | } 41 | 42 | #[derive(Clone)] 43 | pub struct Now; 44 | 45 | impl ClockFn for Now { 46 | cfg_if::cfg_if! { 47 | if #[cfg(all(test, madsim))] { 48 | fn now(&self) -> DateTime { 49 | if madsim::rand::random() { 50 | madsim::time::advance(std::time::Duration::from_millis(madsim::rand::random())); 51 | } 52 | DateTime::from(madsim::time::TimeHandle::current().now_time()) 53 | } 54 | 55 | } else { 56 | fn now(&self) -> DateTime { 57 | Utc::now() 58 | } 59 | 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/db-mem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "db-mem" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | concepts.workspace = true 13 | utils.workspace = true 14 | val-json.workspace = true 15 | 16 | arbitrary.workspace = true 17 | assert_matches.workspace = true 18 | async-trait.workspace = true 19 | chrono.workspace = true 20 | derive_more.workspace = true 21 | hashbrown.workspace = true 22 | itertools.workspace = true 23 | thiserror.workspace = true 24 | tokio.workspace = true 25 | tracing.workspace = true 26 | 27 | [features] 28 | test = [] 29 | 30 | [lints] 31 | workspace = true 32 | -------------------------------------------------------------------------------- /crates/db-mem/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod inmemory_dao; 2 | mod journal; 3 | -------------------------------------------------------------------------------- /crates/db-sqlite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "obeli-sk-db-sqlite" 3 | description = "Internal package of obeli-sk" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | concepts = { workspace = true, features = ["rusqlite"] } 14 | val-json.workspace = true 15 | 16 | arbitrary.workspace = true 17 | assert_matches.workspace = true 18 | async-trait.workspace = true 19 | chrono.workspace = true 20 | derive_more.workspace = true 21 | hashbrown.workspace = true 22 | hdrhistogram.workspace = true 23 | itertools.workspace = true 24 | rusqlite.workspace = true 25 | serde_json.workspace = true 26 | serde.workspace = true 27 | tempfile = { workspace = true, optional = true } 28 | thiserror.workspace = true 29 | tokio.workspace = true 30 | tracing.workspace = true 31 | 32 | [dev-dependencies] 33 | tempfile.workspace = true 34 | 35 | [features] 36 | tempfile = ["dep:tempfile"] 37 | test = [] 38 | 39 | [lints] 40 | workspace = true 41 | -------------------------------------------------------------------------------- /crates/db-sqlite/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod sqlite_dao; 2 | -------------------------------------------------------------------------------- /crates/executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "obeli-sk-executor" 3 | description = "Internal package of obeli-sk" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | concepts.workspace = true 14 | utils.workspace = true 15 | val-json.workspace = true 16 | 17 | arbitrary.workspace = true 18 | assert_matches.workspace = true 19 | async-channel.workspace = true 20 | async-trait.workspace = true 21 | chrono.workspace = true 22 | derive_more.workspace = true 23 | hashbrown.workspace = true 24 | indexmap.workspace = true 25 | opentelemetry.workspace = true 26 | serde.workspace = true 27 | thiserror.workspace = true 28 | tokio.workspace = true 29 | tracing-opentelemetry.workspace = true 30 | tracing.workspace = true 31 | ulid.workspace = true 32 | 33 | [dev-dependencies] 34 | db-mem.workspace = true 35 | db-sqlite = { workspace = true, features = ["tempfile"] } 36 | test-utils.workspace = true 37 | db-tests.workspace = true 38 | 39 | anyhow.workspace = true 40 | madsim.workspace = true 41 | rstest.workspace = true 42 | 43 | [features] 44 | test = [] 45 | 46 | [lints] 47 | workspace = true 48 | -------------------------------------------------------------------------------- /crates/executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | use tokio::task::AbortHandle; 2 | 3 | pub mod executor; 4 | pub mod expired_timers_watcher; 5 | pub mod worker; 6 | 7 | pub struct AbortOnDropHandle(pub AbortHandle); 8 | impl Drop for AbortOnDropHandle { 9 | fn drop(&mut self) { 10 | self.0.abort(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /crates/testing/db-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "db-tests" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | concepts = { workspace = true, features = ["test"] } 13 | db-mem = { workspace = true, features = ["test"] } 14 | db-sqlite = { workspace = true, features = ["tempfile", "test"] } 15 | test-utils.workspace = true 16 | utils.workspace = true 17 | val-json.workspace = true 18 | 19 | arbitrary.workspace = true 20 | assert_matches.workspace = true 21 | async-trait.workspace = true 22 | chrono.workspace = true 23 | madsim.workspace = true 24 | serde_json.workspace = true 25 | tempfile.workspace = true 26 | tokio.workspace = true 27 | tracing.workspace = true 28 | 29 | [dev-dependencies] 30 | rstest.workspace = true 31 | 32 | [lints] 33 | workspace = true 34 | -------------------------------------------------------------------------------- /crates/testing/db-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | use concepts::FunctionFqn; 2 | use db_mem::inmemory_dao::InMemoryPool; 3 | use tempfile::NamedTempFile; 4 | 5 | pub mod db_proxy; 6 | pub use db_proxy::DbConnectionProxy; 7 | pub use db_proxy::DbPoolEnum; 8 | 9 | pub const SOME_FFQN: FunctionFqn = FunctionFqn::new_static("pkg/ifc", "fn"); 10 | 11 | #[derive(Clone, Copy, Debug)] 12 | pub enum Database { 13 | Memory, 14 | Sqlite, 15 | } 16 | 17 | pub enum DbGuard { 18 | Memory, 19 | Sqlite(Option), 20 | } 21 | 22 | impl Database { 23 | pub async fn set_up(self) -> (DbGuard, DbPoolEnum) { 24 | match self { 25 | Database::Memory => (DbGuard::Memory, DbPoolEnum::Memory(InMemoryPool::new())), 26 | Database::Sqlite => { 27 | use db_sqlite::sqlite_dao::tempfile::sqlite_pool; 28 | let (db_pool, guard) = sqlite_pool().await; 29 | (DbGuard::Sqlite(guard), DbPoolEnum::Sqlite(db_pool)) 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-dir-activity" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-dir-activity-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_activity(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/src/lib.rs: -------------------------------------------------------------------------------- 1 | use exports::testing::dir::dir::Guest; 2 | use std::path::PathBuf; 3 | use wit_bindgen::generate; 4 | 5 | generate!({ generate_all }); 6 | struct Component; 7 | export!(Component); 8 | 9 | impl Guest for Component { 10 | fn io() -> Result<(), String> { 11 | let entries = ls(); 12 | // Let's put some content idempotently 13 | let tempfile = PathBuf::from("test.txt"); 14 | std::fs::write(tempfile, b"Lorem ipsum").expect("writing to a file failed"); 15 | std::fs::create_dir_all("foo/bar/baz").expect("create_dir_all failed"); 16 | 17 | if entries.is_empty() { 18 | // Should be retried and succeed.. 19 | return Err( 20 | "failing on first try, should be automatically fixed on the first retry if `reuse-on-retry` is enabled" 21 | .to_string(), 22 | ); 23 | } 24 | 25 | let entries = ls(); 26 | if entries != vec!["./foo", "./test.txt"] { 27 | return Err(format!("unexpected file list: {entries:?}")); 28 | } 29 | 30 | Ok(()) 31 | } 32 | } 33 | 34 | fn ls() -> Vec { 35 | let mut entries = std::fs::read_dir(".") 36 | .expect("read_dir failed") 37 | .map(|res| res.map(|e| e.path().to_string_lossy().into_owned())) 38 | .collect::, _>>() 39 | .expect("iterating over folder failed"); 40 | entries.sort(); 41 | println!("ls:"); 42 | println!("{entries:?}"); 43 | entries 44 | } 45 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/wit/deps/testing_dir/dir.wit: -------------------------------------------------------------------------------- 1 | package testing:dir; 2 | 3 | interface dir { 4 | io: func() -> result<_, string>; 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/dir/activity/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:dir/dir; 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-fibo-activity" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-fibo-activity-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_activity(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/src/lib.rs: -------------------------------------------------------------------------------- 1 | use exports::testing::fibo::fibo::Guest; 2 | use wit_bindgen::generate; 3 | 4 | generate!({ generate_all }); 5 | struct Component; 6 | export!(Component); 7 | 8 | impl Guest for Component { 9 | fn fibo(n: u8) -> u64 { 10 | fibo(n) 11 | } 12 | } 13 | 14 | fn fibo(n: u8) -> u64 { 15 | if n == 0 { 16 | 0 17 | } else if n == 1 { 18 | 1 19 | } else { 20 | fibo(n - 1) + fibo(n - 2) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/wit/deps/testing_fibo: -------------------------------------------------------------------------------- 1 | ../testing_fibo -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:fibo/fibo; 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/wit/testing_fibo-obelisk-ext/fibo-ext.wit: -------------------------------------------------------------------------------- 1 | package testing:fibo-obelisk-ext; 2 | 3 | interface fibo { 4 | use obelisk:types/execution@1.1.0.{execution-id, join-set-id, execution-error}; 5 | 6 | fibo-submit: func(join-set-id: borrow, n: u8) -> execution-id; 7 | 8 | fibo-await-next: func(join-set-id: borrow) -> result, tuple>; 9 | } 10 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/activity/wit/testing_fibo/fibo.wit: -------------------------------------------------------------------------------- 1 | package testing:fibo; 2 | 3 | interface fibo { 4 | fibo: func(n: u8) -> u64; 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-fibo-webhook" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | waki.workspace = true 14 | waki-macros.workspace = true 15 | 16 | [lib] 17 | crate-type = ["cdylib"] 18 | 19 | [lints] 20 | workspace = true 21 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-fibo-webhook-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_webhook_endpoint(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/src/lib.rs: -------------------------------------------------------------------------------- 1 | use crate::obelisk::log::log::info; 2 | use crate::obelisk::types::time::ScheduleAt; 3 | use crate::testing::fibo_workflow::workflow; 4 | use crate::testing::fibo_workflow_obelisk_ext::workflow as workflow_ext; 5 | use waki::{ErrorCode, Request, Response, handler}; 6 | use wit_bindgen::generate; 7 | 8 | generate!({ generate_all }); 9 | 10 | #[handler] 11 | fn handle(_req: Request) -> Result { 12 | let Ok(n) = std::env::var("N") 13 | .expect("env var `N` must be set by Obelisk if routes are configured properly") 14 | .parse() 15 | else { 16 | return Response::builder().status_code(400).build(); 17 | }; 18 | 19 | let Ok(iterations) = std::env::var("ITERATIONS") 20 | .expect("env var `ITERATIONS` must be set by Obelisk if routes are configured properly") 21 | .parse() 22 | else { 23 | return Response::builder().status_code(400).build(); 24 | }; 25 | 26 | let fibo_res = if n >= 10 { 27 | println!("scheduling"); 28 | let execution_id = workflow_ext::fiboa_schedule(ScheduleAt::Now, n, iterations); 29 | format!("scheduled: {}", execution_id.id) 30 | } else if n > 1 { 31 | // Call the execution directly. 32 | println!("direct call"); 33 | let fibo_res = workflow::fiboa(n, iterations); 34 | format!("direct call: {fibo_res}") 35 | } else { 36 | assert_eq!(iterations, 0); // For testing traps 37 | println!("hardcoded"); 38 | "hardcoded: 1".to_string() // For performance testing - no activity is called 39 | }; 40 | info(&format!("Sending response {fibo_res}")); 41 | Response::builder() 42 | .body(format!("fiboa({n}, {iterations}) = {fibo_res}")) 43 | .build() 44 | } 45 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/wit/deps/obelisk_log@1.0.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_log@1.0.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/wit/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/wit/deps/testing_fibo-workflow: -------------------------------------------------------------------------------- 1 | ../../../workflow/wit/testing_fibo-workflow -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/wit/deps/testing_fibo-workflow-obelisk-ext: -------------------------------------------------------------------------------- 1 | ../../../workflow/wit/testing_fibo-workflow-obelisk-ext -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/webhook/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | import obelisk:log/log@1.0.0; 5 | import testing:fibo-workflow-obelisk-ext/workflow; 6 | import testing:fibo-workflow/workflow; 7 | } 8 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-fibo-workflow" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-fibo-workflow-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_workflow(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/obelisk_log@1.0.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_log@1.0.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/obelisk_workflow@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_workflow@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/testing_fibo: -------------------------------------------------------------------------------- 1 | ../../../activity/wit/testing_fibo -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/testing_fibo-obelisk-ext: -------------------------------------------------------------------------------- 1 | ../../../activity/wit/testing_fibo-obelisk-ext -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/testing_fibo-workflow: -------------------------------------------------------------------------------- 1 | ../testing_fibo-workflow -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/deps/testing_fibo-workflow-obelisk-ext: -------------------------------------------------------------------------------- 1 | ../testing_fibo-workflow-obelisk-ext -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:fibo-workflow/workflow-nesting; 5 | export testing:fibo-workflow/workflow; 6 | import obelisk:log/log@1.0.0; 7 | import obelisk:workflow/workflow-support@1.1.0; 8 | import testing:fibo-obelisk-ext/fibo; 9 | import testing:fibo-workflow-obelisk-ext/workflow; 10 | import testing:fibo-workflow/workflow-nesting; 11 | import testing:fibo-workflow/workflow; 12 | import testing:fibo/fibo; 13 | } 14 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/testing_fibo-workflow-obelisk-ext/workflow-ext.wit: -------------------------------------------------------------------------------- 1 | package testing:fibo-workflow-obelisk-ext; 2 | 3 | interface workflow { 4 | use obelisk:types/execution@1.1.0.{execution-id, join-set-id, execution-error}; 5 | use obelisk:types/time@1.1.0.{schedule-at}; 6 | 7 | fiboa-submit: func(join-set-id: borrow, n: u8, iterations: u32) -> execution-id; 8 | fiboa-await-next: func(join-set-id: borrow) -> result, tuple>; 9 | fiboa-schedule: func(schedule-at: schedule-at, n: u8, iterations: u32) -> execution-id; 10 | 11 | fiboa-concurrent-submit: func(join-set-id: borrow, n: u8, iterations: u32) -> execution-id; 12 | fiboa-concurrent-await-next: func(join-set-id: borrow) -> result, tuple>; 13 | } 14 | -------------------------------------------------------------------------------- /crates/testing/test-programs/fibo/workflow/wit/testing_fibo-workflow/ifc.wit: -------------------------------------------------------------------------------- 1 | package testing:fibo-workflow; 2 | 3 | interface workflow { 4 | fibow: func(n: u8, iterations: u32) -> u64; 5 | fiboa: func(n: u8, iterations: u32) -> u64; 6 | fiboa-concurrent: func(n: u8, iterations: u32) -> u64; 7 | } 8 | 9 | interface workflow-nesting { 10 | fibo-nested-workflow: func(n: u8) -> u64; 11 | fibo-start-fiboas: func(n: u8, fiboas: u32, iterations-per-fiboa: u32) -> u64; 12 | } 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-http-get-activity" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | anyhow.workspace = true 13 | waki.workspace = true 14 | wit-bindgen.workspace = true 15 | 16 | [lib] 17 | crate-type = ["cdylib"] 18 | 19 | [lints] 20 | workspace = true 21 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-http-get-activity-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_activity(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/src/lib.rs: -------------------------------------------------------------------------------- 1 | use crate::exports::testing::http::http_get::Guest; 2 | use exports::testing::http::http_get; 3 | use std::time::Duration; 4 | use wit_bindgen::generate; 5 | 6 | generate!({ generate_all }); 7 | struct Component; 8 | export!(Component); 9 | 10 | impl Guest for Component { 11 | fn get(url: String) -> Result { 12 | let resp = Self::get_resp(url)?; 13 | Ok(String::from_utf8_lossy(&resp.body).into_owned()) 14 | } 15 | 16 | fn get_successful(url: String) -> Result { 17 | let resp = Self::get_resp(url)?; 18 | if resp.status_code >= 200 && resp.status_code <= 299 { 19 | Ok(String::from_utf8_lossy(&resp.body).into_owned()) 20 | } else { 21 | assert!((resp.status_code != 418), "418 causes trap"); 22 | Err(format!("wrong status code: {}", resp.status_code)) 23 | } 24 | } 25 | 26 | fn get_resp(url: String) -> Result { 27 | let resp = waki::Client::new() 28 | .get(&url) 29 | .connect_timeout(Duration::from_secs(1)) 30 | .send() 31 | .map_err(|err| format!("{err:?}"))?; 32 | let status_code = resp.status_code(); 33 | let body = resp.body().map_err(|err| format!("{err:?}"))?; 34 | Ok(http_get::Response { body, status_code }) 35 | } 36 | 37 | fn get_stargazers() -> Result { 38 | Ok(http_get::Stargazers { 39 | cursor: "cursor".to_string(), 40 | logins: "logins".to_string(), 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/wit/deps/obelisk_log@1.0.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_log@1.0.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/wit/deps/testing_http: -------------------------------------------------------------------------------- 1 | ../testing_http -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:http/http-get; 5 | import obelisk:log/log@1.0.0; 6 | } 7 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/wit/testing_http-obelisk-ext/http-get-ext.wit: -------------------------------------------------------------------------------- 1 | package testing:http-obelisk-ext; 2 | 3 | interface http-get { 4 | use obelisk:types/execution@1.1.0.{execution-id, join-set-id, execution-error}; 5 | 6 | get-successful-submit: func(join-set-id: borrow, url: string) -> execution-id; 7 | 8 | get-successful-await-next: func(join-set-id: borrow) -> result>, tuple>; 9 | } 10 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/activity/wit/testing_http/http-get.wit: -------------------------------------------------------------------------------- 1 | package testing:http; 2 | 3 | interface http-get { 4 | 5 | record response { 6 | body: list, 7 | status-code: u16, 8 | } 9 | 10 | 11 | record stargazers { 12 | logins: string, 13 | cursor: string, 14 | } 15 | 16 | get: func(url: string) -> result; 17 | get-resp: func(url: string) -> result; 18 | get-stargazers: func() -> result; 19 | get-successful: func(url: string) -> result; 20 | } 21 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-http-get-workflow" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-http-get-workflow-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_workflow(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/src/lib.rs: -------------------------------------------------------------------------------- 1 | use exports::testing::http_workflow::workflow::Guest; 2 | use obelisk::workflow::workflow_support::{ClosingStrategy, new_join_set_generated}; 3 | use testing::{ 4 | http::http_get, 5 | http_obelisk_ext::http_get::{get_successful_await_next, get_successful_submit}, 6 | }; 7 | use wit_bindgen::generate; 8 | 9 | generate!({ generate_all }); 10 | struct Component; 11 | export!(Component); 12 | 13 | impl Guest for Component { 14 | fn get(url: String) -> Result { 15 | http_get::get(&url) 16 | } 17 | 18 | fn get_resp(url: String) -> Result { 19 | let resp = http_get::get_resp(&url)?; 20 | Ok(String::from_utf8_lossy(&resp.body).into_owned()) 21 | } 22 | 23 | fn get_successful(url: String) -> Result { 24 | http_get::get_successful(&url) 25 | } 26 | 27 | fn get_successful_concurrently(urls: Vec) -> Result, String> { 28 | let join_set_id = new_join_set_generated(ClosingStrategy::Complete); 29 | println!("Created join set {}", join_set_id.id()); 30 | let length = urls.len(); 31 | for url in urls { 32 | let _execution_id = get_successful_submit(&join_set_id, &url); 33 | } 34 | let mut list = Vec::with_capacity(length); 35 | for _ in 0..length { 36 | // Mark the whole result as failed if any child execution fails. 37 | let contents = get_successful_await_next(&join_set_id).unwrap().1?; 38 | list.push(contents); 39 | } 40 | Ok(list) 41 | } 42 | 43 | fn get_successful_concurrently_stress( 44 | url: String, 45 | concurrency: u32, 46 | ) -> Result, String> { 47 | let join_set_id = new_join_set_generated(ClosingStrategy::Complete); 48 | for _ in 0..concurrency { 49 | let _execution_id = get_successful_submit(&join_set_id, &url); 50 | } 51 | let mut list = Vec::with_capacity(concurrency as usize); 52 | for _ in 0..concurrency { 53 | // Mark the whole result as failed if any child execution fails. 54 | let contents = get_successful_await_next(&join_set_id).unwrap().1?; 55 | list.push(contents); 56 | } 57 | Ok(list) 58 | } 59 | 60 | fn get_stargazers() { 61 | http_get::get_stargazers().unwrap(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/wit/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/wit/deps/obelisk_workflow@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_workflow@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/wit/deps/testing_http: -------------------------------------------------------------------------------- 1 | ../../../activity/wit/testing_http -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/wit/deps/testing_http-obelisk-ext: -------------------------------------------------------------------------------- 1 | ../../../activity/wit/testing_http-obelisk-ext -------------------------------------------------------------------------------- /crates/testing/test-programs/http/workflow/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package testing:http-workflow; 2 | 3 | interface workflow { 4 | get: func(url: string) -> result; 5 | get-resp: func(url: string) -> result; 6 | get-stargazers: func(); 7 | get-successful: func(url: string) -> result; 8 | get-successful-concurrently: func(urls: list) -> result, string>; 9 | get-successful-concurrently-stress: func(url: string, concurrency: u32) -> result, string>; 10 | } 11 | 12 | world any { 13 | import testing:http/http-get; 14 | import testing:http-obelisk-ext/http-get; 15 | import obelisk:workflow/workflow-support@1.1.0; 16 | export workflow; 17 | } 18 | -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-process-activity" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | anyhow.workspace = true 13 | wasi.workspace = true 14 | wit-bindgen.workspace = true 15 | wstd.workspace = true 16 | 17 | [lib] 18 | crate-type = ["cdylib"] 19 | 20 | [lints] 21 | workspace = true 22 | -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-process-activity-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_activity(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/wit/deps/obelisk_activity@1.0.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_activity@1.0.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/wit/deps/testing_process/process.wit: -------------------------------------------------------------------------------- 1 | package testing:process; 2 | 3 | interface process { 4 | touch: func() -> result<_, string>; 5 | 6 | stdio: func() -> result<_, string>; 7 | 8 | kill: func() -> result<_, string>; 9 | 10 | exec-sleep: func() -> result; 11 | } 12 | -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/wit/deps/wasi_io@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/wasi_io@0.2.3 -------------------------------------------------------------------------------- /crates/testing/test-programs/process/activity/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:process/process; 5 | 6 | import obelisk:activity/process@1.0.0; 7 | } 8 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-sleep-activity" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-sleep-activity-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_activity(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/src/lib.rs: -------------------------------------------------------------------------------- 1 | use exports::testing::sleep::sleep::Guest; 2 | use obelisk::types::time::Duration as DurationEnum; 3 | use std::time::Duration; 4 | use wit_bindgen::generate; 5 | 6 | generate!({ generate_all }); 7 | struct Component; 8 | export!(Component); 9 | 10 | impl Guest for Component { 11 | fn sleep(duration: DurationEnum) { 12 | std::thread::sleep(Duration::from(duration)); 13 | } 14 | 15 | fn sleep_loop(duration: DurationEnum, iterations: u32) { 16 | for _ in 0..iterations { 17 | Self::sleep(duration); 18 | } 19 | } 20 | } 21 | 22 | impl From for Duration { 23 | fn from(value: DurationEnum) -> Self { 24 | match value { 25 | DurationEnum::Milliseconds(millis) => Duration::from_millis(millis), 26 | DurationEnum::Seconds(secs) => Duration::from_secs(secs), 27 | DurationEnum::Minutes(mins) => Duration::from_secs(u64::from(mins * 60)), 28 | DurationEnum::Hours(hours) => Duration::from_secs(u64::from(hours * 60 * 60)), 29 | DurationEnum::Days(days) => Duration::from_secs(u64::from(days * 24 * 60 * 60)), 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/wit/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/wit/deps/testing_sleep: -------------------------------------------------------------------------------- 1 | ../testing_sleep -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:sleep/sleep; 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/wit/testing_sleep-obelisk-ext/sleep-ext.wit: -------------------------------------------------------------------------------- 1 | package testing:sleep-obelisk-ext; 2 | 3 | interface sleep { 4 | use obelisk:types/time@1.1.0.{duration}; 5 | use obelisk:types/execution@1.1.0.{join-set-id, execution-id}; 6 | 7 | sleep-submit: func(join-set-id: borrow, duration: duration) -> execution-id; 8 | } 9 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/activity/wit/testing_sleep/sleep.wit: -------------------------------------------------------------------------------- 1 | package testing:sleep; 2 | 3 | interface sleep { 4 | use obelisk:types/time@1.1.0.{duration}; 5 | 6 | sleep: func(duration: duration); 7 | sleep-loop: func(duration: duration, iterations: u32); 8 | } 9 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-sleep-workflow" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | wit-bindgen.workspace = true 13 | 14 | [lib] 15 | crate-type = ["cdylib"] 16 | 17 | [lints] 18 | workspace = true 19 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-programs-sleep-workflow-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | obelisk-component-builder.workspace = true 13 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/builder/build.rs: -------------------------------------------------------------------------------- 1 | use obelisk_component_builder::BuildConfig; 2 | 3 | fn main() { 4 | obelisk_component_builder::build_workflow(BuildConfig::default()); 5 | } 6 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/gen.rs")); 2 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/src/lib.rs: -------------------------------------------------------------------------------- 1 | use exports::testing::sleep_workflow::workflow::Guest; 2 | use obelisk::types::execution::ExecutionId; 3 | use obelisk::types::time::Duration as DurationEnum; 4 | use obelisk::types::time::ScheduleAt; 5 | use obelisk::workflow::workflow_support::ClosingStrategy; 6 | use obelisk::workflow::workflow_support::{self, new_join_set_generated}; 7 | use testing::sleep::sleep as sleep_activity; 8 | use testing::sleep_obelisk_ext::sleep as sleep_activity_ext; 9 | use testing::sleep_workflow_obelisk_ext::workflow as workflow_ext; 10 | use wit_bindgen::generate; 11 | 12 | generate!({ generate_all }); 13 | struct Component; 14 | export!(Component); 15 | 16 | impl Guest for Component { 17 | fn sleep_host_activity(duration: DurationEnum) { 18 | workflow_support::sleep(duration); 19 | } 20 | 21 | fn sleep_activity(duration: DurationEnum) { 22 | sleep_activity::sleep(duration); 23 | } 24 | 25 | fn sleep_activity_submit(duration: DurationEnum) -> ExecutionId { 26 | let join_set_id = new_join_set_generated(ClosingStrategy::Complete); 27 | sleep_activity_ext::sleep_submit(&join_set_id, duration) 28 | } 29 | 30 | fn reschedule(duration: DurationEnum, iterations: u8) { 31 | if iterations > 0 { 32 | workflow_ext::reschedule_schedule(ScheduleAt::In(duration), duration, iterations - 1); 33 | } 34 | } 35 | 36 | fn sleep_random(min_millis: u64, max_millis_inclusive: u64) { 37 | let random_millis = 38 | workflow_support::random_u64_inclusive(min_millis, max_millis_inclusive); 39 | let random_duration = DurationEnum::Milliseconds(random_millis); 40 | workflow_support::sleep(random_duration); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/deps/obelisk_workflow@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../../../../wit/obelisk_workflow@1.1.0 -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/deps/testing_sleep: -------------------------------------------------------------------------------- 1 | ../../../activity/wit/testing_sleep -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/deps/testing_sleep-obelisk-ext: -------------------------------------------------------------------------------- 1 | ../../../activity/wit/testing_sleep-obelisk-ext -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/deps/testing_sleep-workflow: -------------------------------------------------------------------------------- 1 | ../testing_sleep-workflow -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/deps/testing_sleep-workflow-obelisk-ext: -------------------------------------------------------------------------------- 1 | ../testing_sleep-workflow-obelisk-ext -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | export testing:sleep-workflow/workflow; 5 | import obelisk:workflow/workflow-support@1.1.0; 6 | import testing:sleep-obelisk-ext/sleep; 7 | import testing:sleep-workflow-obelisk-ext/workflow; 8 | import testing:sleep/sleep; 9 | } 10 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/testing_sleep-workflow-obelisk-ext/sleep-ext.wit: -------------------------------------------------------------------------------- 1 | package testing:sleep-workflow-obelisk-ext; 2 | 3 | interface workflow { 4 | use obelisk:types/execution@1.1.0.{execution-id}; 5 | use obelisk:types/time@1.1.0.{duration, schedule-at}; 6 | 7 | reschedule-schedule: func(schedule: schedule-at, duration: duration, iterations: u8) -> execution-id; 8 | } 9 | -------------------------------------------------------------------------------- /crates/testing/test-programs/sleep/workflow/wit/testing_sleep-workflow/sleep.wit: -------------------------------------------------------------------------------- 1 | package testing:sleep-workflow; 2 | 3 | interface workflow { 4 | use obelisk:types/time@1.1.0.{duration}; 5 | use obelisk:types/execution@1.1.0.{execution-id}; 6 | 7 | sleep-host-activity: func(duration: duration); 8 | sleep-activity: func(duration: duration); 9 | sleep-activity-submit: func(duration: duration) -> execution-id; 10 | reschedule: func(duration: duration, iterations: u8); 11 | sleep-random: func(min-millis: u64, max-millis-inclusive: u64); 12 | } 13 | -------------------------------------------------------------------------------- /crates/testing/test-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-utils" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [dependencies] 12 | concepts.workspace = true 13 | utils.workspace = true 14 | 15 | arbitrary.workspace = true 16 | chrono.workspace = true 17 | madsim.workspace = true 18 | tokio.workspace = true 19 | tracing.workspace = true 20 | tracing-subscriber.workspace = true 21 | 22 | [lints] 23 | workspace = true 24 | -------------------------------------------------------------------------------- /crates/testing/test-utils/src/arbitrary.rs: -------------------------------------------------------------------------------- 1 | pub struct UnstructuredHolder { 2 | raw_data: Vec, 3 | } 4 | 5 | impl Default for UnstructuredHolder { 6 | fn default() -> Self { 7 | Self::new() 8 | } 9 | } 10 | 11 | impl UnstructuredHolder { 12 | #[must_use] 13 | pub fn new() -> Self { 14 | let len = madsim::rand::random::() as usize; 15 | let mut raw_data = Vec::with_capacity(len); 16 | while raw_data.len() < len { 17 | raw_data.push(madsim::rand::random::()); 18 | } 19 | Self { raw_data } 20 | } 21 | 22 | #[must_use] 23 | pub fn unstructured(&self) -> arbitrary::Unstructured<'_> { 24 | arbitrary::Unstructured::new(&self.raw_data) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/testing/test-utils/src/sim_clock.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use concepts::time::{ClockFn, Now}; 3 | use std::{sync::Arc, time::Duration}; 4 | use tracing::info; 5 | 6 | #[derive(Clone)] 7 | pub struct SimClock { 8 | current_time: Arc>>, 9 | } 10 | 11 | impl Default for SimClock { 12 | fn default() -> Self { 13 | Self::new(Now.now()) 14 | } 15 | } 16 | 17 | impl SimClock { 18 | #[must_use] 19 | pub fn epoch() -> SimClock { 20 | Self::new(DateTime::from_timestamp_nanos(0)) 21 | } 22 | } 23 | 24 | impl SimClock { 25 | #[must_use] 26 | pub fn new(now: DateTime) -> Self { 27 | Self { 28 | current_time: Arc::new(std::sync::Mutex::new(now)), 29 | } 30 | } 31 | 32 | pub async fn move_time_forward(&self, duration: Duration) { 33 | let (old, new) = { 34 | let mut guard = self.current_time.lock().unwrap(); 35 | let old = *guard; 36 | let new = old + duration; 37 | *guard = new; 38 | (old, new) 39 | }; 40 | tokio::time::sleep(Duration::ZERO).await; // Hack that makes sure the other task have a chance to make progress. 41 | info!("Set clock from `{old}` to `{new}`"); 42 | } 43 | 44 | pub async fn move_time_to(&self, new: DateTime) { 45 | let (old, new) = { 46 | let mut guard = self.current_time.lock().unwrap(); 47 | let old = *guard; 48 | assert!(old <= new); 49 | *guard = new; 50 | (old, new) 51 | }; 52 | tokio::time::sleep(Duration::ZERO).await; // Hack that makes sure the other task have a chance to make progress. 53 | info!("Set clock from `{old}` to `{new}`"); 54 | } 55 | } 56 | impl ClockFn for SimClock { 57 | fn now(&self) -> DateTime { 58 | *self.current_time.lock().unwrap() 59 | } 60 | } 61 | 62 | #[derive(Clone, Copy)] 63 | pub struct ConstClock(pub DateTime); 64 | impl ClockFn for ConstClock { 65 | fn now(&self) -> DateTime { 66 | self.0 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crates/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "obeli-sk-utils" 3 | description = "Internal package of obeli-sk" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | concepts.workspace = true 14 | val-json.workspace = true 15 | 16 | anyhow.workspace = true 17 | async-trait.workspace = true 18 | chrono.workspace = true 19 | const_format.workspace = true 20 | derive_more.workspace = true 21 | hashbrown.workspace = true 22 | hyper.workspace = true 23 | id-arena.workspace = true 24 | indexmap.workspace = true 25 | semver.workspace = true 26 | sha2.workspace = true 27 | thiserror.workspace = true 28 | tokio.workspace = true 29 | tracing.workspace = true 30 | wasmparser.workspace = true 31 | wasmtime-wasi-http.workspace = true 32 | wasmtime-wasi.workspace = true 33 | wasmtime.workspace = true 34 | wit-component.workspace = true 35 | wit-parser.workspace = true 36 | 37 | [dev-dependencies] 38 | insta.workspace = true 39 | madsim.workspace = true 40 | rstest.workspace = true 41 | serde.workspace = true 42 | test-programs-fibo-workflow-builder.workspace = true 43 | test-programs-fibo-activity-builder.workspace = true 44 | test-programs-fibo-webhook-builder.workspace = true 45 | test-programs-http-get-activity-builder.workspace = true 46 | test-programs-http-get-workflow-builder.workspace = true 47 | test-utils.workspace = true 48 | tokio.workspace = true 49 | 50 | [lints] 51 | workspace = true 52 | -------------------------------------------------------------------------------- /crates/utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod sha256sum; 2 | pub mod wasm_tools; 3 | mod wit; 4 | 5 | pub fn tracing_panic_hook(panic_info: &std::panic::PanicHookInfo) { 6 | let payload = panic_info.payload(); 7 | #[expect(clippy::manual_map)] 8 | let payload = if let Some(s) = payload.downcast_ref::<&str>() { 9 | Some(&**s) 10 | } else if let Some(s) = payload.downcast_ref::() { 11 | Some(s.as_str()) 12 | } else { 13 | None 14 | }; 15 | let location = panic_info.location().map(ToString::to_string); 16 | let backtrace = std::backtrace::Backtrace::capture(); 17 | if backtrace.status() == std::backtrace::BacktraceStatus::Captured { 18 | tracing::error!( 19 | panic.payload = payload, 20 | panic.location = location, 21 | "A panic occurred: {backtrace}" 22 | ); 23 | if let Some(payload) = payload { 24 | eprintln!("A panic occurred: {payload}\n{backtrace}"); 25 | } else { 26 | eprintln!("A panic occurred\n{backtrace}"); 27 | } 28 | } else { 29 | tracing::error!( 30 | panic.payload = payload, 31 | panic.location = location, 32 | "A panic occurred", 33 | ); 34 | if let Some(payload) = payload { 35 | eprintln!("A panic occurred: {payload}"); 36 | } else { 37 | eprintln!("A panic occurred"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/utils/src/sha256sum.rs: -------------------------------------------------------------------------------- 1 | use concepts::ContentDigest; 2 | use std::path::Path; 3 | 4 | #[tracing::instrument(skip_all)] 5 | pub async fn calculate_sha256_file>( 6 | path: P, 7 | ) -> Result { 8 | use sha2::Digest; 9 | use tokio::io::AsyncReadExt; 10 | let mut file = tokio::fs::File::open(path).await?; 11 | let mut hasher = sha2::Sha256::default(); 12 | let mut buf = [0; 4096]; 13 | loop { 14 | let n = file.read(&mut buf).await?; 15 | if n == 0 { 16 | break; 17 | } 18 | hasher.update(&buf[..n]); 19 | } 20 | Ok(ContentDigest::new( 21 | concepts::HashType::Sha256, 22 | format!("{:x}", hasher.finalize()), 23 | )) 24 | } 25 | -------------------------------------------------------------------------------- /crates/utils/src/snapshots/obeli_sk_utils__wasm_tools__tests__params@test_programs_fibo_workflow.wasm_exports.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/utils/src/wasm_tools.rs 3 | expression: exports 4 | --- 5 | { 6 | "testing:fibo-workflow/workflow-nesting.fibo-nested-workflow": { 7 | "params": [ 8 | { 9 | "name": "n", 10 | "wit_type": "u8" 11 | } 12 | ], 13 | "return_type": "u64" 14 | }, 15 | "testing:fibo-workflow/workflow-nesting.fibo-start-fiboas": { 16 | "params": [ 17 | { 18 | "name": "n", 19 | "wit_type": "u8" 20 | }, 21 | { 22 | "name": "fiboas", 23 | "wit_type": "u32" 24 | }, 25 | { 26 | "name": "iterations-per-fiboa", 27 | "wit_type": "u32" 28 | } 29 | ], 30 | "return_type": "u64" 31 | }, 32 | "testing:fibo-workflow/workflow.fiboa": { 33 | "params": [ 34 | { 35 | "name": "n", 36 | "wit_type": "u8" 37 | }, 38 | { 39 | "name": "iterations", 40 | "wit_type": "u32" 41 | } 42 | ], 43 | "return_type": "u64" 44 | }, 45 | "testing:fibo-workflow/workflow.fiboa-concurrent": { 46 | "params": [ 47 | { 48 | "name": "n", 49 | "wit_type": "u8" 50 | }, 51 | { 52 | "name": "iterations", 53 | "wit_type": "u32" 54 | } 55 | ], 56 | "return_type": "u64" 57 | }, 58 | "testing:fibo-workflow/workflow.fibow": { 59 | "params": [ 60 | { 61 | "name": "n", 62 | "wit_type": "u8" 63 | }, 64 | { 65 | "name": "iterations", 66 | "wit_type": "u32" 67 | } 68 | ], 69 | "return_type": "u64" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /crates/utils/wit: -------------------------------------------------------------------------------- 1 | ../../wit -------------------------------------------------------------------------------- /crates/val-json/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "obeli-sk-val-json" 3 | description = "Internal package of obeli-sk" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [dependencies] 13 | anyhow.workspace = true 14 | indexmap.workspace = true 15 | itertools.workspace = true 16 | serde_json.workspace = true 17 | serde.workspace = true 18 | thiserror.workspace = true 19 | wasmtime = { workspace = true, optional = true } 20 | wast.workspace = true 21 | 22 | [dev-dependencies] 23 | assert_matches.workspace = true 24 | insta.workspace = true 25 | 26 | [features] 27 | wasmtime = ["dep:wasmtime"] 28 | 29 | [lints] 30 | workspace = true 31 | -------------------------------------------------------------------------------- /crates/val-json/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod type_wrapper; 2 | pub mod wast_val; 3 | pub mod wast_val_ser; 4 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_bool.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": "bool", 7 | "value": true 8 | } 9 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_enum.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "enum": [ 8 | "a", 9 | "b" 10 | ] 11 | }, 12 | "value": "a" 13 | } 14 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_flags.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "flags": [ 8 | "a", 9 | "b", 10 | "c" 11 | ] 12 | }, 13 | "value": [ 14 | "a", 15 | "b" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_list.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "list": "bool" 8 | }, 9 | "value": [ 10 | true 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_option_none.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "option": "bool" 8 | }, 9 | "value": null 10 | } 11 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_option_some.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "option": "bool" 8 | }, 9 | "value": true 10 | } 11 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "record": { 8 | "field1": "bool", 9 | "field2": "u32" 10 | } 11 | }, 12 | "value": { 13 | "field1": true, 14 | "field2": 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_result_err_none.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "result": { 8 | "ok": null, 9 | "err": null 10 | } 11 | }, 12 | "value": { 13 | "err": null 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_result_err_some.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "result": { 8 | "ok": "bool", 9 | "err": "string" 10 | } 11 | }, 12 | "value": { 13 | "err": "test" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_result_ok_none.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "result": { 8 | "ok": null, 9 | "err": null 10 | } 11 | }, 12 | "value": { 13 | "ok": null 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_result_ok_option_none.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "result": { 8 | "ok": { 9 | "option": "string" 10 | }, 11 | "err": "string" 12 | } 13 | }, 14 | "value": { 15 | "ok": null 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_result_ok_some.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "result": { 8 | "ok": "bool", 9 | "err": null 10 | } 11 | }, 12 | "value": { 13 | "ok": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_tuple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "tuple": [ 8 | "bool", 9 | "u32" 10 | ] 11 | }, 12 | "value": [ 13 | false, 14 | 1 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_variant_with_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "variant": { 8 | "milliseconds": "u64", 9 | "seconds": "u64", 10 | "minutes": "u32", 11 | "hours": "u32", 12 | "days": "u32" 13 | } 14 | }, 15 | "value": { 16 | "seconds": 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /crates/val-json/src/snapshots/obeli_sk_val_json__wast_val_ser__tests__serde_variant_without_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/val-json/src/wast_val_ser.rs 3 | expression: json 4 | --- 5 | { 6 | "type": { 7 | "variant": { 8 | "a": null, 9 | "b": null 10 | } 11 | }, 12 | "value": "a" 13 | } 14 | -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-activity/deps/obelisk_activity@1.0.0: -------------------------------------------------------------------------------- 1 | ../../../../wit/obelisk_activity@1.0.0 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-activity/deps/wasi_io@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_io@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-activity/dummy.wit: -------------------------------------------------------------------------------- 1 | // We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps. 2 | package obelisk:any; 3 | 4 | world bindings { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-log/deps/obelisk_log@1.0.0: -------------------------------------------------------------------------------- 1 | ../../../../wit/obelisk_log@1.0.0 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-log/dummy.wit: -------------------------------------------------------------------------------- 1 | // We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps. 2 | package obelisk:any; 3 | 4 | world bindings { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-webhook/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-webhook/dummy.wit: -------------------------------------------------------------------------------- 1 | // We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps. 2 | package obelisk:any; 3 | 4 | world bindings { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/deps/wasi_cli@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_cli@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/deps/wasi_clocks@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_clocks@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/deps/wasi_filesystem@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_filesystem@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/deps/wasi_io@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_io@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/deps/wasi_random@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_random@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/deps/wasi_sockets@0.2.3: -------------------------------------------------------------------------------- 1 | ../../../../wit/wasi_sockets@0.2.3 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow-wasi/dummy.wit: -------------------------------------------------------------------------------- 1 | // We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps. 2 | package obelisk:any; 3 | 4 | world bindings { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow/deps/obelisk_types@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../wit/obelisk_types@1.1.0 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow/deps/obelisk_workflow@1.1.0: -------------------------------------------------------------------------------- 1 | ../../../../wit/obelisk_workflow@1.1.0 -------------------------------------------------------------------------------- /crates/wasm-workers/host-wit-workflow/dummy.wit: -------------------------------------------------------------------------------- 1 | // We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps. 2 | package obelisk:any; 3 | 4 | world bindings { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/activity/mod.rs: -------------------------------------------------------------------------------- 1 | mod activity_ctx; 2 | mod activity_ctx_process; 3 | pub mod activity_worker; 4 | mod process; 5 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/component_logger.rs: -------------------------------------------------------------------------------- 1 | use tracing::{Span, debug, error, info, trace, warn}; 2 | 3 | pub(crate) struct ComponentLogger { 4 | pub(crate) span: Span, 5 | // TODO: dynamic level filtering 6 | } 7 | const TARGET: &str = "app"; 8 | impl ComponentLogger { 9 | pub(crate) fn trace(&self, message: &str) { 10 | self.span.in_scope(|| trace!(target: TARGET, "{}", message)); 11 | } 12 | 13 | pub(crate) fn debug(&self, message: &str) { 14 | self.span.in_scope(|| debug!(target: TARGET, "{}", message)); 15 | } 16 | 17 | pub(crate) fn info(&self, message: &str) { 18 | self.span.in_scope(|| info!(target: TARGET, "{}", message)); 19 | } 20 | 21 | pub(crate) fn warn(&self, message: &str) { 22 | self.span.in_scope(|| warn!(target: TARGET, "{}", message)); 23 | } 24 | 25 | pub(crate) fn error(&self, message: &str) { 26 | self.span.in_scope(|| error!(target: TARGET, "{}", message)); 27 | } 28 | } 29 | 30 | pub(crate) mod log_activities { 31 | 32 | // Generate `obelisk::log::log` 33 | wasmtime::component::bindgen!({ 34 | path: "host-wit-log/", 35 | // interfaces: "import obelisk:log@1.0.0/log;", // Broken in 26.0.0 36 | inline: "package any:any; 37 | world bindings { 38 | import obelisk:log/log@1.0.0; 39 | }", 40 | world: "any:any/bindings", 41 | async: false, 42 | trappable_imports: false, 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/epoch_ticker.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | sync::{Arc, atomic::AtomicBool}, 3 | time::Duration, 4 | }; 5 | use tracing::{debug, info}; 6 | use wasmtime::EngineWeak; 7 | 8 | pub struct EpochTicker { 9 | shutdown: Arc, 10 | } 11 | 12 | impl EpochTicker { 13 | #[must_use] 14 | pub fn spawn_new(engines: Vec, epoch: Duration) -> Self { 15 | info!("Spawning the epoch ticker"); 16 | let shutdown = Arc::new(AtomicBool::new(false)); 17 | std::thread::spawn({ 18 | let shutdown = shutdown.clone(); 19 | move || { 20 | while !shutdown.load(std::sync::atomic::Ordering::Relaxed) { 21 | std::thread::sleep(epoch); 22 | for engine in &engines { 23 | if let Some(engine) = engine.upgrade() { 24 | engine.increment_epoch(); 25 | } 26 | } 27 | } 28 | } 29 | }); 30 | Self { shutdown } 31 | } 32 | } 33 | 34 | impl Drop for EpochTicker { 35 | fn drop(&mut self) { 36 | debug!("Closing the epoch ticker"); 37 | self.shutdown 38 | .store(true, std::sync::atomic::Ordering::Relaxed); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/webhook/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod webhook_trigger; 2 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/workflow/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod event_history; 2 | pub(crate) mod host_exports; 3 | pub(crate) mod wasi; 4 | pub(crate) mod workflow_ctx; 5 | pub mod workflow_worker; 6 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/workflow/wasi/cli.rs: -------------------------------------------------------------------------------- 1 | use super::wasi::cli::{environment, exit, stderr, stdin, stdout}; 2 | use crate::workflow::workflow_ctx::WorkflowCtx; 3 | use concepts::{ 4 | storage::{DbConnection, DbPool}, 5 | time::ClockFn, 6 | }; 7 | use wasmtime::Result; 8 | use wasmtime::component::Resource; 9 | use wasmtime_wasi::p2::{StdinStream as _, pipe}; 10 | use wasmtime_wasi_io::streams::{DynInputStream, DynOutputStream}; 11 | 12 | impl> environment::Host for WorkflowCtx { 13 | fn get_arguments(&mut self) -> Result> { 14 | Ok(Vec::new()) 15 | } 16 | fn get_environment(&mut self) -> Result> { 17 | Ok(Vec::new()) 18 | } 19 | fn initial_cwd(&mut self) -> Result> { 20 | Ok(None) 21 | } 22 | } 23 | 24 | impl> exit::Host for WorkflowCtx { 25 | fn exit(&mut self, _code: Result<(), ()>) -> Result<()> { 26 | Err(wasmtime::Error::msg("wasi:cli/exit is stubbed")) 27 | } 28 | fn exit_with_code(&mut self, _status_code: u8) -> Result<()> { 29 | Err(wasmtime::Error::msg("wasi:cli/exit is stubbed")) 30 | } 31 | } 32 | // see WasiCtxBuilder 33 | impl> stdin::Host for WorkflowCtx { 34 | fn get_stdin(&mut self) -> Result> { 35 | let stdin = pipe::ClosedInputStream; 36 | let stream = stdin.stream(); 37 | Ok(self.resource_table.push(stream)?) 38 | } 39 | } 40 | // see WasiCtxBuilder 41 | impl> stdout::Host for WorkflowCtx { 42 | fn get_stdout(&mut self) -> Result> { 43 | let stdout: DynOutputStream = Box::new(pipe::SinkOutputStream); 44 | Ok(self.resource_table.push(stdout)?) 45 | } 46 | } 47 | // see WasiCtxBuilder 48 | impl> stderr::Host for WorkflowCtx { 49 | fn get_stderr(&mut self) -> Result> { 50 | let stderr: DynOutputStream = Box::new(pipe::SinkOutputStream); 51 | Ok(self.resource_table.push(stderr)?) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/workflow/wasi/clocks.rs: -------------------------------------------------------------------------------- 1 | use super::wasi::clocks::{monotonic_clock, wall_clock}; 2 | use crate::workflow::workflow_ctx::WorkflowCtx; 3 | use concepts::{ 4 | storage::{DbConnection, DbPool}, 5 | time::ClockFn, 6 | }; 7 | use wasmtime::component::Resource; 8 | use wasmtime_wasi_io::poll::DynPollable; 9 | 10 | impl> monotonic_clock::Host for WorkflowCtx { 11 | fn now(&mut self) -> wasmtime::Result { 12 | Ok(monotonic_clock::Instant::MIN) 13 | } 14 | fn resolution(&mut self) -> wasmtime::Result { 15 | Err(wasmtime::Error::msg("wasi:clocks is stubbed")) 16 | } 17 | fn subscribe_duration( 18 | &mut self, 19 | _duration: monotonic_clock::Duration, 20 | ) -> wasmtime::Result> { 21 | Err(wasmtime::Error::msg("wasi:clocks is stubbed")) 22 | } 23 | fn subscribe_instant( 24 | &mut self, 25 | _deadline: monotonic_clock::Instant, 26 | ) -> wasmtime::Result> { 27 | Err(wasmtime::Error::msg("wasi:clocks is stubbed")) 28 | } 29 | } 30 | 31 | impl> wall_clock::Host for WorkflowCtx { 32 | fn now(&mut self) -> wasmtime::Result { 33 | Ok(wall_clock::Datetime { 34 | seconds: 0, 35 | nanoseconds: 0, 36 | }) 37 | } 38 | 39 | fn resolution(&mut self) -> wasmtime::Result { 40 | Err(wasmtime::Error::msg("wasi:clocks is stubbed")) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/wasm-workers/src/workflow/wasi/random.rs: -------------------------------------------------------------------------------- 1 | use super::wasi::random::random; 2 | use crate::workflow::workflow_ctx::WorkflowCtx; 3 | use concepts::{ 4 | storage::{DbConnection, DbPool}, 5 | time::ClockFn, 6 | }; 7 | 8 | impl> random::Host for WorkflowCtx { 9 | fn get_random_bytes(&mut self, len: u64) -> wasmtime::Result> { 10 | let vec = vec![0; usize::try_from(len).unwrap()]; 11 | Ok(vec) 12 | } 13 | fn get_random_u64(&mut self) -> wasmtime::Result { 14 | Ok(0) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/webui-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webui-proxy" 3 | 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | publish = false 8 | repository.workspace = true 9 | rust-version.workspace = true 10 | version.workspace = true 11 | 12 | [build-dependencies] 13 | webui-builder.workspace = true 14 | 15 | [dependencies] 16 | futures-concurrency.workspace = true 17 | wstd.workspace = true 18 | 19 | [lib] 20 | crate-type = ["cdylib"] 21 | 22 | [lints] 23 | workspace = true 24 | -------------------------------------------------------------------------------- /crates/webui-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Webui proxy 2 | This is a webhook endpoint component that: 3 | * serves static files of `webui` package generated by `trunk dist` 4 | * proxies `/api` requests to the `TARGET_URL`, the gRPC endpoint of Obelisk. 5 | 6 | ## Running in obelisk 7 | The component is automatically loaded when `obelisk.toml` contains: 8 | ```toml 9 | webui.listening_addr = "127.0.0.1:8080" 10 | ``` 11 | However, this loads the webui as specified in [webui-version.txt](../../assets/webui-version.txt). 12 | 13 | Build 14 | ```sh 15 | trunk build --config crates/webui/Trunk.toml 16 | cargo build --package webui-proxy --target=wasm32-wasip2 --profile=release_trunk 17 | ``` 18 | 19 | Add the `webui-proxy` as a webhook endpoint: 20 | ```toml 21 | [[http_server]] 22 | name = "webui2" 23 | listening_addr = "127.0.0.1:8081" 24 | 25 | [[webhook_endpoint]] 26 | name = "webui2" 27 | http_server = "webui2" 28 | location.path = "target/wasm32-wasip2/release_trunk/webui_proxy.wasm" 29 | routes = [""] 30 | env_vars = ["TARGET_URL=http://127.0.0.1:5005"] 31 | forward_stdout = "stderr" 32 | forward_stderr = "stderr" 33 | ``` 34 | 35 | ## Pushing to docker hub 36 | See [webui-bump.sh](../../scripts/push-webui.sh). 37 | -------------------------------------------------------------------------------- /crates/webui-proxy/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Just here to trigger webui-builder's build.rs, which needs to run so that the `webui/dist` folder is generated. 3 | webui_builder::dummy(); 4 | } 5 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/command.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.0; 2 | 3 | world command { 4 | include imports; 5 | 6 | export run; 7 | } 8 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/environment.wit: -------------------------------------------------------------------------------- 1 | interface environment { 2 | /// Get the POSIX-style environment variables. 3 | /// 4 | /// Each environment variable is provided as a pair of string variable names 5 | /// and string value. 6 | /// 7 | /// Morally, these are a value import, but until value imports are available 8 | /// in the component model, this import function should return the same 9 | /// values each time it is called. 10 | get-environment: func() -> list>; 11 | 12 | /// Get the POSIX-style arguments to the program. 13 | get-arguments: func() -> list; 14 | 15 | /// Return a path that programs should use as their initial current working 16 | /// directory, interpreting `.` as shorthand for this. 17 | initial-cwd: func() -> option; 18 | } 19 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/exit.wit: -------------------------------------------------------------------------------- 1 | interface exit { 2 | /// Exit the current instance and any linked instances. 3 | exit: func(status: result); 4 | } 5 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/imports.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.0; 2 | 3 | world imports { 4 | include wasi:clocks/imports@0.2.0; 5 | include wasi:filesystem/imports@0.2.0; 6 | include wasi:sockets/imports@0.2.0; 7 | include wasi:random/imports@0.2.0; 8 | include wasi:io/imports@0.2.0; 9 | 10 | import environment; 11 | import exit; 12 | import stdin; 13 | import stdout; 14 | import stderr; 15 | import terminal-input; 16 | import terminal-output; 17 | import terminal-stdin; 18 | import terminal-stdout; 19 | import terminal-stderr; 20 | } 21 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/run.wit: -------------------------------------------------------------------------------- 1 | interface run { 2 | /// Run the program. 3 | run: func() -> result; 4 | } 5 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/stdio.wit: -------------------------------------------------------------------------------- 1 | interface stdin { 2 | use wasi:io/streams@0.2.0.{input-stream}; 3 | 4 | get-stdin: func() -> input-stream; 5 | } 6 | 7 | interface stdout { 8 | use wasi:io/streams@0.2.0.{output-stream}; 9 | 10 | get-stdout: func() -> output-stream; 11 | } 12 | 13 | interface stderr { 14 | use wasi:io/streams@0.2.0.{output-stream}; 15 | 16 | get-stderr: func() -> output-stream; 17 | } 18 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/cli/terminal.wit: -------------------------------------------------------------------------------- 1 | /// Terminal input. 2 | /// 3 | /// In the future, this may include functions for disabling echoing, 4 | /// disabling input buffering so that keyboard events are sent through 5 | /// immediately, querying supported features, and so on. 6 | interface terminal-input { 7 | /// The input side of a terminal. 8 | resource terminal-input; 9 | } 10 | 11 | /// Terminal output. 12 | /// 13 | /// In the future, this may include functions for querying the terminal 14 | /// size, being notified of terminal size changes, querying supported 15 | /// features, and so on. 16 | interface terminal-output { 17 | /// The output side of a terminal. 18 | resource terminal-output; 19 | } 20 | 21 | /// An interface providing an optional `terminal-input` for stdin as a 22 | /// link-time authority. 23 | interface terminal-stdin { 24 | use terminal-input.{terminal-input}; 25 | 26 | /// If stdin is connected to a terminal, return a `terminal-input` handle 27 | /// allowing further interaction with it. 28 | get-terminal-stdin: func() -> option; 29 | } 30 | 31 | /// An interface providing an optional `terminal-output` for stdout as a 32 | /// link-time authority. 33 | interface terminal-stdout { 34 | use terminal-output.{terminal-output}; 35 | 36 | /// If stdout is connected to a terminal, return a `terminal-output` handle 37 | /// allowing further interaction with it. 38 | get-terminal-stdout: func() -> option; 39 | } 40 | 41 | /// An interface providing an optional `terminal-output` for stderr as a 42 | /// link-time authority. 43 | interface terminal-stderr { 44 | use terminal-output.{terminal-output}; 45 | 46 | /// If stderr is connected to a terminal, return a `terminal-output` handle 47 | /// allowing further interaction with it. 48 | get-terminal-stderr: func() -> option; 49 | } 50 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.0; 2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 3 | /// time. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A monotonic clock is a clock which has an unspecified initial value, and 9 | /// successive reads of the clock will produce non-decreasing values. 10 | /// 11 | /// It is intended for measuring elapsed time. 12 | interface monotonic-clock { 13 | use wasi:io/poll@0.2.0.{pollable}; 14 | 15 | /// An instant in time, in nanoseconds. An instant is relative to an 16 | /// unspecified initial value, and can only be compared to instances from 17 | /// the same monotonic-clock. 18 | type instant = u64; 19 | 20 | /// A duration of time, in nanoseconds. 21 | type duration = u64; 22 | 23 | /// Read the current value of the clock. 24 | /// 25 | /// The clock is monotonic, therefore calling this function repeatedly will 26 | /// produce a sequence of non-decreasing values. 27 | now: func() -> instant; 28 | 29 | /// Query the resolution of the clock. Returns the duration of time 30 | /// corresponding to a clock tick. 31 | resolution: func() -> duration; 32 | 33 | /// Create a `pollable` which will resolve once the specified instant 34 | /// occured. 35 | subscribe-instant: func( 36 | when: instant, 37 | ) -> pollable; 38 | 39 | /// Create a `pollable` which will resolve once the given duration has 40 | /// elapsed, starting at the time at which this function was called. 41 | /// occured. 42 | subscribe-duration: func( 43 | when: duration, 44 | ) -> pollable; 45 | } 46 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.0; 2 | /// WASI Wall Clock is a clock API intended to let users query the current 3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 4 | /// is not necessarily monotonic as it may be reset. 5 | /// 6 | /// It is intended to be portable at least between Unix-family platforms and 7 | /// Windows. 8 | /// 9 | /// A wall clock is a clock which measures the date and time according to 10 | /// some external reference. 11 | /// 12 | /// External references may be reset, so this clock is not necessarily 13 | /// monotonic, making it unsuitable for measuring elapsed time. 14 | /// 15 | /// It is intended for reporting the current date and time for humans. 16 | interface wall-clock { 17 | /// A time and date in seconds plus nanoseconds. 18 | record datetime { 19 | seconds: u64, 20 | nanoseconds: u32, 21 | } 22 | 23 | /// Read the current value of the clock. 24 | /// 25 | /// This clock is not monotonic, therefore calling this function repeatedly 26 | /// will not necessarily produce a sequence of non-decreasing values. 27 | /// 28 | /// The returned timestamps represent the number of seconds since 29 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 30 | /// also known as [Unix Time]. 31 | /// 32 | /// The nanoseconds field of the output is always less than 1000000000. 33 | /// 34 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 35 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 36 | now: func() -> datetime; 37 | 38 | /// Query the resolution of the clock. 39 | /// 40 | /// The nanoseconds field of the output is always less than 1000000000. 41 | resolution: func() -> datetime; 42 | } 43 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/clocks/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.0; 2 | 3 | world imports { 4 | import monotonic-clock; 5 | import wall-clock; 6 | } 7 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/filesystem/preopens.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.0; 2 | 3 | interface preopens { 4 | use types.{descriptor}; 5 | 6 | /// Return the set of preopened directories, and their path. 7 | get-directories: func() -> list>; 8 | } 9 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/filesystem/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.0; 2 | 3 | world imports { 4 | import types; 5 | import preopens; 6 | } 7 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/http/handler.wit: -------------------------------------------------------------------------------- 1 | /// This interface defines a handler of incoming HTTP Requests. It should 2 | /// be exported by components which can respond to HTTP Requests. 3 | interface incoming-handler { 4 | use types.{incoming-request, response-outparam}; 5 | 6 | /// This function is invoked with an incoming HTTP Request, and a resource 7 | /// `response-outparam` which provides the capability to reply with an HTTP 8 | /// Response. The response is sent by calling the `response-outparam.set` 9 | /// method, which allows execution to continue after the response has been 10 | /// sent. This enables both streaming to the response body, and performing other 11 | /// work. 12 | /// 13 | /// The implementor of this function must write a response to the 14 | /// `response-outparam` before returning, or else the caller will respond 15 | /// with an error on its behalf. 16 | handle: func( 17 | request: incoming-request, 18 | response-out: response-outparam 19 | ); 20 | } 21 | 22 | /// This interface defines a handler of outgoing HTTP Requests. It should be 23 | /// imported by components which wish to make HTTP Requests. 24 | interface outgoing-handler { 25 | use types.{ 26 | outgoing-request, request-options, future-incoming-response, error-code 27 | }; 28 | 29 | /// This function is invoked with an outgoing HTTP Request, and it returns 30 | /// a resource `future-incoming-response` which represents an HTTP Response 31 | /// which may arrive in the future. 32 | /// 33 | /// The `options` argument accepts optional parameters for the HTTP 34 | /// protocol's transport layer. 35 | /// 36 | /// This function may return an error if the `outgoing-request` is invalid 37 | /// or not allowed to be made. Otherwise, protocol errors are reported 38 | /// through the `future-incoming-response`. 39 | handle: func( 40 | request: outgoing-request, 41 | options: option 42 | ) -> result; 43 | } 44 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/http/proxy.wit: -------------------------------------------------------------------------------- 1 | package wasi:http@0.2.0; 2 | 3 | /// The `wasi:http/proxy` world captures a widely-implementable intersection of 4 | /// hosts that includes HTTP forward and reverse proxies. Components targeting 5 | /// this world may concurrently stream in and out any number of incoming and 6 | /// outgoing HTTP requests. 7 | world proxy { 8 | /// HTTP proxies have access to time and randomness. 9 | include wasi:clocks/imports@0.2.0; 10 | import wasi:random/random@0.2.0; 11 | 12 | /// Proxies have standard output and error streams which are expected to 13 | /// terminate in a developer-facing console provided by the host. 14 | import wasi:cli/stdout@0.2.0; 15 | import wasi:cli/stderr@0.2.0; 16 | 17 | /// TODO: this is a temporary workaround until component tooling is able to 18 | /// gracefully handle the absence of stdin. Hosts must return an eof stream 19 | /// for this import, which is what wasi-libc + tooling will do automatically 20 | /// when this import is properly removed. 21 | import wasi:cli/stdin@0.2.0; 22 | 23 | /// This is the default handler to use when user code simply wants to make an 24 | /// HTTP request (e.g., via `fetch()`). 25 | import outgoing-handler; 26 | 27 | /// The host delivers incoming HTTP requests to a component by calling the 28 | /// `handle` function of this exported interface. A host may arbitrarily reuse 29 | /// or not reuse component instance when delivering incoming HTTP requests and 30 | /// thus a component must be able to handle 0..N calls to `handle`. 31 | export incoming-handler; 32 | } 33 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/io/error.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | 4 | interface error { 5 | /// A resource which represents some error information. 6 | /// 7 | /// The only method provided by this resource is `to-debug-string`, 8 | /// which provides some human-readable information about the error. 9 | /// 10 | /// In the `wasi:io` package, this resource is returned through the 11 | /// `wasi:io/streams/stream-error` type. 12 | /// 13 | /// To provide more specific error information, other interfaces may 14 | /// provide functions to further "downcast" this error into more specific 15 | /// error information. For example, `error`s returned in streams derived 16 | /// from filesystem types to be described using the filesystem's own 17 | /// error-code type, using the function 18 | /// `wasi:filesystem/types/filesystem-error-code`, which takes a parameter 19 | /// `borrow` and returns 20 | /// `option`. 21 | /// 22 | /// The set of functions which can "downcast" an `error` into a more 23 | /// concrete type is open. 24 | resource error { 25 | /// Returns a string that is suitable to assist humans in debugging 26 | /// this error. 27 | /// 28 | /// WARNING: The returned string should not be consumed mechanically! 29 | /// It may change across platforms, hosts, or other implementation 30 | /// details. Parsing this string is a major platform-compatibility 31 | /// hazard. 32 | to-debug-string: func() -> string; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/io/poll.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | /// A poll API intended to let users wait for I/O events on multiple handles 4 | /// at once. 5 | interface poll { 6 | /// `pollable` represents a single I/O event which may be ready, or not. 7 | resource pollable { 8 | 9 | /// Return the readiness of a pollable. This function never blocks. 10 | /// 11 | /// Returns `true` when the pollable is ready, and `false` otherwise. 12 | ready: func() -> bool; 13 | 14 | /// `block` returns immediately if the pollable is ready, and otherwise 15 | /// blocks until ready. 16 | /// 17 | /// This function is equivalent to calling `poll.poll` on a list 18 | /// containing only this pollable. 19 | block: func(); 20 | } 21 | 22 | /// Poll for completion on a set of pollables. 23 | /// 24 | /// This function takes a list of pollables, which identify I/O sources of 25 | /// interest, and waits until one or more of the events is ready for I/O. 26 | /// 27 | /// The result `list` contains one or more indices of handles in the 28 | /// argument list that is ready for I/O. 29 | /// 30 | /// If the list contains more elements than can be indexed with a `u32` 31 | /// value, this function traps. 32 | /// 33 | /// A timeout can be implemented by adding a pollable from the 34 | /// wasi-clocks API to the list. 35 | /// 36 | /// This function does not return a `result`; polling in itself does not 37 | /// do any I/O so it doesn't fail. If any of the I/O sources identified by 38 | /// the pollables has an error, it is indicated by marking the source as 39 | /// being reaedy for I/O. 40 | poll: func(in: list>) -> list; 41 | } 42 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/io/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | world imports { 4 | import streams; 5 | import poll; 6 | } 7 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/random/insecure-seed.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | /// The insecure-seed interface for seeding hash-map DoS resistance. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | interface insecure-seed { 7 | /// Return a 128-bit value that may contain a pseudo-random value. 8 | /// 9 | /// The returned value is not required to be computed from a CSPRNG, and may 10 | /// even be entirely deterministic. Host implementations are encouraged to 11 | /// provide pseudo-random values to any program exposed to 12 | /// attacker-controlled content, to enable DoS protection built into many 13 | /// languages' hash-map implementations. 14 | /// 15 | /// This function is intended to only be called once, by a source language 16 | /// to initialize Denial Of Service (DoS) protection in its hash-map 17 | /// implementation. 18 | /// 19 | /// # Expected future evolution 20 | /// 21 | /// This will likely be changed to a value import, to prevent it from being 22 | /// called multiple times and potentially used for purposes other than DoS 23 | /// protection. 24 | insecure-seed: func() -> tuple; 25 | } 26 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/random/insecure.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | /// The insecure interface for insecure pseudo-random numbers. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | interface insecure { 7 | /// Return `len` insecure pseudo-random bytes. 8 | /// 9 | /// This function is not cryptographically secure. Do not use it for 10 | /// anything related to security. 11 | /// 12 | /// There are no requirements on the values of the returned bytes, however 13 | /// implementations are encouraged to return evenly distributed values with 14 | /// a long period. 15 | get-insecure-random-bytes: func(len: u64) -> list; 16 | 17 | /// Return an insecure pseudo-random `u64` value. 18 | /// 19 | /// This function returns the same type of pseudo-random data as 20 | /// `get-insecure-random-bytes`, represented as a `u64`. 21 | get-insecure-random-u64: func() -> u64; 22 | } 23 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | /// WASI Random is a random data API. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | interface random { 7 | /// Return `len` cryptographically-secure random or pseudo-random bytes. 8 | /// 9 | /// This function must produce data at least as cryptographically secure and 10 | /// fast as an adequately seeded cryptographically-secure pseudo-random 11 | /// number generator (CSPRNG). It must not block, from the perspective of 12 | /// the calling program, under any circumstances, including on the first 13 | /// request and on requests for numbers of bytes. The returned data must 14 | /// always be unpredictable. 15 | /// 16 | /// This function must always return fresh data. Deterministic environments 17 | /// must omit this function, rather than implementing it with deterministic 18 | /// data. 19 | get-random-bytes: func(len: u64) -> list; 20 | 21 | /// Return a cryptographically-secure random or pseudo-random `u64` value. 22 | /// 23 | /// This function returns the same type of data as `get-random-bytes`, 24 | /// represented as a `u64`. 25 | get-random-u64: func() -> u64; 26 | } 27 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/random/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | 3 | world imports { 4 | import random; 5 | import insecure; 6 | import insecure-seed; 7 | } 8 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | interface instance-network { 4 | use network.{network}; 5 | 6 | /// Get a handle to the default network. 7 | instance-network: func() -> network; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | interface tcp-create-socket { 3 | use network.{network, error-code, ip-address-family}; 4 | use tcp.{tcp-socket}; 5 | 6 | /// Create a new TCP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 9 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 10 | /// 11 | /// This function does not require a network capability handle. This is considered to be safe because 12 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` 13 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 14 | /// 15 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 16 | /// 17 | /// # Typical errors 18 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 19 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 20 | /// 21 | /// # References 22 | /// - 23 | /// - 24 | /// - 25 | /// - 26 | create-tcp-socket: func(address-family: ip-address-family) -> result; 27 | } 28 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | interface udp-create-socket { 3 | use network.{network, error-code, ip-address-family}; 4 | use udp.{udp-socket}; 5 | 6 | /// Create a new UDP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 9 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 10 | /// 11 | /// This function does not require a network capability handle. This is considered to be safe because 12 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, 13 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 14 | /// 15 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 16 | /// 17 | /// # Typical errors 18 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 19 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 20 | /// 21 | /// # References: 22 | /// - 23 | /// - 24 | /// - 25 | /// - 26 | create-udp-socket: func(address-family: ip-address-family) -> result; 27 | } 28 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/deps/sockets/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:sockets@0.2.0; 2 | 3 | world imports { 4 | import instance-network; 5 | import network; 6 | import udp; 7 | import udp-create-socket; 8 | import tcp; 9 | import tcp-create-socket; 10 | import ip-name-lookup; 11 | } 12 | -------------------------------------------------------------------------------- /crates/webui-proxy/wit/impl.wit: -------------------------------------------------------------------------------- 1 | package any:any; 2 | 3 | world any { 4 | import wasi:http/outgoing-handler@0.2.0; 5 | export wasi:http/incoming-handler@0.2.0; 6 | } 7 | -------------------------------------------------------------------------------- /crates/webui/.gitignore: -------------------------------------------------------------------------------- 1 | blueprint.css 2 | syntect.css 3 | -------------------------------------------------------------------------------- /crates/webui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webui" 3 | description = "Web UI for obelisk" 4 | 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | publish = false 9 | repository.workspace = true 10 | rust-version.workspace = true 11 | version.workspace = true 12 | 13 | [dependencies] 14 | val-json = { workspace = true } 15 | 16 | anyhow.workspace = true 17 | assert_matches.workspace = true 18 | chrono.workspace = true 19 | derive_more.workspace = true 20 | futures.workspace = true 21 | gloo.workspace = true 22 | hashbrown.workspace = true 23 | implicit-clone.workspace = true 24 | indexmap.workspace = true 25 | log.workspace = true 26 | prost-wkt-types.workspace = true 27 | prost.workspace = true 28 | rand = { workspace = true, features = ["small_rng", "std"] } 29 | serde_json.workspace = true 30 | syntect = { workspace = true, features = ["html"] } 31 | tonic = { workspace = true, default-features = false, features = [ 32 | "codegen", 33 | "prost", 34 | ] } 35 | tonic-web-wasm-client.workspace = true 36 | ulid.workspace = true 37 | wasm-bindgen-futures.workspace = true 38 | wasm-bindgen.workspace = true 39 | wasm-logger.workspace = true 40 | web-sys.workspace = true 41 | wit-component.workspace = true 42 | wit-parser.workspace = true 43 | yew-router.workspace = true 44 | yew.workspace = true 45 | yew_icons.workspace = true 46 | yewprint.workspace = true 47 | once_cell = "1.21.3" 48 | 49 | [build-dependencies] 50 | cargo_metadata.workspace = true 51 | syntect = { workspace = true, features = ["html"] } 52 | tonic-build = { workspace = true, default-features = false, features = [ 53 | "prost", 54 | ] } 55 | yewprint-css.workspace = true 56 | -------------------------------------------------------------------------------- /crates/webui/README.md: -------------------------------------------------------------------------------- 1 | # Web UI 2 | A [yew](https://yew.rs) based UI for Obelisk. 3 | 4 | ## Developing locally 5 | Run the Obelisk server. Execute `trunk` in development mode: 6 | ```sh 7 | trunk --config Trunk-dev.toml serve 8 | ``` 9 | Navigate to [localhost:8081](http://127.0.0.1:8081) 10 | 11 | All gRPC requests go through the `webui-proxy` listening 12 | on localhost:8080 . 13 | If no interaction with `webui-proxy` is desired, change the `backend` 14 | to use Obelisk gRPC port directly: 15 | ```toml 16 | backend = "http://127.0.0.1:5005" 17 | ``` 18 | 19 | ## Building the release 20 | ```sh 21 | trunk build 22 | ``` 23 | -------------------------------------------------------------------------------- /crates/webui/Trunk-dev.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | release = false 3 | filehash = true 4 | dist = "dist-dev" 5 | 6 | [serve] 7 | port = 8081 8 | 9 | [[proxy]] 10 | # Only /api prefixed requests should be proxied: 11 | # Remove /api path prefix using the rewrite rule, then add it back. 12 | rewrite = "/api" 13 | backend = "http://127.0.0.1:8080/api" 14 | 15 | [tools] 16 | wasm_opt = "version_120" 17 | wasm_bindgen = "0.2.100" 18 | -------------------------------------------------------------------------------- /crates/webui/Trunk.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | release = true 3 | filehash = false 4 | cargo_profile = "release_trunk" 5 | 6 | [tools] 7 | wasm_opt = "version_120" 8 | wasm_bindgen = "0.2.100" 9 | -------------------------------------------------------------------------------- /crates/webui/builder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webui-builder" 3 | authors.workspace = true 4 | edition.workspace = true 5 | license.workspace = true 6 | publish = false 7 | repository.workspace = true 8 | rust-version.workspace = true 9 | version.workspace = true 10 | 11 | [build-dependencies] 12 | cargo_metadata.workspace = true 13 | yewprint-css.workspace = true 14 | 15 | [lints] 16 | workspace = true 17 | -------------------------------------------------------------------------------- /crates/webui/builder/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Must be called from webui-proxy build.rs in order to run this package's build.rs 2 | pub fn dummy() {} 3 | -------------------------------------------------------------------------------- /crates/webui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Obelisk WebUI 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /crates/webui/src/components/code/code_block.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | #[derive(Properties, PartialEq)] 4 | pub struct CodeBlockProps { 5 | pub source: Html, 6 | } 7 | 8 | #[function_component(CodeBlock)] 9 | pub fn code_block(CodeBlockProps { source }: &CodeBlockProps) -> Html { 10 | html! { 11 |
12 |
13 |                 { source.clone() }
14 |             
15 |
16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/webui/src/components/code/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod code_block; 2 | pub mod syntect_code_block; 3 | -------------------------------------------------------------------------------- /crates/webui/src/components/debugger/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod debugger_view; 2 | -------------------------------------------------------------------------------- /crates/webui/src/components/execution_detail/history/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod join_next; 2 | pub mod join_set_created; 3 | pub mod join_set_request; 4 | pub mod persist; 5 | pub mod schedule; 6 | -------------------------------------------------------------------------------- /crates/webui/src/components/execution_detail/history/persist.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | components::execution_detail::tree_component::TreeComponent, 3 | grpc::{grpc_client, version::VersionType}, 4 | }; 5 | use yew::prelude::*; 6 | use yewprint::{ 7 | Icon, NodeData, TreeData, 8 | id_tree::{InsertBehavior, Node, TreeBuilder}, 9 | }; 10 | 11 | #[derive(Properties, PartialEq, Clone)] 12 | pub struct HistoryPersistEventProps { 13 | pub event: grpc_client::execution_event::history_event::Persist, 14 | pub version: VersionType, 15 | pub is_selected: bool, 16 | } 17 | 18 | impl HistoryPersistEventProps { 19 | fn construct_tree(&self) -> TreeData { 20 | let mut tree = TreeBuilder::new().build(); 21 | let root_id = tree 22 | .insert(Node::new(NodeData::default()), InsertBehavior::AsRoot) 23 | .unwrap(); 24 | 25 | // Add node for Persist event 26 | tree.insert( 27 | Node::new(NodeData { 28 | icon: Icon::History, 29 | label: format!("{}. Persist Event", self.version).into_html(), 30 | is_selected: self.is_selected, 31 | ..Default::default() 32 | }), 33 | InsertBehavior::UnderNode(&root_id), 34 | ) 35 | .unwrap(); 36 | // Not showing the data 37 | TreeData::from(tree) 38 | } 39 | } 40 | 41 | #[function_component(HistoryPersistEvent)] 42 | pub fn history_persist_event(props: &HistoryPersistEventProps) -> Html { 43 | let tree = props.construct_tree(); 44 | html! { 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/webui/src/components/execution_detail/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod created; 2 | pub mod finished; 3 | pub mod history; 4 | pub mod http_trace; 5 | pub mod locked; 6 | pub mod temporarily_failed; 7 | pub mod timed_out; 8 | pub mod tree_component; 9 | pub mod unlocked; 10 | pub mod utils; 11 | -------------------------------------------------------------------------------- /crates/webui/src/components/execution_detail/tree_component.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | use yewprint::{TreeData, id_tree::NodeId}; 3 | 4 | #[derive(Debug)] 5 | pub enum TreeComponentAction { 6 | ExpandNode(NodeId), 7 | } 8 | 9 | #[derive(Properties, PartialEq)] 10 | pub struct TreeComponentProps { 11 | pub tree: TreeData, 12 | } 13 | 14 | pub struct TreeComponent { 15 | pub tree: TreeData, 16 | pub on_expand_node: Callback<(NodeId, MouseEvent)>, 17 | } 18 | 19 | impl Component for TreeComponent { 20 | type Message = TreeComponentAction; 21 | 22 | type Properties = TreeComponentProps; 23 | 24 | fn create(ctx: &Context) -> Self { 25 | let props = ctx.props(); 26 | let tree = props.tree.clone(); 27 | Self { 28 | tree, 29 | on_expand_node: ctx 30 | .link() 31 | .callback(|(node_id, _)| TreeComponentAction::ExpandNode(node_id)), 32 | } 33 | } 34 | 35 | fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { 36 | match msg { 37 | TreeComponentAction::ExpandNode(node_id) => { 38 | let mut tree = self.tree.borrow_mut(); 39 | let node = tree.get_mut(&node_id).unwrap(); 40 | let data = node.data_mut(); 41 | data.is_expanded ^= true; 42 | } 43 | } 44 | true 45 | } 46 | 47 | fn changed(&mut self, ctx: &Context, _old_props: &Self::Properties) -> bool { 48 | self.tree = ctx.props().tree.clone(); 49 | true 50 | } 51 | 52 | fn view(&self, _ctx: &Context) -> Html { 53 | html! { 54 | 55 | tree={&self.tree} 56 | on_collapse={Some(self.on_expand_node.clone())} 57 | on_expand={Some(self.on_expand_node.clone())} 58 | onclick={Some(self.on_expand_node.clone())} 59 | /> 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/webui/src/components/execution_detail/unlocked.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | components::execution_detail::tree_component::TreeComponent, 3 | grpc::{grpc_client, version::VersionType}, 4 | }; 5 | use yew::prelude::*; 6 | use yewprint::{ 7 | Icon, NodeData, TreeData, 8 | id_tree::{InsertBehavior, Node, TreeBuilder}, 9 | }; 10 | 11 | #[derive(Properties, PartialEq, Clone)] 12 | pub struct UnlockedEventProps { 13 | pub event: grpc_client::execution_event::Unlocked, 14 | pub version: VersionType, 15 | pub is_selected: bool, 16 | } 17 | 18 | impl UnlockedEventProps { 19 | fn construct_tree(&self) -> TreeData { 20 | let mut tree = TreeBuilder::new().build(); 21 | let root_id = tree 22 | .insert(Node::new(NodeData::default()), InsertBehavior::AsRoot) 23 | .unwrap(); 24 | 25 | let unlocked_node_id = tree 26 | .insert( 27 | Node::new(NodeData { 28 | icon: Icon::Unlock, 29 | label: format!("{}. Execution Unlocked", self.version).into_html(), 30 | is_selected: self.is_selected, 31 | ..Default::default() 32 | }), 33 | InsertBehavior::UnderNode(&root_id), 34 | ) 35 | .unwrap(); 36 | 37 | // Add reason node 38 | tree.insert( 39 | Node::new(NodeData { 40 | icon: Icon::Error, 41 | label: self.event.reason.as_str().into_html(), 42 | ..Default::default() 43 | }), 44 | InsertBehavior::UnderNode(&unlocked_node_id), 45 | ) 46 | .unwrap(); 47 | 48 | TreeData::from(tree) 49 | } 50 | } 51 | 52 | #[function_component(UnlockedEvent)] 53 | pub fn unlocked_event(props: &UnlockedEventProps) -> Html { 54 | let tree = props.construct_tree(); 55 | html! { 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/webui/src/components/execution_header.rs: -------------------------------------------------------------------------------- 1 | use crate::app::Route; 2 | use crate::components::execution_status::ExecutionStatus; 3 | use crate::grpc::execution_id::ExecutionIdExt; 4 | use crate::grpc::grpc_client::ExecutionId; 5 | use yew::prelude::*; 6 | use yew_router::prelude::Link; 7 | 8 | #[derive(Properties, PartialEq)] 9 | pub struct ExecutionHeaderProps { 10 | pub execution_id: ExecutionId, 11 | pub link: ExecutionLink, 12 | } 13 | #[function_component(ExecutionHeader)] 14 | pub fn execution_header( 15 | ExecutionHeaderProps { execution_id, link }: &ExecutionHeaderProps, 16 | ) -> Html { 17 | html! { 18 |
19 | 28 | 29 | 30 | 31 |
32 | } 33 | } 34 | 35 | #[derive(Debug, Clone, Copy, PartialEq)] 36 | pub enum ExecutionLink { 37 | Trace, 38 | Log, 39 | Debug, 40 | } 41 | 42 | impl ExecutionLink { 43 | pub fn link(&self, execution_id: ExecutionId, title: &str) -> Html { 44 | match self { 45 | ExecutionLink::Trace => html! { 46 | to={Route::ExecutionTrace { execution_id }}> 47 | {title} 48 | > 49 | }, 50 | ExecutionLink::Log => html! { 51 | to={Route::ExecutionLog { execution_id }}> 52 | {title} 53 | > 54 | }, 55 | ExecutionLink::Debug => html! { 56 | to={Route::ExecutionDebugger { execution_id }}> 57 | {title} 58 | > 59 | }, 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/webui/src/components/ffqn_with_links.rs: -------------------------------------------------------------------------------- 1 | use crate::{app::Route, grpc::ffqn::FunctionFqn}; 2 | use yew::prelude::*; 3 | use yew_router::prelude::Link; 4 | use yewprint::Icon; 5 | 6 | #[derive(Properties, PartialEq)] 7 | pub struct FfqnWithLinksProps { 8 | pub ffqn: FunctionFqn, 9 | #[prop_or_default] 10 | pub fully_qualified: bool, 11 | #[prop_or_default] 12 | pub hide_submit: bool, 13 | #[prop_or_default] 14 | pub hide_find: bool, 15 | } 16 | #[function_component(FfqnWithLinks)] 17 | pub fn ffqn_with_links( 18 | FfqnWithLinksProps { 19 | ffqn, 20 | fully_qualified, 21 | hide_submit, 22 | hide_find, 23 | }: &FfqnWithLinksProps, 24 | ) -> Html { 25 | let label = if *fully_qualified { 26 | ffqn.to_string() 27 | } else { 28 | ffqn.function_name.clone() 29 | }; 30 | let ext = ffqn.ifc_fqn.pkg_fqn.is_extension(); 31 | html! { 32 |
33 | // Finding makes no sense when rendering an extension function. 34 | if !ext && !hide_find { 35 | to={Route::ExecutionListByFfqn { ffqn: ffqn.clone() } }> 36 | 37 | > 38 | } 39 | if !hide_submit { 40 | to={Route::ExecutionSubmit { ffqn: ffqn.clone() } }> 41 | 42 | > 43 | } 44 | { label } 45 |
46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/webui/src/components/function_signature.rs: -------------------------------------------------------------------------------- 1 | use crate::grpc::grpc_client; 2 | use yew::prelude::*; 3 | 4 | #[derive(Properties, PartialEq)] 5 | pub struct FunctionSignatureProps { 6 | pub params: Vec, 7 | pub return_type: Option, 8 | } 9 | #[function_component(FunctionSignature)] 10 | pub fn function_signature( 11 | FunctionSignatureProps { 12 | params, 13 | return_type, 14 | }: &FunctionSignatureProps, 15 | ) -> Html { 16 | html! {<> 17 | {"func ("} 18 | 19 | {")"} 20 | if let Some(return_type) = return_type { 21 | {" -> "} 22 | { &return_type.wit_type } 23 | } 24 | } 25 | } 26 | 27 | #[derive(Properties, PartialEq)] 28 | pub struct FunctionParameterListProps { 29 | pub params: Vec, 30 | } 31 | #[function_component(FunctionParameterList)] 32 | pub fn function_parameter_list( 33 | FunctionParameterListProps { params }: &FunctionParameterListProps, 34 | ) -> Html { 35 | params 36 | .iter() 37 | .enumerate() 38 | .map(|(idx, param)| { 39 | let ty = param 40 | .r#type 41 | .as_ref() 42 | .expect("`FunctionParameter.type` is sent") 43 | .wit_type 44 | .as_str(); 45 | html! {<> 46 | if idx > 0 { 47 | {", "} 48 | } 49 | { format!("{}: {}", param.name, ty) } 50 | } 51 | }) 52 | .collect() 53 | } 54 | -------------------------------------------------------------------------------- /crates/webui/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod code; 2 | pub mod component_list_page; 3 | pub mod component_tree; 4 | pub mod debugger; 5 | pub mod execution_detail; 6 | pub mod execution_detail_page; 7 | pub mod execution_header; 8 | pub mod execution_list_page; 9 | pub mod execution_status; 10 | pub mod execution_submit; 11 | pub mod execution_submit_page; 12 | pub mod ffqn_with_links; 13 | pub mod function_signature; 14 | pub mod json_tree; 15 | pub mod not_found; 16 | pub mod trace; 17 | -------------------------------------------------------------------------------- /crates/webui/src/components/not_found.rs: -------------------------------------------------------------------------------- 1 | use yew::prelude::*; 2 | 3 | #[function_component(NotFound)] 4 | pub fn not_found() -> Html { 5 | html! { 6 |
7 | {"The URL was not found"} 8 |
9 | } 10 | } 11 | -------------------------------------------------------------------------------- /crates/webui/src/components/trace/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | pub mod execution_trace; 3 | pub mod trace_view; 4 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/ffqn.rs: -------------------------------------------------------------------------------- 1 | use anyhow::bail; 2 | use yew::ToHtml; 3 | 4 | use crate::grpc::grpc_client; 5 | use std::{fmt::Display, str::FromStr}; 6 | 7 | use super::ifc_fqn::IfcFqn; 8 | 9 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 10 | pub struct FunctionFqn { 11 | pub ifc_fqn: IfcFqn, 12 | pub function_name: String, 13 | } 14 | impl FunctionFqn { 15 | pub fn from_fn_detail( 16 | fn_detail: &grpc_client::FunctionDetail, 17 | ) -> Result { 18 | let Some(function) = &fn_detail.function_name else { 19 | unreachable!("FunctionDetails.function is sent by the server"); 20 | }; 21 | Ok(FunctionFqn { 22 | ifc_fqn: IfcFqn::from_str(&function.interface_name)?, 23 | function_name: function.function_name.clone(), 24 | }) 25 | } 26 | } 27 | 28 | impl FromStr for FunctionFqn { 29 | type Err = anyhow::Error; 30 | 31 | fn from_str(s: &str) -> Result { 32 | if let Some((ifc_fqn, function_name)) = s.rsplit_once('.') { 33 | Ok(FunctionFqn { 34 | ifc_fqn: IfcFqn::from_str(ifc_fqn)?, 35 | function_name: function_name.to_string(), 36 | }) 37 | } else { 38 | bail!("delimiter `.` not found") 39 | } 40 | } 41 | } 42 | 43 | impl Display for FunctionFqn { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | write!(f, "{}.{}", self.ifc_fqn, self.function_name) 46 | } 47 | } 48 | 49 | impl From for grpc_client::FunctionName { 50 | fn from(value: FunctionFqn) -> Self { 51 | Self { 52 | interface_name: value.ifc_fqn.to_string(), 53 | function_name: value.function_name, 54 | } 55 | } 56 | } 57 | 58 | impl From for FunctionFqn { 59 | fn from(value: grpc_client::FunctionName) -> Self { 60 | Self { 61 | ifc_fqn: IfcFqn::from_str(&value.interface_name).expect("ffqn must be parseable"), 62 | function_name: value.function_name, 63 | } 64 | } 65 | } 66 | 67 | impl ToHtml for FunctionFqn { 68 | fn to_html(&self) -> yew::Html { 69 | self.to_string().into_html() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/function_detail.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use super::{grpc_client::FunctionDetail, ifc_fqn::IfcFqn}; 4 | use indexmap::IndexMap; 5 | 6 | pub fn is_extension_interface(ifc: &IfcFqn) -> bool { 7 | ifc.pkg_fqn.is_extension() 8 | } 9 | 10 | #[derive(Clone, Copy, PartialEq, Eq, Default)] 11 | pub enum InterfaceFilter { 12 | #[default] 13 | WithExtensions, 14 | WithoutExtensions, 15 | } 16 | 17 | impl InterfaceFilter { 18 | pub fn is_with_extensions(&self) -> bool { 19 | matches!(self, InterfaceFilter::WithExtensions) 20 | } 21 | } 22 | 23 | pub fn map_interfaces_to_fn_details( 24 | functions: &[FunctionDetail], 25 | filter: InterfaceFilter, 26 | ) -> IndexMap> { 27 | let mut interfaces_to_fn_details: IndexMap> = IndexMap::new(); 28 | for function_detail in functions { 29 | let function_name = function_detail 30 | .function_name 31 | .clone() 32 | .expect("function and its name is sent by the server"); 33 | let ifc_fqn = IfcFqn::from_str(&function_name.interface_name) 34 | .expect("received interface must be well-formed"); 35 | if filter == InterfaceFilter::WithExtensions || !ifc_fqn.pkg_fqn.is_extension() { 36 | interfaces_to_fn_details 37 | .entry(ifc_fqn) 38 | .or_default() 39 | .push(function_detail.clone()); 40 | } 41 | } 42 | interfaces_to_fn_details.sort_keys(); 43 | // sort functions in each interface 44 | for (_, fns) in interfaces_to_fn_details.iter_mut() { 45 | fns.sort_by(|a, b| { 46 | a.function_name 47 | .as_ref() 48 | .map(|f| &f.function_name) 49 | .cmp(&b.function_name.as_ref().map(|f| &f.function_name)) 50 | }); 51 | } 52 | interfaces_to_fn_details 53 | } 54 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/grpc_client.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, hash::Hash, str::FromStr}; 2 | 3 | use implicit_clone::ImplicitClone; 4 | 5 | tonic::include_proto!("obelisk"); 6 | 7 | impl ImplicitClone for FunctionDetail {} 8 | 9 | impl Display for ComponentId { 10 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 11 | write!(f, "{}", self.id) 12 | } 13 | } 14 | 15 | impl Eq for ComponentId {} 16 | impl Hash for ComponentId { 17 | fn hash(&self, state: &mut H) { 18 | self.id.hash(state); 19 | } 20 | } 21 | 22 | impl ImplicitClone for ComponentId {} 23 | 24 | impl FromStr for ComponentId { 25 | type Err = (); 26 | 27 | fn from_str(s: &str) -> Result { 28 | Ok(ComponentId { id: s.to_string() }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/ifc_fqn.rs: -------------------------------------------------------------------------------- 1 | use super::pkg_fqn::PkgFqn; 2 | use anyhow::bail; 3 | use std::{fmt::Display, str::FromStr}; 4 | 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 6 | pub struct IfcFqn { 7 | pub pkg_fqn: PkgFqn, 8 | pub ifc_name: String, 9 | } 10 | 11 | impl Display for IfcFqn { 12 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 13 | let IfcFqn { 14 | pkg_fqn: 15 | PkgFqn { 16 | namespace, 17 | package_name, 18 | version, 19 | }, 20 | ifc_name, 21 | } = self; 22 | if let Some(version) = version { 23 | write!(f, "{namespace}:{package_name}/{ifc_name}@{version}") 24 | } else { 25 | write!(f, "{namespace}:{package_name}/{ifc_name}") 26 | } 27 | } 28 | } 29 | 30 | impl FromStr for IfcFqn { 31 | type Err = anyhow::Error; 32 | 33 | fn from_str(input: &str) -> Result { 34 | let Some((namespace, rest)) = input.split_once(':') else { 35 | bail!("Separator `:` is missing") 36 | }; 37 | let Some((package_name, rest)) = rest.split_once('/') else { 38 | bail!("Separator `/` is missing") 39 | }; 40 | let (ifc_name, version) = if let Some((ifc_name, version)) = rest.split_once("@") { 41 | (ifc_name, Some(version.to_string())) 42 | } else { 43 | (rest, None) 44 | }; 45 | 46 | Ok(Self { 47 | pkg_fqn: PkgFqn { 48 | namespace: namespace.to_string(), 49 | package_name: package_name.to_string(), 50 | version, 51 | }, 52 | ifc_name: ifc_name.to_string(), 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/join_set_id.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, hash::Hash}; 2 | 3 | use super::grpc_client; 4 | 5 | const JOIN_SET_ID_INFIX: char = ':'; 6 | 7 | impl Hash for grpc_client::JoinSetId { 8 | fn hash(&self, state: &mut H) { 9 | self.kind.hash(state); 10 | self.name.hash(state); 11 | } 12 | } 13 | 14 | impl Eq for grpc_client::JoinSetId {} 15 | 16 | impl Display for grpc_client::JoinSetId { 17 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 18 | let code = match self.kind() { 19 | grpc_client::join_set_id::JoinSetKind::OneOff => "o", 20 | grpc_client::join_set_id::JoinSetKind::Named => "n", 21 | grpc_client::join_set_id::JoinSetKind::Generated => "g", 22 | }; 23 | write!(f, "{code}{JOIN_SET_ID_INFIX}{}", self.name) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/mod.rs: -------------------------------------------------------------------------------- 1 | use grpc_client::{ComponentType, result_detail}; 2 | 3 | pub mod execution_id; 4 | pub mod ffqn; 5 | pub mod function_detail; 6 | pub mod grpc_client; 7 | pub mod ifc_fqn; 8 | pub mod join_set_id; 9 | pub mod pkg_fqn; 10 | pub mod version; 11 | 12 | pub const NAMESPACE_OBELISK: &str = "obelisk"; // TODO: unify with concepts 13 | pub const SUFFIX_PKG_EXT: &str = "-obelisk-ext"; // TODO: unify with concepts 14 | 15 | pub trait ResultValueExt { 16 | fn is_ok(&self) -> bool; 17 | fn is_err(&self) -> bool; 18 | } 19 | 20 | impl ResultValueExt for result_detail::Value { 21 | fn is_ok(&self) -> bool { 22 | matches!(self, result_detail::Value::Ok(_)) 23 | } 24 | 25 | fn is_err(&self) -> bool { 26 | !self.is_ok() 27 | } 28 | } 29 | 30 | impl grpc_client::Component { 31 | pub fn as_type(&self) -> ComponentType { 32 | ComponentType::try_from(self.r#type) 33 | .expect("generated ComponentType must contain all types") 34 | } 35 | } 36 | 37 | impl grpc_client::ComponentId { 38 | pub fn as_type(&self) -> ComponentType { 39 | let ty = self 40 | .id 41 | .split_once(":") 42 | .expect("ComponentId must contain `:` infix") 43 | .0 44 | .to_uppercase(); 45 | ComponentType::from_str_name(&ty) 46 | .unwrap_or_else(|| panic!("ComponentType must be found for {ty}")) 47 | } 48 | } 49 | 50 | impl yew::ToHtml for ComponentType { 51 | fn to_html(&self) -> yew::Html { 52 | match self { 53 | ComponentType::Workflow => "Workflow", 54 | ComponentType::ActivityWasm => "Activity", 55 | ComponentType::WebhookEndpoint => "Webhook Endpoint", 56 | } 57 | .to_html() 58 | } 59 | } 60 | 61 | impl ComponentType { 62 | pub fn as_icon(&self) -> yewprint::Icon { 63 | match self { 64 | ComponentType::Workflow => yewprint::Icon::GanttChart, 65 | ComponentType::ActivityWasm => yewprint::Icon::CodeBlock, 66 | ComponentType::WebhookEndpoint => yewprint::Icon::GlobeNetwork, 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/pkg_fqn.rs: -------------------------------------------------------------------------------- 1 | use super::{NAMESPACE_OBELISK, SUFFIX_PKG_EXT, ifc_fqn::IfcFqn}; 2 | use anyhow::Context; 3 | use std::fmt::Display; 4 | use wit_parser::{Interface, PackageName}; 5 | 6 | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 7 | pub struct PkgFqn { 8 | pub namespace: String, 9 | pub package_name: String, 10 | pub version: Option, 11 | } 12 | impl Display for PkgFqn { 13 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 14 | let PkgFqn { 15 | namespace, 16 | package_name, 17 | version, 18 | } = self; 19 | if let Some(version) = version { 20 | write!(f, "{namespace}:{package_name}@{version}") 21 | } else { 22 | write!(f, "{namespace}:{package_name}") 23 | } 24 | } 25 | } 26 | 27 | impl PkgFqn { 28 | #[must_use] 29 | pub fn is_extension(&self) -> bool { 30 | self.package_name.ends_with(SUFFIX_PKG_EXT) 31 | } 32 | 33 | #[must_use] 34 | pub fn package_strip_extension_suffix(&self) -> Option<&str> { 35 | self.package_name.as_str().strip_suffix(SUFFIX_PKG_EXT) 36 | } 37 | 38 | #[must_use] 39 | pub fn is_namespace_obelisk(&self) -> bool { 40 | self.namespace == NAMESPACE_OBELISK 41 | } 42 | 43 | pub fn ifc_fqn(&self, interface: &Interface) -> Result { 44 | Ok(IfcFqn { 45 | pkg_fqn: self.clone(), 46 | ifc_name: interface 47 | .name 48 | .clone() 49 | .context("inline interfaces are not supported")?, 50 | }) 51 | } 52 | } 53 | 54 | impl From<&PackageName> for PkgFqn { 55 | fn from(value: &PackageName) -> Self { 56 | Self { 57 | namespace: value.namespace.clone(), 58 | package_name: value.name.clone(), 59 | version: value.version.as_ref().map(|v| v.to_string()), 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /crates/webui/src/grpc/version.rs: -------------------------------------------------------------------------------- 1 | pub type VersionType = u32; // TODO: unify with concepts 2 | -------------------------------------------------------------------------------- /crates/webui/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod app; 2 | pub mod components; 3 | pub mod grpc; 4 | pub mod util; 5 | -------------------------------------------------------------------------------- /crates/webui/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod wit_highlighter; 2 | 3 | use std::rc::Rc; 4 | 5 | pub fn trace_id() -> Rc { 6 | use rand::SeedableRng; 7 | let mut rng = rand::rngs::SmallRng::from_entropy(); 8 | Rc::from( 9 | (0..5) 10 | .map(|_| (rand::Rng::gen_range(&mut rng, b'a'..=b'z') as char)) 11 | .collect::(), 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /dev-deps.txt: -------------------------------------------------------------------------------- 1 | cargo-binstall 1.12.5 2 | cargo-edit-upgrade 0.13.6 3 | cargo-expand 1.0.106 4 | cargo-nextest 0.9.95 5 | cargo-semver-checks 0.41.0 6 | litecli Version: 1.12.3 7 | nixpkgs-fmt 1.3.0 8 | pkg-config 0.29.2 9 | libprotoc 30.2 10 | rustc 1.87.0 (17067e9ac 2025-05-09) 11 | wasm-tools 1.231.0 12 | wasmtime 32.0.0 13 | cargo-zigbuild 0.20.0 14 | wasm-opt version 123 15 | trunk 0.21.14 16 | wasm-bindgen 0.2.100 17 | ldd (GNU libc) 2.40 18 | -------------------------------------------------------------------------------- /download.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Downloads the latest binary from GitHub Releases into the current directory. 4 | # Usage: 5 | # curl -L --tlsv1.2 -sSf https://raw.githubusercontent.com/obeli-sk/obelisk/main/download.sh | bash 6 | 7 | set -eu 8 | 9 | # Set pipefail if it works in a subshell, disregard if unsupported 10 | (set -o pipefail 2> /dev/null) && set -o pipefail 11 | 12 | base_url="https://github.com/obeli-sk/obelisk/releases/latest/download/obelisk-" 13 | 14 | os="$(uname -s)" 15 | if [ "$os" = "Linux" ]; then 16 | machine="$(uname -m)" 17 | case "$machine" in 18 | x86_64) target="x86_64-unknown-linux-" ;; 19 | aarch64) target="aarch64-unknown-linux-" ;; 20 | *) echo "Unsupported architecture ${machine}" && exit 1 ;; 21 | esac 22 | 23 | # Use musl on NixOS and musl-based systems. 24 | ldd_version=$(ldd --version 2>&1 || true) 25 | issue=$(cat /etc/issue 2>/dev/null || true) 26 | if echo "$ldd_version" | grep -q "musl" || echo "$issue" | grep -q "NixOS"; then 27 | lib="musl" 28 | if echo "$issue" | grep -q "NixOS"; then 29 | echo -e "Downloading musl-based bianry on NixOS. Consider installing with\nnix profile install github:obeli-sk/obelisk/latest" 30 | fi 31 | else 32 | lib="gnu" 33 | fi 34 | url="${base_url}${target}${lib}.tar.gz" 35 | 36 | elif [ "$os" = "Darwin" ]; then 37 | machine="$(uname -m)" 38 | case "$machine" in 39 | x86_64) target="x86_64-apple-darwin" ;; 40 | arm64) target="aarch64-apple-darwin" ;; 41 | *) echo "Unsupported architecture ${machine}" && exit 1 ;; 42 | esac 43 | 44 | url="${base_url}${target}.tar.gz" 45 | 46 | else 47 | echo "Unsupported OS ${os}" 48 | exit 1 49 | fi 50 | 51 | # Download and extract the tarball 52 | echo "Downloading and extracting $url" 53 | curl -L --proto '=https' --tlsv1.2 -sSf "$url" | tar -xvzf - 54 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1748460289, 24 | "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixos-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs", 41 | "rust-overlay": "rust-overlay" 42 | } 43 | }, 44 | "rust-overlay": { 45 | "inputs": { 46 | "nixpkgs": [ 47 | "nixpkgs" 48 | ] 49 | }, 50 | "locked": { 51 | "lastModified": 1748658947, 52 | "narHash": "sha256-F+nGITu6D7RswJlm8qCuU1PCuOSgDeAqaDKWW1n1jmQ=", 53 | "owner": "oxalica", 54 | "repo": "rust-overlay", 55 | "rev": "fc82ce758cc5df6a6d5d24e75710321cdbdc787a", 56 | "type": "github" 57 | }, 58 | "original": { 59 | "owner": "oxalica", 60 | "repo": "rust-overlay", 61 | "type": "github" 62 | } 63 | }, 64 | "systems": { 65 | "locked": { 66 | "lastModified": 1681028828, 67 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 68 | "owner": "nix-systems", 69 | "repo": "default", 70 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 71 | "type": "github" 72 | }, 73 | "original": { 74 | "owner": "nix-systems", 75 | "repo": "default", 76 | "type": "github" 77 | } 78 | } 79 | }, 80 | "root": "root", 81 | "version": 7 82 | } 83 | -------------------------------------------------------------------------------- /garnix.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - branch: latest 3 | include: 4 | - "packages.x86_64-linux.default" 5 | 6 | - "packages.aarch64-linux.default" 7 | 8 | - "packages.aarch64-darwin.default" 9 | -------------------------------------------------------------------------------- /proto/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 obeli-sk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rust-toolchain-cross.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.87" 3 | targets = [ 4 | "wasm32-unknown-unknown", 5 | "wasm32-wasip2", 6 | "aarch64-unknown-linux-gnu", 7 | "aarch64-unknown-linux-musl", 8 | "x86_64-unknown-linux-gnu", 9 | "x86_64-unknown-linux-musl", 10 | "x86_64-apple-darwin", 11 | "aarch64-apple-darwin", 12 | ] 13 | components = ["rustfmt", "clippy", "rust-src", "rust-analyzer", "cargo"] 14 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.87" 3 | targets = ["wasm32-unknown-unknown", "wasm32-wasip2"] 4 | components = ["rustfmt", "clippy", "rust-src", "rust-analyzer", "cargo"] 5 | -------------------------------------------------------------------------------- /scripts/cargo-publish-workspace.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Publish all packages in the workspace except for those listed in assets/unpublishable-packages.txt. 4 | # Requires Rust nightly ATM, see `flake.nix` for exact version. 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | 8 | EXCLUDE_PACKAGES=$(awk '{printf " --exclude %s", $1}' "assets/unpublishable-packages.txt") 9 | 10 | cargo publish -Z package-workspace --workspace ${EXCLUDE_PACKAGES[@]} "$@" 11 | -------------------------------------------------------------------------------- /scripts/clippy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run clippy normally and with `madsim` cfg flag. 4 | 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | 8 | cargo clippy --workspace --all-targets --fix --allow-dirty --allow-staged -- -D warnings 9 | RUSTFLAGS="--cfg madsim" cargo clippy --workspace --target-dir=target/debug/madsim --fix --allow-dirty --allow-staged --all-targets -- -D warnings 10 | cargo fmt 11 | 12 | git status -s 13 | -------------------------------------------------------------------------------- /scripts/dev-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Collect versions of binaries installed by `nix develop` producing file `dev-deps.txt`. 4 | # This script should be executed after every `nix flake update`. 5 | 6 | set -exuo pipefail 7 | cd "$(dirname "$0")/.." 8 | 9 | rm -f dev-deps.txt 10 | echo "cargo-binstall $(cargo-binstall -V)" >> dev-deps.txt 11 | cargo upgrade --version >> dev-deps.txt 12 | cargo-expand --version >> dev-deps.txt 13 | # cargo-insta --version >> dev-deps.txt # Broken as of 1.39-unstable-2024-08-22 14 | cargo nextest --version | head -n 1 >> dev-deps.txt 15 | cargo semver-checks --version >> dev-deps.txt 16 | echo "litecli $(litecli --version)" >> dev-deps.txt 17 | echo $(nixpkgs-fmt --version) >> dev-deps.txt 18 | echo "pkg-config $(pkg-config --version)" >> dev-deps.txt 19 | # protobuf 20 | protoc --version >> dev-deps.txt 21 | rustc --version >> dev-deps.txt 22 | wasm-tools --version >> dev-deps.txt 23 | wasmtime --version >> dev-deps.txt 24 | cargo-zigbuild --version >> dev-deps.txt 25 | # web 26 | nix develop .#web --command wasm-opt --version >> dev-deps.txt # binaryen 27 | nix develop .#web --command trunk --version >> dev-deps.txt 28 | nix develop .#web --command wasm-bindgen --version >> dev-deps.txt 29 | 30 | # libc 31 | ldd --version | head -n 1 >> dev-deps.txt 32 | 33 | 34 | -------------------------------------------------------------------------------- /scripts/extract-release-info.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Called by the release CI to extract the changelog section for a given version. 4 | # Usage: ./scripts/extract-release-info.sh 5 | 6 | set -exuo pipefail 7 | cd "$(dirname "$0")/.." 8 | 9 | if [ -z "$1" ]; then 10 | echo "Usage: $0 " 11 | exit 1 12 | fi 13 | 14 | VERSION="$1" 15 | 16 | START_LINE=$(awk -v version="$VERSION" '$0 ~ "^## \\[" version "\\]" {print NR}' CHANGELOG.md) 17 | 18 | END_LINE=$(awk -v start="$((START_LINE + 1))" 'NR >= start && /^## / {print NR; exit}' CHANGELOG.md) 19 | 20 | sed -n "$((START_LINE + 1)),$((END_LINE - 1))p" CHANGELOG.md 21 | -------------------------------------------------------------------------------- /scripts/push-test-components.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Push all WASM components from crates/testing/test-programs to the Docker Hub and update obelisk-oci.toml 4 | 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | 8 | TAG="$1" 9 | TOML_FILE="obelisk.toml" 10 | PREFIX="docker.io/getobelisk/" 11 | 12 | push() { 13 | RELATIVE_PATH=$1 14 | FILE_NAME_WITHOUT_EXT=$(basename "$RELATIVE_PATH" | sed 's/\.[^.]*$//') 15 | OCI_LOCATION="${PREFIX}${FILE_NAME_WITHOUT_EXT}:${TAG}" 16 | echo "Pushing ${RELATIVE_PATH} to ${OCI_LOCATION}..." 17 | OUTPUT=$(obelisk client component push "$RELATIVE_PATH" "$OCI_LOCATION") 18 | 19 | # Replace the old location with the actual OCI location 20 | sed -i -E "/name = \"${FILE_NAME_WITHOUT_EXT}\"/{n;s|location\.oci = \".*\"|location.oci = \"${OUTPUT}\"|}" "$TOML_FILE" 21 | } 22 | 23 | # Make sure all components are fresh 24 | cargo check --workspace 25 | 26 | push "target/wasm32-wasip2/release/test_programs_fibo_activity.wasm" 27 | push "target/wasm32-unknown-unknown/release/test_programs_fibo_workflow.wasm" 28 | push "target/wasm32-wasip2/release/test_programs_fibo_webhook.wasm" 29 | push "target/wasm32-wasip2/release/test_programs_http_get_activity.wasm" 30 | push "target/wasm32-unknown-unknown/release/test_programs_http_get_workflow.wasm" 31 | push "target/wasm32-wasip2/release/test_programs_sleep_activity.wasm" 32 | push "target/wasm32-unknown-unknown/release/test_programs_sleep_workflow.wasm" 33 | 34 | echo "All components pushed and TOML file updated successfully." 35 | -------------------------------------------------------------------------------- /scripts/push-webui.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Rebuild webui and webui-proxy, then push the proxy WASM component to Docker Hub. 4 | 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | 8 | TAG="$1" 9 | rm -rf crates/webui/dist 10 | 11 | # Run trunk in webui-builder 12 | CARGO_PROFILE_RELEASE_DEBUG=limited RUN_TRUNK=true \ 13 | cargo build --package webui-proxy --target=wasm32-wasip2 --profile=release_trunk 14 | 15 | if [ "$TAG" != "dry-run" ]; then 16 | echo -n $(obelisk client component push "target/wasm32-wasip2/release_trunk/webui_proxy.wasm" \ 17 | "docker.io/getobelisk/webui:$TAG") > assets/webui-version.txt 18 | fi 19 | -------------------------------------------------------------------------------- /scripts/test-madsim.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run madsim tests 4 | 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | # set MADSIM_TEST_NUM to run multiple simulations per test 8 | RUST_BACKTRACE=1 MADSIM_ALLOW_SYSTEM_THREAD=1 RUSTFLAGS="--cfg madsim" \ 9 | RUST_LOG="${RUST_LOG:-obeli=debug}" \ 10 | cargo nextest run --workspace --target-dir target/debug/madsim -P ci-test-madsim "$@" 11 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run regular tests. 4 | 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | 8 | RUST_BACKTRACE=1 \ 9 | RUST_LOG="${RUST_LOG:-obeli=debug}" \ 10 | cargo nextest run --workspace -P ci-test-nosim "$@" 11 | -------------------------------------------------------------------------------- /scripts/unpublishable-packages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Find all packages with "publish = false" in their Cargo.toml files and save them to a file. 4 | 5 | set -exuo pipefail 6 | cd "$(dirname "$0")/.." 7 | 8 | OUTPUT_FILE="assets/unpublishable-packages.txt" 9 | 10 | # Find all Cargo.toml files and check if "publish = false" is set 11 | PACKAGES=() 12 | while IFS= read -r file; do 13 | PACKAGE_NAME=$(grep -m1 '^name\s*=\s*"' "$file" | sed -E 's/name\s*=\s*"([^"]+)"/\1/') 14 | if [ -n "$PACKAGE_NAME" ]; then 15 | PACKAGES+=("$PACKAGE_NAME") 16 | fi 17 | done < <(grep -rl "publish\s*=\s*false" --include="Cargo.toml" crates) 18 | 19 | # Sort and write to file 20 | printf "%s\n" "${PACKAGES[@]}" | sort > "$OUTPUT_FILE" 21 | 22 | echo "Unpublishable packages saved to $OUTPUT_FILE" 23 | -------------------------------------------------------------------------------- /scripts/update-toml-schema.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -exuo pipefail 4 | cd "$(dirname "$0")/.." 5 | 6 | cargo run -- generate config-schema toml/schema.json 7 | -------------------------------------------------------------------------------- /src/command/generate.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{BufWriter, Write as _, stdout}, 4 | path::PathBuf, 5 | }; 6 | 7 | use crate::config::toml::ConfigToml; 8 | use schemars::schema_for; 9 | 10 | pub(crate) fn generate_toml_schema(output: Option) -> Result<(), anyhow::Error> { 11 | let schema = schema_for!(ConfigToml); 12 | if let Some(output) = output { 13 | // Save to a file 14 | let mut writer = BufWriter::new(File::create(&output)?); 15 | serde_json::to_writer_pretty(&mut writer, &schema)?; 16 | writer.write_all(b"\n")?; 17 | writer.flush()?; // Do not swallow errors 18 | } else { 19 | serde_json::to_writer_pretty(stdout().lock(), &schema)?; 20 | } 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /src/command/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod component; 2 | pub(crate) mod execution; 3 | pub(crate) mod generate; 4 | pub(crate) mod server; 5 | 6 | // TODO: move to parent module 7 | #[allow(clippy::too_many_lines)] 8 | #[allow(clippy::default_trait_access)] 9 | #[allow(clippy::struct_field_names)] 10 | #[allow(clippy::similar_names)] 11 | #[allow(clippy::wildcard_imports)] 12 | #[allow(clippy::doc_markdown)] 13 | pub(crate) mod grpc { 14 | tonic::include_proto!("obelisk"); 15 | } 16 | -------------------------------------------------------------------------------- /src/config/env_var.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Deserializer}; 3 | 4 | #[derive(Clone, derive_more::Debug, Hash, JsonSchema)] 5 | pub struct EnvVarConfig { 6 | pub key: String, 7 | #[debug(skip)] 8 | pub val: Option, 9 | } 10 | 11 | struct EnvVarConfigVisitor; 12 | 13 | impl serde::de::Visitor<'_> for EnvVarConfigVisitor { 14 | type Value = EnvVarConfig; 15 | 16 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 17 | formatter 18 | .write_str("either key of environment varaible to be forwarded from host, or key=value") 19 | } 20 | 21 | fn visit_str(self, input: &str) -> Result 22 | where 23 | E: serde::de::Error, 24 | { 25 | Ok(match input.split_once('=') { 26 | None => EnvVarConfig { 27 | key: input.to_string(), 28 | val: None, 29 | }, 30 | Some((k, input)) => EnvVarConfig { 31 | key: k.to_string(), 32 | val: Some(input.to_string()), 33 | }, 34 | }) 35 | } 36 | } 37 | impl<'de> Deserialize<'de> for EnvVarConfig { 38 | fn deserialize(deserializer: D) -> Result 39 | where 40 | D: Deserializer<'de>, 41 | { 42 | deserializer.deserialize_str(EnvVarConfigVisitor) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/env_vars.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "tokio-console")] 2 | #[derive(strum::Display, Clone, Copy)] 3 | #[expect(non_camel_case_types)] 4 | pub(crate) enum SupportedEnvVar { 5 | TOKIO_CONSOLE, 6 | } 7 | 8 | #[cfg(feature = "tokio-console")] 9 | pub(crate) fn is_env_true(key: &SupportedEnvVar) -> bool { 10 | std::env::var(key.to_string()) 11 | .ok() 12 | .and_then(|val| val.parse::().ok()) 13 | .unwrap_or_default() 14 | } 15 | -------------------------------------------------------------------------------- /toml/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 obeli-sk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wit/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 obeli-sk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wit/obelisk_log@1.0.0/log@1.0.0.wit: -------------------------------------------------------------------------------- 1 | package obelisk:log@1.0.0; 2 | 3 | interface log { 4 | trace: func(message: string); 5 | debug: func(message: string); 6 | info: func(message: string); 7 | warn: func(message: string); 8 | error: func(message: string); 9 | } 10 | -------------------------------------------------------------------------------- /wit/obelisk_types@1.1.0/types@1.1.0.wit: -------------------------------------------------------------------------------- 1 | package obelisk:types@1.1.0; 2 | 3 | interface time { 4 | 5 | variant duration { 6 | milliseconds(u64), 7 | seconds(u64), 8 | minutes(u32), 9 | hours(u32), 10 | days(u32), 11 | } 12 | 13 | /// A time and date in seconds plus nanoseconds. 14 | // Extracted from wasi:clocks@0.2.0 to avoid dependency on wasi:io 15 | record datetime { 16 | seconds: u64, 17 | nanoseconds: u32, 18 | } 19 | 20 | variant schedule-at { 21 | now, 22 | at(datetime), 23 | in(duration), 24 | } 25 | } 26 | 27 | interface execution { 28 | resource join-set-id { 29 | id: func() -> string; 30 | // TODO: sleep-submit 31 | // TODO: await-next 32 | } 33 | record execution-id { 34 | id: string, 35 | } 36 | 37 | record delay-id { 38 | id: string, 39 | } 40 | 41 | variant execution-error { 42 | activity-trap(string), 43 | permanent-timeout, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /wit/obelisk_workflow@1.1.0/workflow-support@1.1.0.wit: -------------------------------------------------------------------------------- 1 | package obelisk:workflow@1.1.0; 2 | 3 | interface workflow-support { 4 | use obelisk:types/time@1.1.0.{duration}; 5 | use obelisk:types/execution@1.1.0.{join-set-id}; 6 | 7 | /// The closing strategy of a join set. Join sets are closed when execution finishes. 8 | enum closing-strategy { 9 | /// All submitted executions that were not awaited are awaited. 10 | complete, 11 | 12 | // All submitted executions that were not awaited are cancelled. 13 | // TODO: Implement cancel 14 | } 15 | 16 | /// Returns a random u64 in the range [min, max). 17 | random-u64: func(min: u64, max-exclusive: u64) -> u64; 18 | /// Returns a random u64 in the range [min, max]. 19 | random-u64-inclusive: func(min: u64, max-inclusive: u64) -> u64; 20 | 21 | /// Returns a random string with a length in the range [min_length, max_length). 22 | /// The string consists only of alphanumeric characters (lowercase and uppercase letters, digits). 23 | random-string: func(min-length: u16, max-length-exclusive: u16) -> string; 24 | 25 | /// Persistent sleep. 26 | sleep: func(duration: duration); 27 | 28 | /// Create a new completing join set. 29 | new-join-set-named: func(name: string, closing-strategy: closing-strategy) -> join-set-id; 30 | 31 | /// Create a new completing join set with a generated name. 32 | new-join-set-generated: func(closing-strategy: closing-strategy) -> join-set-id; 33 | } 34 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/command.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world command { 5 | @since(version = 0.2.0) 6 | include imports; 7 | 8 | @since(version = 0.2.0) 9 | export run; 10 | } 11 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/environment.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface environment { 3 | /// Get the POSIX-style environment variables. 4 | /// 5 | /// Each environment variable is provided as a pair of string variable names 6 | /// and string value. 7 | /// 8 | /// Morally, these are a value import, but until value imports are available 9 | /// in the component model, this import function should return the same 10 | /// values each time it is called. 11 | @since(version = 0.2.0) 12 | get-environment: func() -> list>; 13 | 14 | /// Get the POSIX-style arguments to the program. 15 | @since(version = 0.2.0) 16 | get-arguments: func() -> list; 17 | 18 | /// Return a path that programs should use as their initial current working 19 | /// directory, interpreting `.` as shorthand for this. 20 | @since(version = 0.2.0) 21 | initial-cwd: func() -> option; 22 | } 23 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/exit.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface exit { 3 | /// Exit the current instance and any linked instances. 4 | @since(version = 0.2.0) 5 | exit: func(status: result); 6 | 7 | /// Exit the current instance and any linked instances, reporting the 8 | /// specified status code to the host. 9 | /// 10 | /// The meaning of the code depends on the context, with 0 usually meaning 11 | /// "success", and other values indicating various types of failure. 12 | /// 13 | /// This function does not return; the effect is analogous to a trap, but 14 | /// without the connotation that something bad has happened. 15 | @unstable(feature = cli-exit-with-code) 16 | exit-with-code: func(status-code: u8); 17 | } 18 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/imports.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | include wasi:clocks/imports@0.2.3; 7 | @since(version = 0.2.0) 8 | include wasi:filesystem/imports@0.2.3; 9 | @since(version = 0.2.0) 10 | include wasi:sockets/imports@0.2.3; 11 | @since(version = 0.2.0) 12 | include wasi:random/imports@0.2.3; 13 | @since(version = 0.2.0) 14 | include wasi:io/imports@0.2.3; 15 | 16 | @since(version = 0.2.0) 17 | import environment; 18 | @since(version = 0.2.0) 19 | import exit; 20 | @since(version = 0.2.0) 21 | import stdin; 22 | @since(version = 0.2.0) 23 | import stdout; 24 | @since(version = 0.2.0) 25 | import stderr; 26 | @since(version = 0.2.0) 27 | import terminal-input; 28 | @since(version = 0.2.0) 29 | import terminal-output; 30 | @since(version = 0.2.0) 31 | import terminal-stdin; 32 | @since(version = 0.2.0) 33 | import terminal-stdout; 34 | @since(version = 0.2.0) 35 | import terminal-stderr; 36 | } 37 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/run.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface run { 3 | /// Run the program. 4 | @since(version = 0.2.0) 5 | run: func() -> result; 6 | } 7 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/stdio.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface stdin { 3 | @since(version = 0.2.0) 4 | use wasi:io/streams@0.2.3.{input-stream}; 5 | 6 | @since(version = 0.2.0) 7 | get-stdin: func() -> input-stream; 8 | } 9 | 10 | @since(version = 0.2.0) 11 | interface stdout { 12 | @since(version = 0.2.0) 13 | use wasi:io/streams@0.2.3.{output-stream}; 14 | 15 | @since(version = 0.2.0) 16 | get-stdout: func() -> output-stream; 17 | } 18 | 19 | @since(version = 0.2.0) 20 | interface stderr { 21 | @since(version = 0.2.0) 22 | use wasi:io/streams@0.2.3.{output-stream}; 23 | 24 | @since(version = 0.2.0) 25 | get-stderr: func() -> output-stream; 26 | } 27 | -------------------------------------------------------------------------------- /wit/wasi_cli@0.2.3/terminal.wit: -------------------------------------------------------------------------------- 1 | /// Terminal input. 2 | /// 3 | /// In the future, this may include functions for disabling echoing, 4 | /// disabling input buffering so that keyboard events are sent through 5 | /// immediately, querying supported features, and so on. 6 | @since(version = 0.2.0) 7 | interface terminal-input { 8 | /// The input side of a terminal. 9 | @since(version = 0.2.0) 10 | resource terminal-input; 11 | } 12 | 13 | /// Terminal output. 14 | /// 15 | /// In the future, this may include functions for querying the terminal 16 | /// size, being notified of terminal size changes, querying supported 17 | /// features, and so on. 18 | @since(version = 0.2.0) 19 | interface terminal-output { 20 | /// The output side of a terminal. 21 | @since(version = 0.2.0) 22 | resource terminal-output; 23 | } 24 | 25 | /// An interface providing an optional `terminal-input` for stdin as a 26 | /// link-time authority. 27 | @since(version = 0.2.0) 28 | interface terminal-stdin { 29 | @since(version = 0.2.0) 30 | use terminal-input.{terminal-input}; 31 | 32 | /// If stdin is connected to a terminal, return a `terminal-input` handle 33 | /// allowing further interaction with it. 34 | @since(version = 0.2.0) 35 | get-terminal-stdin: func() -> option; 36 | } 37 | 38 | /// An interface providing an optional `terminal-output` for stdout as a 39 | /// link-time authority. 40 | @since(version = 0.2.0) 41 | interface terminal-stdout { 42 | @since(version = 0.2.0) 43 | use terminal-output.{terminal-output}; 44 | 45 | /// If stdout is connected to a terminal, return a `terminal-output` handle 46 | /// allowing further interaction with it. 47 | @since(version = 0.2.0) 48 | get-terminal-stdout: func() -> option; 49 | } 50 | 51 | /// An interface providing an optional `terminal-output` for stderr as a 52 | /// link-time authority. 53 | @since(version = 0.2.0) 54 | interface terminal-stderr { 55 | @since(version = 0.2.0) 56 | use terminal-output.{terminal-output}; 57 | 58 | /// If stderr is connected to a terminal, return a `terminal-output` handle 59 | /// allowing further interaction with it. 60 | @since(version = 0.2.0) 61 | get-terminal-stderr: func() -> option; 62 | } 63 | -------------------------------------------------------------------------------- /wit/wasi_clocks@0.2.3/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 3 | /// time. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A monotonic clock is a clock which has an unspecified initial value, and 9 | /// successive reads of the clock will produce non-decreasing values. 10 | @since(version = 0.2.0) 11 | interface monotonic-clock { 12 | @since(version = 0.2.0) 13 | use wasi:io/poll@0.2.3.{pollable}; 14 | 15 | /// An instant in time, in nanoseconds. An instant is relative to an 16 | /// unspecified initial value, and can only be compared to instances from 17 | /// the same monotonic-clock. 18 | @since(version = 0.2.0) 19 | type instant = u64; 20 | 21 | /// A duration of time, in nanoseconds. 22 | @since(version = 0.2.0) 23 | type duration = u64; 24 | 25 | /// Read the current value of the clock. 26 | /// 27 | /// The clock is monotonic, therefore calling this function repeatedly will 28 | /// produce a sequence of non-decreasing values. 29 | @since(version = 0.2.0) 30 | now: func() -> instant; 31 | 32 | /// Query the resolution of the clock. Returns the duration of time 33 | /// corresponding to a clock tick. 34 | @since(version = 0.2.0) 35 | resolution: func() -> duration; 36 | 37 | /// Create a `pollable` which will resolve once the specified instant 38 | /// has occurred. 39 | @since(version = 0.2.0) 40 | subscribe-instant: func( 41 | when: instant, 42 | ) -> pollable; 43 | 44 | /// Create a `pollable` that will resolve after the specified duration has 45 | /// elapsed from the time this function is invoked. 46 | @since(version = 0.2.0) 47 | subscribe-duration: func( 48 | when: duration, 49 | ) -> pollable; 50 | } 51 | -------------------------------------------------------------------------------- /wit/wasi_clocks@0.2.3/wall-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | /// WASI Wall Clock is a clock API intended to let users query the current 3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 4 | /// is not necessarily monotonic as it may be reset. 5 | /// 6 | /// It is intended to be portable at least between Unix-family platforms and 7 | /// Windows. 8 | /// 9 | /// A wall clock is a clock which measures the date and time according to 10 | /// some external reference. 11 | /// 12 | /// External references may be reset, so this clock is not necessarily 13 | /// monotonic, making it unsuitable for measuring elapsed time. 14 | /// 15 | /// It is intended for reporting the current date and time for humans. 16 | @since(version = 0.2.0) 17 | interface wall-clock { 18 | /// A time and date in seconds plus nanoseconds. 19 | @since(version = 0.2.0) 20 | record datetime { 21 | seconds: u64, 22 | nanoseconds: u32, 23 | } 24 | 25 | /// Read the current value of the clock. 26 | /// 27 | /// This clock is not monotonic, therefore calling this function repeatedly 28 | /// will not necessarily produce a sequence of non-decreasing values. 29 | /// 30 | /// The returned timestamps represent the number of seconds since 31 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 32 | /// also known as [Unix Time]. 33 | /// 34 | /// The nanoseconds field of the output is always less than 1000000000. 35 | /// 36 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 37 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 38 | @since(version = 0.2.0) 39 | now: func() -> datetime; 40 | 41 | /// Query the resolution of the clock. 42 | /// 43 | /// The nanoseconds field of the output is always less than 1000000000. 44 | @since(version = 0.2.0) 45 | resolution: func() -> datetime; 46 | } 47 | -------------------------------------------------------------------------------- /wit/wasi_clocks@0.2.3/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import monotonic-clock; 7 | @since(version = 0.2.0) 8 | import wall-clock; 9 | @unstable(feature = clocks-timezone) 10 | import timezone; 11 | } 12 | -------------------------------------------------------------------------------- /wit/wasi_filesystem@0.2.3/preopens.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | interface preopens { 5 | @since(version = 0.2.0) 6 | use types.{descriptor}; 7 | 8 | /// Return the set of preopened directories, and their paths. 9 | @since(version = 0.2.0) 10 | get-directories: func() -> list>; 11 | } 12 | -------------------------------------------------------------------------------- /wit/wasi_filesystem@0.2.3/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import types; 7 | @since(version = 0.2.0) 8 | import preopens; 9 | } 10 | -------------------------------------------------------------------------------- /wit/wasi_io@0.2.3/error.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | interface error { 5 | /// A resource which represents some error information. 6 | /// 7 | /// The only method provided by this resource is `to-debug-string`, 8 | /// which provides some human-readable information about the error. 9 | /// 10 | /// In the `wasi:io` package, this resource is returned through the 11 | /// `wasi:io/streams/stream-error` type. 12 | /// 13 | /// To provide more specific error information, other interfaces may 14 | /// offer functions to "downcast" this error into more specific types. For example, 15 | /// errors returned from streams derived from filesystem types can be described using 16 | /// the filesystem's own error-code type. This is done using the function 17 | /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` 18 | /// parameter and returns an `option`. 19 | /// 20 | /// The set of functions which can "downcast" an `error` into a more 21 | /// concrete type is open. 22 | @since(version = 0.2.0) 23 | resource error { 24 | /// Returns a string that is suitable to assist humans in debugging 25 | /// this error. 26 | /// 27 | /// WARNING: The returned string should not be consumed mechanically! 28 | /// It may change across platforms, hosts, or other implementation 29 | /// details. Parsing this string is a major platform-compatibility 30 | /// hazard. 31 | @since(version = 0.2.0) 32 | to-debug-string: func() -> string; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /wit/wasi_io@0.2.3/poll.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | /// A poll API intended to let users wait for I/O events on multiple handles 4 | /// at once. 5 | @since(version = 0.2.0) 6 | interface poll { 7 | /// `pollable` represents a single I/O event which may be ready, or not. 8 | @since(version = 0.2.0) 9 | resource pollable { 10 | 11 | /// Return the readiness of a pollable. This function never blocks. 12 | /// 13 | /// Returns `true` when the pollable is ready, and `false` otherwise. 14 | @since(version = 0.2.0) 15 | ready: func() -> bool; 16 | 17 | /// `block` returns immediately if the pollable is ready, and otherwise 18 | /// blocks until ready. 19 | /// 20 | /// This function is equivalent to calling `poll.poll` on a list 21 | /// containing only this pollable. 22 | @since(version = 0.2.0) 23 | block: func(); 24 | } 25 | 26 | /// Poll for completion on a set of pollables. 27 | /// 28 | /// This function takes a list of pollables, which identify I/O sources of 29 | /// interest, and waits until one or more of the events is ready for I/O. 30 | /// 31 | /// The result `list` contains one or more indices of handles in the 32 | /// argument list that is ready for I/O. 33 | /// 34 | /// This function traps if either: 35 | /// - the list is empty, or: 36 | /// - the list contains more elements than can be indexed with a `u32` value. 37 | /// 38 | /// A timeout can be implemented by adding a pollable from the 39 | /// wasi-clocks API to the list. 40 | /// 41 | /// This function does not return a `result`; polling in itself does not 42 | /// do any I/O so it doesn't fail. If any of the I/O sources identified by 43 | /// the pollables has an error, it is indicated by marking the source as 44 | /// being ready for I/O. 45 | @since(version = 0.2.0) 46 | poll: func(in: list>) -> list; 47 | } 48 | -------------------------------------------------------------------------------- /wit/wasi_io@0.2.3/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import streams; 7 | 8 | @since(version = 0.2.0) 9 | import poll; 10 | } 11 | -------------------------------------------------------------------------------- /wit/wasi_random@0.2.3/insecure-seed.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// The insecure-seed interface for seeding hash-map DoS resistance. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface insecure-seed { 8 | /// Return a 128-bit value that may contain a pseudo-random value. 9 | /// 10 | /// The returned value is not required to be computed from a CSPRNG, and may 11 | /// even be entirely deterministic. Host implementations are encouraged to 12 | /// provide pseudo-random values to any program exposed to 13 | /// attacker-controlled content, to enable DoS protection built into many 14 | /// languages' hash-map implementations. 15 | /// 16 | /// This function is intended to only be called once, by a source language 17 | /// to initialize Denial Of Service (DoS) protection in its hash-map 18 | /// implementation. 19 | /// 20 | /// # Expected future evolution 21 | /// 22 | /// This will likely be changed to a value import, to prevent it from being 23 | /// called multiple times and potentially used for purposes other than DoS 24 | /// protection. 25 | @since(version = 0.2.0) 26 | insecure-seed: func() -> tuple; 27 | } 28 | -------------------------------------------------------------------------------- /wit/wasi_random@0.2.3/insecure.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// The insecure interface for insecure pseudo-random numbers. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface insecure { 8 | /// Return `len` insecure pseudo-random bytes. 9 | /// 10 | /// This function is not cryptographically secure. Do not use it for 11 | /// anything related to security. 12 | /// 13 | /// There are no requirements on the values of the returned bytes, however 14 | /// implementations are encouraged to return evenly distributed values with 15 | /// a long period. 16 | @since(version = 0.2.0) 17 | get-insecure-random-bytes: func(len: u64) -> list; 18 | 19 | /// Return an insecure pseudo-random `u64` value. 20 | /// 21 | /// This function returns the same type of pseudo-random data as 22 | /// `get-insecure-random-bytes`, represented as a `u64`. 23 | @since(version = 0.2.0) 24 | get-insecure-random-u64: func() -> u64; 25 | } 26 | -------------------------------------------------------------------------------- /wit/wasi_random@0.2.3/random.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// WASI Random is a random data API. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface random { 8 | /// Return `len` cryptographically-secure random or pseudo-random bytes. 9 | /// 10 | /// This function must produce data at least as cryptographically secure and 11 | /// fast as an adequately seeded cryptographically-secure pseudo-random 12 | /// number generator (CSPRNG). It must not block, from the perspective of 13 | /// the calling program, under any circumstances, including on the first 14 | /// request and on requests for numbers of bytes. The returned data must 15 | /// always be unpredictable. 16 | /// 17 | /// This function must always return fresh data. Deterministic environments 18 | /// must omit this function, rather than implementing it with deterministic 19 | /// data. 20 | @since(version = 0.2.0) 21 | get-random-bytes: func(len: u64) -> list; 22 | 23 | /// Return a cryptographically-secure random or pseudo-random `u64` value. 24 | /// 25 | /// This function returns the same type of data as `get-random-bytes`, 26 | /// represented as a `u64`. 27 | @since(version = 0.2.0) 28 | get-random-u64: func() -> u64; 29 | } 30 | -------------------------------------------------------------------------------- /wit/wasi_random@0.2.3/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import random; 7 | 8 | @since(version = 0.2.0) 9 | import insecure; 10 | 11 | @since(version = 0.2.0) 12 | import insecure-seed; 13 | } 14 | -------------------------------------------------------------------------------- /wit/wasi_sockets@0.2.3/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | @since(version = 0.2.0) 4 | interface instance-network { 5 | @since(version = 0.2.0) 6 | use network.{network}; 7 | 8 | /// Get a handle to the default network. 9 | @since(version = 0.2.0) 10 | instance-network: func() -> network; 11 | } 12 | -------------------------------------------------------------------------------- /wit/wasi_sockets@0.2.3/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface tcp-create-socket { 3 | @since(version = 0.2.0) 4 | use network.{network, error-code, ip-address-family}; 5 | @since(version = 0.2.0) 6 | use tcp.{tcp-socket}; 7 | 8 | /// Create a new TCP socket. 9 | /// 10 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 11 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 12 | /// 13 | /// This function does not require a network capability handle. This is considered to be safe because 14 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` 15 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 16 | /// 17 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 18 | /// 19 | /// # Typical errors 20 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 21 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 22 | /// 23 | /// # References 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | create-tcp-socket: func(address-family: ip-address-family) -> result; 30 | } 31 | -------------------------------------------------------------------------------- /wit/wasi_sockets@0.2.3/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface udp-create-socket { 3 | @since(version = 0.2.0) 4 | use network.{network, error-code, ip-address-family}; 5 | @since(version = 0.2.0) 6 | use udp.{udp-socket}; 7 | 8 | /// Create a new UDP socket. 9 | /// 10 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 11 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 12 | /// 13 | /// This function does not require a network capability handle. This is considered to be safe because 14 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, 15 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 16 | /// 17 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 18 | /// 19 | /// # Typical errors 20 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 21 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 22 | /// 23 | /// # References: 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | create-udp-socket: func(address-family: ip-address-family) -> result; 30 | } 31 | -------------------------------------------------------------------------------- /wit/wasi_sockets@0.2.3/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:sockets@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import instance-network; 7 | @since(version = 0.2.0) 8 | import network; 9 | @since(version = 0.2.0) 10 | import udp; 11 | @since(version = 0.2.0) 12 | import udp-create-socket; 13 | @since(version = 0.2.0) 14 | import tcp; 15 | @since(version = 0.2.0) 16 | import tcp-create-socket; 17 | @since(version = 0.2.0) 18 | import ip-name-lookup; 19 | } 20 | --------------------------------------------------------------------------------