├── .cargo └── config.toml ├── .github ├── release.yml └── workflows │ ├── check-labels.yml │ ├── ci.yml │ ├── publish-docs.yml │ └── release.yml ├── .gitignore ├── .markdownlint.yaml ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── Cross.toml ├── EXAMPLE_CONFIG.json5 ├── LICENSE ├── NOTICE.md ├── README.md ├── ci └── scripts │ └── bump-and-tag.bash ├── deno.lock ├── package.json ├── rust-toolchain.toml ├── rustfmt.toml ├── yarn.lock ├── zenoh-keyexpr-wasm ├── Cargo.toml ├── README.md ├── rust-toolchain.toml └── src │ └── lib.rs ├── zenoh-plugin-remote-api ├── Cargo.toml ├── EXAMPLE_CONFIG.json5 ├── README.md ├── build.rs ├── config.json5 ├── config_schema.json5 └── src │ ├── config.rs │ ├── interface │ └── mod.rs │ ├── lib.rs │ └── remote_state.rs └── zenoh-ts ├── .release-it.json ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── LICENSE ├── NOTICE.md ├── eslint.config.cjs ├── examples ├── browser │ └── chat │ │ ├── assets │ │ ├── index.css │ │ └── index.html │ │ ├── package.json │ │ ├── src │ │ ├── chat_session.ts │ │ └── main.ts │ │ ├── tsconfig.json │ │ ├── webpack.config.js │ │ └── yarn.lock └── deno │ ├── deno.json │ ├── deno.lock │ ├── package.json │ ├── scripts │ └── start.sh │ ├── src │ ├── parse_args.ts │ ├── z_bytes.ts │ ├── z_delete.ts │ ├── z_get.ts │ ├── z_get_liveliness.ts │ ├── z_info.ts │ ├── z_liveliness.ts │ ├── z_ping.ts │ ├── z_pong.ts │ ├── z_pub.ts │ ├── z_pub_thr.ts │ ├── z_put.ts │ ├── z_querier.ts │ ├── z_queryable.ts │ ├── z_sub.ts │ ├── z_sub_liveliness.ts │ └── z_sub_thr.ts │ ├── tsconfig.json │ └── yarn.lock ├── importmap.json ├── package.json ├── scripts ├── build.sh ├── clean.sh └── start.sh ├── src ├── channels.ts ├── closure.ts ├── config.ts ├── encoding.ts ├── enums.ts ├── ext │ ├── index.ts │ └── serialization.ts ├── index.ts ├── key_expr.ts ├── key_expr │ ├── zenoh_keyexpr_wrapper.d.ts │ ├── zenoh_keyexpr_wrapper.js │ ├── zenoh_keyexpr_wrapper_bg.js │ ├── zenoh_keyexpr_wrapper_bg.wasm │ └── zenoh_keyexpr_wrapper_bg.wasm.d.ts ├── link.ts ├── liveliness.ts ├── message.ts ├── pubsub.ts ├── querier.ts ├── query.ts ├── sample.ts ├── session.ts ├── session_inner.ts ├── timestamp.ts ├── z_bytes.ts └── zid.ts ├── tests ├── deno.json ├── deno.lock ├── package.json ├── scripts │ └── start.sh ├── src │ ├── bench │ │ └── z_serialization.ts │ ├── z_api_pub_sub.ts │ ├── z_api_queryable_get.ts │ ├── z_channels.ts │ ├── z_encoding.ts │ ├── z_keyexpr.ts │ ├── z_liveliness.ts │ ├── z_parameters.ts │ ├── z_serialization.ts │ └── z_timestamp.ts ├── tsconfig.json └── yarn.lock ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-musl] 2 | rustflags = "-Ctarget-feature=-crt-static" 3 | 4 | [target.aarch64-unknown-linux-musl] 5 | rustflags = "-Ctarget-feature=-crt-static" -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 ZettaScale Technology 3 | # 4 | # This program and the accompanying materials are made available under the 5 | # terms of the Eclipse Public License 2.0 which is available at 6 | # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | # which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | # 11 | # Contributors: 12 | # ZettaScale Zenoh Team, 13 | # 14 | 15 | changelog: 16 | categories: 17 | - title: Breaking changes 💥 18 | labels: 19 | - breaking-change 20 | - title: New features 🎉 21 | labels: 22 | - enhancement 23 | - new feature 24 | exclude: 25 | labels: 26 | - internal 27 | - title: Bug fixes 🐞 28 | labels: 29 | - bug 30 | exclude: 31 | labels: 32 | - internal 33 | - title: Documentation 📝 34 | labels: 35 | - documentation 36 | exclude: 37 | labels: 38 | - internal 39 | - title: Dependencies 👷 40 | labels: 41 | - dependencies 42 | exclude: 43 | labels: 44 | - internal 45 | - title: Other changes 46 | labels: 47 | - "*" 48 | exclude: 49 | labels: 50 | - internal -------------------------------------------------------------------------------- /.github/workflows/check-labels.yml: -------------------------------------------------------------------------------- 1 | name: Check required labels 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, synchronize, reopened, labeled] 6 | branches: ["**"] 7 | 8 | jobs: 9 | check-labels: 10 | name: Check PR labels 11 | uses: eclipse-zenoh/ci/.github/workflows/check-labels.yml@main 12 | secrets: 13 | github-token: ${{ secrets.GITHUB_TOKEN }} 14 | permissions: 15 | pull-requests: write -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2024 ZettaScale Technology 3 | # 4 | # This program and the accompanying materials are made available under the 5 | # terms of the Eclipse Public License 2.0 which is available at 6 | # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | # which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | # 11 | # Contributors: 12 | # ZettaScale Zenoh Team, 13 | # 14 | name: CI 15 | 16 | on: 17 | push: 18 | branches: 19 | - "**" 20 | pull_request: 21 | branches: 22 | - "**" 23 | schedule: 24 | - cron: "0 6 * * 1-5" 25 | 26 | jobs: 27 | check_rust: 28 | name: Check zenoh-ts using Rust 1.75 29 | runs-on: ubuntu-latest 30 | strategy: 31 | fail-fast: false 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: dtolnay/rust-toolchain@1.75.0 35 | - uses: Swatinem/rust-cache@v2 36 | with: 37 | cache-bin: false 38 | 39 | - name: Check zenoh-ts with rust 1.75.0 40 | run: | 41 | cargo +1.75.0 check --release --bins --lib 42 | 43 | rust: 44 | name: Lints and doc tests on ${{ matrix.os }} 45 | runs-on: ${{ matrix.os }} 46 | strategy: 47 | fail-fast: false 48 | matrix: 49 | os: [ubuntu-latest, macOS-latest, windows-latest] 50 | 51 | steps: 52 | - uses: actions/checkout@v4 53 | - uses: dtolnay/rust-toolchain@stable 54 | with: 55 | components: rustfmt, clippy 56 | - uses: Swatinem/rust-cache@v2 57 | with: 58 | cache-bin: false 59 | 60 | - name: Install rustfmt and clippy if not installed 61 | run: | 62 | rustup component add rustfmt clippy || true 63 | rustup component list --installed 64 | 65 | - name: Code format check 66 | run: cargo fmt --check -- --config "unstable_features=true,imports_granularity=Crate,group_imports=StdExternalCrate" 67 | 68 | - name: Clippy 69 | run: cargo clippy --all -- -D warnings 70 | 71 | - name: Run rust tests 72 | run: cargo test 73 | 74 | typescript: 75 | name: Run typescript tests on ${{ matrix.os }} 76 | runs-on: ${{ matrix.os }} 77 | strategy: 78 | fail-fast: false 79 | matrix: 80 | os: [ubuntu-latest, macOS-latest] 81 | steps: 82 | - uses: actions/checkout@v4 83 | - uses: dtolnay/rust-toolchain@stable 84 | - uses: Swatinem/rust-cache@v2 85 | with: 86 | cache-bin: false 87 | 88 | - name: Build typescript plugin 89 | run: cargo build --release --all-targets 90 | 91 | - name: Install zenohd for testing 92 | run: | 93 | cargo install cargo-run-bin 94 | cargo bin --install zenohd 95 | 96 | - name: Setup Node 97 | uses: actions/setup-node@v4 98 | with: 99 | node-version: "21" 100 | 101 | - name: Install Deno 102 | uses: denoland/setup-deno@v1 103 | with: 104 | deno-version: latest 105 | 106 | - name: Verify build of zenoh-ts, tests and examples 107 | uses: borales/actions-yarn@v4 108 | with: 109 | cmd: build ALL 110 | dir: "zenoh-ts" 111 | 112 | - name: Run linter 113 | uses: borales/actions-yarn@v4 114 | with: 115 | cmd: lint 116 | dir: "zenoh-ts" 117 | 118 | - name: Run tests 119 | uses: borales/actions-yarn@v4 120 | with: 121 | cmd: start DAEMON test ALL 122 | dir: "zenoh-ts" 123 | 124 | markdown_lint: 125 | runs-on: ubuntu-latest 126 | steps: 127 | - uses: actions/checkout@v4 128 | - uses: DavidAnson/markdownlint-cli2-action@v18 129 | with: 130 | config: '.markdownlint.yaml' 131 | globs: '**/README.md' 132 | 133 | # NOTE: In GitHub repository settings, the "Require status checks to pass 134 | # before merging" branch protection rule ensures that commits are only merged 135 | # from branches where specific status checks have passed. These checks are 136 | # specified manually as a list of workflow job names. Thus we use this extra 137 | # job to signal whether all CI checks have passed. 138 | ci: 139 | name: CI status checks 140 | runs-on: ubuntu-latest 141 | needs: [check_rust, rust, typescript, markdown_lint] 142 | if: always() 143 | steps: 144 | - name: Check whether all jobs pass 145 | run: echo '${{ toJson(needs) }}' | jq -e 'all(.result == "success")' 146 | -------------------------------------------------------------------------------- /.github/workflows/publish-docs.yml: -------------------------------------------------------------------------------- 1 | name: "typedoc" 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * 1-5" 6 | workflow_dispatch: 7 | push: 8 | tags: 9 | - '[0-9]+.*' 10 | 11 | permissions: 12 | contents: read 13 | pages: write 14 | id-token: write 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: actions/setup-node@v3 22 | - run: cd zenoh-ts && yarn install 23 | # Generate your TypeDoc documentation 24 | - run: cd zenoh-ts && npx typedoc src/index.ts 25 | # https://github.com/actions/upload-pages-artifact 26 | - uses: actions/upload-pages-artifact@v2 27 | with: 28 | path: ./zenoh-ts/docs # This should be your TypeDoc "out" path. 29 | deploy: 30 | runs-on: ubuntu-latest 31 | environment: 32 | name: github-pages 33 | url: ${{ steps.deployment.outputs.page_url }} 34 | needs: build 35 | steps: 36 | - name: Deploy to GitHub Pages 37 | id: deployment 38 | # https://github.com/actions/deploy-pages 39 | uses: actions/deploy-pages@v2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | cp_plugin.sh 3 | TEST_CONFIG.json5 4 | .bin 5 | 6 | zenoh-ts/package-lock.json 7 | zenoh-ts/examples/web/src/index.js 8 | zenoh-ts/cjs/ 9 | zenoh-ts/esm/ 10 | zenoh-plugin-remote-api/bindings/** 11 | zenoh-keyexpr-wasm/pkg/** 12 | 13 | **/build 14 | **/target 15 | **/node_modules 16 | **/dist 17 | **/coverage_profile 18 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": false, # Line length limitation 3 | "MD033": false, # Enable Inline HTML 4 | "MD041": false, # Allow first line heading 5 | "MD045": false, # Allow Images have no alternate text 6 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Eclipse zenoh 2 | 3 | Thanks for your interest in this project. 4 | 5 | ## Project description 6 | 7 | Eclipse zenoh provides is a stack designed to 8 | 1. minimize network overhead, 9 | 2. support extremely constrained devices, 10 | 3. supports devices with low duty-cycle by allowing the negotiation of data exchange modes and schedules, 11 | 4. provide a rich set of abstraction for distributing, querying and storing data along the entire system, and 12 | 5. provide extremely low latency and high throughput. 13 | 14 | * https://projects.eclipse.org/projects/iot.zenoh 15 | 16 | ## Developer resources 17 | 18 | Information regarding source code management, builds, coding standards, and 19 | more. 20 | 21 | * https://projects.eclipse.org/projects/iot.zenoh/developer 22 | 23 | The project maintains the following source code repositories 24 | 25 | * https://github.com/eclipse-zenoh 26 | 27 | ## Eclipse Contributor Agreement 28 | 29 | Before your contribution can be accepted by the project team contributors must 30 | electronically sign the Eclipse Contributor Agreement (ECA). 31 | 32 | * http://www.eclipse.org/legal/ECA.php 33 | 34 | Commits that are provided by non-committers must have a Signed-off-by field in 35 | the footer indicating that the author is aware of the terms by which the 36 | contribution has been provided to the project. The non-committer must 37 | additionally have an Eclipse Foundation account and must have a signed Eclipse 38 | Contributor Agreement (ECA) on file. 39 | 40 | For more information, please see the Eclipse Committer Handbook: 41 | https://www.eclipse.org/projects/handbook/#resources-commit 42 | 43 | ## Contact 44 | 45 | Contact the project developers via the project's "dev" list. 46 | 47 | * https://accounts.eclipse.org/mailing-list/zenoh-dev 48 | 49 | Or via the Discord server. 50 | 51 | * https://discord.gg/vSDSpqnbkm 52 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [workspace] 3 | members = ["zenoh-plugin-remote-api", "zenoh-keyexpr-wasm"] 4 | resolver = "1" 5 | 6 | [workspace.package] 7 | version = "1.4.0" 8 | repository = "https://github.com/eclipse-zenoh/zenoh-ts" 9 | homepage = "http://zenoh.io" 10 | authors = ["Charles Schleich "] 11 | edition = "2021" 12 | license = "EPL-2.0 OR Apache-2.0" 13 | categories = ["network-programming"] 14 | description = "Remote API Plugin for Zenoh using Websockets" 15 | 16 | [workspace.dependencies] 17 | tokio = { version = "1.40.0", features = ["rt", "macros", "time"] } 18 | tokio-tungstenite = "0.24.0" 19 | tokio-rustls = { version = "0.26.0", default-features = false } 20 | futures-util = { version = "0.3.31", default-features = false } 21 | rustls-pemfile = "2.1.2" 22 | base64 = "0.22.1" 23 | flume = "0.11" 24 | futures = "0.3.5" 25 | git-version = "0.3.5" 26 | lazy_static = "1.4.0" 27 | ts-rs = { version = "9.0", features = [ 28 | "serde-compat", 29 | "uuid-impl", 30 | "serde-json-impl", 31 | "no-serde-warnings", 32 | "import-esm", 33 | ] } 34 | lru = "0.14.0" 35 | tracing = "0.1" 36 | schemars = { version = "0.8.21", features = ["either"] } 37 | serde = { version = "1.0.210", default-features = false, features = [ 38 | "derive", 39 | ] } # Default features are disabled due to usage in no_std crates 40 | serde_json = "1.0.128" 41 | zenoh = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", features = [ 42 | "plugins", 43 | ], version = "1.4.0" } 44 | zenoh-ext = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 45 | zenoh_backend_traits = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 46 | zenoh-plugin-trait = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 47 | zenoh-util = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 48 | zenoh-result = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 49 | zenoh-keyexpr = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 50 | 51 | uuid = { version = "1.3.0", default-features = false, features = [ 52 | "v4", 53 | "serde", 54 | ] } 55 | uhlc = { version = "0.8.0", default-features = false } # Default features are disabled due to usage in no_std crates 56 | cargo-run-bin = "1.7.4" 57 | 58 | [workspace.metadata.bin] 59 | zenohd = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "main", version = "1.4.0" } 60 | 61 | [profile.release.package.zenoh-keyexpr-wasm] 62 | # Tell `rustc` to optimize for small code size for wasm project 63 | opt-level = "s" 64 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-musl] 2 | image = "jenoch/rust-cross:x86_64-unknown-linux-musl" 3 | 4 | [target.arm-unknown-linux-gnueabi] 5 | image = "jenoch/rust-cross:arm-unknown-linux-gnueabi" 6 | 7 | [target.arm-unknown-linux-gnueabihf] 8 | image = "jenoch/rust-cross:arm-unknown-linux-gnueabihf" 9 | 10 | [target.armv7-unknown-linux-gnueabihf] 11 | image = "jenoch/rust-cross:armv7-unknown-linux-gnueabihf" 12 | 13 | [target.aarch64-unknown-linux-gnu] 14 | image = "jenoch/rust-cross:aarch64-unknown-linux-gnu" 15 | 16 | [target.aarch64-unknown-linux-musl] 17 | image = "jenoch/rust-cross:aarch64-unknown-linux-musl" 18 | 19 | -------------------------------------------------------------------------------- /EXAMPLE_CONFIG.json5: -------------------------------------------------------------------------------- 1 | { 2 | mode: 'router', 3 | plugins_loading: { 4 | enabled: true, 5 | search_dirs: ['./target/release', './target/debug'], 6 | }, 7 | plugins: { 8 | remote_api: { 9 | "__config__": "zenoh-plugin-remote-api/EXAMPLE_CONFIG.json5" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | # Notices for Eclipse zenoh 2 | 3 | This content is produced and maintained by the Eclipse zenoh project. 4 | 5 | * Project home: https://projects.eclipse.org/projects/iot.zenoh 6 | 7 | ## Trademarks 8 | 9 | Eclipse zenoh is trademark of the Eclipse Foundation. 10 | Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. 11 | 12 | ## Copyright 13 | 14 | All content is the property of the respective authors or their employers. 15 | For more information regarding authorship of content, please consult the 16 | listed source code repository logs. 17 | 18 | ## Declared Project Licenses 19 | 20 | This program and the accompanying materials are made available under the 21 | terms of the Eclipse Public License 2.0 which is available at 22 | http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 23 | which is available at https://www.apache.org/licenses/LICENSE-2.0. 24 | 25 | SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 26 | 27 | ## Source Code 28 | 29 | The project maintains the following source code repositories: 30 | 31 | * https://github.com/eclipse-zenoh/zenoh.git 32 | * https://github.com/eclipse-zenoh/zenoh-c.git 33 | * https://github.com/eclipse-zenoh/zenoh-java.git 34 | * https://github.com/eclipse-zenoh/zenoh-go.git 35 | * https://github.com/eclipse-zenoh/zenoh-python.git 36 | 37 | ## Third-party Content 38 | 39 | *To be completed...* 40 | 41 | -------------------------------------------------------------------------------- /ci/scripts/bump-and-tag.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -xeo pipefail 4 | 5 | readonly live_run=${LIVE_RUN:-false} 6 | # Release number 7 | readonly version=${VERSION:?input VERSION is required} 8 | # Dependencies' pattern 9 | readonly bump_deps_pattern=${BUMP_DEPS_PATTERN:-''} 10 | # Dependencies' version 11 | readonly bump_deps_version=${BUMP_DEPS_VERSION:-''} 12 | # Dependencies' git branch 13 | readonly bump_deps_branch=${BUMP_DEPS_BRANCH:-''} 14 | # Git actor name 15 | readonly git_user_name=${GIT_USER_NAME:?input GIT_USER_NAME is required} 16 | # Git actor email 17 | readonly git_user_email=${GIT_USER_EMAIL:?input GIT_USER_EMAIL is required} 18 | 19 | export GIT_AUTHOR_NAME=$git_user_name 20 | export GIT_AUTHOR_EMAIL=$git_user_email 21 | export GIT_COMMITTER_NAME=$git_user_name 22 | export GIT_COMMITTER_EMAIL=$git_user_email 23 | 24 | cargo +stable install toml-cli 25 | # NOTE(fuzzypixelz): toml-cli doesn't yet support in-place modification 26 | # See: https://github.com/gnprice/toml-cli?tab=readme-ov-file#writing-ish-toml-set 27 | function toml_set_in_place() { 28 | local tmp=$(mktemp) 29 | toml set "$1" "$2" "$3" > "$tmp" 30 | mv "$tmp" "$1" 31 | } 32 | 33 | package_json_path="./zenoh-ts/package.json" 34 | plugin_toml_path="./zenoh-plugin-remote-api/Cargo.toml" 35 | # Bump Cargo version of library and top level toml 36 | toml_set_in_place ${plugin_toml_path} "package.version" "$version" 37 | toml_set_in_place Cargo.toml "workspace.package.version" "$version" 38 | # Bump workspace.metadata.bin version 39 | toml_set_in_place Cargo.toml "workspace.metadata.bin.zenohd.version" "$version" 40 | 41 | # Bump package.json version 42 | JQ=".version=\"$version\"" 43 | package_tmp=$(mktemp) 44 | cat ${package_json_path} | jq "$JQ" > "$package_tmp" 45 | mv ${package_tmp} ${package_json_path} 46 | 47 | git commit Cargo.toml ${plugin_toml_path} ${package_json_path} -m "chore: Bump version to $version" 48 | 49 | # Select all package dependencies that match $bump_deps_pattern and bump them to $bump_deps_version 50 | if [[ "$bump_deps_pattern" != '' ]]; then 51 | deps=$(toml get Cargo.toml workspace.dependencies | jq -r "keys[] | select(test(\"$bump_deps_pattern\"))") 52 | for dep in $deps; do 53 | if [[ -n $bump_deps_version ]]; then 54 | toml_set_in_place Cargo.toml "workspace.dependencies.$dep.version" "$bump_deps_version" 55 | fi 56 | 57 | if [[ -n $bump_deps_branch ]]; then 58 | toml_set_in_place Cargo.toml "workspace.dependencies.$dep.branch" "$bump_deps_branch" 59 | fi 60 | done 61 | 62 | package_metadata_old=$(toml get zenoh-plugin-remote-api/Cargo.toml package.metadata.deb.depends) 63 | package_metadata_new=$(sed "s/.*/zenohd (=$bump_deps_version)/" <<< $package_metadata_old) 64 | toml_set_in_place ${plugin_toml_path} "package.metadata.deb.depends" "$package_metadata_new" 65 | 66 | # Update lockfile 67 | cargo check 68 | 69 | if [[ -n $bump_deps_version || -n $bump_deps_branch ]]; then 70 | git commit Cargo.toml ${plugin_toml_path} Cargo.lock -m "chore: Bump $bump_deps_pattern version to $bump_deps_version" 71 | else 72 | echo "warn: no changes have been made to any workspace.dependencies matching $bump_deps_pattern" 73 | fi 74 | fi 75 | 76 | if [[ ${live_run} ]]; then 77 | git tag --force "$version" -m "v$version" 78 | fi 79 | 80 | git log -10 81 | git show-ref --tags 82 | git push origin 83 | git push --force origin "$version" -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5", 3 | "remote": { 4 | "https://deno.land/std@0.192.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 5 | "https://deno.land/std@0.192.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 6 | "https://deno.land/std@0.192.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 7 | "https://deno.land/std@0.192.0/testing/asserts.ts": "e16d98b4d73ffc4ed498d717307a12500ae4f2cbe668f1a215632d19fcffc22f" 8 | }, 9 | "workspace": { 10 | "packageJson": { 11 | "dependencies": [ 12 | "npm:copy-webpack-plugin@^12.0.2" 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "EPL-2.0", 3 | "dependencies": { 4 | "copy-webpack-plugin": "^12.0.2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.85.0" 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | use_try_shorthand = true 3 | use_field_init_shorthand = true -------------------------------------------------------------------------------- /zenoh-keyexpr-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zenoh-keyexpr-wasm" 3 | version = "0.1.0" 4 | authors = ["Charles Schleich"] 5 | edition = "2018" 6 | publish = false 7 | description = "A wrapper for zenoh-keyexpr for use with WebAssembly." 8 | repository = "https://github.com/eclipse-zenoh/zenoh-ts" 9 | license = "EPL-2.0" 10 | 11 | [lib] 12 | crate-type = ["cdylib", "rlib"] 13 | 14 | [features] 15 | default = [] 16 | 17 | [dependencies] 18 | wasm-bindgen = "0.2.84" 19 | zenoh-keyexpr = { workspace = true, features = [ 20 | "js", 21 | ] } 22 | 23 | [dev-dependencies] 24 | wasm-bindgen-test = "0.3.34" 25 | 26 | # Disable warninng `unexpected `cfg` condition name: `wasm_bindgen_unstable_test_coverage`` 27 | [lints.rust] 28 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] } 29 | -------------------------------------------------------------------------------- /zenoh-keyexpr-wasm/README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ WARNING ⚠️ 2 | 3 | This crate is intended for Zenoh's internal use. 4 | 5 | - [Click here for Zenoh's main repository](https://github.com/eclipse-zenoh/zenoh) 6 | - [Click here for Zenoh's documentation](https://zenoh.io) 7 | 8 | This crate is not meant to be built by users of Zenoh. 9 | 10 | To build 11 | `wasm-pack build` 12 | 13 | The files: 14 | 15 | - zenoh-keyexpr-wasm/pkg/zenoh_keyexpr_wrapper_bg.js 16 | - zenoh-keyexpr-wasm/pkg/zenoh_keyexpr_wrapper_bg.wasm 17 | - zenoh-keyexpr-wasm/pkg/zenoh_keyexpr_wrapper_bg.wasm.d.ts 18 | - zenoh-keyexpr-wasm/pkg/zenoh_keyexpr_wrapper.d.ts 19 | - zenoh-keyexpr-wasm/pkg/zenoh_keyexpr_wrapper.js 20 | 21 | Are used from in zenoh-ts under `/src/key_expr` 22 | -------------------------------------------------------------------------------- /zenoh-keyexpr-wasm/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.84.0" -------------------------------------------------------------------------------- /zenoh-keyexpr-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | use wasm_bindgen::prelude::*; 4 | use zenoh_keyexpr::key_expr; 5 | 6 | #[wasm_bindgen] 7 | pub fn new_key_expr(key_expr_str: String) -> Result<(), String> { 8 | key_expr::OwnedKeyExpr::new(key_expr_str).map_err(|x| x.to_string())?; 9 | Ok(()) 10 | } 11 | 12 | #[wasm_bindgen] 13 | pub fn join(ke1: String, ke2: String) -> Result { 14 | let a = key_expr::OwnedKeyExpr::new(ke1).map_err(|x| x.to_string())?; 15 | let b = key_expr::OwnedKeyExpr::new(ke2).map_err(|x| x.to_string())?; 16 | 17 | a.join(&b) 18 | .map(|x| x.to_string()) 19 | .map_err(|err| err.to_string()) 20 | } 21 | 22 | // Currently concat is not exposed in the commons::zenoh-keyexpr crate, 23 | // its exposed in API, which does not compile to WASM. 24 | // For now this is a simple reimplementation of the logic in API 25 | // TODO: remove this logic and call concat once its been moved to zenoh-keyexpr 26 | #[wasm_bindgen] 27 | pub fn concat(ke1: String, ke2: String) -> Result { 28 | if ke1.ends_with('*') && ke2.starts_with('*') { 29 | Err(format!("Tried to concatenate {} (ends with *) and {} (starts with *), which would likely have caused bugs. If you're sure you want to do this, concatenate these into a string and then try to convert.", ke1, ke2)) 30 | } else { 31 | key_expr::OwnedKeyExpr::try_from(format!("{ke1}{ke2}")) 32 | .map_err(|x| x.to_string()) 33 | .map(|x| x.to_string()) 34 | } 35 | } 36 | 37 | #[wasm_bindgen] 38 | pub fn includes(ke1: String, ke2: String) -> Result { 39 | let a = key_expr::OwnedKeyExpr::new(ke1).map_err(|x| x.to_string())?; 40 | let b = key_expr::OwnedKeyExpr::new(ke2).map_err(|x| x.to_string())?; 41 | Ok(a.includes(&b)) 42 | } 43 | 44 | #[wasm_bindgen] 45 | pub fn intersects(ke1: String, ke2: String) -> Result { 46 | let a = key_expr::OwnedKeyExpr::new(ke1).map_err(|x| x.to_string())?; 47 | let b = key_expr::OwnedKeyExpr::new(ke2).map_err(|x| x.to_string())?; 48 | Ok(a.intersects(&b)) 49 | } 50 | 51 | #[wasm_bindgen] 52 | pub fn autocanonize(ke: String) -> Result { 53 | let keyexpr = key_expr::OwnedKeyExpr::autocanonize(ke).map_err(|x| x.to_string())?; 54 | Ok(keyexpr.to_string()) 55 | } 56 | -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2024 ZettaScale Technology 3 | # 4 | # This program and the accompanying materials are made available under the 5 | # terms of the Eclipse Public License 2.0 which is available at 6 | # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | # which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | # 11 | # Contributors: 12 | # ZettaScale Zenoh Team, 13 | # 14 | [package] 15 | rust-version = "1.75.0" 16 | name = "zenoh-plugin-remote-api" 17 | version = "1.4.0" 18 | repository = "https://github.com/eclipse-zenoh/zenoh-ts" 19 | homepage = "http://zenoh.io" 20 | authors = [ 21 | "Charles Schleich ", 22 | "Olivier Hécart ", 23 | "Luca Cominardi ", 24 | "Pierre Avital ", 25 | ] 26 | edition = "2021" 27 | license = "EPL-2.0 OR Apache-2.0" 28 | categories = [ 29 | "network-programming", 30 | "web-programming::websocket", 31 | ] 32 | description = "Zenoh: The Zero Overhead Pub/Sub/Query Protocol." 33 | 34 | [features] 35 | default = ["dynamic_plugin"] 36 | dynamic_plugin = [] 37 | 38 | [lib] 39 | name = "zenoh_plugin_remote_api" 40 | crate-type = ["cdylib", "rlib"] 41 | 42 | [dependencies] 43 | tokio = { workspace = true } 44 | tokio-tungstenite = { workspace = true } 45 | tokio-rustls = { workspace = true } 46 | futures-util = { workspace = true } 47 | rustls-pemfile = { workspace = true } 48 | base64 = { workspace = true } 49 | flume = { workspace = true } 50 | futures = { workspace = true } 51 | git-version = { workspace = true } 52 | lazy_static = { workspace = true } 53 | ts-rs = { workspace = true, features = [ 54 | "serde-compat", 55 | "uuid-impl", 56 | "serde-json-impl", 57 | "no-serde-warnings", 58 | ] } 59 | tracing = { workspace = true } 60 | schemars = { workspace = true } 61 | serde = { workspace = true, default-features = false, features = [ 62 | "derive", 63 | ] } # Default features are disabled due to usage in no_std crates 64 | serde_json = { workspace = true } 65 | zenoh = { workspace = true, features = ["plugins"] } 66 | zenoh-ext = { workspace = true } 67 | zenoh_backend_traits = { workspace = true } 68 | zenoh-plugin-trait = { workspace = true } 69 | zenoh-util = { workspace = true } 70 | zenoh-result = { workspace = true } 71 | uuid = { workspace=true, default-features = false, features = [ 72 | "v4", 73 | "serde", 74 | ] } 75 | uhlc = { workspace=true, default-features = false } # Default features are disabled due to usage in no_std crates 76 | lru = { workspace = true } 77 | 78 | [build-dependencies] 79 | rustc_version = "0.4.0" 80 | schemars = { version = "0.8.12", features = ["either"] } 81 | serde = { version = "1.0.154", default-features = false, features = ["derive"] } 82 | serde_json = "1.0.114" 83 | jsonschema = { version = "0.18.0", default-features = false } 84 | cargo-run-bin = { workspace = true } 85 | 86 | [package.metadata.deb] 87 | name = "zenoh-plugin-remote-api" 88 | maintainer = "zenoh-dev@eclipse.org" 89 | copyright = "2024 ZettaScale Technology" 90 | section = "net" 91 | license-file = ["../zenoh-ts/LICENSE", "0"] 92 | depends = "zenohd (=1.4.0)" 93 | -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/EXAMPLE_CONFIG.json5: -------------------------------------------------------------------------------- 1 | { 2 | websocket_port: '10000' 3 | } 4 | -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![Discussion](https://img.shields.io/badge/discussion-on%20github-blue)](https://github.com/eclipse-zenoh/roadmap/discussions) 4 | [![Discord](https://img.shields.io/badge/chat-on%20discord-blue)](https://discord.gg/2GJ958VuHs) 5 | [![License](https://img.shields.io/badge/License-EPL%202.0-blue)](https://choosealicense.com/licenses/epl-2.0/) 6 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 7 | 8 | # Eclipse Zenoh 9 | 10 | The Eclipse Zenoh: Zero Overhead Pub/sub, Store/Query and Compute. 11 | 12 | Zenoh (pronounce _/zeno/_) unifies data in motion, data at rest and computations. It carefully blends traditional pub/sub with geo-distributed storages, queries and computations, while retaining a level of time and space efficiency that is well beyond any of the mainstream stacks. 13 | 14 | Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.com/eclipse-zenoh/roadmap) for more detailed information. 15 | 16 | ------------------------------- 17 | 18 | # Remote API Plugin 19 | 20 | In Zenoh, the remote API plugin is an library loaded into a Zenohd instance at start up, which allows the creation of a Session, declaration of zenoh resources (Subscribers, Publishers, Queryables) remotely via websockets, for runtime environments where it is currently unsupported to run a zenoh binary. 21 | The Remote API was designed to support the Typescript Zenoh bindings, running in a browser. 22 | 23 | ------------------------------- 24 | 25 | ## **Examples of usage** 26 | 27 | Prerequisites: 28 | 29 | - You have a zenoh router (`zenohd`) installed, and the `libzenoh_plugin_remote_api.so` library file is available in `~/.zenoh/lib` or `~/target/debug`. 30 | 31 | ### **Setup via a JSON5 configuration file** 32 | 33 | - Create a `zenoh.json5` configuration file containing for example: 34 | 35 | ```json5 36 | { 37 | mode: "router", 38 | plugins_loading: { 39 | enabled: true, 40 | search_dirs: ["./target/debug", "~/.zenoh/lib"], 41 | }, 42 | plugins: { 43 | remote_api: { 44 | "websocket_port": "10000", 45 | }, 46 | }, 47 | } 48 | 49 | ``` 50 | 51 | - Run the zenoh router with: `zenohd -c EXAMPLE_CONFIG.json5` 52 | 53 | ------------------------------- 54 | 55 | ## How to build it 56 | 57 | > :warning: **WARNING** :warning: : Zenoh and its ecosystem are under active development. When you build from git, make sure you also build from git any other Zenoh repository you plan to use (e.g. binding, plugin, backend, etc.). It may happen that some changes in git are not compatible with the most recent packaged Zenoh release (e.g. deb, docker, pip). We put particular effort in mantaining compatibility between the various git repositories in the Zenoh project. 58 | 59 | At first, install [Cargo and Rust](https://doc.rust-lang.org/cargo/getting-started/installation.html). If you already have the Rust toolchain installed, make sure it is up-to-date with: 60 | 61 | ```bash 62 | rustup update 63 | ``` 64 | 65 | > :warning: **WARNING** :warning: : As Rust doesn't have a stable ABI, the backend library should be 66 | built with the exact same Rust version than `zenohd`, and using for `zenoh` dependency the same version (or commit number) than 'zenohd'. 67 | Otherwise, incompatibilities in memory mapping of shared types between `zenohd` and the library can lead to a `"SIGSEV"` crash. 68 | 69 | To know the Rust version you're `zenohd` has been built with, use the `--version` option. 70 | 71 | ### Example with a downloaded zenohd 72 | 73 | ```bash 74 | $ zenohd --version 75 | zenohd v1.0.0-beta.2 built with rustc 1.75.0 (82e1608df 2023-12-21) 76 | ``` 77 | 78 | Here, `zenohd` is version `v1.0.0-beta.2` has been built with the rustc version `1.75.0`. 79 | Install and use this same toolchain with the following command: 80 | 81 | ```bash 82 | rustup default 1.75.0 83 | ``` 84 | 85 | And edit the update `Cargo.toml` file to make all the `zenoh` dependencies to use the same version number: 86 | 87 | ```toml 88 | zenoh = { version = "v1.0.0-beta.2", features = [ "unstable" ] } 89 | ``` 90 | 91 | Then build the plugin: 92 | 93 | ```bash 94 | cargo build --release --all-targets -p zenoh-plugin-remote-api 95 | ``` 96 | 97 | ### Example with a version built from sources 98 | 99 | ```bash 100 | $ zenohd --version 101 | zenohd v1.0.0-beta.2 built with rustc 1.75.0 (82e1608df 2023-12-21) 102 | ``` 103 | 104 | Here, `zenohd` version is `v1.0.0-beta.2` where: 105 | 106 | - `v1.0.0-beta.2` means it's a development version for the future `v1.0.0` release 107 | - `82e1608df` indicates the commit hash 108 | - And it has been built with the `rustc` version `1.75.0`. 109 | 110 | Install and use this same toolchain with the following command: 111 | 112 | ```bash 113 | rustup default 1.75.0 114 | ``` 115 | -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/build.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | use schemars::schema_for; 15 | 16 | use crate::config::Config; 17 | 18 | #[path = "src/config.rs"] 19 | mod config; 20 | 21 | fn main() { 22 | // Add rustc version to zenohd 23 | let version_meta = rustc_version::version_meta().unwrap(); 24 | println!( 25 | "cargo:rustc-env=RUSTC_VERSION={}", 26 | version_meta.short_version_string 27 | ); 28 | 29 | let schema = serde_json::to_value(schema_for!(Config)).unwrap(); 30 | let schema = jsonschema::JSONSchema::compile(&schema).unwrap(); 31 | println!("{schema:#?}"); 32 | let config = std::fs::read_to_string("config.json5").unwrap(); 33 | let config: serde_json::Value = serde_json::from_str(&config).unwrap(); 34 | 35 | if let Err(es) = schema.validate(&config) { 36 | let es = es.map(|e| format!("{}", e)).collect::>().join("\n"); 37 | panic!("config.json5 schema validation error: {}", es); 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/config.json5: -------------------------------------------------------------------------------- 1 | { 2 | "websocket_port": "10000", 3 | "secure_websocket": { 4 | "certificate_path" : "/path/to/certificate", 5 | "private_key_path" : "/path/to/private_key" 6 | } 7 | } -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/config_schema.json5: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Config", 4 | "type": "object", 5 | "required": [ 6 | "websocket_port" 7 | ], 8 | "properties": { 9 | "__config__": { 10 | "type": [ 11 | "string", 12 | "null" 13 | ] 14 | }, 15 | "__path__": { 16 | "default": null, 17 | "type": [ 18 | "array", 19 | "null" 20 | ], 21 | "items": { 22 | "type": "string" 23 | } 24 | }, 25 | "__required__": { 26 | "type": [ 27 | "boolean", 28 | "null" 29 | ] 30 | }, 31 | "websocket_port": { 32 | "type": "string" 33 | } 34 | }, 35 | "additionalProperties": false 36 | } -------------------------------------------------------------------------------- /zenoh-plugin-remote-api/src/config.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | use std::fmt; 15 | 16 | use schemars::JsonSchema; 17 | use serde::{ 18 | de, 19 | de::{Unexpected, Visitor}, 20 | Deserialize, Deserializer, 21 | }; 22 | 23 | const DEFAULT_HTTP_INTERFACE: &str = "[::]"; 24 | 25 | #[derive(JsonSchema, Deserialize, serde::Serialize, Clone, Debug)] 26 | #[serde(deny_unknown_fields)] 27 | pub struct Config { 28 | #[serde(deserialize_with = "deserialize_ws_port")] 29 | pub websocket_port: String, 30 | 31 | pub secure_websocket: Option, 32 | 33 | #[serde(default, deserialize_with = "deserialize_path")] 34 | __path__: Option>, 35 | __required__: Option, 36 | __config__: Option, 37 | } 38 | 39 | #[derive(JsonSchema, Deserialize, serde::Serialize, Clone, Debug)] 40 | #[serde(deny_unknown_fields)] 41 | pub struct SecureWebsocket { 42 | pub certificate_path: String, 43 | pub private_key_path: String, 44 | } 45 | 46 | impl From<&Config> for serde_json::Value { 47 | fn from(c: &Config) -> Self { 48 | serde_json::to_value(c).unwrap() 49 | } 50 | } 51 | 52 | fn deserialize_ws_port<'de, D>(deserializer: D) -> Result 53 | where 54 | D: Deserializer<'de>, 55 | { 56 | deserializer.deserialize_any(WebsocketVisitor) 57 | } 58 | 59 | struct WebsocketVisitor; 60 | 61 | impl Visitor<'_> for WebsocketVisitor { 62 | type Value = String; 63 | 64 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 65 | formatter.write_str(r#"either a port number as an integer or a string, either a string with format ":""#) 66 | } 67 | 68 | fn visit_u64(self, value: u64) -> Result 69 | where 70 | E: de::Error, 71 | { 72 | Ok(format!("{DEFAULT_HTTP_INTERFACE}:{value}")) 73 | } 74 | 75 | fn visit_str(self, value: &str) -> Result 76 | where 77 | E: de::Error, 78 | { 79 | let parts: Vec<&str> = value.split(':').collect(); 80 | if parts.len() > 2 { 81 | return Err(E::invalid_value(Unexpected::Str(value), &self)); 82 | } 83 | let (interface, port) = if parts.len() == 1 { 84 | (DEFAULT_HTTP_INTERFACE, parts[0]) 85 | } else { 86 | (parts[0], parts[1]) 87 | }; 88 | if port.parse::().is_err() { 89 | return Err(E::invalid_value(Unexpected::Str(port), &self)); 90 | } 91 | Ok(format!("{interface}:{port}")) 92 | } 93 | } 94 | 95 | fn deserialize_path<'de, D>(deserializer: D) -> Result>, D::Error> 96 | where 97 | D: Deserializer<'de>, 98 | { 99 | deserializer.deserialize_option(OptPathVisitor) 100 | } 101 | 102 | struct OptPathVisitor; 103 | 104 | impl<'de> serde::de::Visitor<'de> for OptPathVisitor { 105 | type Value = Option>; 106 | 107 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 108 | write!(formatter, "none or a string or an array of strings") 109 | } 110 | 111 | fn visit_none(self) -> Result 112 | where 113 | E: de::Error, 114 | { 115 | Ok(None) 116 | } 117 | 118 | fn visit_some(self, deserializer: D) -> Result 119 | where 120 | D: Deserializer<'de>, 121 | { 122 | deserializer.deserialize_any(PathVisitor).map(Some) 123 | } 124 | } 125 | 126 | struct PathVisitor; 127 | 128 | impl<'de> serde::de::Visitor<'de> for PathVisitor { 129 | type Value = Vec; 130 | 131 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 132 | write!(formatter, "a string or an array of strings") 133 | } 134 | 135 | fn visit_str(self, v: &str) -> Result 136 | where 137 | E: de::Error, 138 | { 139 | Ok(vec![v.into()]) 140 | } 141 | 142 | fn visit_seq(self, mut seq: A) -> Result 143 | where 144 | A: de::SeqAccess<'de>, 145 | { 146 | let mut v = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity); 147 | 148 | while let Some(s) = seq.next_element()? { 149 | v.push(s); 150 | } 151 | Ok(v) 152 | } 153 | } 154 | 155 | #[cfg(test)] 156 | mod tests { 157 | use super::{Config, DEFAULT_HTTP_INTERFACE}; 158 | 159 | #[test] 160 | fn test_path_field() { 161 | // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 162 | let config = serde_json::from_str::( 163 | r#"{"__path__": "/example/path", "websocket_port": 8080}"#, 164 | ); 165 | 166 | assert!(config.is_ok()); 167 | let Config { 168 | websocket_port, 169 | __required__, 170 | __path__, 171 | .. 172 | } = config.unwrap(); 173 | 174 | assert_eq!(websocket_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); 175 | assert_eq!(__path__, Some(vec![String::from("/example/path")])); 176 | assert_eq!(__required__, None); 177 | } 178 | 179 | #[test] 180 | fn test_required_field() { 181 | // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 182 | let config = 183 | serde_json::from_str::(r#"{"__required__": true, "websocket_port": 8080}"#); 184 | assert!(config.is_ok()); 185 | let Config { 186 | websocket_port, 187 | __required__, 188 | __path__, 189 | .. 190 | } = config.unwrap(); 191 | 192 | assert_eq!(websocket_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); 193 | assert_eq!(__path__, None); 194 | assert_eq!(__required__, Some(true)); 195 | } 196 | 197 | #[test] 198 | fn test_path_field_and_required_field() { 199 | // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 200 | let config = serde_json::from_str::( 201 | r#"{"__path__": "/example/path", "__required__": true, "websocket_port": 8080}"#, 202 | ); 203 | 204 | assert!(config.is_ok()); 205 | let Config { 206 | websocket_port, 207 | __required__, 208 | __path__, 209 | .. 210 | } = config.unwrap(); 211 | 212 | assert_eq!(websocket_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); 213 | assert_eq!(__path__, Some(vec![String::from("/example/path")])); 214 | assert_eq!(__required__, Some(true)); 215 | } 216 | 217 | #[test] 218 | fn test_no_path_field_and_no_required_field() { 219 | // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 220 | let config = serde_json::from_str::(r#"{"websocket_port": 8080}"#); 221 | 222 | assert!(config.is_ok()); 223 | let Config { 224 | websocket_port, 225 | __required__, 226 | __path__, 227 | .. 228 | } = config.unwrap(); 229 | 230 | assert_eq!(websocket_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); 231 | assert_eq!(__path__, None); 232 | assert_eq!(__required__, None); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /zenoh-ts/.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "release": false 4 | }, 5 | "increment": false, 6 | "git": { 7 | "requireCleanWorkingDir": false, 8 | "commit": false, 9 | "tag": false, 10 | "push": false 11 | }, 12 | "npm": { 13 | "contents": [ 14 | "dist" 15 | ] 16 | } 17 | } -------------------------------------------------------------------------------- /zenoh-ts/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Eclipse zenoh 2 | 3 | Thanks for your interest in this project. 4 | 5 | ## Project description 6 | 7 | Eclipse zenoh provides is a stack designed to 8 | 9 | 1. minimize network overhead, 10 | 2. support extremely constrained devices, 11 | 3. supports devices with low duty-cycle by allowing the negotiation of data exchange modes and schedules, 12 | 4. provide a rich set of abstraction for distributing, querying and storing data along the entire system, and 13 | 5. provide extremely low latency and high throughput. 14 | 15 | - https://projects.eclipse.org/projects/iot.zenoh 16 | 17 | ## Developer resources 18 | 19 | Information regarding source code management, builds, coding standards, and 20 | more. 21 | 22 | - https://projects.eclipse.org/projects/iot.zenoh/developer 23 | 24 | The project maintains the following source code repositories 25 | 26 | - https://github.com/eclipse-zenoh 27 | 28 | ## Eclipse Contributor Agreement 29 | 30 | Before your contribution can be accepted by the project team contributors must 31 | electronically sign the Eclipse Contributor Agreement (ECA). 32 | 33 | - http://www.eclipse.org/legal/ECA.php 34 | 35 | Commits that are provided by non-committers must have a Signed-off-by field in 36 | the footer indicating that the author is aware of the terms by which the 37 | contribution has been provided to the project. The non-committer must 38 | additionally have an Eclipse Foundation account and must have a signed Eclipse 39 | Contributor Agreement (ECA) on file. 40 | 41 | For more information, please see the Eclipse Committer Handbook: 42 | https://www.eclipse.org/projects/handbook/#resources-commit 43 | 44 | ## Contact 45 | 46 | Contact the project developers via the project's "dev" list. 47 | 48 | - https://accounts.eclipse.org/mailing-list/zenoh-dev 49 | 50 | Or via the Discord channel. 51 | 52 | - https://discord.com/invite/cRFZDJfS3g 53 | 54 | Please raise an issue if this invite expires 55 | -------------------------------------------------------------------------------- /zenoh-ts/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors to Eclipse zenoh 2 | 3 | These are the contributors to Eclipse zenoh (the initial contributors and the contributors listed in the Git log). 4 | 5 | | GitHub username | Name | 6 | | --------------- | ----------------------------- | 7 | | kydos | Angelo Corsaro (ZettaScale) | 8 | | JEnoch | Julien Enoch (ZettaScale) | 9 | | OlivierHecart | Olivier Hécart (ZettaScale) | 10 | | gabrik | Gabriele Baldoni (ZettaScale) | 11 | | Mallets | Luca Cominardi (ZettaScale) | 12 | | IvanPaez | Ivan Paez (ZettaScale) | 13 | -------------------------------------------------------------------------------- /zenoh-ts/NOTICE.md: -------------------------------------------------------------------------------- 1 | # Notices for Eclipse zenoh-ts 2 | 3 | This content is produced and maintained by the Eclipse zenoh project. 4 | 5 | - Project home: https://projects.eclipse.org/projects/iot.zenoh 6 | 7 | ## Trademarks 8 | 9 | Eclipse zenoh is trademark of the Eclipse Foundation. 10 | Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. 11 | 12 | ## Copyright 13 | 14 | All content is the property of the respective authors or their employers. 15 | For more information regarding authorship of content, please consult the 16 | listed source code repository logs. 17 | 18 | ## Declared Project Licenses 19 | 20 | This program and the accompanying materials are made available under the 21 | terms of the Eclipse Public License 2.0 which is available at 22 | http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 23 | which is available at https://www.apache.org/licenses/LICENSE-2.0. 24 | 25 | SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 26 | 27 | ## Source Code 28 | 29 | The project maintains the following source code repositories: 30 | 31 | - https://github.com/eclipse-zenoh/zenoh.git 32 | - https://github.com/eclipse-zenoh/zenoh-c.git 33 | - https://github.com/eclipse-zenoh/zenoh-java.git 34 | - https://github.com/eclipse-zenoh/zenoh-go.git 35 | - https://github.com/eclipse-zenoh/zenoh-python.git 36 | 37 | ## Third-party Content 38 | 39 | _To be completed..._ 40 | -------------------------------------------------------------------------------- /zenoh-ts/eslint.config.cjs: -------------------------------------------------------------------------------- 1 | const tseslint = require('typescript-eslint'); 2 | 3 | module.exports = tseslint.config( 4 | { 5 | ignores: [ 6 | '**/node_modules/**', 7 | '**/dist/*.js', 8 | '**/examples/**/dist/*.js', 9 | '**/*.d.ts', // Ignore all declaration files which are often generated 10 | 'src/remote_api/interface/**/*.ts' // Ignore auto-generated TypeScript interface files 11 | ], 12 | }, 13 | // Main config for src files with TypeScript type checking 14 | { 15 | files: ['src/**/*.ts', 'src/**/*.tsx'], 16 | languageOptions: { 17 | ecmaVersion: 2020, 18 | sourceType: 'module', 19 | parser: tseslint.parser, 20 | parserOptions: { 21 | project: './tsconfig.json', 22 | }, 23 | }, 24 | plugins: { 25 | '@typescript-eslint': tseslint.plugin, 26 | }, 27 | rules: { 28 | // Using a limited set of rules to avoid compatibility issues 29 | // Allow unused variables with underscore prefix 30 | '@typescript-eslint/no-unused-vars': ['warn', { 31 | 'argsIgnorePattern': '^_', 32 | 'varsIgnorePattern': '^_' 33 | }], 34 | // Allow use before define, common in TypeScript for hoisted classes/functions 35 | '@typescript-eslint/no-use-before-define': 'off', 36 | '@typescript-eslint/naming-convention': [ 37 | 'warn', 38 | { 39 | 'selector': 'default', 40 | 'format': ['camelCase'], 41 | 'trailingUnderscore': 'allow' 42 | }, 43 | { 44 | 'selector': 'variable', 45 | 'format': ['camelCase'], 46 | 'trailingUnderscore': 'allow' 47 | }, 48 | { 49 | 'selector': 'variable', 50 | 'format': ['camelCase', 'UPPER_CASE'], 51 | 'trailingUnderscore': 'allow', 52 | 'modifiers': ['const'] 53 | }, 54 | { 55 | 'selector': 'parameter', 56 | 'format': ['camelCase'], 57 | 'trailingUnderscore': 'allow' 58 | }, 59 | { 60 | 'selector': 'parameterProperty', 61 | 'format': ['camelCase'], 62 | 'trailingUnderscore': 'allow' 63 | }, 64 | { 65 | 'selector': 'memberLike', 66 | 'format': ['camelCase'], 67 | }, 68 | { 69 | 'selector': 'typeLike', 70 | 'format': ['PascalCase'] 71 | }, 72 | { 73 | 'selector': 'interface', 74 | 'format': ['PascalCase'] 75 | }, 76 | { 77 | 'selector': 'property', 78 | 'format': ['camelCase'], 79 | 'trailingUnderscore': 'allow', 80 | }, 81 | { 82 | 'selector': 'classProperty', 83 | 'format': ['camelCase', 'UPPER_CASE'], 84 | 'trailingUnderscore': 'allow', 85 | 'modifiers': ['static', 'readonly'] 86 | }, 87 | { 88 | 'selector': 'objectLiteralProperty', 89 | 'format': ['camelCase', 'PascalCase', 'UPPER_CASE', 'snake_case'], 90 | 'trailingUnderscore': 'allow', 91 | }, 92 | { 93 | 'selector': 'method', 94 | 'format': ['camelCase'], 95 | 'trailingUnderscore': 'allow', 96 | }, 97 | { 98 | 'selector': 'function', 99 | 'format': ['camelCase'], 100 | 'trailingUnderscore': 'allow', 101 | }, 102 | { 103 | 'selector': 'enumMember', 104 | 'format': ['PascalCase', 'UPPER_CASE'] 105 | } 106 | ] 107 | } 108 | }, 109 | // Config for examples, tests, and dist with no type checking 110 | { 111 | files: [ 112 | 'examples/**/*.ts', 'examples/**/*.tsx', 113 | 'tests/**/*.ts', 'tests/**/*.tsx' 114 | ], 115 | ignores: ['**/node_modules/**'], 116 | languageOptions: { 117 | ecmaVersion: 2020, 118 | sourceType: 'module', 119 | parser: tseslint.parser, 120 | // No project reference for these files 121 | }, 122 | plugins: { 123 | '@typescript-eslint': tseslint.plugin, 124 | }, 125 | rules: { 126 | // Disable the problematic rule for examples and tests 127 | '@typescript-eslint/no-use-before-define': 'off', 128 | '@typescript-eslint/naming-convention': [ 129 | 'warn', 130 | { 131 | 'selector': 'default', 132 | 'format': ['camelCase'], 133 | 'leadingUnderscore': 'allow' 134 | }, 135 | { 136 | 'selector': 'variable', 137 | 'format': ['camelCase'], 138 | 'leadingUnderscore': 'allow' 139 | }, 140 | { 141 | 'selector': 'variable', 142 | 'format': ['camelCase', 'UPPER_CASE'], 143 | 'leadingUnderscore': 'allow', 144 | 'modifiers': ['const'] 145 | }, 146 | { 147 | 'selector': 'parameter', 148 | 'format': ['camelCase'], 149 | 'leadingUnderscore': 'allow', 150 | 'trailingUnderscore': 'allow' 151 | }, 152 | { 153 | 'selector': 'parameterProperty', 154 | 'format': ['camelCase'], 155 | 'leadingUnderscore': 'allow', 156 | 'trailingUnderscore': 'allow' 157 | }, 158 | { 159 | 'selector': 'memberLike', 160 | 'format': ['camelCase'], 161 | 'leadingUnderscore': 'allow' 162 | }, 163 | { 164 | 'selector': 'typeLike', 165 | 'format': ['PascalCase'] 166 | }, 167 | { 168 | 'selector': 'interface', 169 | 'format': ['PascalCase'] 170 | }, 171 | { 172 | 'selector': 'property', 173 | 'format': ['camelCase'], 174 | 'leadingUnderscore': 'allow' 175 | }, 176 | { 177 | 'selector': 'classProperty', 178 | 'format': ['camelCase', 'UPPER_CASE'], 179 | 'leadingUnderscore': 'allow', 180 | 'modifiers': ['static', 'readonly'] 181 | }, 182 | { 183 | 'selector': 'objectLiteralProperty', 184 | 'format': ['camelCase', 'PascalCase', 'UPPER_CASE', 'snake_case'], 185 | 'leadingUnderscore': 'allow' 186 | }, 187 | { 188 | 'selector': 'method', 189 | 'format': ['camelCase'], 190 | 'leadingUnderscore': 'allow' 191 | }, 192 | { 193 | 'selector': 'function', 194 | 'format': ['camelCase'], 195 | 'leadingUnderscore': 'allow' 196 | }, 197 | { 198 | 'selector': 'enumMember', 199 | 'format': ['PascalCase', 'UPPER_CASE'] 200 | } 201 | ] 202 | } 203 | } 204 | ); 205 | -------------------------------------------------------------------------------- /zenoh-ts/examples/browser/chat/assets/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | margin: 0; /* Remove default margin */ 4 | overflow: hidden; /* Prevent vertical scrollbar */ 5 | padding: 10px; /* Add padding from page borders */ 6 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 7 | } 8 | 9 | .chat-container { 10 | display: flex; 11 | flex-direction: column; 12 | height: calc(100vh - 20px); /* Adjust height to account for body padding */ 13 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 14 | } 15 | 16 | .main-panel { 17 | display: flex; 18 | flex: 1; 19 | overflow: hidden; /* Prevent overflow */ 20 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 21 | } 22 | 23 | .user-list { 24 | width: 20%; 25 | border-right: 1px solid #ccc; 26 | padding: 10px; 27 | overflow-y: auto; /* Make user list scrollable */ 28 | background-color: #f0f0f0; 29 | border-radius: 8px; /* Round corners */ 30 | margin-right: 5px; /* Reduce padding between panels */ 31 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 32 | } 33 | 34 | .user-list ul { 35 | margin: 0; /* Remove default margin */ 36 | padding-left: 0; /* Remove default left padding */ 37 | list-style-type: none; /* Remove default list style */ 38 | } 39 | 40 | .chat-panel { 41 | width: 80%; 42 | display: flex; 43 | flex-direction: column; 44 | padding: 10px; 45 | overflow-y: auto; /* Make chat panel scrollable */ 46 | background-color: #f0f0f0; 47 | border-radius: 8px; /* Round corners */ 48 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 49 | } 50 | 51 | .chat-log { 52 | flex: 1; 53 | border: 1px solid #ccc; 54 | padding: 10px; 55 | overflow-y: auto; /* Make chat log scrollable */ 56 | margin-bottom: 5px; /* Reduce padding between panels */ 57 | background-color: #fff; 58 | border-radius: 8px; /* Round corners */ 59 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 60 | } 61 | 62 | .chat-input { 63 | display: flex; 64 | margin-bottom: 5px; /* Reduce padding between panels */ 65 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 66 | } 67 | 68 | .chat-input input { 69 | flex: 1; 70 | padding: 5px; 71 | } 72 | 73 | .chat-input button { 74 | padding: 5px 10px; 75 | } 76 | 77 | .technical-log-panel { 78 | background-color: #f0f0f0; 79 | padding: 10px; 80 | height: 30vh; 81 | /* 30% of the screen height */ 82 | box-sizing: border-box; 83 | display: flex; 84 | flex-direction: column; 85 | border-radius: 8px; /* Round corners */ 86 | margin-top: 5px; /* Reduce padding between panels */ 87 | } 88 | 89 | .technical-log-panel.hidden { 90 | display: none; 91 | } 92 | 93 | .technical-log { 94 | border-top: 1px solid #ccc; 95 | padding-top: 10px; 96 | margin: 0; 97 | height: 100%; 98 | /* Adjust height to fit within the panel */ 99 | overflow-y: auto; 100 | background-color: #fff; 101 | border-radius: 8px; /* Round corners */ 102 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 103 | } 104 | 105 | .toggle-log-panel { 106 | position: fixed; 107 | bottom: 10px; 108 | left: 10px; 109 | } 110 | 111 | #toggle-log-button { 112 | margin-top: 10px; 113 | } 114 | 115 | .server-panel { 116 | display: flex; 117 | align-items: center; 118 | padding: 10px; 119 | background-color: #f0f0f0; 120 | border-radius: 8px; /* Round corners */ 121 | margin-bottom: 5px; /* Reduce padding between panels */ 122 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 123 | width: 100%; /* Ensure server panel fits the screen width */ 124 | flex-wrap: wrap; /* Allow wrapping to prevent overflow */ 125 | } 126 | 127 | .server-panel label { 128 | font-size: 12px; /* Make text smaller */ 129 | margin-bottom: 5px; 130 | } 131 | 132 | .server-panel input { 133 | min-width: 0; /* Reduce minimum width of input fields */ 134 | flex: 1; /* Ensure input fields resize */ 135 | width: 100%; /* Ensure input fields take full width of their container */ 136 | } 137 | 138 | .input-buttons { 139 | width: 100px; 140 | height: 100%; 141 | margin-right: 20px; /* Add margin inside input group */ 142 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 143 | } 144 | 145 | .input-username { 146 | width: 150px; 147 | margin-right: 20px; /* Add margin inside input group */ 148 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 149 | } 150 | 151 | .input-server-url { 152 | width: 200px; 153 | margin-right: 20px; /* Add margin inside input group */ 154 | box-sizing: border-box; /* Include padding and border in element's total width and height */ 155 | } 156 | 157 | .server-panel button { 158 | width: 100%; 159 | height: 100%; 160 | flex: 1; /* Ensure buttons take full height of their container */ 161 | } -------------------------------------------------------------------------------- /zenoh-ts/examples/browser/chat/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Zenoh TS Example 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 |
26 |
27 |
28 |
    29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 |
42 | 47 |
48 | 49 |
50 |
51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /zenoh-ts/examples/browser/chat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenoh-ts-chat", 3 | "version": "1.0.0", 4 | "description": "Simple example of using zenoh-ts library on the web page", 5 | "repository": "git@github.com:eclipse-zenoh/zenoh-ts", 6 | "author": "Michael Ilyin ", 7 | "license": "EPL-2.0", 8 | "type": "module", 9 | "scripts": { 10 | "clean": "rm -rf ./node_modules ./dist ./esm", 11 | "build": "webpack --config webpack.config.js", 12 | "start": "webpack serve --config webpack.config.js --mode development" 13 | }, 14 | "devDependencies": { 15 | "webpack": "^5.64.4", 16 | "webpack-cli": "^4.9.1", 17 | "webpack-dev-server": "^4.15.1", 18 | "typescript": "^5.7.3", 19 | "ts-loader": "^9.2.6" 20 | }, 21 | "dependencies": { 22 | "@eclipse-zenoh/zenoh-ts": "file:../../.." 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /zenoh-ts/examples/browser/chat/src/main.ts: -------------------------------------------------------------------------------- 1 | import { KeyExpr } from '@eclipse-zenoh/zenoh-ts'; 2 | import { ChatSession, ChatUser } from './chat_session'; 3 | 4 | let globalChatSession: ChatSession | null = null; 5 | 6 | function initialize() { 7 | const toggleLogButton = document.getElementById('toggle-log-button') as HTMLButtonElement; 8 | const technicalLogPanel = document.getElementById('technical-log-panel') as HTMLDivElement; 9 | const connectButton = document.getElementById('connect-button') as HTMLButtonElement; 10 | const disconnectButton = document.getElementById('disconnect-button') as HTMLButtonElement; 11 | const sendButton = document.getElementById('send-button') as HTMLButtonElement; 12 | const serverUrlInput = document.getElementById('server-url') as HTMLInputElement; 13 | const usernameInput = document.getElementById('username') as HTMLInputElement; 14 | const messageInput = document.getElementById('message-input') as HTMLInputElement; 15 | const usersList = document.getElementById('users') as HTMLUListElement; 16 | const chatLog = document.getElementById('chat-log') as HTMLDivElement; 17 | 18 | const adjectives = [ 19 | 'adorable', 'beautiful', 'clean', 'drab', 'elegant', 'fancy', 'glamorous', 'handsome', 'long', 'magnificent', 20 | 'old-fashioned', 'plain', 'quaint', 'sparkling', 'ugliest', 'unsightly', 'angry', 'bewildered', 'clumsy', 'defeated', 21 | 'embarrassed', 'fierce', 'grumpy', 'helpless', 'itchy', 'jealous', 'lazy', 'mysterious', 'nervous', 'obnoxious' 22 | ]; 23 | const animals = [ 24 | 'ant', 'bear', 'cat', 'dog', 'elephant', 'frog', 'giraffe', 'horse', 'iguana', 'jaguar', 'kangaroo', 'lion', 'monkey', 25 | 'newt', 'owl', 'penguin', 'quail', 'rabbit', 'snake', 'tiger', 'unicorn', 'vulture', 'walrus', 'xerus', 'yak', 'zebra' 26 | ]; 27 | let randomUsername = `${adjectives[Math.floor(Math.random() * adjectives.length)]}-${animals[Math.floor(Math.random() * animals.length)]}`; 28 | usernameInput.value = randomUsername; 29 | 30 | toggleLogButton.addEventListener('click', () => { 31 | technicalLogPanel.classList.toggle('hidden'); 32 | }); 33 | 34 | function onConnect(chatSession: ChatSession) { 35 | usersList.innerHTML = ''; 36 | chatSession.getUsers().forEach(user => { 37 | const li = document.createElement('li'); 38 | li.textContent = user.toString(); 39 | usersList.appendChild(li); 40 | usersList.scrollTop = usersList.scrollHeight; // Scroll to the latest user 41 | }); 42 | chatLog.innerHTML = ''; 43 | chatSession.getMessages().forEach(message => { 44 | addMessageToChat(chatSession, new ChatUser(message.user), message.message); 45 | }); 46 | chatLog.scrollTop = chatLog.scrollHeight; // Scroll to the latest message 47 | connectButton.style.display = 'none'; 48 | disconnectButton.style.display = 'inline-block'; 49 | messageInput.disabled = false; 50 | sendButton.disabled = false; 51 | } 52 | 53 | function onDisconnect(chatSession: ChatSession) { 54 | usersList.innerHTML = ''; 55 | chatLog.innerHTML = ''; 56 | connectButton.style.display = 'inline-block'; 57 | disconnectButton.style.display = 'none'; 58 | messageInput.disabled = true; 59 | sendButton.disabled = true; 60 | } 61 | 62 | function onChangeUsers(chatSession: ChatSession) { 63 | usersList.innerHTML = ''; 64 | chatSession.getUsers().forEach(user => { 65 | const li = document.createElement('li'); 66 | li.textContent = user.toString(); 67 | usersList.appendChild(li); 68 | usersList.scrollTop = usersList.scrollHeight; // Scroll to the latest user 69 | }); 70 | } 71 | 72 | function onNewMessage(chatSession: ChatSession, user: ChatUser, message: string) { 73 | addMessageToChat(chatSession, user, message); 74 | chatLog.scrollTop = chatLog.scrollHeight; // Scroll to the latest message 75 | } 76 | 77 | function addMessageToChat(chatSession: ChatSession, user: ChatUser, message: string) { 78 | const messageElement = document.createElement('div'); 79 | const usernameElement = document.createElement('span'); 80 | const messageTextElement = document.createElement('span'); 81 | if (user.username === chatSession.getUser().username) { 82 | usernameElement.innerHTML = `${user.toString()}: `; 83 | } else { 84 | usernameElement.textContent = `${user.toString()}: `; 85 | } 86 | messageTextElement.textContent = message; 87 | messageElement.appendChild(usernameElement); 88 | messageElement.appendChild(messageTextElement); 89 | chatLog.appendChild(messageElement); 90 | } 91 | 92 | messageInput.disabled = true; 93 | sendButton.disabled = true; 94 | 95 | connectButton?.addEventListener('click', () => { 96 | logCatch(async () => { 97 | let user = ChatUser.fromString(usernameInput.value); 98 | if (!user) { 99 | log(`Invalid username: ${usernameInput.value}`); 100 | return; 101 | } 102 | let chatSession: ChatSession = new ChatSession(new KeyExpr("chat"), user); 103 | chatSession.onChangeUsers(onChangeUsers); 104 | chatSession.onNewMessage(onNewMessage); 105 | chatSession.onConnect(onConnect); 106 | chatSession.onDisconnect(onDisconnect); 107 | if (globalChatSession) { 108 | await globalChatSession.disconnect(); 109 | } 110 | globalChatSession = chatSession; 111 | await chatSession.connect(serverUrlInput.value); 112 | }); 113 | }); 114 | 115 | disconnectButton?.addEventListener('click', () => { 116 | logCatch(async () => { 117 | if (globalChatSession) { 118 | await globalChatSession.disconnect(); 119 | globalChatSession = null; 120 | } 121 | }); 122 | }); 123 | 124 | sendButton?.addEventListener('click', () => { 125 | if (globalChatSession) { 126 | globalChatSession.sendMessage(messageInput.value); 127 | messageInput.value = ''; 128 | } 129 | }); 130 | 131 | messageInput?.addEventListener('keypress', (event) => { 132 | if (event.key === 'Enter') { 133 | if (globalChatSession) { 134 | globalChatSession.sendMessage(messageInput.value); 135 | messageInput.value = ''; 136 | } 137 | } 138 | }); 139 | } 140 | 141 | if (document.readyState === 'loading') { 142 | document.addEventListener('DOMContentLoaded', initialize); 143 | } else { 144 | initialize(); 145 | } 146 | 147 | function log(message: string) { 148 | const technicalLog = document.getElementById('technical-log') as HTMLDivElement; 149 | const timestamp = new Date().toLocaleTimeString(); 150 | const logMessage = document.createElement('div'); 151 | logMessage.textContent = `[${timestamp}] ${message}`; 152 | technicalLog.appendChild(logMessage); 153 | technicalLog.scrollTop = technicalLog.scrollHeight; // Scroll to the latest log message 154 | } 155 | 156 | async function logCatch(asyncFunc: () => Promise) { 157 | try { 158 | await asyncFunc(); 159 | } catch (error) { 160 | log(`Error: ${error}`); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /zenoh-ts/examples/browser/chat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "moduleResolution": "bundler", 5 | "target": "es2021", 6 | "strict": true, 7 | "baseUrl": ".", 8 | "esModuleInterop": true, 9 | "outDir": "./dist", 10 | "allowJs": true 11 | }, 12 | "include": [ 13 | "./src/main.ts" 14 | ] 15 | } -------------------------------------------------------------------------------- /zenoh-ts/examples/browser/chat/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | 4 | const __filename = fileURLToPath(import.meta.url); 5 | const __dirname = path.dirname(__filename); 6 | 7 | export default { 8 | entry: './src/main.ts', 9 | output: { 10 | filename: 'bundle.js', 11 | path: path.resolve(__dirname, 'dist') 12 | }, 13 | resolve: { 14 | extensions: ['.ts', '.js'] 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.ts$/, 20 | use: 'ts-loader', 21 | exclude: /node_modules/ 22 | }, 23 | { 24 | test: /\.wasm$/, 25 | type: 'webassembly/async' 26 | } 27 | ] 28 | }, 29 | experiments: { 30 | asyncWebAssembly: true 31 | }, 32 | devtool: 'source-map', 33 | mode: 'development', 34 | devServer: { 35 | static: { 36 | directory: path.join(__dirname, 'assets'), 37 | }, 38 | compress: true, 39 | port: 8080, 40 | open: true 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@std/cli": "jsr:@std/cli@^1.0.10" 4 | }, 5 | "compilerOptions": { 6 | "lib": [ 7 | "ES2020", 8 | "DOM", 9 | "DOM.Iterable", 10 | "deno.ns" 11 | ], 12 | "types": [ 13 | "deno.ns", 14 | "deno.unstable" 15 | ], 16 | "strict": true, 17 | "noUnusedLocals": false, 18 | "noUnusedParameters": true, 19 | "noFallthroughCasesInSwitch": true 20 | }, 21 | "unstable": [ 22 | "sloppy-imports" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4", 3 | "specifiers": { 4 | "jsr:@std/cli@^1.0.10": "1.0.10", 5 | "npm:@types/uuid@10": "10.0.0", 6 | "npm:typescript@^5.2.2": "5.6.3" 7 | }, 8 | "jsr": { 9 | "@std/cli@1.0.10": { 10 | "integrity": "d047f6f4954a5c2827fe0963765ddd3d8b6cc7b7518682842645b95f571539dc" 11 | } 12 | }, 13 | "npm": { 14 | "@types/uuid@10.0.0": { 15 | "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" 16 | }, 17 | "typescript@5.6.3": { 18 | "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==" 19 | } 20 | }, 21 | "redirects": { 22 | "https://deno.land/std/flags/mod.ts": "https://deno.land/std@0.224.0/flags/mod.ts" 23 | }, 24 | "remote": { 25 | "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", 26 | "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", 27 | "https://deno.land/std@0.224.0/flags/mod.ts": "88553267f34519c8982212185339efdb2d2e62c159ec558f47eb50c8952a6be3" 28 | }, 29 | "workspace": { 30 | "dependencies": [ 31 | "jsr:@std/cli@^1.0.10" 32 | ], 33 | "packageJson": { 34 | "dependencies": [ 35 | "npm:@types/uuid@10", 36 | "npm:typescript@^5.2.2" 37 | ] 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenoh-ts-examples", 3 | "private": false, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "license": "EPL-2.0", 7 | "scripts": { 8 | "clean": "rm -rf ./node_modules", 9 | "verify": "deno check src", 10 | "start": "./scripts/start.sh" 11 | }, 12 | "devDependencies": { 13 | "@types/uuid": "^10.0.0", 14 | "typescript": "^5.2.2" 15 | }, 16 | "dependencies": { 17 | "@eclipse-zenoh/zenoh-ts": "file:../.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ORIGINAL_DIR="$(pwd)" 4 | SCRIPTDIR="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)" 5 | cd "$SCRIPTDIR/.." 6 | 7 | if [ "$1" = "" ]; then 8 | echo 9 | echo "Arguments: example_name" 10 | echo " example_name: name of the example to run" 11 | echo 12 | echo "Available examples:" 13 | ls src/*.ts | sed -e "s/src\///" -e "s/\.ts//" 14 | echo 15 | else 16 | EXAMPLE_NAME="$1" 17 | shift 18 | deno run -A --no-prompt "src/$EXAMPLE_NAME.ts" "$@" 19 | EXIT_CODE=$? 20 | fi 21 | 22 | cd "$ORIGINAL_DIR" 23 | exit ${EXIT_CODE:-0} -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/parse_args.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Priority } from "@eclipse-zenoh/zenoh-ts"; 16 | import { parseArgs } from "@std/cli/parse-args"; 17 | 18 | export abstract class BaseParseArgs { 19 | [key: string]: any; 20 | 21 | abstract getNamedArgsHelp(): Record; 22 | abstract getPositionalArgsHelp(): [string, string][]; 23 | 24 | static fillTypesFromObject(obj: Record): Record { 25 | const types: Record = {}; 26 | for (const [key, value] of Object.entries(obj)) { 27 | if (key == "positional") continue; 28 | const type = typeof value; 29 | if (!types[type]) { 30 | types[type] = []; 31 | } 32 | types[type].push(key); 33 | } 34 | return types; 35 | } 36 | 37 | public parse() { 38 | const types = (this.constructor as typeof BaseParseArgs).fillTypesFromObject(this); 39 | const args = parseArgs(Deno.args, types); 40 | const positionalArgs = this.getPositionalArgsHelp(); 41 | if (args.help) { 42 | let s = "Usage: [OPTIONS]"; 43 | for (const p of positionalArgs) { 44 | s += " <" + p[0] + ">"; 45 | } 46 | if (positionalArgs.length != 0) { 47 | console.log("Arguments:"); 48 | for (let i = 0; i < positionalArgs.length; i++) { 49 | console.log(`<${positionalArgs[i][0]}> <${typeof this.positional[i]}>`); 50 | console.log(`\t${positionalArgs[i][1]}`); 51 | } 52 | } 53 | 54 | const namedHelp = this.getNamedArgsHelp(); 55 | console.log("Options:") 56 | console.log("--help\n\tPrint this help message"); 57 | for (const [arg, helpMessage] of Object.entries(namedHelp)) { 58 | // find type of the argument 59 | let type = Object.keys(types).find(key => types[key].includes(arg)); 60 | console.log(`--${arg} <${type}>`); 61 | const defaultValue = this[arg]; 62 | if (defaultValue) { 63 | console.log(`\t[default: ${defaultValue}]`); 64 | } 65 | console.log(`\t${helpMessage}`); 66 | } 67 | Deno.exit(0); 68 | } else { 69 | if (positionalArgs.length != args._.length) { 70 | throw new Error("Incorrect number of positional arguments"); 71 | } 72 | for (let i = 0; i < positionalArgs.length; i++) { 73 | if (typeof this.positional[i] === 'number') { 74 | this.positional[i] = Number(args._[i]).valueOf(); 75 | } else if (typeof this.positional[i] === 'string') { 76 | this.positional[i] = String(args._[i]).valueOf(); 77 | } else { 78 | throw new Error("Unsupported argument type"); 79 | } 80 | } 81 | } 82 | // assign all the properties of args to instance 83 | for (const [key, value] of Object.entries(args)) { 84 | this[key] = value; 85 | } 86 | } 87 | } 88 | 89 | export function priorityFromInt(prioU8: number): Priority { 90 | switch (prioU8) { 91 | case 1: 92 | return Priority.REAL_TIME; 93 | case 2: 94 | return Priority.INTERACTIVE_HIGH; 95 | case 3: 96 | return Priority.INTERACTIVE_LOW; 97 | case 4: 98 | return Priority.DATA_HIGH; 99 | case 5: 100 | return Priority.DATA; 101 | case 6: 102 | return Priority.DATA_LOW; 103 | case 7: 104 | return Priority.BACKGROUND; 105 | default: 106 | console.warn("Unknown Priority Variant, default to Data"); 107 | return Priority.DATA; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_bytes.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { ZBytes } from "@eclipse-zenoh/zenoh-ts"; 16 | import { ZBytesSerializer, ZBytesDeserializer, ZSerializeable, ZDeserializeable, zserialize, zdeserialize, ZS, ZD, NumberFormat, BigIntFormat } from "@eclipse-zenoh/zenoh-ts/ext"; 17 | 18 | 19 | class MyStruct implements ZSerializeable, ZDeserializeable { 20 | v1: number; 21 | v2: string; 22 | v3: number[]; 23 | 24 | constructor(v1?: number, v2?: string, v3?: number[]) { 25 | if (typeof v1 !== `undefined`) { 26 | this.v1 = v1; 27 | } else { 28 | this.v1 = 0; 29 | } 30 | if (typeof v2 !== `undefined`) { 31 | this.v2 = v2; 32 | } else { 33 | this.v2 = "" 34 | } 35 | if (typeof v3 !== `undefined`) { 36 | this.v3 = v3; 37 | } else { 38 | this.v3 = new Array() 39 | } 40 | } 41 | 42 | serializeWithZSerializer(serializer: ZBytesSerializer): void { 43 | serializer.serialize(this.v1, ZS.number(NumberFormat.Int32)) 44 | serializer.serialize(this.v2) 45 | serializer.serialize(this.v3, ZS.array(ZS.number(NumberFormat.Int8))) 46 | } 47 | 48 | deserializeWithZDeserializer(deserializer: ZBytesDeserializer): void { 49 | this.v1 = deserializer.deserialize(ZD.number(NumberFormat.Uint32)) 50 | this.v2 = deserializer.deserialize(ZD.string()) 51 | this.v3 = deserializer.deserialize(ZD.array(ZD.number(NumberFormat.Int8))) 52 | } 53 | 54 | toString(): string { 55 | return JSON.stringify(this) 56 | } 57 | 58 | } 59 | 60 | export async function main() { 61 | // using raw data 62 | // string 63 | { 64 | let input = "test" 65 | let payload = new ZBytes(input) 66 | let output = payload.toString() 67 | console.log(`Input: ${input}, Output: ${output}`) 68 | } 69 | // Uint8Array 70 | { 71 | let input = new Uint8Array([1, 2, 3, 4]) 72 | let payload = new ZBytes(input) 73 | let output = payload.toBytes() 74 | console.log(`Input: ${input}, Output: ${output}`) 75 | } 76 | 77 | // serialization 78 | // array 79 | { 80 | let input = [1, 2, 3, 4] 81 | // by default number is serialized/deserialized as 64-bit float, 82 | // other formats, like Int32, for example, must be specified explicitly 83 | let payload = zserialize(input, ZS.array(ZS.number(NumberFormat.Int32))) 84 | let output = zdeserialize(ZD.array(ZD.number(NumberFormat.Int32)), payload) 85 | // let payload = zserialize(input) 86 | // let output = zdeserialize(ZD.array(ZD.number()), payload) 87 | console.log(`Input: ${input}, Output: ${output}`) 88 | } 89 | // typed array 90 | { 91 | let input = new Int32Array([1, 2, 3, 4]) 92 | let payload = zserialize(input) 93 | let output = zdeserialize(ZD.int32array(), payload) 94 | console.log(`Input: ${input}, Output: ${output}`) 95 | } 96 | // map 97 | { 98 | let input = new Map() 99 | input.set(0n, "abc") 100 | input.set(1n, "def") 101 | // by default bigint is serialized/deserialized as 64-bit signed integer, 102 | // other formats, like UInt64, for example, must be specified explicitly 103 | let payload = zserialize(input, ZS.map(ZS.bigint(BigIntFormat.Uint64), ZS.string())) 104 | let output = zdeserialize(ZD.map(ZD.bigint(BigIntFormat.Uint64), ZD.string()), payload) 105 | // let payload = zserialize(input) 106 | // let output = zdeserialize(ZD.map(ZD.bigint(), ZD.string()), payload) 107 | console.log(`Input:`) 108 | console.table(input) 109 | console.log(`Output:`) 110 | console.table(output) 111 | } 112 | // Custom class 113 | { 114 | let input = new MyStruct(1234, "test", [1, 2, 3, 4]) 115 | let payload = zserialize(input) 116 | let output = zdeserialize(ZD.object(MyStruct), payload) 117 | console.log(`Input: ${input.toString()}, Output: ${output.toString()}`) 118 | } 119 | } 120 | 121 | main(); 122 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_delete.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Config, Session } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | 21 | console.log("Opening session..."); 22 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 23 | 24 | console.log(`Deleting resources matching '${args.key}...`); 25 | await session.delete(args.key); 26 | 27 | await session.close(); 28 | } 29 | 30 | class ParseArgs extends BaseParseArgs { 31 | public key: string = "demo/example/zenoh-ts-put"; 32 | 33 | constructor() { 34 | super(); 35 | this.parse(); 36 | } 37 | 38 | public getNamedArgsHelp(): Record { 39 | return { 40 | key: "Key expression for the deletion" 41 | }; 42 | } 43 | 44 | getPositionalArgsHelp(): [string, string][] { 45 | return []; 46 | } 47 | } 48 | 49 | main(); 50 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_get.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { ReplyError, Config, Sample, Session, QueryTarget, ChannelReceiver, Reply } from "@eclipse-zenoh/zenoh-ts"; 16 | import { Duration, Milliseconds } from 'typed-duration' 17 | const { milliseconds } = Duration 18 | import { BaseParseArgs } from "./parse_args.ts"; 19 | 20 | export async function main() { 21 | let args = new ParseArgs(); 22 | 23 | console.warn("Opening session..."); 24 | await using session = await Session.open(new Config("ws/127.0.0.1:10000")); 25 | 26 | // Callback get query 27 | console.warn("Sending Query '" + args.selector + "'..."); 28 | 29 | // const get_callback = function (reply: Reply) { 30 | // let resp = reply.result(); 31 | // if (resp instanceof Sample) { 32 | // let sample: Sample = resp; 33 | // console.warn(">> Received ('", sample.keyexpr(), ":", sample.payload().toString()), "')"); 34 | // } else { 35 | // let reply_error: ReplyError = resp; 36 | // console.warn(">> Received (ERROR: '", reply_error.payload().toString(), "')"); 37 | // } 38 | // }; 39 | 40 | // await session.get("demo/example/**", get_callback); 41 | 42 | // Poll receiver 43 | const receiver = await session.get(args.selector, { 44 | payload: args.payload, 45 | timeout: args.getTimeout(), 46 | target: args.getQueryTarget() 47 | }); 48 | 49 | for await (const reply of receiver as ChannelReceiver) { 50 | const resp = reply.result(); 51 | if (resp instanceof Sample) { 52 | const sample: Sample = resp; 53 | console.warn(">> Received ('", sample.keyexpr().toString(), ":", sample.payload().toString(), "')"); 54 | } else { 55 | const replyError: ReplyError = resp; 56 | console.warn(">> Received (ERROR: '{", replyError.payload().toString(), "}')"); 57 | } 58 | } 59 | console.warn("Get Finished"); 60 | } 61 | 62 | class ParseArgs extends BaseParseArgs { 63 | public selector: string = "demo/example/**"; 64 | public payload: string = ""; 65 | public target: string = "BEST_MATCHING"; 66 | public timeout: number = 10000; 67 | 68 | constructor() { 69 | super(); 70 | this.parse(); 71 | } 72 | 73 | public getTimeout(): Milliseconds { 74 | return milliseconds.of(this.timeout); 75 | } 76 | 77 | public getQueryTarget(): QueryTarget { 78 | switch (this.target) { 79 | case "BEST_MATCHING": 80 | return QueryTarget.BEST_MATCHING; 81 | case "ALL": 82 | return QueryTarget.ALL; 83 | case "ALL_COMPLETE": 84 | return QueryTarget.ALL_COMPLETE; 85 | default: 86 | return QueryTarget.BEST_MATCHING; 87 | } 88 | } 89 | 90 | public getNamedArgsHelp(): Record { 91 | return { 92 | selector: "Selector for the query", 93 | payload: "Payload for the query", 94 | target: "Query target. Possible values: BEST_MATCHING, ALL, ALL_COMPLETE", 95 | timeout: "Timeout for the query" 96 | }; 97 | } 98 | 99 | getPositionalArgsHelp(): [string, string][] { 100 | return []; 101 | } 102 | } 103 | 104 | main(); 105 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_get_liveliness.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { 16 | Sample, Config, Session, KeyExpr, 17 | ReplyError, 18 | ChannelReceiver, 19 | Reply 20 | } from "@eclipse-zenoh/zenoh-ts"; 21 | import { Duration, Milliseconds } from 'typed-duration' 22 | import { BaseParseArgs } from "./parse_args.ts"; 23 | 24 | const { milliseconds } = Duration 25 | 26 | export async function main() { 27 | const args = new ParseArgs(); 28 | 29 | console.log("Opening session..."); 30 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 31 | const keyExpr = new KeyExpr(args.key); 32 | console.log(`Sending Liveliness Query '${args.key}'...`); 33 | 34 | const receiver = await session.liveliness().get(keyExpr, { timeout: args.getTimeout() }); 35 | 36 | for await (const reply of receiver as ChannelReceiver) { 37 | const resp = reply.result(); 38 | if (resp instanceof Sample) { 39 | const sample: Sample = resp; 40 | console.warn(">> Alive token ('", sample.keyexpr().toString(), ")"); 41 | } else { 42 | const replyError: ReplyError = resp; 43 | console.warn(">> Received (ERROR: '", replyError.payload().toString(), "')"); 44 | } 45 | } 46 | console.warn("Liveliness query finished"); 47 | await session.close(); 48 | } 49 | 50 | class ParseArgs extends BaseParseArgs { 51 | public key: string = "group1/**"; 52 | public timeout: number = 10000; 53 | 54 | constructor() { 55 | super(); 56 | this.parse(); 57 | } 58 | 59 | public getTimeout(): Milliseconds { 60 | return milliseconds.of(this.timeout); 61 | } 62 | 63 | public getNamedArgsHelp(): Record { 64 | return { 65 | key: "Key expression for the liveliness query", 66 | timeout: "Timeout for the liveliness query" 67 | }; 68 | } 69 | 70 | getPositionalArgsHelp(): [string, string][] { 71 | return []; 72 | } 73 | } 74 | 75 | main(); 76 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_info.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { 16 | SessionInfo, Config, Session 17 | } from "@eclipse-zenoh/zenoh-ts"; 18 | 19 | export async function main() { 20 | console.log!("Opening session..."); 21 | await using session = await Session.open(new Config("ws/127.0.0.1:10000")); 22 | 23 | console.log!("Get Info..."); 24 | const info: SessionInfo = await session.info(); 25 | 26 | console.log!("zid: ", info.zid().toString()); 27 | 28 | console.log!(`routers zid: ${info.routersZid()}`); 29 | 30 | console.log!(`peers zid: ${info.peersZid()}`); 31 | 32 | } 33 | 34 | main(); 35 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_liveliness.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | import { 15 | Config, Session, KeyExpr, LivelinessToken 16 | } from "@eclipse-zenoh/zenoh-ts"; 17 | import { BaseParseArgs } from "./parse_args.ts"; 18 | 19 | export async function main() { 20 | const args = new ParseArgs(); 21 | 22 | console.log("Opening session..."); 23 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 24 | const keyExpr = new KeyExpr(args.key); 25 | console.log(`Declaring Liveliness token on '${args.key}'...`); 26 | 27 | const token: LivelinessToken = await session.liveliness().declareToken(keyExpr); 28 | // LivelinessTokens are NOT automatically closed when dropped 29 | // please call token.undeclare(); 30 | 31 | console.log("Press CTRL-C to undeclare LivelinessToken and quit..."); 32 | while (true) { 33 | await sleep(10000); 34 | } 35 | } 36 | 37 | class ParseArgs extends BaseParseArgs { 38 | public key: string = "group1/zenoh-ts"; 39 | 40 | constructor() { 41 | super(); 42 | this.parse(); 43 | } 44 | 45 | public getNamedArgsHelp(): Record { 46 | return { 47 | key: "Key expression for the liveliness token" 48 | }; 49 | } 50 | 51 | getPositionalArgsHelp(): [string, string][] { 52 | return []; 53 | } 54 | } 55 | 56 | function sleep(ms: number) { 57 | return new Promise((resolve) => setTimeout(resolve, ms)); 58 | } 59 | 60 | main(); 61 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_ping.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { ChannelReceiver, FifoChannel, Sample, Encoding, CongestionControl, Config, Session } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 21 | 22 | const sub = await session.declareSubscriber("test/pong", { handler: new FifoChannel(256) } ); 23 | const pub = await session.declarePublisher( 24 | "test/ping", 25 | { 26 | encoding: Encoding.default(), 27 | congestionControl: CongestionControl.BLOCK, 28 | express: !args.no_express 29 | }, 30 | ); 31 | 32 | const payloadSize = args.positional[0]; 33 | let payload = new Uint8Array(payloadSize); 34 | console.warn(`Will publish ${payloadSize} B payload.`); 35 | for (let i = 0; i < payloadSize; i++) { 36 | payload[i] = i; 37 | } 38 | 39 | const startTime = performance.now(); 40 | 41 | // Warm up 42 | console.warn(`Warming up for ${args.warmup} seconds...`); 43 | while (elapsedMs(startTime) < args.warmup * 1000) { 44 | await pub.put(payload); 45 | await (sub.receiver() as ChannelReceiver).receive(); 46 | } 47 | 48 | const samples = args.samples; 49 | const samplesOut = []; 50 | for (let i = 0; i < samples; i++) { 51 | const writeTime = performance.now(); 52 | await pub.put(payload); 53 | await (sub.receiver() as ChannelReceiver).receive(); 54 | samplesOut.push(elapsedMs(writeTime)); 55 | } 56 | 57 | for (let i = 0; i < samplesOut.length; i++) { 58 | const rtt = samplesOut[i]; 59 | console.warn( 60 | payload.length + 61 | "bytes: seq=" + 62 | i + 63 | " rtt=" + 64 | rtt + 65 | "ms lat=" + 66 | rtt / 2 + 67 | "ms", 68 | ); 69 | } 70 | await session.close(); 71 | } 72 | 73 | function elapsedMs(startTime: number) { 74 | const endTime = performance.now(); 75 | return endTime - startTime; 76 | } 77 | 78 | 79 | class ParseArgs extends BaseParseArgs { 80 | // eslint-disable-next-line @typescript-eslint/naming-convention 81 | public no_express: boolean = false; 82 | public warmup: number = 1; 83 | public samples: number = 100; 84 | public positional: [number] = [0]; 85 | 86 | constructor() { 87 | super(); 88 | this.parse(); 89 | } 90 | 91 | public getNamedArgsHelp(): Record { 92 | return { 93 | no_express: "Express for sending data", 94 | warmup: "Number of seconds to warm up", 95 | samples: "Number of round-trips to measure" 96 | }; 97 | } 98 | 99 | getPositionalArgsHelp(): [string, string][] { 100 | return [["PAYLOAD_SIZE", "Size of the payload to publish"]]; 101 | } 102 | } 103 | 104 | main(); 105 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_pong.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Encoding, CongestionControl, Sample, Config, Session } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 21 | 22 | const pub = await session.declarePublisher( 23 | "test/pong", 24 | { 25 | encoding: Encoding.default(), 26 | congestionControl: CongestionControl.BLOCK, 27 | express: !args.no_express, 28 | }, 29 | ); 30 | 31 | const subscriberCallback = function (sample: Sample) { 32 | pub.put(sample.payload()); 33 | }; 34 | 35 | await session.declareSubscriber("test/ping", { handler: subscriberCallback } ); 36 | 37 | while (true) { 38 | const seconds = 100; 39 | await sleep(1000 * seconds); 40 | } 41 | } 42 | 43 | function sleep(ms: number) { 44 | return new Promise((resolve) => setTimeout(resolve, ms)); 45 | } 46 | 47 | class ParseArgs extends BaseParseArgs { 48 | // eslint-disable-next-line @typescript-eslint/naming-convention 49 | public no_express: boolean = false; 50 | 51 | constructor() { 52 | super(); 53 | this.parse(); 54 | } 55 | 56 | public getNamedArgsHelp(): Record { 57 | return { 58 | no_express: "Express for sending data", 59 | }; 60 | } 61 | 62 | getPositionalArgsHelp(): [string, string][] { 63 | return []; 64 | } 65 | } 66 | 67 | main(); 68 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_pub.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Priority, Reliability, Encoding, CongestionControl, Config, KeyExpr, Publisher, Session } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | 21 | console.log("Opening session..."); 22 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 23 | 24 | const keyExpr = args.getKeyexpr(); 25 | const publisher: Publisher = await session.declarePublisher( 26 | keyExpr, 27 | { 28 | encoding: Encoding.default(), 29 | congestionControl: CongestionControl.BLOCK, 30 | priority: Priority.DATA, 31 | express: true, 32 | reliability: Reliability.RELIABLE 33 | } 34 | ); 35 | 36 | for (let idx = 0; idx < Number.MAX_VALUE; idx++) { 37 | const buf = `[${idx}] ${args.payload}`; 38 | console.warn(`Putting Data ('${keyExpr}': '${buf}')...`); 39 | let attachment = args.attach.length == 0 ? undefined : args.attach 40 | await publisher.put(buf, { encoding: Encoding.TEXT_PLAIN, attachment }); 41 | await sleep(1000); 42 | } 43 | } 44 | 45 | class ParseArgs extends BaseParseArgs { 46 | public payload: string = "Pub from Typescript!"; 47 | public key: string = "demo/example/zenoh-ts-pub"; 48 | public attach: string = ""; 49 | 50 | constructor() { 51 | super(); 52 | this.parse(); 53 | } 54 | 55 | public getKeyexpr(): KeyExpr { 56 | return KeyExpr.autocanonize(this.key); 57 | } 58 | 59 | public getNamedArgsHelp(): Record { 60 | return { 61 | payload: "Payload for the publication", 62 | key: "Key expression for the publication", 63 | attach: "Attachment for the publication" 64 | }; 65 | } 66 | 67 | getPositionalArgsHelp(): [string, string][] { 68 | return []; 69 | } 70 | } 71 | 72 | function sleep(ms: number) { 73 | return new Promise((resolve) => setTimeout(resolve, ms)); 74 | } 75 | 76 | main(); 77 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_pub_thr.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Priority, Reliability, Encoding, CongestionControl, Config, KeyExpr, Publisher, Session } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs, priorityFromInt } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | 21 | console.log("Opening session..."); 22 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 23 | 24 | const publisher: Publisher = await session.declarePublisher( 25 | "test/thr", 26 | { 27 | express: args.express, 28 | priority: args.getPriority(), 29 | } 30 | ); 31 | 32 | const payloadSize = args.positional[0]; 33 | let payload = new Uint8Array(payloadSize); 34 | console.warn(`Will publish ${payloadSize} B payload.`); 35 | for (let i = 0; i < payloadSize; i++) { 36 | payload[i] = i; 37 | } 38 | 39 | let startTime = performance.now(); 40 | let n = 0; 41 | while (true) { 42 | await publisher.put(payload); 43 | n++; 44 | 45 | if (n % 1000000 == 0) { 46 | const endTime = performance.now(); 47 | console.log(1000000 / (endTime - startTime) * 1000, " msg/s") 48 | startTime = performance.now(); 49 | } 50 | } 51 | await session.close(); 52 | } 53 | 54 | class ParseArgs extends BaseParseArgs { 55 | public positional: [number] = [0]; 56 | public express: boolean = false; 57 | public priority: number = 5; 58 | 59 | constructor() { 60 | super(); 61 | this.parse(); 62 | } 63 | 64 | public getKeyexpr(): KeyExpr { 65 | return KeyExpr.autocanonize(this.key); 66 | } 67 | 68 | public getNamedArgsHelp(): Record { 69 | return { 70 | express: "Express for sending data", 71 | priority: "Priority for sending data [1-7]", 72 | number: "Number of messages in each throughput measurement" 73 | }; 74 | } 75 | 76 | public getPositionalArgsHelp(): [string, string][] { 77 | return [["PAYLOAD_SIZE", "payload size"] ]; 78 | }; 79 | 80 | public getPriority(): Priority { 81 | return priorityFromInt(this.priority); 82 | } 83 | } 84 | 85 | main(); 86 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_put.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Config, Session } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | console.warn('Opening session...'); 21 | 22 | await using session = await Session.open(new Config("ws/127.0.0.1:10000")); 23 | console.warn(`Putting Data ('${args.key}: '${args.payload}')...`); 24 | await session.put(args.key, args.payload); 25 | } 26 | 27 | class ParseArgs extends BaseParseArgs { 28 | public payload: string = "Put from Typescript!"; 29 | public key: string = "demo/example/zenoh-ts-put"; 30 | 31 | constructor() { 32 | super(); 33 | this.parse(); 34 | } 35 | 36 | public getNamedArgsHelp(): Record { 37 | return { 38 | payload: "Payload for the put operation", 39 | key: "Key expression for the put operation" 40 | }; 41 | } 42 | 43 | getPositionalArgsHelp(): [string, string][] { 44 | return []; 45 | } 46 | } 47 | 48 | main(); 49 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_querier.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { ReplyError, Config, Sample, Session, QueryTarget, Selector, ChannelReceiver, Reply, } from "@eclipse-zenoh/zenoh-ts"; 16 | import { Duration, Milliseconds } from 'typed-duration' 17 | import { BaseParseArgs } from "./parse_args.ts"; 18 | 19 | const { milliseconds } = Duration 20 | 21 | export async function main() { 22 | const args = new ParseArgs(); 23 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 24 | 25 | const querier = await session.declareQuerier(args.getSelector().keyExpr(), { 26 | target: args.getQueryTarget(), 27 | timeout: args.getTimeout(), 28 | }); 29 | 30 | for (let i = 0; i < 1000; i++) { 31 | await sleep(1000); 32 | const payload = `[${i}] ${args.payload}`; 33 | console.log!(`Querying '${args.getSelector().toString()}' with payload: '${payload}'...`); 34 | const receiver = await querier.get({ parameters: args.getSelector().parameters(), payload: payload }) as ChannelReceiver; 35 | 36 | for await (const reply of receiver) { 37 | const resp = reply.result(); 38 | if (resp instanceof Sample) { 39 | const sample: Sample = resp; 40 | console.warn(">> Received ('", sample.keyexpr().toString(), ":", sample.payload().toString(), "')"); 41 | } else { 42 | const replyError: ReplyError = resp; 43 | console.warn(">> Received (ERROR: '{", replyError.payload().toString(), "}')"); 44 | } 45 | } 46 | console.warn("Get Finished"); 47 | } 48 | } 49 | 50 | class ParseArgs extends BaseParseArgs { 51 | public selector: string = "demo/example/**"; 52 | public payload: string = "Querier Get from Zenoh-ts!"; 53 | public target: string = "BEST_MATCHING"; 54 | public timeout: number = 10000; 55 | 56 | constructor() { 57 | super(); 58 | this.parse(); 59 | } 60 | 61 | public getNamedArgsHelp(): Record { 62 | return { 63 | selector: "Selector for the query", 64 | payload: "Payload for the query", 65 | target: "Target for the query", 66 | timeout: "Timeout for the query in milliseconds" 67 | }; 68 | } 69 | 70 | getPositionalArgsHelp(): [string, string][] { 71 | return []; 72 | } 73 | 74 | public getSelector(): Selector { 75 | const [keyExpr, parameters] = this.selector.split("?"); 76 | return new Selector(keyExpr, parameters); 77 | } 78 | 79 | public getQueryTarget(): QueryTarget { 80 | switch (this.target) { 81 | case "BEST_MATCHING": 82 | return QueryTarget.BEST_MATCHING; 83 | case "ALL": 84 | return QueryTarget.ALL; 85 | case "ALL_COMPLETE": 86 | return QueryTarget.ALL_COMPLETE; 87 | default: 88 | return QueryTarget.BEST_MATCHING; 89 | } 90 | } 91 | 92 | public getTimeout(): Milliseconds { 93 | return milliseconds.of(this.timeout); 94 | } 95 | } 96 | 97 | 98 | function sleep(ms: number) { 99 | return new Promise((resolve) => setTimeout(resolve, ms)); 100 | } 101 | 102 | main(); 103 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_queryable.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { ChannelReceiver, Config, KeyExpr, Query, Queryable, Session, ZBytes } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | 18 | export async function main() { 19 | const args = new ParseArgs(); 20 | console.warn('Opening session...'); 21 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 22 | 23 | const keyExpr = new KeyExpr(args.key); 24 | console.warn(`Declaring Queryable on: '${args.key}'...`); 25 | 26 | const response = args.payload; 27 | 28 | console.warn("Press CTRL-C to quit..."); 29 | 30 | // Declaring a queryable with a callback 31 | // function callback(query: Query) { 32 | // let zbytes: ZBytes | undefined = query.payload(); 33 | 34 | // if (zbytes == undefined) { 35 | // console.warn!(`>> [Queryable ] Received Query ${query.selector().toString()}`); 36 | // } else { 37 | // console.warn!( 38 | // `>> [Queryable ] Received Query ${query.selector().toString()} with payload '${zbytes}'`, 39 | // ); 40 | // } 41 | 42 | // console.warn( 43 | // `>> [Queryable ] Responding ${key_expr.toString()} with payload '${response}'`, 44 | // ); 45 | // query.reply(key_expr, response); 46 | // query.finalize(); 47 | // } 48 | 49 | // let queryable_cb: Queryable = await session.declare_queryable(key_expr, { 50 | // complete: true, 51 | // callback: callback, 52 | // }); 53 | // while(true) { 54 | // await sleep(1000 * 5); 55 | // } 56 | // await queryable_cb.undeclare(); 57 | 58 | 59 | 60 | const queryable: Queryable = await session.declareQueryable(keyExpr, { 61 | complete: args.complete, 62 | }); 63 | 64 | for await (const query of queryable.receiver() as ChannelReceiver) { 65 | await using scopedQuery = query; 66 | const zbytes: ZBytes | undefined = query.payload(); 67 | 68 | if (zbytes == undefined) { 69 | console.warn!(`>> [Queryable ] Received Query ${scopedQuery.selector().toString()}`); 70 | } else { 71 | console.warn!( 72 | `>> [Queryable ] Received Query ${scopedQuery.selector().toString()} with payload '${zbytes.toString()}'`, 73 | ); 74 | } 75 | 76 | console.warn( 77 | `>> [Queryable ] Responding ${keyExpr.toString()} with payload '${response}'`, 78 | ); 79 | await scopedQuery.reply(keyExpr, response); 80 | } 81 | } 82 | 83 | class ParseArgs extends BaseParseArgs { 84 | public key: string = "demo/example/zenoh-ts-queryable"; 85 | public payload: string = "Queryable from Zenoh-ts!"; 86 | public complete: boolean = true; 87 | 88 | constructor() { 89 | super(); 90 | this.parse(); 91 | } 92 | 93 | public getNamedArgsHelp(): Record { 94 | return { 95 | key: "Key expression for the queryable", 96 | payload: "Payload for the queryable", 97 | complete: "Complete flag for the queryable" 98 | }; 99 | } 100 | 101 | getPositionalArgsHelp(): [string, string][] { 102 | return []; 103 | } 104 | } 105 | 106 | 107 | main(); 108 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_sub.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { 16 | Config, Subscriber, Session, KeyExpr, RingChannel, ChannelReceiver, Sample, 17 | SampleKind 18 | } from "@eclipse-zenoh/zenoh-ts"; 19 | import { BaseParseArgs } from "./parse_args.ts"; 20 | 21 | export async function main() { 22 | const args = new ParseArgs(); 23 | console.warn('Opening session...'); 24 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 25 | const keyExpr = new KeyExpr(args.key); 26 | 27 | console.warn(`Declaring Subscriber on '${args.key}'...`); 28 | const pollSubscriber: Subscriber = await session.declareSubscriber(keyExpr, { handler: new RingChannel(10) }); 29 | console.warn("Press CTRL-C to quit..."); 30 | 31 | for await (const sample of pollSubscriber.receiver() as ChannelReceiver) { 32 | console.warn!( 33 | ">> [Subscriber] Received " + 34 | SampleKind[sample.kind()] + " ('" + 35 | sample.keyexpr() + "': '" + 36 | sample.payload().toString() + "')", 37 | ); 38 | } 39 | 40 | await pollSubscriber.undeclare(); 41 | } 42 | 43 | class ParseArgs extends BaseParseArgs { 44 | public key: string = "demo/example/**"; 45 | 46 | constructor() { 47 | super(); 48 | this.parse(); 49 | } 50 | 51 | public getNamedArgsHelp(): Record { 52 | return { 53 | key: "Key expression for the subscriber" 54 | }; 55 | } 56 | 57 | getPositionalArgsHelp(): [string, string][] { 58 | return []; 59 | } 60 | } 61 | 62 | main(); 63 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_sub_liveliness.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { 16 | Config, Subscriber, Session, KeyExpr, 17 | SampleKind, 18 | ChannelReceiver, 19 | Sample 20 | } from "@eclipse-zenoh/zenoh-ts"; 21 | import { BaseParseArgs } from "./parse_args.ts"; 22 | 23 | export async function main() { 24 | const args = new ParseArgs(); 25 | console.log("Opening session..."); 26 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 27 | const keyExpr = new KeyExpr(args.key); 28 | console.log(`Declaring Liveliness Subscriber on '${args.key}'`); 29 | 30 | const livelinessSubscriber: Subscriber = await session.liveliness().declareSubscriber(keyExpr, { history: args.history }); 31 | 32 | for await (const sample of livelinessSubscriber.receiver() as ChannelReceiver) { 33 | switch (sample.kind()) { 34 | case SampleKind.PUT: { 35 | console.log!( 36 | ">> [LivelinessSubscriber] New alive token ", 37 | sample.keyexpr().toString() 38 | ); 39 | break; 40 | } 41 | case SampleKind.DELETE: { 42 | console.log!( 43 | ">> [LivelinessSubscriber] Dropped token ", 44 | sample.keyexpr().toString() 45 | ); 46 | break; 47 | } 48 | } 49 | } 50 | await livelinessSubscriber.undeclare(); 51 | await session.close(); 52 | } 53 | 54 | class ParseArgs extends BaseParseArgs { 55 | public key: string = "group1/**"; 56 | public history: boolean = true; 57 | 58 | constructor() { 59 | super(); 60 | this.parse(); 61 | } 62 | 63 | public getNamedArgsHelp(): Record { 64 | return { 65 | key: "Key expression for the liveliness subscriber", 66 | history: "History flag for the liveliness subscriber" 67 | }; 68 | } 69 | 70 | getPositionalArgsHelp(): [string, string][] { 71 | return []; 72 | } 73 | } 74 | 75 | main(); 76 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/src/z_sub_thr.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Config, Session, Sample, KeyExpr } from "@eclipse-zenoh/zenoh-ts"; 16 | import { BaseParseArgs } from "./parse_args.ts"; 17 | // Throughput test 18 | class Stats { 19 | roundCount: number; 20 | roundSize: number; 21 | finishedRounds: number; 22 | roundStart: number; 23 | globalStart: number; 24 | 25 | constructor(roundSize: number) { 26 | this.roundCount = 0; 27 | this.roundSize = roundSize; 28 | this.finishedRounds = 0; 29 | this.roundStart = performance.now(); 30 | this.globalStart = 0; 31 | } 32 | 33 | increment() { 34 | if (this.roundCount == 0) { 35 | this.roundStart = performance.now(); 36 | if (this.globalStart == 0) { 37 | this.globalStart = this.roundStart; 38 | } 39 | this.roundCount += 1; 40 | } else if (this.roundCount < this.roundSize) { 41 | this.roundCount += 1; 42 | } else { 43 | this.printRound(); 44 | this.finishedRounds += 1; 45 | this.roundCount = 0; 46 | } 47 | } 48 | 49 | printRound() { 50 | const elapsedMs = performance.now() - this.roundStart; 51 | const throughput = (this.roundSize) / (elapsedMs / 1000); 52 | console.warn(throughput, " msg/s"); 53 | } 54 | } 55 | 56 | export async function main() { 57 | const args = new ParseArgs(); 58 | 59 | console.warn('Opening session...'); 60 | const session: Session = await Session.open(new Config("ws/127.0.0.1:10000")); 61 | const stats = new Stats(args.number); 62 | const subscriberCallback = function (_sample: Sample): void { 63 | stats.increment(); 64 | }; 65 | 66 | await session.declareSubscriber( 67 | "test/thr", 68 | { handler: subscriberCallback } 69 | ); 70 | 71 | while (stats.finishedRounds < args.samples) { 72 | await sleep(500); 73 | } 74 | 75 | await session.close(); 76 | } 77 | 78 | function sleep(ms: number) { 79 | return new Promise((resolve) => setTimeout(resolve, ms)); 80 | } 81 | 82 | 83 | class ParseArgs extends BaseParseArgs { 84 | public samples: number = 10; 85 | public number: number = 100000; 86 | 87 | constructor() { 88 | super(); 89 | this.parse(); 90 | } 91 | 92 | public getKeyexpr(): KeyExpr { 93 | return KeyExpr.autocanonize(this.key); 94 | } 95 | 96 | public getNamedArgsHelp(): Record { 97 | return { 98 | samples: "Number of throughput measurements", 99 | number: "Number of messages in each throughput measurements", 100 | }; 101 | } 102 | 103 | public getPositionalArgsHelp(): [string, string][] { 104 | return []; 105 | }; 106 | 107 | } 108 | 109 | main(); 110 | -------------------------------------------------------------------------------- /zenoh-ts/examples/deno/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable", "deno.ns"], 7 | "skipLibCheck": true, 8 | "types": ["deno.ns", "deno.unstable"], 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "paths": { 17 | "@eclipse-zenoh/zenoh-ts": ["../../src/index.ts"], 18 | "@eclipse-zenoh/zenoh-ts/*": ["../../src/*"] 19 | }, 20 | 21 | /* Linting */ 22 | "strict": true, 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": true, 25 | "noFallthroughCasesInSwitch": true 26 | }, 27 | "include": ["src"] 28 | } 29 | -------------------------------------------------------------------------------- /zenoh-ts/importmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": [ 3 | "browser", 4 | "development", 5 | "module" 6 | ], 7 | "imports": { 8 | "./node_modules/channel-ts/lib": "./node_modules/channel-ts/lib/index.js", 9 | "./node_modules/channel-ts/lib/channel": "./node_modules/channel-ts/lib/channel.js", 10 | "./node_modules/channel-ts/lib/mutex": "./node_modules/channel-ts/lib/mutex.js", 11 | "./node_modules/channel-ts/lib/observe": "./node_modules/channel-ts/lib/observe.js", 12 | "./node_modules/tslog/dist/esm/runtime/nodejs/index.js": "./node_modules/tslog/dist/esm/runtime/browser/index.js", 13 | "./node_modules/typed-duration/dist/lib": "./node_modules/typed-duration/dist/lib/index.js", 14 | "base64-arraybuffer": "./node_modules/base64-arraybuffer/dist/base64-arraybuffer.es5.js", 15 | "channel-ts": "./node_modules/channel-ts/lib/index.js", 16 | "channel-ts/lib/channel.js": "./node_modules/channel-ts/lib/channel.js", 17 | "tslog": "./node_modules/tslog/dist/esm/index.js", 18 | "typed-duration": "./node_modules/typed-duration/dist/index.js", 19 | "uuid": "./node_modules/uuid/dist/esm-browser/index.js" 20 | } 21 | } -------------------------------------------------------------------------------- /zenoh-ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eclipse-zenoh/zenoh-ts", 3 | "version": "1.4.0", 4 | "license": "(Apache-2.0 OR EPL-2.0)", 5 | "type": "module", 6 | "description": "Zenoh: The Zero Overhead Pub/Sub/Query Protocol", 7 | "exports": { 8 | ".": { 9 | "import": "./dist/index.js", 10 | "types": "./dist/index.d.ts" 11 | }, 12 | "./ext": { 13 | "import": "./dist/ext/index.js", 14 | "types": "./dist/ext/index.d.ts" 15 | } 16 | }, 17 | "main": "dist/index.js", 18 | "types": "dist/index.d.ts", 19 | "files": [ 20 | "package.json", 21 | "README.md", 22 | "dist", 23 | "LICENSE" 24 | ], 25 | "dependencies": { 26 | "@thi.ng/leb128": "^3.1.36", 27 | "base64-arraybuffer": "^1.0.2", 28 | "channel-ts": "^0.1.2", 29 | "eslint": "^9.10.0", 30 | "tslog": "^4.9.3", 31 | "typed-duration": "^2.0.0", 32 | "uuid": "^10.0.0" 33 | }, 34 | "devDependencies": { 35 | "@types/node": "20.9.1", 36 | "@types/uuid": "^10.0.0", 37 | "typedoc": "^0.26.5", 38 | "typescript": "^5.8.3", 39 | "typescript-eslint": "^8.32.1" 40 | }, 41 | "scripts": { 42 | "clean": "./scripts/clean.sh", 43 | "build": "./scripts/build.sh", 44 | "start": "./scripts/start.sh", 45 | "release": "npx release-it", 46 | "lint": "npx eslint ." 47 | }, 48 | "publishConfig": { 49 | "access": "public", 50 | "registry": "https://registry.npmjs.org" 51 | }, 52 | "directories": { 53 | "example": "examples" 54 | }, 55 | "repository": { 56 | "type": "git", 57 | "url": "github.com/eclipse-zenoh/zenoh-ts" 58 | }, 59 | "keywords": [ 60 | "networking" 61 | ], 62 | "author": "Charles Schleich", 63 | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" 64 | } 65 | -------------------------------------------------------------------------------- /zenoh-ts/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ORIGINAL_DIR="$(pwd)" 4 | SCRIPTDIR="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)" 5 | cd "$SCRIPTDIR/.." 6 | 7 | # Print usage info 8 | print_usage() { 9 | echo "Usage: yarn build [component] [subcomponent]" 10 | echo "Options:" 11 | echo " --help - Show this help message" 12 | echo "" 13 | echo "Components (default: library):" 14 | echo " library - Build only library (WASM module and TypeScript)" 15 | echo " tests - Build only tests" 16 | echo " examples - Build only examples" 17 | echo " Subcomponents for examples:" 18 | echo " deno - Build only Deno examples" 19 | echo " browser - Build only browser examples" 20 | echo " ALL - Build everything" 21 | echo "" 22 | echo "If no component is specified, builds the library by default." 23 | } 24 | 25 | # Install common dependencies 26 | install_deps() { 27 | # install dependencies if needed 28 | if [ ! -d "./node_modules" ]; then 29 | yarn install || exit 1 30 | fi 31 | } 32 | 33 | # Build the library (WASM module and TypeScript) 34 | build_library() { 35 | echo "Building library..." 36 | # build wasm module 37 | cd ../zenoh-keyexpr-wasm 38 | cargo install wasm-pack || exit 1 39 | wasm-pack build --target bundler --out-dir ../zenoh-ts/src/key_expr || exit 1 40 | cd "$SCRIPTDIR/.." 41 | 42 | # compile typescript and copy wasm module 43 | npx tsc || exit 1 44 | cp ./src/key_expr/*wasm* ./dist/key_expr/ 45 | echo "Library build completed." 46 | } 47 | 48 | # Build tests 49 | build_tests() { 50 | echo "Building tests..." 51 | cd tests 52 | # yarn install works too but test coverage works only with deno install 53 | deno install || exit 1 54 | yarn verify || exit 1 55 | cd "$SCRIPTDIR/.." 56 | echo "Tests build completed." 57 | } 58 | 59 | # Build Deno examples 60 | build_examples_deno() { 61 | echo "Building Deno examples..." 62 | cd examples/deno 63 | deno install || exit 1 64 | yarn verify || exit 1 65 | cd "$SCRIPTDIR/.." 66 | echo "Deno examples build completed." 67 | } 68 | 69 | # Build browser examples 70 | build_examples_browser() { 71 | echo "Building browser examples..." 72 | cd examples/browser/chat 73 | yarn install || exit 1 74 | yarn build || exit 1 75 | cd "$SCRIPTDIR/.." 76 | echo "Browser examples build completed." 77 | } 78 | 79 | # Build examples 80 | build_examples() { 81 | local subcomponent="$1" 82 | 83 | if [ -z "$subcomponent" ] || [ "$subcomponent" = "ALL" ]; then 84 | echo "Building all examples..." 85 | build_examples_deno 86 | build_examples_browser 87 | echo "All examples build completed." 88 | elif [ "$subcomponent" = "deno" ]; then 89 | build_examples_deno 90 | elif [ "$subcomponent" = "browser" ]; then 91 | build_examples_browser 92 | else 93 | echo "Unknown examples subcomponent: $subcomponent" 94 | print_usage 95 | exit 1 96 | fi 97 | } 98 | 99 | # Process command line arguments 100 | component="$1" 101 | 102 | # Check for help flag 103 | if [ "$component" = "--help" ] || [ "$component" = "-h" ]; then 104 | print_usage 105 | exit 0 106 | fi 107 | 108 | # If no parameters passed, build library by default 109 | if [ -z "$component" ]; then 110 | component="library" 111 | fi 112 | 113 | subcomponent="$2" 114 | 115 | case "${component}" in 116 | "library") 117 | install_deps 118 | build_library 119 | ;; 120 | "tests") 121 | install_deps 122 | build_library 123 | build_tests 124 | ;; 125 | "examples") 126 | install_deps 127 | build_library 128 | build_examples "$subcomponent" 129 | ;; 130 | "ALL") 131 | install_deps 132 | build_library 133 | build_tests 134 | build_examples 135 | ;; 136 | *) 137 | print_usage 138 | exit 1 139 | ;; 140 | esac 141 | 142 | cd "$ORIGINAL_DIR" -------------------------------------------------------------------------------- /zenoh-ts/scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ORIGINAL_DIR="$(pwd)" 4 | SCRIPTDIR="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)" 5 | cd "$SCRIPTDIR/.." 6 | 7 | rm -rf ./node_modules ./dist ./esm 8 | cd tests && yarn clean || exit 1 && cd "$SCRIPTDIR/.." 9 | cd examples/deno && yarn clean || exit 1 && cd "$SCRIPTDIR/.." 10 | cd examples/browser/chat && yarn clean || exit 1 && cd "$SCRIPTDIR/.." 11 | 12 | cd "$ORIGINAL_DIR" -------------------------------------------------------------------------------- /zenoh-ts/scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ORIGINAL_DIR="$(pwd)" 4 | EXIT_CODE=0 5 | SCRIPTDIR="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)" 6 | cd "$SCRIPTDIR/.." 7 | 8 | start_daemon() { 9 | CURRENT_DIR="$(pwd)" 10 | cd "$SCRIPTDIR/../.." 11 | cargo build --release 12 | cargo bin -i zenohd 13 | ZENOHD=$(find ./.bin/rust-* -name "zenohd" -type f | head -n 2) 14 | if [ $(echo "$ZENOHD" | wc -l) -ne 1 ]; then 15 | echo "Error: More than one or no 'zenohd' file found in ./.bin/rust-* directories:" 16 | echo "$ZENOHD" 17 | exit 1 18 | fi 19 | echo "\"$ZENOHD\" --config EXAMPLE_CONFIG.json5" 20 | "$ZENOHD" --config EXAMPLE_CONFIG.json5 & 21 | 22 | ZPID=$! 23 | echo "zenohd started with PID $ZPID" 24 | sleep 5 25 | 26 | if ! ps -p $ZPID > /dev/null; then 27 | echo "Error: zenohd process not found" 28 | exit 1 29 | fi 30 | 31 | # Trap SIGINT to ensure zenohd is killed on ^C 32 | trap "echo 'Stopping zenohd with PID $ZPID'; kill $ZPID; exit 1" SIGINT 33 | 34 | cd "$CURRENT_DIR" 35 | } 36 | 37 | stop_daemon() { 38 | if [ ! -z "$ZPID" ]; then 39 | echo "Stopping zenohd with PID $ZPID" 40 | kill "$ZPID" 41 | fi 42 | } 43 | 44 | # run build if needed 45 | if [ ! -d "./dist" ]; then 46 | yarn build library || exit 1 47 | fi 48 | 49 | # Check if DAEMON is the first argument 50 | USE_DAEMON=0 51 | if [ "$1" = "DAEMON" ]; then 52 | USE_DAEMON=1 53 | shift 54 | fi 55 | 56 | if [ "$1" = "test" ]; then 57 | cd tests 58 | deno install || exit 1 59 | if [ $USE_DAEMON -eq 1 ]; then 60 | start_daemon 61 | fi 62 | yarn start "${@:2}" 63 | EXIT_CODE=$? 64 | elif [ "$1" = "example" ] && [ "$2" = "deno" ]; then 65 | cd examples/deno 66 | deno install || exit 1 67 | if [ $USE_DAEMON -eq 1 ]; then 68 | start_daemon 69 | fi 70 | yarn start "${@:3}" 71 | EXIT_CODE=$? 72 | elif [ "$1" = "example" ] && [ "$2" = "browser" ] && [ "$3" = "" ]; then 73 | # there is only "chat" example for now, but later list of examples can be shown 74 | cd examples/browser/chat 75 | yarn install || exit 1 76 | if [ $USE_DAEMON -eq 1 ]; then 77 | start_daemon 78 | fi 79 | yarn start 80 | EXIT_CODE=$? 81 | else 82 | echo 83 | echo "Available options:" 84 | echo 85 | echo "yarn start [DAEMON] test [test-name|ALL] [COVERAGE]" 86 | echo "yarn start [DAEMON] example deno [example-name]" 87 | echo "yarn start [DAEMON] example browser" 88 | echo 89 | fi 90 | 91 | stop_daemon 92 | 93 | cd "$ORIGINAL_DIR" 94 | 95 | # Disable debug command tracing 96 | set +x 97 | 98 | exit $EXIT_CODE -------------------------------------------------------------------------------- /zenoh-ts/src/channels.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { SimpleChannel, TryReceived, TryReceivedKind, ChannelState } from "channel-ts"; 16 | import { Callback, Drop } from "./closure"; 17 | 18 | /** 19 | * A receiver interface for channel. 20 | */ 21 | export interface ChannelReceiver { 22 | state: () => ChannelState; 23 | receive: () => Promise; 24 | tryReceive(): TryReceived; 25 | [Symbol.asyncIterator]: () => AsyncIterableIterator; 26 | } 27 | 28 | /** 29 | * A sender interface for channel. 30 | */ 31 | export interface ChannelSender { 32 | send: (data: T) => void; 33 | close: () => void; 34 | } 35 | 36 | export interface IntoSenderReceiverPair { 37 | intoSenderReceiverPair: () => [ChannelSender, ChannelReceiver] 38 | } 39 | 40 | 41 | export function isIntoSenderReceiverPair(object: any): object is IntoSenderReceiverPair { 42 | return (>(object)).intoSenderReceiverPair != undefined; 43 | } 44 | 45 | export type Handler = Callback | [Callback, Drop] | IntoSenderReceiverPair; 46 | 47 | 48 | export function intoCbDropReceiver(handler: Handler): [Callback, Drop, ChannelReceiver?] { 49 | if (isIntoSenderReceiverPair(handler)) { 50 | let [sender, receiver] = handler.intoSenderReceiverPair(); 51 | let cb = (data: T): void => { 52 | sender.send(data); 53 | }; 54 | let drop = (): void => { 55 | sender.close(); 56 | } 57 | return [cb, drop, receiver]; 58 | } else if (Array.isArray(handler)) { // [callback, drop] 59 | return [handler[0], handler[1], undefined]; 60 | } else { 61 | let drop = (): void => { }; 62 | return [handler, drop, undefined]; 63 | } 64 | } 65 | 66 | /** 67 | * A FIFO channel. When maximum capacity is reached all new values will be ignored. 68 | */ 69 | export class FifoChannel { 70 | protected chan: SimpleChannel; 71 | protected capacity: number; 72 | protected size: number; 73 | 74 | constructor(capacity: number) { 75 | this.capacity = capacity; 76 | this.size = 0; 77 | this.chan = new SimpleChannel(); 78 | } 79 | 80 | /** 81 | * Send new data onto channel. When maximum capacity is reached all new values will be ignored. 82 | */ 83 | send(data: T) { 84 | if (this.size != this.capacity) { 85 | this.chan.send(data); 86 | this.size++; 87 | } 88 | } 89 | 90 | /** 91 | * Get state of the channel. 92 | */ 93 | state(): ChannelState { 94 | return this.chan.state; 95 | } 96 | 97 | /** 98 | * Attempt to receive the data in a non-blocking way. 99 | */ 100 | tryReceive(): TryReceived { 101 | let res = this.chan.tryReceive(); 102 | if (res.kind == TryReceivedKind.value) { 103 | this.size--; 104 | } 105 | return res; 106 | } 107 | 108 | /** 109 | * Receive data in a blocking way. If channel is empty, this call will block until new data arrives or the channel is closed. 110 | */ 111 | async receive(): Promise { 112 | let res = await this.chan.receive(); 113 | this.size--; 114 | return res; 115 | } 116 | 117 | /** 118 | * Close the channel. This`will unblock all currently pending @see receive calls. 119 | */ 120 | close(): void { 121 | this.chan.close(); 122 | } 123 | 124 | async *[Symbol.asyncIterator](): AsyncIterableIterator { 125 | try { 126 | while (true) { 127 | yield await this.receive(); 128 | } 129 | } catch { 130 | 131 | } 132 | } 133 | 134 | /** 135 | * Split Channel into @interface ChannelSender and @interface ChannelReceiver pair. 136 | */ 137 | intoSenderReceiverPair(): [ChannelSender, ChannelReceiver] { 138 | return [this, this]; 139 | } 140 | } 141 | 142 | /** 143 | * A circular buffer channel. When maximum capacity is reached the older values will be removed to provide space for new ones. 144 | */ 145 | export class RingChannel extends FifoChannel { 146 | constructor(capacity: number) { 147 | super(capacity) 148 | } 149 | 150 | /** 151 | * Send new data onto channel. When maximum capacity is reached the older values will be removed to provide space for new ones. 152 | */ 153 | override send(data: T) { 154 | if (this.capacity == 0) return; 155 | if (this.size == this.capacity) { 156 | this.chan.tryReceive(); 157 | } 158 | this.chan.send(data); 159 | this.size++; 160 | } 161 | } 162 | 163 | export {TryReceivedKind, ChannelState, TryReceived} 164 | 165 | -------------------------------------------------------------------------------- /zenoh-ts/src/closure.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | export type Callback = (value: T) => void; 16 | export type Drop = () => void; 17 | 18 | export interface Closure { 19 | callback: Callback, 20 | drop: Drop 21 | } -------------------------------------------------------------------------------- /zenoh-ts/src/config.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | // ██████ ██████ ███ ██ ███████ ██ ██████ 16 | // ██ ██ ██ ████ ██ ██ ██ ██ 17 | // ██ ██ ██ ██ ██ ██ █████ ██ ██ ███ 18 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 19 | // ██████ ██████ ██ ████ ██ ██ ██████ 20 | 21 | /** 22 | * The configuration for a Zenoh Session. 23 | */ 24 | export class Config { 25 | /** 26 | * Construct a new config, containing a locator 27 | * @param {string} locator - A string that respects the Locator to connect to. Currently this can be only the address of zenohd remote-api plugin websocket. 28 | * It accepts either 29 | * - zenoh canon form: `/
[?]` where can be `ws` and `wss` only, e.g. `ws/127.0.0.1:10000` 30 | * - common url form, e.g. `ws://127.0.0.1:10000` 31 | * @param {number} messageResponseTimeoutMs - timeout value in milliseconds for receiving a response from zenoh-plugin-remote-api. 32 | * Defaults to 500 ms. 33 | * @returns {Config} configuration instance 34 | */ 35 | constructor(public locator: string, public messageResponseTimeoutMs: number = 500) {} 36 | } 37 | -------------------------------------------------------------------------------- /zenoh-ts/src/enums.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | // Message priority. 16 | export enum Priority { 17 | REAL_TIME = 1, 18 | INTERACTIVE_HIGH = 2, 19 | INTERACTIVE_LOW = 3, 20 | DATA_HIGH = 4, 21 | DATA = 5, 22 | DATA_LOW = 6, 23 | BACKGROUND = 7, 24 | DEFAULT = DATA, 25 | } 26 | 27 | // Congestion control strategy. 28 | export enum CongestionControl { 29 | // When transmitting a message in a node with a full queue, the node may drop the message. 30 | DROP = 0, 31 | // When transmitting a message in a node with a full queue, the node will wait for queue to 32 | // progress. 33 | BLOCK = 1, 34 | DEFAULT_PUSH = DROP, 35 | DEFAULT_REQUEST = BLOCK, 36 | DEFAULT_RESPONSE = BLOCK 37 | } 38 | 39 | // The publisher reliability. 40 | // Currently `reliability` does not trigger any data retransmission on the wire. 41 | // It is rather used as a marker on the wire and it may be used to select the best link available (e.g. TCP for reliable data and UDP for best effort data). 42 | export enum Reliability { 43 | BEST_EFFORT = 0, 44 | RELIABLE = 1, 45 | DEFAULT = RELIABLE 46 | } 47 | 48 | // The locality of samples to be received by subscribers or targeted by publishers. 49 | export enum Locality { 50 | SESSION_LOCAL = 0, 51 | REMOTE = 1, 52 | ANY = 2, 53 | DEFAULT = ANY 54 | } 55 | 56 | 57 | export enum SampleKind { 58 | PUT = 0, 59 | DELETE = 1 60 | } 61 | 62 | // The `zenoh.queryable.Queryables that should be target of a `zenoh.Session.get()`. 63 | export enum QueryTarget { 64 | // Let Zenoh find the BestMatching queryable capabale of serving the query. 65 | BEST_MATCHING = 0, 66 | // Deliver the query to all queryables matching the query's key expression. 67 | ALL = 1, 68 | // Deliver the query to all queryables matching the query's key expression that are declared as complete. 69 | ALL_COMPLETE = 2, 70 | DEFAULT = BEST_MATCHING 71 | } 72 | 73 | // The kind of consolidation to apply to a query. 74 | export enum ConsolidationMode { 75 | // Apply automatic consolidation based on queryable's preferences 76 | AUTO = 0, 77 | // No consolidation applied: multiple samples may be received for the same key-timestamp. 78 | NONE = 1, 79 | // Monotonic consolidation immediately forwards samples, except if one with an equal or more recent timestamp 80 | // has already been sent with the same key. 81 | // 82 | // This optimizes latency while potentially reducing bandwidth. 83 | // 84 | // Note that this doesn't cause re-ordering, but drops the samples for which a more recent timestamp has already 85 | // been observed with the same key. 86 | MONOTONIC = 2, 87 | // Holds back samples to only send the set of samples that had the highest timestamp for their key. 88 | LATEST = 3, 89 | DEFAULT = AUTO 90 | } 91 | 92 | // The kind of accepted query replies. 93 | export enum ReplyKeyExpr { 94 | // Accept replies whose key expressions may not match the query key expression. 95 | ANY = 0, 96 | // // Accept replies whose key expressions match the query key expression. 97 | MATCHING_QUERY = 1, 98 | DEFAULT = MATCHING_QUERY 99 | } -------------------------------------------------------------------------------- /zenoh-ts/src/ext/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | // Serialization 16 | import { ZBytesSerializer, ZBytesDeserializer, ZSerializeable, ZDeserializeable, zserialize, zdeserialize, NumberFormat, BigIntFormat, ZS, ZD } from "./serialization.js" 17 | 18 | 19 | 20 | // Exports 21 | export { ZBytesSerializer, ZBytesDeserializer, ZSerializeable, ZDeserializeable, zserialize, zdeserialize, NumberFormat, BigIntFormat, ZS, ZD } -------------------------------------------------------------------------------- /zenoh-ts/src/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | // API Layer Files 16 | import { KeyExpr, IntoKeyExpr } from "./key_expr.js"; 17 | import { ZBytes, IntoZBytes } from "./z_bytes.js"; 18 | import { CongestionControl, ConsolidationMode, Locality, Priority, QueryTarget, Reliability, SampleKind } from "./enums.js"; 19 | import { Sample } from "./sample.js"; 20 | import { Timestamp } from "./timestamp.js"; 21 | import { ZenohId } from "./zid.js"; 22 | import { Publisher, Subscriber } from "./pubsub.js"; 23 | import { IntoSelector, Parameters, IntoParameters, Query, Queryable, Reply, ReplyError, Selector } from "./query.js"; 24 | import { Session, DeleteOptions, PutOptions, GetOptions, QuerierOptions, QueryableOptions, PublisherOptions, SessionInfo } from "./session.js"; 25 | import { Config } from "./config.js"; 26 | import { Encoding, IntoEncoding } from "./encoding.js"; 27 | import { Liveliness, LivelinessToken } from "./liveliness.js"; 28 | import { Querier, QuerierGetOptions } from './querier.js' 29 | import { FifoChannel, RingChannel, ChannelReceiver, ChannelSender, TryReceived, TryReceivedKind, ChannelState } from "./channels.js"; 30 | 31 | // Re-export duration external library 32 | import { Duration } from 'typed-duration' 33 | 34 | 35 | // Exports 36 | export { KeyExpr, IntoKeyExpr }; 37 | export { ZBytes, IntoZBytes }; 38 | export { CongestionControl, ConsolidationMode, Locality, Priority, QueryTarget, Reliability, Sample, SampleKind }; 39 | export { Publisher, Subscriber}; 40 | export { IntoSelector, Parameters, IntoParameters, Query, Queryable, Reply, ReplyError, Selector }; 41 | export { Session, DeleteOptions, PutOptions, GetOptions, QuerierOptions, QueryableOptions, PublisherOptions, SessionInfo}; 42 | export { ZenohId, Timestamp } 43 | export { Config }; 44 | export { Encoding, IntoEncoding }; 45 | export { Liveliness, LivelinessToken }; 46 | export { Duration }; 47 | export { Querier, QuerierGetOptions } 48 | export { FifoChannel, RingChannel, ChannelReceiver, ChannelSender, TryReceived, TryReceivedKind, ChannelState} -------------------------------------------------------------------------------- /zenoh-ts/src/key_expr.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | // ██ ██ ███████ ██ ██ ███████ ██ ██ ██████ ██████ 15 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 16 | // █████ █████ ████ █████ ███ ██████ ██████ 17 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 18 | // ██ ██ ███████ ██ ███████ ██ ██ ██ ██ ██ 19 | 20 | import { new_key_expr, join, concat, includes, intersects, autocanonize } from "./key_expr/zenoh_keyexpr_wrapper.js" 21 | 22 | export type IntoKeyExpr = KeyExpr | String | string; 23 | 24 | export class KeyExpr { 25 | /** 26 | * Class to represent a Key Expression in Zenoh 27 | */ 28 | 29 | // not to be used directly 30 | private inner_: string; 31 | 32 | constructor(keyexpr: IntoKeyExpr) { 33 | let ke; 34 | if (keyexpr instanceof KeyExpr) { 35 | this.inner_ = keyexpr.inner_; 36 | return 37 | } else if (keyexpr instanceof String) { 38 | ke = keyexpr.toString(); 39 | } else { 40 | ke = keyexpr; 41 | } 42 | // `new_key_expr` calls the `key_expr::OwnedKeyExpr::new` in Rust 43 | // if this function fails, the keyexpr is invalid, and an exception is thrown in Wasm and propagated here 44 | // else the Key Expression is valid and we can store the string it represents in the class 45 | new_key_expr(ke); 46 | this.inner_ = ke; 47 | } 48 | 49 | toString(): string { 50 | return this.inner_; 51 | } 52 | 53 | /** 54 | * Joins both sides, inserting a / in between them. 55 | * This should be your preferred method when concatenating path segments. 56 | * @returns KeyExpr 57 | */ 58 | join(other: IntoKeyExpr): KeyExpr { 59 | const keyExpr = join(this.inner_, KeyExpr.intoString(other)); 60 | return new KeyExpr(keyExpr) 61 | } 62 | 63 | /** 64 | * Performs string concatenation and returns the result as a KeyExpr if possible. 65 | * @returns KeyExpr 66 | */ 67 | concat(other: IntoKeyExpr): KeyExpr { 68 | const keyExpr = concat(this.inner_, KeyExpr.intoString(other)); 69 | return new KeyExpr(keyExpr) 70 | } 71 | 72 | /** 73 | * Returns true if this includes other, i.e. the set defined by this contains every key belonging to the set defined by other. 74 | * @returns KeyExpr 75 | */ 76 | includes(other: IntoKeyExpr): boolean { 77 | return includes(this.inner_, KeyExpr.intoString(other)) 78 | } 79 | 80 | /** 81 | * Returns true if the keyexprs intersect, i.e. there exists at least one key which is contained in both of the sets defined by self and other. 82 | * @returns KeyExpr 83 | */ 84 | intersects(other: IntoKeyExpr): boolean { 85 | return intersects(this.inner_, KeyExpr.intoString(other)) 86 | } 87 | 88 | /** 89 | * Returns the canon form of a key_expr 90 | * @returns KeyExpr 91 | */ 92 | static autocanonize(other: IntoKeyExpr): KeyExpr { 93 | const keyExpr = autocanonize(KeyExpr.intoString(other)); 94 | return new KeyExpr(keyExpr) 95 | } 96 | 97 | private static intoString(other: IntoKeyExpr): string { 98 | if (other instanceof KeyExpr) { 99 | return other.inner_; 100 | } else if (other instanceof String) { 101 | return other.toString(); 102 | } else { 103 | return other; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /zenoh-ts/src/key_expr/zenoh_keyexpr_wrapper.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export function new_key_expr(key_expr_str: string): void; 4 | export function join(ke1: string, ke2: string): string; 5 | export function concat(ke1: string, ke2: string): string; 6 | export function includes(ke1: string, ke2: string): boolean; 7 | export function intersects(ke1: string, ke2: string): boolean; 8 | export function autocanonize(ke: string): string; 9 | -------------------------------------------------------------------------------- /zenoh-ts/src/key_expr/zenoh_keyexpr_wrapper.js: -------------------------------------------------------------------------------- 1 | import * as wasm from "./zenoh_keyexpr_wrapper_bg.wasm"; 2 | export * from "./zenoh_keyexpr_wrapper_bg.js"; 3 | import { __wbg_set_wasm } from "./zenoh_keyexpr_wrapper_bg.js"; 4 | __wbg_set_wasm(wasm); 5 | wasm.__wbindgen_start(); 6 | -------------------------------------------------------------------------------- /zenoh-ts/src/key_expr/zenoh_keyexpr_wrapper_bg.js: -------------------------------------------------------------------------------- 1 | let wasm; 2 | export function __wbg_set_wasm(val) { 3 | wasm = val; 4 | } 5 | 6 | 7 | const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; 8 | 9 | let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 10 | 11 | cachedTextDecoder.decode(); 12 | 13 | let cachedUint8ArrayMemory0 = null; 14 | 15 | function getUint8ArrayMemory0() { 16 | if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { 17 | cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); 18 | } 19 | return cachedUint8ArrayMemory0; 20 | } 21 | 22 | function getStringFromWasm0(ptr, len) { 23 | ptr = ptr >>> 0; 24 | return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); 25 | } 26 | 27 | let WASM_VECTOR_LEN = 0; 28 | 29 | const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder; 30 | 31 | let cachedTextEncoder = new lTextEncoder('utf-8'); 32 | 33 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 34 | ? function (arg, view) { 35 | return cachedTextEncoder.encodeInto(arg, view); 36 | } 37 | : function (arg, view) { 38 | const buf = cachedTextEncoder.encode(arg); 39 | view.set(buf); 40 | return { 41 | read: arg.length, 42 | written: buf.length 43 | }; 44 | }); 45 | 46 | function passStringToWasm0(arg, malloc, realloc) { 47 | 48 | if (realloc === undefined) { 49 | const buf = cachedTextEncoder.encode(arg); 50 | const ptr = malloc(buf.length, 1) >>> 0; 51 | getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); 52 | WASM_VECTOR_LEN = buf.length; 53 | return ptr; 54 | } 55 | 56 | let len = arg.length; 57 | let ptr = malloc(len, 1) >>> 0; 58 | 59 | const mem = getUint8ArrayMemory0(); 60 | 61 | let offset = 0; 62 | 63 | for (; offset < len; offset++) { 64 | const code = arg.charCodeAt(offset); 65 | if (code > 0x7F) break; 66 | mem[ptr + offset] = code; 67 | } 68 | 69 | if (offset !== len) { 70 | if (offset !== 0) { 71 | arg = arg.slice(offset); 72 | } 73 | ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; 74 | const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); 75 | const ret = encodeString(arg, view); 76 | 77 | offset += ret.written; 78 | ptr = realloc(ptr, len, offset, 1) >>> 0; 79 | } 80 | 81 | WASM_VECTOR_LEN = offset; 82 | return ptr; 83 | } 84 | 85 | function takeFromExternrefTable0(idx) { 86 | const value = wasm.__wbindgen_export_0.get(idx); 87 | wasm.__externref_table_dealloc(idx); 88 | return value; 89 | } 90 | /** 91 | * @param {string} key_expr_str 92 | */ 93 | export function new_key_expr(key_expr_str) { 94 | const ptr0 = passStringToWasm0(key_expr_str, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 95 | const len0 = WASM_VECTOR_LEN; 96 | const ret = wasm.new_key_expr(ptr0, len0); 97 | if (ret[1]) { 98 | throw takeFromExternrefTable0(ret[0]); 99 | } 100 | } 101 | 102 | /** 103 | * @param {string} ke1 104 | * @param {string} ke2 105 | * @returns {string} 106 | */ 107 | export function join(ke1, ke2) { 108 | let deferred4_0; 109 | let deferred4_1; 110 | try { 111 | const ptr0 = passStringToWasm0(ke1, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 112 | const len0 = WASM_VECTOR_LEN; 113 | const ptr1 = passStringToWasm0(ke2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 114 | const len1 = WASM_VECTOR_LEN; 115 | const ret = wasm.join(ptr0, len0, ptr1, len1); 116 | var ptr3 = ret[0]; 117 | var len3 = ret[1]; 118 | if (ret[3]) { 119 | ptr3 = 0; len3 = 0; 120 | throw takeFromExternrefTable0(ret[2]); 121 | } 122 | deferred4_0 = ptr3; 123 | deferred4_1 = len3; 124 | return getStringFromWasm0(ptr3, len3); 125 | } finally { 126 | wasm.__wbindgen_free(deferred4_0, deferred4_1, 1); 127 | } 128 | } 129 | 130 | /** 131 | * @param {string} ke1 132 | * @param {string} ke2 133 | * @returns {string} 134 | */ 135 | export function concat(ke1, ke2) { 136 | let deferred4_0; 137 | let deferred4_1; 138 | try { 139 | const ptr0 = passStringToWasm0(ke1, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 140 | const len0 = WASM_VECTOR_LEN; 141 | const ptr1 = passStringToWasm0(ke2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 142 | const len1 = WASM_VECTOR_LEN; 143 | const ret = wasm.concat(ptr0, len0, ptr1, len1); 144 | var ptr3 = ret[0]; 145 | var len3 = ret[1]; 146 | if (ret[3]) { 147 | ptr3 = 0; len3 = 0; 148 | throw takeFromExternrefTable0(ret[2]); 149 | } 150 | deferred4_0 = ptr3; 151 | deferred4_1 = len3; 152 | return getStringFromWasm0(ptr3, len3); 153 | } finally { 154 | wasm.__wbindgen_free(deferred4_0, deferred4_1, 1); 155 | } 156 | } 157 | 158 | /** 159 | * @param {string} ke1 160 | * @param {string} ke2 161 | * @returns {boolean} 162 | */ 163 | export function includes(ke1, ke2) { 164 | const ptr0 = passStringToWasm0(ke1, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 165 | const len0 = WASM_VECTOR_LEN; 166 | const ptr1 = passStringToWasm0(ke2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 167 | const len1 = WASM_VECTOR_LEN; 168 | const ret = wasm.includes(ptr0, len0, ptr1, len1); 169 | if (ret[2]) { 170 | throw takeFromExternrefTable0(ret[1]); 171 | } 172 | return ret[0] !== 0; 173 | } 174 | 175 | /** 176 | * @param {string} ke1 177 | * @param {string} ke2 178 | * @returns {boolean} 179 | */ 180 | export function intersects(ke1, ke2) { 181 | const ptr0 = passStringToWasm0(ke1, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 182 | const len0 = WASM_VECTOR_LEN; 183 | const ptr1 = passStringToWasm0(ke2, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 184 | const len1 = WASM_VECTOR_LEN; 185 | const ret = wasm.intersects(ptr0, len0, ptr1, len1); 186 | if (ret[2]) { 187 | throw takeFromExternrefTable0(ret[1]); 188 | } 189 | return ret[0] !== 0; 190 | } 191 | 192 | /** 193 | * @param {string} ke 194 | * @returns {string} 195 | */ 196 | export function autocanonize(ke) { 197 | let deferred3_0; 198 | let deferred3_1; 199 | try { 200 | const ptr0 = passStringToWasm0(ke, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 201 | const len0 = WASM_VECTOR_LEN; 202 | const ret = wasm.autocanonize(ptr0, len0); 203 | var ptr2 = ret[0]; 204 | var len2 = ret[1]; 205 | if (ret[3]) { 206 | ptr2 = 0; len2 = 0; 207 | throw takeFromExternrefTable0(ret[2]); 208 | } 209 | deferred3_0 = ptr2; 210 | deferred3_1 = len2; 211 | return getStringFromWasm0(ptr2, len2); 212 | } finally { 213 | wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); 214 | } 215 | } 216 | 217 | export function __wbindgen_init_externref_table() { 218 | const table = wasm.__wbindgen_export_0; 219 | const offset = table.grow(4); 220 | table.set(0, undefined); 221 | table.set(offset + 0, undefined); 222 | table.set(offset + 1, null); 223 | table.set(offset + 2, true); 224 | table.set(offset + 3, false); 225 | ; 226 | }; 227 | 228 | export function __wbindgen_string_new(arg0, arg1) { 229 | const ret = getStringFromWasm0(arg0, arg1); 230 | return ret; 231 | }; 232 | 233 | export function __wbindgen_throw(arg0, arg1) { 234 | throw new Error(getStringFromWasm0(arg0, arg1)); 235 | }; 236 | 237 | -------------------------------------------------------------------------------- /zenoh-ts/src/key_expr/zenoh_keyexpr_wrapper_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eclipse-zenoh/zenoh-ts/ea0efc9a62bd649545d942461690b52c7dee394e/zenoh-ts/src/key_expr/zenoh_keyexpr_wrapper_bg.wasm -------------------------------------------------------------------------------- /zenoh-ts/src/key_expr/zenoh_keyexpr_wrapper_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export const new_key_expr: (a: number, b: number) => [number, number]; 5 | export const join: (a: number, b: number, c: number, d: number) => [number, number, number, number]; 6 | export const concat: (a: number, b: number, c: number, d: number) => [number, number, number, number]; 7 | export const includes: (a: number, b: number, c: number, d: number) => [number, number, number]; 8 | export const intersects: (a: number, b: number, c: number, d: number) => [number, number, number]; 9 | export const autocanonize: (a: number, b: number) => [number, number, number, number]; 10 | export const __wbindgen_export_0: WebAssembly.Table; 11 | export const __wbindgen_malloc: (a: number, b: number) => number; 12 | export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; 13 | export const __externref_table_dealloc: (a: number) => void; 14 | export const __wbindgen_free: (a: number, b: number, c: number) => void; 15 | export const __wbindgen_start: () => void; 16 | -------------------------------------------------------------------------------- /zenoh-ts/src/link.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | const MAX_WS_BUFFER_SIZE = 2 * 1024 * 1024; // 2 MB buffer size for websocket 16 | 17 | const RETRY_TIMEOUT_MS = 2000; 18 | const MAX_RETRIES: number = 10; 19 | 20 | 21 | export class RemoteLink { 22 | private constructor(private ws: WebSocket) { 23 | this.ws = ws; 24 | } 25 | 26 | static async new(locator: string): Promise { 27 | let websocketEndpoint = this.parseZenohLocator(locator); 28 | 29 | let retries = 0; 30 | let retryTimeoutMs = RETRY_TIMEOUT_MS; 31 | 32 | while (retries < MAX_RETRIES) { 33 | let ws = new WebSocket(websocketEndpoint); 34 | ws.binaryType = "arraybuffer"; 35 | 36 | ws.onerror = function (event: any) { 37 | console.warn("WebSocket error: ", event); 38 | }; 39 | 40 | ws.onclose = function (event: any) { 41 | console.warn(`WebSocket has been disconnected from remote-api-plugin: ${event.code}`) 42 | }; 43 | 44 | let wait = 0; 45 | while (ws.readyState != 1) { 46 | await sleep(100); 47 | wait += 100; 48 | if (wait > (retryTimeoutMs)) { 49 | ws.close(); 50 | retryTimeoutMs *= 2; 51 | break; 52 | } 53 | } 54 | 55 | if (ws.readyState == 1) { 56 | console.warn("Connected to", websocketEndpoint); 57 | return new RemoteLink(ws); 58 | } else { 59 | ws = new WebSocket(websocketEndpoint); 60 | console.warn("Restart connection"); 61 | } 62 | } 63 | 64 | throw new Error(`Failed to connect to locator endpoint: ${locator} after ${MAX_RETRIES}`); 65 | } 66 | 67 | onmessage(onmessage: (msg: Uint8Array) => void) { 68 | this.ws.onmessage = function (event: any) { 69 | onmessage(new Uint8Array(event.data)); 70 | }; 71 | } 72 | 73 | async send(msg: Uint8Array) { 74 | if (!this.isOk) { 75 | throw new Error("WebSocket is closed"); 76 | } 77 | while (this.ws.bufferedAmount > MAX_WS_BUFFER_SIZE) { 78 | await sleep(10); 79 | if (!this.isOk) { 80 | throw new Error("WebSocket is closed"); 81 | } 82 | } 83 | this.ws.send(msg); 84 | } 85 | 86 | isOk(): boolean { 87 | return this.ws.readyState == WebSocket.OPEN; 88 | } 89 | 90 | close() { 91 | this.ws.onmessage = null; 92 | this.ws.close(); 93 | } 94 | 95 | 96 | private static parseZenohLocator(locator: string): string { 97 | let parts = locator.split("/", 2); 98 | if (parts.length != 2) { 99 | return locator; 100 | } 101 | let protocol = parts[0]; 102 | let address = parts[1]; 103 | if (protocol != "ws" && protocol != "wss") { 104 | return locator; 105 | } 106 | return `${protocol}://${address}`; 107 | } 108 | } 109 | 110 | 111 | function sleep(ms: number) { 112 | return new Promise((resolve) => setTimeout(resolve, ms)); 113 | } -------------------------------------------------------------------------------- /zenoh-ts/src/liveliness.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { IntoKeyExpr, KeyExpr } from "./key_expr.js"; 16 | import { Sample } from "./sample.js"; 17 | import { Reply } from "./query.js"; 18 | 19 | import { Subscriber } from "./pubsub.js"; 20 | import { Duration, TimeDuration } from 'typed-duration' 21 | import { ChannelReceiver, FifoChannel, Handler, intoCbDropReceiver } from "./channels.js"; 22 | import { SessionInner } from "./session_inner.js"; 23 | import { DEFAULT_QUERY_TIMEOUT_MS } from "./session.js"; 24 | 25 | /** 26 | * Options for a Liveliness Subscriber 27 | * @prop {boolean=} history - If true, subscriber will receive the state change notifications for liveliness tokens that were declared before its declaration 28 | * @prop {Handler=} handler - Handler for this subscriber 29 | */ 30 | interface LivelinessSubscriberOptions { 31 | history?: boolean, 32 | handler?: Handler, 33 | } 34 | 35 | /** 36 | * Options for a Liveliness Subscriber 37 | * @prop {TimeDuration=} timeout - This liveliness query timeout value 38 | * @prop {Handler=} handler - Handler for this liveliness query 39 | */ 40 | interface LivelinessGetOptions { 41 | timeout?: TimeDuration, 42 | handler?: Handler, 43 | } 44 | 45 | export class Liveliness { 46 | 47 | constructor(private session: SessionInner) { } 48 | 49 | async declareToken(intoKeyExpr: IntoKeyExpr): Promise { 50 | let tokenId = await this.session.declareLivelinessToken(new KeyExpr(intoKeyExpr)); 51 | return new LivelinessToken(this.session, tokenId); 52 | } 53 | 54 | /** 55 | * Declares a subscriber on liveliness tokens that intersect specified keyexpr 56 | * 57 | * @param {IntoKeyExpr} intoKeyExpr - The key expression to subscribe to 58 | * @param {LivelinessSubscriberOptions=} livelinessSubscriberOpts - options for the liveliness subscriber 59 | * 60 | * @returns Liveliness Subscriber 61 | */ 62 | async declareSubscriber( 63 | intoKeyExpr: IntoKeyExpr, livelinessSubscriberOpts?: LivelinessSubscriberOptions 64 | ): Promise { 65 | let handler = livelinessSubscriberOpts?.handler ?? new FifoChannel(256); 66 | let [callback, drop, receiver] = intoCbDropReceiver(handler); 67 | let keyexpr = new KeyExpr(intoKeyExpr); 68 | let subscriberId = await this.session.declareLivelinessSubscriber( 69 | { 70 | keyexpr, 71 | history: livelinessSubscriberOpts?.history ?? false, 72 | }, 73 | { callback, drop } 74 | ); 75 | 76 | return new Subscriber(this.session, subscriberId, keyexpr, receiver); 77 | } 78 | 79 | /** 80 | * Queries liveliness tokens currently on the network intersecting with specified key expression 81 | * @param intoKeyExpr - key expression to query 82 | * @param livelinessGetOpts - options passed to get operation 83 | * 84 | */ 85 | async get(intoKeyExpr: IntoKeyExpr, livelinessGetOpts?: LivelinessGetOptions): Promise | undefined> { 86 | let handler = livelinessGetOpts?.handler ?? new FifoChannel(256); 87 | let [callback, drop, receiver] = intoCbDropReceiver(handler); 88 | await this.session.livelinessGet( 89 | { 90 | keyexpr: new KeyExpr(intoKeyExpr), 91 | timeoutMs: livelinessGetOpts?.timeout ? Duration.milliseconds.from(livelinessGetOpts.timeout) : DEFAULT_QUERY_TIMEOUT_MS, 92 | }, 93 | { callback, drop } 94 | ); 95 | return receiver; 96 | } 97 | } 98 | 99 | /** A token whose liveliness is tied to the Zenoh [`Session`](Session). 100 | * 101 | * A declared liveliness token will be seen as alive by any other Zenoh 102 | * application in the system that monitors it while the liveliness token 103 | * is not undeclared or dropped, while the Zenoh application that declared 104 | * it is alive (didn't stop or crashed) and while the Zenoh application 105 | * that declared the token has Zenoh connectivity with the Zenoh application 106 | * that monitors it. 107 | */ 108 | export class LivelinessToken { 109 | constructor(private session: SessionInner, private id: number) { } 110 | 111 | async undeclare() { 112 | await this.session.undeclareLivelinessToken(this.id); 113 | } 114 | 115 | async [Symbol.asyncDispose]() { 116 | await this.undeclare(); 117 | } 118 | } -------------------------------------------------------------------------------- /zenoh-ts/src/pubsub.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | import { KeyExpr } from "./key_expr.js"; 15 | import { IntoZBytes, ZBytes } from "./z_bytes.js"; 16 | import { Sample } from "./sample.js"; 17 | import { Encoding, IntoEncoding } from "./encoding.js"; 18 | import { Timestamp } from "./timestamp.js"; 19 | import { ChannelReceiver } from "./channels.js"; 20 | import { SessionInner } from "./session_inner.js"; 21 | import { PublisherDelete, PublisherProperties, PublisherPut } from "./message.js"; 22 | import { CongestionControl, Priority, Reliability } from "./enums.js"; 23 | 24 | 25 | // ███████ ██ ██ ██████ ███████ ██████ ██████ ██ ██████ ███████ ██████ 26 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 27 | // ███████ ██ ██ ██████ ███████ ██ ██████ ██ ██████ █████ ██████ 28 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 29 | // ███████ ██████ ██████ ███████ ██████ ██ ██ ██ ██████ ███████ ██ ██ 30 | 31 | 32 | /** 33 | * Class to represent a Subscriber on Zenoh, 34 | * created via calling `declare_subscriber()` on a `session` 35 | */ 36 | 37 | export class Subscriber { 38 | /** 39 | * @ignore 40 | */ 41 | async [Symbol.asyncDispose]() { 42 | await this.undeclare(); 43 | } 44 | /** 45 | * @ignore 46 | */ 47 | constructor( 48 | private session: SessionInner, 49 | private id: number, 50 | private keyExpr_: KeyExpr, 51 | private receiver_?: ChannelReceiver, 52 | ) { } 53 | 54 | /** 55 | * returns the key expression of an object 56 | * @returns KeyExpr 57 | */ 58 | keyExpr(): KeyExpr { 59 | return this.keyExpr_ 60 | } 61 | /** 62 | * returns a sample receiver for non-callback subscriber, undefined otherwise. 63 | * 64 | * @returns ChannelReceiver | undefined 65 | */ 66 | receiver(): ChannelReceiver | undefined { 67 | return this.receiver_; 68 | } 69 | 70 | /** 71 | * Undeclares a subscriber on the session 72 | * 73 | */ 74 | async undeclare() { 75 | await this.session.undeclareSubscriber(this.id); 76 | } 77 | } 78 | 79 | // ██████ ██ ██ ██████ ██ ██ ███████ ██ ██ ███████ ██████ 80 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 81 | // ██████ ██ ██ ██████ ██ ██ ███████ ███████ █████ ██████ 82 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 83 | // ██ ██████ ██████ ███████ ██ ███████ ██ ██ ███████ ██ ██ 84 | 85 | /** 86 | * @param {IntoEncoding=} encoding - Encoding parameter for Zenoh data 87 | * @param {IntoZBytes=} attachment - optional extra data to send with Payload 88 | */ 89 | export interface PublisherPutOptions { 90 | encoding?: IntoEncoding, 91 | attachment?: IntoZBytes, 92 | timestamp?: Timestamp; 93 | } 94 | 95 | /** 96 | * @param {IntoZBytes=} attachment - optional extra data to send with Payload 97 | */ 98 | export interface PublisherDeleteOptions { 99 | attachment?: IntoZBytes, 100 | timestamp?: Timestamp 101 | } 102 | 103 | /** 104 | * Class that represents a Zenoh Publisher, 105 | * created by calling `Session.declarePublisher()` 106 | */ 107 | export class Publisher { 108 | /** 109 | * @ignore 110 | */ 111 | async [Symbol.asyncDispose]() { 112 | await this.undeclare(); 113 | } 114 | 115 | /** 116 | * @ignore 117 | */ 118 | constructor( 119 | private session: SessionInner, 120 | private publisherId: number, 121 | private properties: PublisherProperties, 122 | ) { } 123 | 124 | /** 125 | * gets the Key Expression from Publisher 126 | * 127 | * @returns {KeyExpr} instance 128 | */ 129 | keyExpr(): KeyExpr { 130 | return this.properties.keyexpr; 131 | } 132 | 133 | /** 134 | * Puts a payload on the publisher associated with this class instance 135 | * 136 | * @param {IntoZBytes} payload 137 | * @param {PublisherPutOptions} putOptions 138 | * 139 | * @returns void 140 | */ 141 | async put( 142 | payload: IntoZBytes, 143 | putOptions?: PublisherPutOptions, 144 | ) { 145 | await this.session.publisherPut( 146 | new PublisherPut( 147 | this.publisherId, 148 | new ZBytes(payload), 149 | putOptions?.encoding ? Encoding.from(putOptions.encoding) : undefined, 150 | putOptions?.attachment ? new ZBytes(putOptions.attachment) : undefined, 151 | putOptions?.timestamp 152 | ) 153 | ); 154 | } 155 | 156 | /** 157 | * get Encoding declared for Publisher 158 | * 159 | * @returns {Encoding} 160 | */ 161 | encoding(): Encoding { 162 | return this.properties.encoding; 163 | } 164 | 165 | /** 166 | * get Priority declared for Publisher 167 | * 168 | * @returns {Priority} 169 | */ 170 | priority(): Priority { 171 | return this.properties.qos.priority; 172 | } 173 | 174 | /** 175 | * get Reliability declared for Publisher 176 | * 177 | * @returns {Reliability} 178 | */ 179 | reliability(): Reliability { 180 | return this.properties.qos.reliability; 181 | } 182 | 183 | /** 184 | * get Congestion Control declared for a Publisher 185 | * 186 | * @returns {CongestionControl} 187 | */ 188 | congestionControl(): CongestionControl { 189 | return this.properties.qos.congestionControl; 190 | } 191 | 192 | /** 193 | * 194 | * executes delete on publisher 195 | * @param {PublisherDeleteOptions=} deleteOptions: Options associated with a publishers delete 196 | * @returns void 197 | */ 198 | async delete(deleteOptions?: PublisherDeleteOptions) { 199 | await this.session.publisherDelete( 200 | new PublisherDelete( 201 | this.publisherId, 202 | deleteOptions?.attachment ? new ZBytes(deleteOptions.attachment) : undefined, 203 | deleteOptions?.timestamp 204 | ) 205 | ) 206 | } 207 | 208 | /** 209 | * undeclares publisher 210 | * 211 | * @returns void 212 | */ 213 | async undeclare() { 214 | await this.session.undeclarePublisher(this.publisherId); 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /zenoh-ts/src/querier.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | import { IntoZBytes, ZBytes } from "./z_bytes.js"; 15 | import { KeyExpr } from "./key_expr.js"; 16 | import { IntoParameters, Parameters, Reply } from "./query.js"; 17 | import { ChannelReceiver, FifoChannel, Handler, intoCbDropReceiver } from "./channels.js"; 18 | import { Encoding, IntoEncoding } from "./encoding.js"; 19 | import { SessionInner } from "./session_inner.js"; 20 | import { CongestionControl, Priority, ReplyKeyExpr } from "./enums.js"; 21 | 22 | /** 23 | * Options for a Querier Get operation 24 | * @prop {IntoParameters=} parameters - Optional query parameters 25 | * @prop {IntoEncoding=} encoding - Encoding type of payload 26 | * @prop {IntoZBytes=} payload - Payload associated with the query 27 | * @prop {IntoZBytes=} attachment - Additional Data sent with the query 28 | * @prop {Handler=} handler - A reply handler 29 | */ 30 | export interface QuerierGetOptions { 31 | parameters?: IntoParameters, 32 | encoding?: IntoEncoding, 33 | payload?: IntoZBytes, 34 | attachment?: IntoZBytes, 35 | handler?: Handler 36 | } 37 | 38 | /** 39 | * Queryable class used to receive Query's from the network and handle Reply's 40 | * created by Session.declare_queryable 41 | */ 42 | export class Querier { 43 | /** 44 | * @ignore 45 | */ 46 | async [Symbol.asyncDispose]() { 47 | await this.undeclare(); 48 | } 49 | 50 | /** 51 | * @ignore 52 | */ 53 | constructor( 54 | private session: SessionInner, 55 | private querierId: number, 56 | private keyExpr_: KeyExpr, 57 | private congestionControl_: CongestionControl, 58 | private priority_: Priority, 59 | private acceptReplies_: ReplyKeyExpr, 60 | ) { } 61 | 62 | /** 63 | * Undeclares Queryable 64 | * @returns void 65 | */ 66 | async undeclare() { 67 | await this.session.undeclareQuerier(this.querierId); 68 | } 69 | 70 | /** 71 | * returns key expression for this Querier 72 | * @returns KeyExpr 73 | */ 74 | keyExpr() { 75 | return this.keyExpr_; 76 | } 77 | 78 | /** 79 | * returns Congestion Control for this Querier 80 | * @returns CongestionControl 81 | */ 82 | congestionControl() { 83 | return this.congestionControl_; 84 | } 85 | 86 | /** 87 | * returns Priority for this Querier 88 | * @returns Priority 89 | */ 90 | priority() { 91 | return this.priority_; 92 | } 93 | 94 | /** 95 | * returns ReplyKeyExpr for this Querier 96 | * @returns ReplyKeyExpr 97 | */ 98 | acceptReplies() { 99 | return this.acceptReplies_; 100 | } 101 | 102 | /** 103 | * Issue a Get request on this querier 104 | * @returns Promise 105 | */ 106 | async get(getOpts?: QuerierGetOptions): Promise | undefined> { 107 | 108 | let handler = getOpts?.handler ?? new FifoChannel(256); 109 | let [callback, drop, receiver] = intoCbDropReceiver(handler); 110 | 111 | await this.session.querierGet( 112 | { 113 | querierId: this.querierId, 114 | parameters: getOpts?.parameters ? new Parameters(getOpts.parameters).toString() : "", 115 | payload: getOpts?.payload ? new ZBytes(getOpts.payload) : undefined, 116 | encoding: getOpts?.encoding ? Encoding.from(getOpts.encoding) : undefined, 117 | attachment: getOpts?.attachment ? new ZBytes(getOpts.attachment) : undefined, 118 | }, 119 | { callback, drop } 120 | ); 121 | return receiver; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /zenoh-ts/src/sample.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { KeyExpr } from "./key_expr.js"; 16 | import { ZBytes } from "./z_bytes.js"; 17 | import { Encoding } from "./encoding.js"; 18 | import { CongestionControl, Priority, SampleKind } from "./enums.js"; 19 | import { Timestamp } from "./timestamp.js"; 20 | 21 | export class Sample { 22 | /** 23 | * @internal 24 | */ 25 | constructor( 26 | private readonly keyexpr_: KeyExpr, 27 | private readonly payload_: ZBytes, 28 | private readonly kind_: SampleKind, 29 | private readonly encoding_: Encoding, 30 | private readonly attachment_: ZBytes | undefined, 31 | private readonly timestamp_: Timestamp | undefined, 32 | private readonly priority_: Priority, 33 | private readonly congestionControl_: CongestionControl, 34 | private readonly express_: boolean, 35 | ) { } 36 | 37 | keyexpr(): KeyExpr { 38 | return this.keyexpr_; 39 | } 40 | payload(): ZBytes { 41 | return this.payload_; 42 | } 43 | kind(): SampleKind { 44 | return this.kind_; 45 | } 46 | encoding(): Encoding { 47 | return this.encoding_; 48 | } 49 | timestamp(): Timestamp | undefined { 50 | return this.timestamp_; 51 | } 52 | congestionControl(): CongestionControl { 53 | return this.congestionControl_; 54 | } 55 | priority(): Priority { 56 | return this.priority_; 57 | } 58 | express(): boolean { 59 | return this.express_; 60 | } 61 | attachment(): ZBytes | undefined { 62 | return this.attachment_; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /zenoh-ts/src/timestamp.ts: -------------------------------------------------------------------------------- 1 | import { ZenohId } from "./zid.js"; 2 | 3 | export class Timestamp { 4 | constructor(private readonly zid: ZenohId, private readonly ntp64: bigint) {} 5 | 6 | getId(): ZenohId { 7 | return this.zid; 8 | } 9 | 10 | /** 11 | * Gets the NTP64 timestamp value as received from Zenoh 12 | */ 13 | getNtp64(): bigint { 14 | return this.ntp64; 15 | } 16 | 17 | /** 18 | * Converts NTP64 timestamp to milliseconds since Unix epoch 19 | * NTP64 format: upper 32 bits = seconds since Unix epoch, lower 32 bits = fractional seconds 20 | */ 21 | getMsSinceUnixEpoch(): number { 22 | // Extract upper 32 bits (seconds since Unix epoch) 23 | const seconds = Number(this.ntp64 >> 32n); 24 | 25 | // Extract lower 32 bits (fractional part in 2^32 units) 26 | const fraction = Number(this.ntp64 & 0xffffffffn); 27 | 28 | // Convert fractional part to milliseconds 29 | // fraction / 2^32 * 1000 = fraction * 1000 / 2^32 30 | const millisecondFraction = (fraction * 1000) / 4294967296; // 2^32 = 4294967296 31 | 32 | return seconds * 1000 + millisecondFraction; 33 | } 34 | 35 | asDate(): Date { 36 | return new Date(this.getMsSinceUnixEpoch()); 37 | } 38 | } -------------------------------------------------------------------------------- /zenoh-ts/src/z_bytes.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | /** 16 | * Union Type to convert various primitives and containers into ZBytes 17 | */ 18 | export type IntoZBytes = 19 | | ZBytes 20 | | Uint8Array 21 | | String 22 | | string; 23 | 24 | /** 25 | * Class to represent an Array of Bytes received from Zenoh 26 | */ 27 | export class ZBytes { 28 | private buffer_: Uint8Array; 29 | 30 | /** 31 | * new function to create a ZBytes 32 | * 33 | * @returns ZBytes 34 | */ 35 | constructor(bytes: IntoZBytes) { 36 | if (bytes instanceof ZBytes) { 37 | this.buffer_ = bytes.buffer_; 38 | } else if (bytes instanceof String || typeof bytes === "string") { 39 | const encoder = new TextEncoder(); 40 | const encoded = encoder.encode(bytes.toString()); 41 | this.buffer_ = encoded; 42 | } else { 43 | this.buffer_ = Uint8Array.from(bytes); 44 | } 45 | } 46 | 47 | /** 48 | * returns the length of the ZBytes buffer 49 | * 50 | * @returns number 51 | */ 52 | public len(): number { 53 | return this.buffer_.length; 54 | } 55 | 56 | /** 57 | * returns if the ZBytes Buffer is empty 58 | * 59 | * @returns boolean 60 | */ 61 | public isEmpty(): boolean { 62 | return this.buffer_.length == 0; 63 | } 64 | 65 | /** 66 | * returns an empty ZBytes buffer 67 | * 68 | * @returns ZBytes 69 | */ 70 | public empty(): ZBytes { 71 | return new ZBytes(new Uint8Array()); 72 | } 73 | 74 | /** 75 | * returns the underlying Uint8Array buffer 76 | * 77 | * @returns Uint8Array 78 | */ 79 | public toBytes(): Uint8Array { 80 | return this.buffer_ 81 | } 82 | 83 | /** 84 | * decodes the underlying Uint8Array buffer as UTF-8 string 85 | * 86 | * @returns string 87 | */ 88 | public toString(): string { 89 | let decoder = new TextDecoder(); 90 | return decoder.decode(this.buffer_) 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /zenoh-ts/src/zid.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | export class ZenohId { 16 | private static KEY = '0123456789abcdef'; 17 | constructor(private readonly zid: Uint8Array) { 18 | if (zid.length != 16 ) { 19 | throw new Error("Zid should contain exactly 16 bytes"); 20 | } 21 | } 22 | 23 | toString() { 24 | let out: string = ""; 25 | for (let i = this.zid.length - 1; i >= 0; --i) { 26 | let b = this.zid[i] as number; 27 | out += ZenohId.KEY[b >> 4]; 28 | out += ZenohId.KEY[b & 15]; 29 | } 30 | return out; 31 | } 32 | 33 | toLeBytes() { 34 | return this.zid; 35 | } 36 | } -------------------------------------------------------------------------------- /zenoh-ts/tests/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "ES2020", 5 | "DOM", 6 | "DOM.Iterable", 7 | "deno.ns" 8 | ], 9 | "strict": true, 10 | "noUnusedLocals": false, 11 | "noUnusedParameters": true, 12 | "noFallthroughCasesInSwitch": true 13 | }, 14 | "unstable": [ 15 | "sloppy-imports" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /zenoh-ts/tests/deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5", 3 | "specifiers": { 4 | "npm:@types/uuid@10": "10.0.0", 5 | "npm:typescript@^5.2.2": "5.7.3" 6 | }, 7 | "npm": { 8 | "@types/uuid@10.0.0": { 9 | "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" 10 | }, 11 | "typescript@5.7.3": { 12 | "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", 13 | "bin": true 14 | } 15 | }, 16 | "remote": { 17 | "https://deno.land/std@0.192.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 18 | "https://deno.land/std@0.192.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 19 | "https://deno.land/std@0.192.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 20 | "https://deno.land/std@0.192.0/testing/asserts.ts": "e16d98b4d73ffc4ed498d717307a12500ae4f2cbe668f1a215632d19fcffc22f" 21 | }, 22 | "workspace": { 23 | "packageJson": { 24 | "dependencies": [ 25 | "npm:@types/uuid@10", 26 | "npm:typescript@^5.2.2" 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /zenoh-ts/tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenoh-ts-tests", 3 | "private": false, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "license": "EPL-2.0", 7 | "scripts": { 8 | "clean": "rm -rf ./node_modules", 9 | "verify": "deno check src", 10 | "start": "./scripts/start.sh" 11 | }, 12 | "devDependencies": { 13 | "@types/uuid": "^10.0.0", 14 | "typescript": "^5.2.2" 15 | }, 16 | "dependencies": { 17 | "@eclipse-zenoh/zenoh-ts": "file:.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /zenoh-ts/tests/scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ORIGINAL_DIR="$(pwd)" 4 | SCRIPTDIR="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)" 5 | cd "$SCRIPTDIR/.." 6 | 7 | if [ "$1" = "" ]; then 8 | echo 9 | echo "Arguments: test_name|ALL [COVERAGE]" 10 | echo " test_name: name of the test to run or ALL to run all tests" 11 | echo " COVERAGE: generate coverage report" 12 | echo 13 | echo "Available tests:" 14 | ls src/*.ts | sed -e "s/src\///" -e "s/\.ts//" | sort 15 | echo 16 | echo "Available performance tests:" 17 | ls src/bench/*.ts | sed -e "s/src\///" -e "s/\.ts//" | sort 18 | echo 19 | else 20 | EXIT_CODE=0 21 | COVERAGE_OPTS="" 22 | if [ "$2" = "COVERAGE" ]; then 23 | COVERAGE_OPTS="--coverage=coverage_profile" 24 | fi 25 | 26 | if [ "$1" = "ALL" ]; then 27 | deno test -A $COVERAGE_OPTS src/*.ts 28 | EXIT_CODE=$? 29 | if [ $EXIT_CODE -eq 0 ]; then 30 | deno test -A $COVERAGE_OPTS src/bench/*.ts 31 | EXIT_CODE=$? 32 | fi 33 | else 34 | TEST_PATH="src/$1.ts" 35 | if [ -f "$TEST_PATH" ]; then 36 | if [[ "$TEST_PATH" == *"/bench/"* ]]; then 37 | deno bench -A $COVERAGE_OPTS "$TEST_PATH" 38 | else 39 | deno test -A $COVERAGE_OPTS "$TEST_PATH" 40 | fi 41 | EXIT_CODE=$? 42 | else 43 | echo "Test file not found: $TEST_PATH" 44 | EXIT_CODE=1 45 | fi 46 | fi 47 | fi 48 | 49 | cd "$ORIGINAL_DIR" 50 | exit ${EXIT_CODE:-0} -------------------------------------------------------------------------------- /zenoh-ts/tests/src/bench/z_serialization.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 ZettaScale Technology 2 | // 3 | // This program and the accompanying materials are made available under the 4 | // terms of the Eclipse Public License 2.0 which is available at 5 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 6 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 7 | // 8 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 9 | // 10 | // Contributors: 11 | // ZettaScale Zenoh Team, 12 | // 13 | 14 | import { 15 | zserialize, 16 | zdeserialize, 17 | ZS, 18 | ZD 19 | } from "@eclipse-zenoh/zenoh-ts/ext"; 20 | import { ZBytes } from "@eclipse-zenoh/zenoh-ts"; 21 | /** 22 | * Configuration for the performance tests 23 | */ 24 | const TEST_CONFIG = { 25 | arraySize: 10000, // Size of test arrays 26 | maxStringLength: 8, // Length of test strings is randomized but capped 27 | }; 28 | 29 | /** 30 | * Test case definition for a specific type 31 | */ 32 | interface TestCase { 33 | name: string; 34 | data: T; 35 | serialize(value: T): ZBytes; 36 | deserialize(bytes: ZBytes): T; 37 | } 38 | 39 | /** 40 | * Helper function to generate a random string 41 | */ 42 | function stringGen() { 43 | const len = Math.floor(Math.random() * TEST_CONFIG.maxStringLength) + 1; 44 | return Array.from({ length: len }, () => 45 | String.fromCharCode(Math.floor(Math.random() * 26) + 97)).join(''); 46 | } 47 | 48 | /** 49 | * Create test cases for each data type 50 | */ 51 | function createTestCases(): TestCase[] { 52 | return [ 53 | { 54 | name: "uint8Array", 55 | data: new Uint8Array(TEST_CONFIG.arraySize) 56 | .map(() => Math.floor(Math.random() * 256)), 57 | serialize: (v: Uint8Array) => zserialize(v, ZS.uint8array()), 58 | deserialize: (b: ZBytes) => zdeserialize(ZD.uint8array(), b) as Uint8Array, 59 | }, 60 | { 61 | name: "uint16Array", 62 | data: new Uint16Array(TEST_CONFIG.arraySize) 63 | .map(() => Math.floor(Math.random() * 65536)), 64 | serialize: (v: Uint16Array) => zserialize(v, ZS.uint16array()), 65 | deserialize: (b: ZBytes) => zdeserialize(ZD.uint16array(), b) as Uint16Array, 66 | }, 67 | { 68 | name: "uint32Array", 69 | data: new Uint32Array(TEST_CONFIG.arraySize) 70 | .map(() => Math.floor(Math.random() * 4294967296)), 71 | serialize: (v: Uint32Array) => zserialize(v, ZS.uint32array()), 72 | deserialize: (b: ZBytes) => zdeserialize(ZD.uint32array(), b) as Uint32Array, 73 | }, 74 | { 75 | name: "bigUint64Array", 76 | data: new BigUint64Array(TEST_CONFIG.arraySize) 77 | .fill(BigInt(Number.MAX_SAFE_INTEGER)), 78 | serialize: (v: BigUint64Array) => zserialize(v, ZS.biguint64array()), 79 | deserialize: (b: ZBytes) => zdeserialize(ZD.biguint64array(), b) as BigUint64Array, 80 | }, 81 | { 82 | name: "int8Array", 83 | data: new Int8Array(TEST_CONFIG.arraySize) 84 | .map(() => Math.floor(Math.random() * 256) - 128), 85 | serialize: (v: Int8Array) => zserialize(v, ZS.int8array()), 86 | deserialize: (b: ZBytes) => zdeserialize(ZD.int8array(), b) as Int8Array, 87 | }, 88 | { 89 | name: "int16Array", 90 | data: new Int16Array(TEST_CONFIG.arraySize) 91 | .map(() => Math.floor(Math.random() * 65536) - 32768), 92 | serialize: (v: Int16Array) => zserialize(v, ZS.int16array()), 93 | deserialize: (b: ZBytes) => zdeserialize(ZD.int16array(), b) as Int16Array, 94 | }, 95 | { 96 | name: "int32Array", 97 | data: new Int32Array(TEST_CONFIG.arraySize) 98 | .map(() => Math.floor(Math.random() * 4294967296) - 2147483648), 99 | serialize: (v: Int32Array) => zserialize(v, ZS.int32array()), 100 | deserialize: (b: ZBytes) => zdeserialize(ZD.int32array(), b) as Int32Array, 101 | }, 102 | { 103 | name: "bigInt64Array", 104 | data: new BigInt64Array(TEST_CONFIG.arraySize) 105 | .fill(BigInt("-9223372036854775808")), 106 | serialize: (v: BigInt64Array) => zserialize(v, ZS.bigint64array()), 107 | deserialize: (b: ZBytes) => zdeserialize(ZD.bigint64array(), b) as BigInt64Array, 108 | }, 109 | { 110 | name: "float32Array", 111 | data: new Float32Array(TEST_CONFIG.arraySize) 112 | .map(() => Math.random() * 1000), 113 | serialize: (v: Float32Array) => zserialize(v, ZS.float32array()), 114 | deserialize: (b: ZBytes) => zdeserialize(ZD.float32array(), b) as Float32Array, 115 | }, 116 | { 117 | name: "float64Array", 118 | data: new Float64Array(TEST_CONFIG.arraySize) 119 | .map(() => Math.random() * 1000), 120 | serialize: (v: Float64Array) => zserialize(v, ZS.float64array()), 121 | deserialize: (b: ZBytes) => zdeserialize(ZD.float64array(), b) as Float64Array, 122 | }, 123 | { 124 | name: "strings", 125 | data: Array.from({ length: TEST_CONFIG.arraySize }, stringGen), 126 | serialize: (value: string[]) => zserialize(value, ZS.array(ZS.string())), 127 | deserialize: (bytes: ZBytes) => zdeserialize(ZD.array(ZD.string()), bytes) as string[], 128 | }, 129 | { 130 | name: "numberMap", 131 | data: new Map( 132 | Array.from({ length: TEST_CONFIG.arraySize }, (_, i) => [i, i] as [number, number]) 133 | ), 134 | serialize: (value: Map) => zserialize(value, ZS.map(ZS.number(), ZS.number())), 135 | deserialize: (bytes: ZBytes) => zdeserialize(ZD.map(ZD.number(), ZD.number()), bytes) as Map, 136 | } 137 | ]; 138 | } 139 | 140 | // Create test cases once to reuse across benchmarks 141 | const testCases = createTestCases(); 142 | 143 | // Run serialization benchmarks 144 | for (const testCase of testCases) { 145 | Deno.bench({ 146 | name: `serialize ${testCase.name}`, 147 | fn: () => { 148 | testCase.serialize(testCase.data); 149 | } 150 | }); 151 | } 152 | 153 | // Run deserialization benchmarks 154 | for (const testCase of testCases) { 155 | // Pre-serialize data once for deserialization benchmarks 156 | const serializedData = testCase.serialize(testCase.data); 157 | Deno.bench({ 158 | name: `deserialize ${testCase.name}`, 159 | fn: () => { 160 | testCase.deserialize(serializedData); 161 | } 162 | }); 163 | } 164 | -------------------------------------------------------------------------------- /zenoh-ts/tests/src/z_api_pub_sub.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Config, Session, Subscriber, Sample } from "@eclipse-zenoh/zenoh-ts"; 16 | import { assertEquals } from "https://deno.land/std@0.192.0/testing/asserts.ts"; 17 | 18 | function sleep(ms: number) { 19 | return new Promise((resolve) => setTimeout(resolve, ms)); 20 | } 21 | 22 | Deno.test("API - Put/Subscribe", async () => { 23 | let session1: Session | undefined; 24 | let session2: Session | undefined; 25 | let subscriber: Subscriber | undefined; 26 | 27 | try { 28 | // Open two sessions 29 | session1 = await Session.open(new Config("ws/127.0.0.1:10000")); 30 | session2 = await Session.open(new Config("ws/127.0.0.1:10000")); 31 | 32 | // Delay to ensure sessions are ready 33 | await sleep(100); 34 | 35 | const receivedMessages: Array<{ key: string; payload: string }> = []; 36 | 37 | // Declare a subscriber on session2 38 | subscriber = await session2.declareSubscriber("zenoh/test", { 39 | handler: (sample: Sample) => { 40 | receivedMessages.push({ 41 | key: sample.keyexpr().toString(), 42 | payload: sample.payload().toString(), 43 | }); 44 | }, 45 | }); 46 | 47 | // Delay to ensure subscriber is ready 48 | await sleep(100); 49 | 50 | // Publish messages using session1 51 | await session1.put("zenoh/test", "first"); 52 | await session1.put("zenoh/test", "second"); 53 | 54 | // Delay to ensure messages are received 55 | await sleep(100); 56 | 57 | assertEquals(receivedMessages.length, 2, "Expected 2 messages"); 58 | assertEquals(receivedMessages[0].key, "zenoh/test", "Key mismatch for first message"); 59 | assertEquals(receivedMessages[0].payload, "first", "Payload mismatch for first message"); 60 | assertEquals(receivedMessages[1].key, "zenoh/test", "Key mismatch for second message"); 61 | assertEquals(receivedMessages[1].payload, "second", "Payload mismatch for second message"); 62 | } finally { 63 | // Cleanup in reverse order of creation 64 | if (subscriber) { 65 | await subscriber.undeclare(); 66 | } 67 | if (session2) { 68 | await session2.close(); 69 | } 70 | if (session1) { 71 | await session1.close(); 72 | } 73 | await sleep(100); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /zenoh-ts/tests/src/z_channels.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { FifoChannel, ChannelSender, ChannelReceiver, TryReceivedKind, ChannelState } from "@eclipse-zenoh/zenoh-ts"; 16 | import { RingChannel } from "@eclipse-zenoh/zenoh-ts"; 17 | import { assertEquals } from "https://deno.land/std@0.192.0/testing/asserts.ts"; 18 | 19 | Deno.test("Channels - FIFO", async () => { 20 | const channel = new FifoChannel(3); 21 | const [sender, receiver] = channel.intoSenderReceiverPair(); 22 | 23 | assertEquals(receiver.state(), ChannelState.empty, "Channel is not empty"); 24 | 25 | const res = receiver.tryReceive(); 26 | assertEquals(res.kind, TryReceivedKind.notReceived, "Channel is not empty"); 27 | 28 | sender.send(1); 29 | sender.send(2); 30 | sender.send(3); 31 | sender.send(4); 32 | assertEquals(receiver.state(), ChannelState.data, "Channel is empty"); 33 | assertEquals(await receiver.receive(), 1, "Received incorrect value"); 34 | assertEquals(receiver.state(), ChannelState.data, "Channel is empty"); 35 | assertEquals(await receiver.receive(), 2, "Received incorrect value"); 36 | assertEquals(receiver.state(), ChannelState.data, "Channel is empty"); 37 | 38 | const res2 = receiver.tryReceive(); 39 | assertEquals(res2.kind, TryReceivedKind.value, "Received incorrect value"); 40 | if (res2.kind == TryReceivedKind.value) { 41 | assertEquals(res2.value, 3, "Received incorrect value"); 42 | } 43 | 44 | assertEquals(receiver.state(), ChannelState.empty, "Channel is not empty"); 45 | 46 | const res3 = receiver.tryReceive(); 47 | assertEquals(res3.kind, TryReceivedKind.notReceived, "Channel is not empty"); 48 | 49 | sender.send(1); 50 | sender.send(2); 51 | sender.send(3); 52 | sender.send(4); 53 | sender.close(); 54 | 55 | const out: number[] = []; 56 | 57 | for await (const n of receiver) { 58 | out.push(n); 59 | } 60 | assertEquals(out, [1, 2, 3], "Received in correct data"); 61 | assertEquals(receiver.state(), ChannelState.close, "Channel is not closed"); 62 | }); 63 | 64 | Deno.test("Channels - Ring", async () => { 65 | const channel = new RingChannel(3); 66 | const [sender, receiver] = channel.intoSenderReceiverPair(); 67 | 68 | assertEquals(receiver.state(), ChannelState.empty, "Channel is not empty"); 69 | 70 | const res = receiver.tryReceive(); 71 | assertEquals(res.kind, TryReceivedKind.notReceived, "Channel is not empty"); 72 | 73 | sender.send(1); 74 | sender.send(2); 75 | sender.send(3); 76 | sender.send(4); 77 | assertEquals(receiver.state(), ChannelState.data, "Channel is empty"); 78 | assertEquals(await receiver.receive(), 2, "Received incorrect value"); 79 | assertEquals(receiver.state(), ChannelState.data, "Channel is empty"); 80 | assertEquals(await receiver.receive(), 3, "Received incorrect value"); 81 | assertEquals(receiver.state(), ChannelState.data, "Channel is empty"); 82 | 83 | const res2 = receiver.tryReceive(); 84 | assertEquals(res2.kind, TryReceivedKind.value, "Received incorrect value"); 85 | if (res2.kind == TryReceivedKind.value) { 86 | assertEquals(res2.value, 4, "Received incorrect value"); 87 | } 88 | 89 | assertEquals(receiver.state(), ChannelState.empty, "Channel is not empty"); 90 | 91 | const res3 = receiver.tryReceive(); 92 | assertEquals(res3.kind, TryReceivedKind.notReceived, "Channel is not empty"); 93 | 94 | sender.send(1); 95 | sender.send(2); 96 | sender.send(3); 97 | sender.send(4); 98 | sender.close(); 99 | 100 | const out: number[] = []; 101 | 102 | for await (const n of receiver) { 103 | out.push(n); 104 | } 105 | assertEquals(out, [2, 3, 4], "Received in correct data"); 106 | assertEquals(receiver.state(), ChannelState.close, "Channel is not closed"); 107 | }); -------------------------------------------------------------------------------- /zenoh-ts/tests/src/z_encoding.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { assertEquals } from "https://deno.land/std@0.192.0/testing/asserts.ts"; 16 | import { Encoding } from "@eclipse-zenoh/zenoh-ts"; 17 | 18 | Deno.test("Encoding - Basic", () => { 19 | assertEquals(Encoding.default(), Encoding.ZENOH_BYTES); 20 | assertEquals(Encoding.default().toString(), "zenoh/bytes"); 21 | assertEquals(Encoding.default().withSchema("foobar").toString(), "zenoh/bytes;foobar"); 22 | assertEquals(Encoding.TEXT_PLAIN.toString(), "text/plain"); 23 | assertEquals(Encoding.TEXT_PLAIN.withSchema("charset=utf-8").toString(), "text/plain;charset=utf-8"); 24 | assertEquals(Encoding.fromString("text/plain;charset=utf-8").withSchema("charset=ascii").toString(), "text/plain;charset=ascii"); 25 | assertEquals(Encoding.fromString("zenoh/bytes"), Encoding.ZENOH_BYTES); 26 | assertEquals(Encoding.fromString("zenoh/bytes;foobar").toString(), "zenoh/bytes;foobar"); 27 | assertEquals(Encoding.fromString("custom/encoding").toString(), "custom/encoding"); 28 | assertEquals(Encoding.fromString("custom/encoding;foobar"), Encoding.fromString("custom/encoding").withSchema("foobar")); 29 | }); -------------------------------------------------------------------------------- /zenoh-ts/tests/src/z_keyexpr.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Config, Session, KeyExpr } from "@eclipse-zenoh/zenoh-ts"; 16 | import { assertEquals, assert } from "https://deno.land/std@0.192.0/testing/asserts.ts"; 17 | 18 | function sleep(ms: number) { 19 | return new Promise((resolve) => setTimeout(resolve, ms)); 20 | } 21 | 22 | Deno.test("KeyExpr - Basic", () => { 23 | const foo = new KeyExpr("FOO"); 24 | assert(foo.toString() === "FOO", "KeyExpr string representation mismatch"); 25 | }); 26 | 27 | Deno.test("KeyExpr - Canonize", () => { 28 | const nonСanon = "a/**/**/c"; 29 | const canon = "a/**/c"; 30 | 31 | // Test autocanonization 32 | const kOk = KeyExpr.autocanonize(nonСanon); 33 | assertEquals(kOk.toString(), canon, "Canonization failed"); 34 | 35 | // Verify that canonized expression remains unchanged when canonized again 36 | const kOk2 = KeyExpr.autocanonize(canon); 37 | assertEquals(kOk2.toString(), canon, "Re-canonization changed canonical form"); 38 | }); 39 | 40 | Deno.test("KeyExpr - Concat", () => { 41 | const foo = new KeyExpr("FOO"); 42 | const foobar = foo.concat("BAR"); 43 | assertEquals(foobar.toString(), "FOOBAR", "Concatenation failed"); 44 | }); 45 | 46 | Deno.test("KeyExpr - Join", () => { 47 | const foo = new KeyExpr("FOO"); 48 | const bar = new KeyExpr("BAR"); 49 | const foobar = foo.join(bar); 50 | assertEquals(foobar.toString(), "FOO/BAR", "Join failed"); 51 | }); 52 | 53 | Deno.test("KeyExpr - Equals", () => { 54 | const foo = new KeyExpr("FOO"); 55 | const foo2 = new KeyExpr("FOO"); 56 | const bar = new KeyExpr("BAR"); 57 | 58 | assert(foo.toString() !== bar.toString(), "Expected foo != bar"); 59 | assert(foo.toString() === foo.toString(), "Expected foo == foo"); 60 | assert(foo.toString() === foo2.toString(), "Expected foo == foo2"); 61 | assertEquals(foo.toString(), "FOO", "Expected foo == 'FOO'"); 62 | assert(foo.toString() !== "BAR", "Expected foo != 'BAR'"); 63 | }); 64 | 65 | Deno.test("KeyExpr - Includes", () => { 66 | const foostar = new KeyExpr("FOO/*"); 67 | const foobar = new KeyExpr("FOO/BAR"); 68 | assert(foostar.includes(foobar), "Expected FOO/* to include FOO/BAR"); 69 | assert(!foobar.includes(foostar), "Expected FOO/BAR to not include FOO/*"); 70 | }); 71 | 72 | Deno.test("KeyExpr - Intersects", () => { 73 | const foostar = new KeyExpr("FOO/*"); 74 | const foobar = new KeyExpr("FOO/BAR"); 75 | const starbuz = new KeyExpr("*/BUZ"); 76 | const foobuz = new KeyExpr("FOO/BUZ"); 77 | 78 | assert(foostar.intersects(foobar), "Expected FOO/* to intersect with FOO/BAR"); 79 | assert(!starbuz.intersects(foobar), "Expected */BUZ to not intersect with FOO/BAR"); 80 | assert(foobuz.intersects(starbuz), "Expected FOO/BUZ to intersect with */BUZ"); 81 | assert(starbuz.intersects(foobuz), "Expected */BUZ to intersect with FOO/BUZ"); 82 | }); 83 | 84 | Deno.test("KeyExpr - Declare", async () => { 85 | const session = await Session.open(new Config("ws/127.0.0.1:10000")); 86 | try { 87 | const foobar = new KeyExpr("FOO/BAR"); 88 | const foostar = new KeyExpr("FOO/*"); 89 | const declared = session.declareKeyexpr(foobar); 90 | 91 | assertEquals(declared.toString(), "FOO/BAR", "Declared keyexpr mismatch"); 92 | assertEquals(declared.toString(), foobar.toString(), "Declared keyexpr != foobar"); 93 | assert(foostar.includes(declared), "Expected FOO/* to include declared"); 94 | assert(declared.intersects(foobar), "Expected declared to intersect with FOO/BAR"); 95 | } finally { 96 | await session.close(); 97 | await sleep(100); 98 | } 99 | }); -------------------------------------------------------------------------------- /zenoh-ts/tests/src/z_liveliness.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | import { Config, Session, Sample, KeyExpr, SampleKind } from "@eclipse-zenoh/zenoh-ts"; 16 | import { assertEquals, assert } from "https://deno.land/std@0.192.0/testing/asserts.ts"; 17 | 18 | function sleep(ms: number) { 19 | return new Promise((resolve) => setTimeout(resolve, ms)); 20 | } 21 | 22 | type Token = { undeclare(): Promise }; 23 | type Subscriber = { undeclare(): Promise }; 24 | 25 | Deno.test("Liveliness - Token Get", async () => { 26 | let session1: Session | undefined; 27 | let session2: Session | undefined; 28 | let token: Token | undefined; 29 | 30 | try { 31 | // Open two sessions 32 | session1 = await Session.open(new Config("ws/127.0.0.1:10000")); 33 | session2 = await Session.open(new Config("ws/127.0.0.1:10000")); 34 | 35 | // Delay to ensure sessions are ready 36 | await sleep(1000); 37 | 38 | const ke = new KeyExpr("zenoh/liveliness/test/*"); 39 | const tokenKe = new KeyExpr("zenoh/liveliness/test/1"); 40 | 41 | // Declare a token on session1 42 | token = await session1.liveliness().declareToken(tokenKe); 43 | 44 | // Delay to ensure token is declared 45 | await sleep(500); 46 | 47 | // Get liveliness on session2 48 | const receiver = await session2.liveliness().get(ke); 49 | if (!receiver) { 50 | throw new Error("Failed to get liveliness receiver"); 51 | } 52 | let reply = await receiver.receive(); 53 | 54 | const result = reply.result(); 55 | // Check that the result is a Sample and not a ReplyError 56 | if (result instanceof Sample) { 57 | assertEquals(result.keyexpr().toString(), "zenoh/liveliness/test/1", "Key mismatch for liveliness token"); 58 | } else { 59 | throw new Error("Expected result to be a Sample, got ReplyError"); 60 | } 61 | 62 | await token.undeclare(); 63 | await sleep(100); 64 | 65 | const receiver2 = await session2.liveliness().get(ke); 66 | if (!receiver2) { 67 | throw new Error("Failed to get liveliness receiver"); 68 | } 69 | try { 70 | reply = await receiver2.receive(); 71 | assert(false, "Received reply on undeclared token"); 72 | } catch { 73 | // we should correctly fail to receive reply on undeclared token 74 | } 75 | } finally { 76 | // Clean up all resources even if test fails 77 | if (token) await token.undeclare().catch(() => {}); 78 | if (session1) await session1.close(); 79 | if (session2) await session2.close(); 80 | await sleep(100); 81 | } 82 | }); 83 | 84 | Deno.test("Liveliness - Subscriber", async () => { 85 | let session1: Session | undefined; 86 | let session2: Session | undefined; 87 | let subscriber: Subscriber | undefined; 88 | let token1: Token | undefined; 89 | let token2: Token | undefined; 90 | 91 | try { 92 | // Open two sessions 93 | session1 = await Session.open(new Config("ws/127.0.0.1:10000")); 94 | session2 = await Session.open(new Config("ws/127.0.0.1:10000")); 95 | 96 | // Delay to ensure sessions are ready 97 | await sleep(1000); 98 | 99 | const ke = new KeyExpr("zenoh/liveliness/test/*"); 100 | const tokenKe1 = new KeyExpr("zenoh/liveliness/test/1"); 101 | const tokenKe2 = new KeyExpr("zenoh/liveliness/test/2"); 102 | 103 | const putTokens: Set = new Set(); 104 | const deleteTokens: Set = new Set(); 105 | 106 | // Declare a subscriber on session1 107 | subscriber = await session1.liveliness().declareSubscriber(ke, { 108 | handler: (sample: Sample) => { 109 | if (sample.kind() === SampleKind.PUT) { 110 | putTokens.add(sample.keyexpr().toString()); 111 | } else if (sample.kind() === SampleKind.DELETE) { 112 | deleteTokens.add(sample.keyexpr().toString()); 113 | } 114 | }, 115 | history: true 116 | }); 117 | 118 | // Delay to ensure subscriber is ready 119 | await sleep(1000); 120 | 121 | // Declare tokens on session2 122 | token1 = await session2.liveliness().declareToken(tokenKe1); 123 | token2 = await session2.liveliness().declareToken(tokenKe2); 124 | 125 | // Delay to ensure tokens are declared 126 | await sleep(1000); 127 | 128 | assertEquals(putTokens.size, 2, "Expected 2 PUT tokens"); 129 | assert(putTokens.has("zenoh/liveliness/test/1"), "Expected token 1 in PUT set"); 130 | assert(putTokens.has("zenoh/liveliness/test/2"), "Expected token 2 in PUT set"); 131 | 132 | // Undeclare first token 133 | await token1.undeclare(); 134 | 135 | // Delay to ensure token is undeclared 136 | await sleep(1000); 137 | 138 | assertEquals(deleteTokens.size, 1, "Expected 1 DELETE token"); 139 | assert(deleteTokens.has("zenoh/liveliness/test/1"), "Expected token 1 in DELETE set"); 140 | 141 | // Undeclare second token 142 | await token2.undeclare(); 143 | 144 | // Delay to ensure token is undeclared 145 | await sleep(1000); 146 | 147 | assertEquals(deleteTokens.size, 2, "Expected 2 DELETE tokens"); 148 | assert(deleteTokens.has("zenoh/liveliness/test/2"), "Expected token 2 in DELETE set"); 149 | 150 | } finally { 151 | // Ensure everything is cleaned up even if test fails 152 | if (subscriber) await subscriber.undeclare().catch(() => {}); 153 | if (token1) await token1.undeclare().catch(() => {}); 154 | if (session1) await session1.close(); 155 | if (session2) await session2.close(); 156 | await sleep(100); 157 | } 158 | }); -------------------------------------------------------------------------------- /zenoh-ts/tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "baseUrl": ".", 16 | "paths": { 17 | "@eclipse-zenoh/zenoh-ts": ["../src/index.ts"], 18 | "@eclipse-zenoh/zenoh-ts/*": ["../src/*"] 19 | }, 20 | 21 | /* Linting */ 22 | "strict": true, 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": true, 25 | "noFallthroughCasesInSwitch": true 26 | }, 27 | "include": ["src"] 28 | } 29 | -------------------------------------------------------------------------------- /zenoh-ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | { 15 | "compilerOptions": { 16 | "module": "esnext", 17 | "target": "es2020", 18 | "moduleResolution": "node", 19 | "strict": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noImplicitOverride": true, 22 | "noPropertyAccessFromIndexSignature": true, 23 | "noUncheckedIndexedAccess": true, 24 | "noUnusedParameters": true, 25 | "experimentalDecorators": true, 26 | "noUnusedLocals": true, 27 | "sourceMap": true, 28 | "outDir": "./dist", 29 | "emitDeclarationOnly": false, 30 | "baseUrl": ".", 31 | "paths": { 32 | "*": [ 33 | "src/types/*" 34 | ], 35 | }, 36 | "lib": [ 37 | "DOM", 38 | "ES2021" 39 | ], 40 | "declaration": true, 41 | "esModuleInterop": true, 42 | "allowJs": true, 43 | "noImplicitAny": true, 44 | "noImplicitReturns": true 45 | }, 46 | "include": [ 47 | "./src/**/*.d.ts", 48 | "./src/**/*.ts", 49 | "./src/**/*.js" 50 | ] 51 | } -------------------------------------------------------------------------------- /zenoh-ts/webpack.config.js: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 ZettaScale Technology 3 | // 4 | // This program and the accompanying materials are made available under the 5 | // terms of the Eclipse Public License 2.0 which is available at 6 | // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 7 | // which is available at https://www.apache.org/licenses/LICENSE-2.0. 8 | // 9 | // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 10 | // 11 | // Contributors: 12 | // ZettaScale Zenoh Team, 13 | // 14 | 15 | // const path = require('path'); 16 | import path from "path"; 17 | import { fileURLToPath } from "url"; 18 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 19 | 20 | export default { 21 | mode: "production", 22 | // entry: './esm/index.js', 23 | entry: "./src/index.ts", 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.tsx?$/, 28 | use: "ts-loader", 29 | exclude: /node_modules/, 30 | }, 31 | ], 32 | }, 33 | resolve: { 34 | extensions: [".tsx", ".ts", ".js", ".wasm"], 35 | }, 36 | output: { 37 | filename: "bundle.js", 38 | path: path.resolve(__dirname, "dist"), 39 | }, 40 | }; 41 | --------------------------------------------------------------------------------