mirror of
https://github.com/extism/extism.git
synced 2026-01-11 23:08:06 -05:00
Compare commits
1 Commits
test-java-
...
fix-php-sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4eb1da47b |
22
.github/actions/extism/action.yml
vendored
22
.github/actions/extism/action.yml
vendored
@@ -7,24 +7,12 @@ runs:
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
- name: Download libextism
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Cache Rust environment
|
||||
uses: Swatinem/rust-cache@v1
|
||||
- name: Cache libextism
|
||||
id: cache-libextism
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-libextism-${{ hashFiles('runtime/**') }}-${{ hashFiles('manifest/**') }}-${{ hashFiles('libextism/**') }}
|
||||
- name: Build
|
||||
if: steps.cache-libextism.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: cargo build --release -p libextism
|
||||
name: libextism-${{ matrix.os }}
|
||||
- name: Install extism shared library
|
||||
shell: bash
|
||||
run: |
|
||||
sudo make install
|
||||
sudo cp libextism.* /usr/local/lib
|
||||
sudo cp runtime/extism.h /usr/local/include
|
||||
36
.github/workflows/browser-ci.yml
vendored
36
.github/workflows/browser-ci.yml
vendored
@@ -1,36 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-node.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- browser/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Browser CI
|
||||
|
||||
jobs:
|
||||
node:
|
||||
name: Browser
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Node env
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Test Browser Runtime
|
||||
run: |
|
||||
cd browser
|
||||
npm i
|
||||
npm run test
|
||||
39
.github/workflows/ci-cpp.yml
vendored
39
.github/workflows/ci-cpp.yml
vendored
@@ -1,39 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-cpp.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- cpp/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: C++ CI
|
||||
|
||||
jobs:
|
||||
cpp:
|
||||
name: C++
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Install C++ SDK deps
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
brew install jsoncpp googletest pkg-config
|
||||
- name: Install C++ SDK deps
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
sudo apt-get install g++ libjsoncpp-dev libgtest-dev pkg-config
|
||||
- name: Run C++ tests
|
||||
run: |
|
||||
cd cpp
|
||||
LD_LIBRARY_PATH=/usr/local/lib make example
|
||||
LD_LIBRARY_PATH=/usr/local/lib make test
|
||||
34
.github/workflows/ci-dotnet.yml
vendored
34
.github/workflows/ci-dotnet.yml
vendored
@@ -1,34 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-dotnet.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- dotnet/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: .NET CI
|
||||
|
||||
jobs:
|
||||
dotnet:
|
||||
name: .NET
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup .NET Core SDK
|
||||
uses: actions/setup-dotnet@v3.0.3
|
||||
with:
|
||||
dotnet-version: 7.x
|
||||
- name: Test .NET Sdk
|
||||
run: |
|
||||
cd dotnet
|
||||
LD_LIBRARY_PATH=/usr/local/lib dotnet test ./Extism.sln
|
||||
41
.github/workflows/ci-elixir.yml
vendored
41
.github/workflows/ci-elixir.yml
vendored
@@ -1,41 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-elixir.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- rust/**
|
||||
- elixir/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Elixir CI
|
||||
|
||||
jobs:
|
||||
elixir:
|
||||
name: Elixir
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
MIX_ENV: test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Elixir Host SDK
|
||||
if: ${{ runner.os != 'macOS' }}
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
experimental-otp: true
|
||||
otp-version: '25.0.4'
|
||||
elixir-version: '1.14.0'
|
||||
|
||||
- name: Test Elixir Host SDK
|
||||
if: ${{ runner.os != 'macOS' }}
|
||||
run: |
|
||||
cd elixir
|
||||
LD_LIBRARY_PATH=/usr/local/lib mix do deps.get, test
|
||||
39
.github/workflows/ci-go.yml
vendored
39
.github/workflows/ci-go.yml
vendored
@@ -1,39 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-go.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- extism.go
|
||||
- extism_test.go
|
||||
- go.mod
|
||||
- libextism.pc
|
||||
- go/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Go CI
|
||||
|
||||
jobs:
|
||||
go:
|
||||
name: Go
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Go env
|
||||
uses: actions/setup-go@v3
|
||||
|
||||
- name: Test Go Host SDK
|
||||
run: |
|
||||
go version
|
||||
cd go
|
||||
LD_LIBRARY_PATH=/usr/local/lib go run main.go
|
||||
LD_LIBRARY_PATH=/usr/local/lib go test
|
||||
47
.github/workflows/ci-haskell.yml
vendored
47
.github/workflows/ci-haskell.yml
vendored
@@ -1,47 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-haskell.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- haskell/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Haskell CI
|
||||
|
||||
jobs:
|
||||
haskell:
|
||||
name: Haskell
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Haskell env
|
||||
uses: haskell/actions/setup@v2
|
||||
with:
|
||||
enable-stack: false
|
||||
- name: Cache Haskell
|
||||
id: cache-haskell
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./haskell/dist-newstyle
|
||||
key: ${{ runner.os }}-haskell-${{ hashFiles('haskell/**') }}
|
||||
- name: Build Haskell Host SDK
|
||||
if: steps.cache-haskell.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd haskell
|
||||
cabal update
|
||||
LD_LIBRARY_PATH=/usr/local/lib cabal build
|
||||
- name: Test Haskell SDK
|
||||
run: |
|
||||
cd haskell
|
||||
cabal update
|
||||
LD_LIBRARY_PATH=/usr/local/lib cabal test
|
||||
42
.github/workflows/ci-java.yml
vendored
42
.github/workflows/ci-java.yml
vendored
@@ -1,42 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-java.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- java/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Java CI
|
||||
|
||||
jobs:
|
||||
java:
|
||||
name: Java
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
version: [8, 11, 17]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '${{ matrix.version }}'
|
||||
- name: Test Java
|
||||
run: |
|
||||
cd java
|
||||
cat pom.xml | sed 's/<java.version>17/<java.version>${{ matrix.version }}/' > updated.xml
|
||||
mv updated.xml pom.xml
|
||||
mvn --batch-mode -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn verify
|
||||
#- name: Examine logs
|
||||
# if: success() || failure()
|
||||
# run: |
|
||||
# cat /tmp/extism.log
|
||||
38
.github/workflows/ci-node.yml
vendored
38
.github/workflows/ci-node.yml
vendored
@@ -1,38 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-node.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- node/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Node CI
|
||||
|
||||
jobs:
|
||||
node:
|
||||
name: Node
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Node env
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Test Node Host SDK
|
||||
run: |
|
||||
cd node
|
||||
npm i
|
||||
LD_LIBRARY_PATH=/usr/local/lib npm run build
|
||||
LD_LIBRARY_PATH=/usr/local/lib npm run example
|
||||
LD_LIBRARY_PATH=/usr/local/lib npm run test
|
||||
50
.github/workflows/ci-ocaml.yml
vendored
50
.github/workflows/ci-ocaml.yml
vendored
@@ -1,50 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-ocaml.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- ocaml/**
|
||||
- dune-project
|
||||
- extism.opam
|
||||
workflow_dispatch:
|
||||
|
||||
name: OCaml CI
|
||||
|
||||
jobs:
|
||||
ocaml:
|
||||
name: OCaml
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup OCaml env
|
||||
uses: ocaml/setup-ocaml@v2
|
||||
with:
|
||||
ocaml-compiler: ocaml-base-compiler.5.0.0
|
||||
- name: Cache OCaml
|
||||
id: cache-ocaml
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: _build
|
||||
key: ${{ runner.os }}-ocaml-${{ hashFiles('ocaml/**.ml') }}-${{ hashFiles('dune-project') }}
|
||||
- name: Build OCaml Host SDK
|
||||
if: steps.cache-ocaml.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
opam install -y --deps-only .
|
||||
cd ocaml
|
||||
LD_LIBRARY_PATH=/usr/local/lib opam exec -- dune build
|
||||
- name: Test OCaml Host SDK
|
||||
run: |
|
||||
opam install -y --deps-only .
|
||||
cd ocaml
|
||||
LD_LIBRARY_PATH=/usr/local/lib opam exec -- dune exec ./bin/main.exe ../wasm/code.wasm count_vowels -- --input "qwertyuiop"
|
||||
LD_LIBRARY_PATH=/usr/local/lib opam exec -- dune runtest
|
||||
42
.github/workflows/ci-php.yml
vendored
42
.github/workflows/ci-php.yml
vendored
@@ -1,42 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-php.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- php/**
|
||||
- composer.json
|
||||
- composer.lock
|
||||
workflow_dispatch:
|
||||
|
||||
name: PHP CI
|
||||
|
||||
jobs:
|
||||
php:
|
||||
name: PHP
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup PHP env
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.1"
|
||||
extensions: ffi
|
||||
tools: composer
|
||||
env:
|
||||
fail-fast: true
|
||||
|
||||
- name: Test PHP SDK
|
||||
run: |
|
||||
cd php/example
|
||||
composer install
|
||||
php index.php
|
||||
40
.github/workflows/ci-python.yml
vendored
40
.github/workflows/ci-python.yml
vendored
@@ -1,40 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-python.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- python/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Python CI
|
||||
|
||||
jobs:
|
||||
python:
|
||||
name: Python
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Python env
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
check-latest: true
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
- name: Test Python Host SDK
|
||||
run: |
|
||||
cd python
|
||||
cp ../README.md .
|
||||
poetry install --no-dev
|
||||
poetry run python example.py
|
||||
poetry run python -m unittest discover
|
||||
38
.github/workflows/ci-ruby.yml
vendored
38
.github/workflows/ci-ruby.yml
vendored
@@ -1,38 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-ruby.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- ruby/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Ruby CI
|
||||
|
||||
jobs:
|
||||
ruby:
|
||||
name: Ruby
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Ruby env
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: "3.0"
|
||||
|
||||
- name: Test Ruby Host SDK
|
||||
run: |
|
||||
cd ruby
|
||||
bundle install
|
||||
ruby example.rb
|
||||
rake test
|
||||
|
||||
105
.github/workflows/ci-rust.yml
vendored
105
.github/workflows/ci-rust.yml
vendored
@@ -1,105 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-rust.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- rust/**
|
||||
- libextism/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Rust CI
|
||||
|
||||
env:
|
||||
RUNTIME_CRATE: extism-runtime
|
||||
LIBEXTISM_CRATE: libextism
|
||||
RUST_SDK_CRATE: extism
|
||||
|
||||
jobs:
|
||||
lib:
|
||||
name: Extism runtime lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Cache Rust environment
|
||||
uses: Swatinem/rust-cache@v1
|
||||
- name: Cache libextism
|
||||
id: cache-libextism
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/release/libextism.*
|
||||
key: ${{ runner.os }}-libextism-${{ hashFiles('runtime/**') }}-${{ hashFiles('manifest/**') }}
|
||||
- name: Cache target
|
||||
id: cache-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ env.GITHUB_SHA }}
|
||||
- name: Build
|
||||
if: steps.cache-libextism.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: cargo build --release -p ${{ env.LIBEXTISM_CRATE }}
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: libextism-${{ matrix.os }}
|
||||
path: |
|
||||
target/release/libextism.*
|
||||
lint_and_test:
|
||||
name: Extism runtime lint and test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Cache Rust environment
|
||||
uses: Swatinem/rust-cache@v1
|
||||
- name: Cache target
|
||||
id: cache-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ env.GITHUB_SHA }}
|
||||
- name: Format
|
||||
run: cargo fmt --check -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Lint
|
||||
run: cargo clippy --release --all-features --no-deps -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Test
|
||||
run: cargo test --all-features --release -p ${{ env.RUNTIME_CRATE }}
|
||||
|
||||
rust:
|
||||
name: Rust
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Test Rust Host SDK
|
||||
run: LD_LIBRARY_PATH=/usr/local/lib cargo test --release -p ${{ env.RUST_SDK_CRATE }}
|
||||
34
.github/workflows/ci-zig.yml
vendored
34
.github/workflows/ci-zig.yml
vendored
@@ -1,34 +0,0 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-zig.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- zig/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Zig CI
|
||||
|
||||
jobs:
|
||||
zig:
|
||||
name: Zig
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Zig env
|
||||
uses: goto-bus-stop/setup-zig@v2
|
||||
|
||||
- name: Test Zig Host SDK
|
||||
run: |
|
||||
zig version
|
||||
cd zig
|
||||
LD_LIBRARY_PATH=/usr/local/lib zig build test
|
||||
355
.github/workflows/ci.yml
vendored
355
.github/workflows/ci.yml
vendored
@@ -2,7 +2,361 @@ on: [pull_request, workflow_dispatch]
|
||||
|
||||
name: CI
|
||||
|
||||
env:
|
||||
RUNTIME_CRATE: extism-runtime
|
||||
LIBEXTISM_CRATE: libextism
|
||||
RUST_SDK_CRATE: extism
|
||||
|
||||
jobs:
|
||||
lib:
|
||||
name: Extism runtime lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Cache Rust environment
|
||||
uses: Swatinem/rust-cache@v1
|
||||
- name: Cache libextism
|
||||
id: cache-libextism
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/release/libextism.*
|
||||
key: ${{ runner.os }}-libextism-${{ hashFiles('runtime/**') }}-${{ hashFiles('manifest/**') }}
|
||||
- name: Cache target
|
||||
id: cache-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ env.GITHUB_SHA }}
|
||||
- name: Build
|
||||
if: steps.cache-libextism.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: cargo build --release -p ${{ env.LIBEXTISM_CRATE }}
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: libextism-${{ matrix.os }}
|
||||
path: |
|
||||
target/release/libextism.*
|
||||
lint_and_test:
|
||||
name: Extism runtime lint and test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Cache Rust environment
|
||||
uses: Swatinem/rust-cache@v1
|
||||
- name: Cache target
|
||||
id: cache-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ env.GITHUB_SHA }}
|
||||
- name: Format
|
||||
run: cargo fmt --check -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Lint
|
||||
run: cargo clippy --release --all-features --no-deps -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Test
|
||||
run: cargo test --all-features --release -p ${{ env.RUNTIME_CRATE }}
|
||||
|
||||
rust:
|
||||
name: Rust
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Test Rust Host SDK
|
||||
run: LD_LIBRARY_PATH=/usr/local/lib cargo test --release -p ${{ env.RUST_SDK_CRATE }}
|
||||
|
||||
elixir:
|
||||
name: Elixir
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
MIX_ENV: test
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Elixir Host SDK
|
||||
if: ${{ runner.os != 'macOS' }}
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
experimental-otp: true
|
||||
otp-version: '25.0.4'
|
||||
elixir-version: '1.14.0'
|
||||
|
||||
- name: Test Elixir Host SDK
|
||||
if: ${{ runner.os != 'macOS' }}
|
||||
run: |
|
||||
cd elixir
|
||||
LD_LIBRARY_PATH=/usr/local/lib mix do deps.get, test
|
||||
|
||||
go:
|
||||
name: Go
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Go env
|
||||
uses: actions/setup-go@v3
|
||||
|
||||
- name: Test Go Host SDK
|
||||
run: |
|
||||
go version
|
||||
cd go
|
||||
LD_LIBRARY_PATH=/usr/local/lib go run main.go
|
||||
LD_LIBRARY_PATH=/usr/local/lib go test
|
||||
|
||||
python:
|
||||
name: Python
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Python env
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
check-latest: true
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
- name: Test Python Host SDK
|
||||
run: |
|
||||
cd python
|
||||
cp ../README.md .
|
||||
poetry install
|
||||
poetry run python example.py
|
||||
poetry run python -m unittest discover
|
||||
|
||||
ruby:
|
||||
name: Ruby
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Ruby env
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: "3.0"
|
||||
|
||||
- name: Test Ruby Host SDK
|
||||
run: |
|
||||
cd ruby
|
||||
bundle install
|
||||
ruby example.rb
|
||||
rake test
|
||||
|
||||
node:
|
||||
name: Node
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Node env
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Test Node Host SDK
|
||||
run: |
|
||||
cd node
|
||||
npm i
|
||||
LD_LIBRARY_PATH=/usr/local/lib npm run build
|
||||
LD_LIBRARY_PATH=/usr/local/lib npm run example
|
||||
LD_LIBRARY_PATH=/usr/local/lib npm run test
|
||||
|
||||
- name: Test Browser Runtime
|
||||
run: |
|
||||
cd browser
|
||||
npm i
|
||||
npm run test
|
||||
|
||||
ocaml:
|
||||
name: OCaml
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup OCaml env
|
||||
uses: ocaml/setup-ocaml@v2
|
||||
with:
|
||||
ocaml-compiler: ocaml-base-compiler.5.0.0~beta1
|
||||
- name: Cache OCaml
|
||||
id: cache-ocaml
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: _build
|
||||
key: ${{ runner.os }}-ocaml-${{ hashFiles('ocaml/lib/**') }}-${{ hashFiles('ocaml/bin/**') }}-${{ hashFiles('dune-project') }}
|
||||
- name: Build OCaml Host SDK
|
||||
if: steps.cache-ocaml.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
opam install -y --deps-only .
|
||||
cd ocaml
|
||||
LD_LIBRARY_PATH=/usr/local/lib opam exec -- dune build
|
||||
- name: Test OCaml Host SDK
|
||||
run: |
|
||||
opam install -y --deps-only .
|
||||
cd ocaml
|
||||
LD_LIBRARY_PATH=/usr/local/lib opam exec -- dune exec ./bin/main.exe
|
||||
LD_LIBRARY_PATH=/usr/local/lib opam exec -- dune runtest
|
||||
|
||||
haskell:
|
||||
name: Haskell
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup Haskell env
|
||||
uses: haskell/actions/setup@v2
|
||||
with:
|
||||
enable-stack: true
|
||||
stack-version: "latest"
|
||||
- name: Cache Haskell
|
||||
id: cache-haskell
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: .stack-work
|
||||
key: ${{ runner.os }}-haskell-${{ hashFiles('haskell/**') }}
|
||||
- name: Build Haskell Host SDK
|
||||
if: steps.cache-haskell.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd haskell
|
||||
LD_LIBRARY_PATH=/usr/local/lib stack build
|
||||
- name: Test Haskell SDK
|
||||
run: |
|
||||
cd haskell
|
||||
LD_LIBRARY_PATH=/usr/local/lib stack test
|
||||
|
||||
php:
|
||||
name: PHP
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup PHP env
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.1"
|
||||
extensions: ffi
|
||||
tools: composer
|
||||
env:
|
||||
fail-fast: true
|
||||
|
||||
- name: Test PHP SDK
|
||||
run: |
|
||||
cd php/example
|
||||
composer install
|
||||
php index.php
|
||||
|
||||
cpp:
|
||||
name: C++
|
||||
needs: lib
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Install C++ SDK deps
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
brew install jsoncpp googletest pkg-config
|
||||
- name: Install C++ SDK deps
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
sudo apt-get install g++ libjsoncpp-dev libgtest-dev pkg-config
|
||||
- name: Run C++ tests
|
||||
run: |
|
||||
cd cpp
|
||||
LD_LIBRARY_PATH=/usr/local/lib make example
|
||||
LD_LIBRARY_PATH=/usr/local/lib make test
|
||||
|
||||
sdk_api_coverage:
|
||||
name: SDK API Coverage Report
|
||||
runs-on: ubuntu-latest
|
||||
@@ -22,4 +376,3 @@ jobs:
|
||||
id: coverage
|
||||
run: |
|
||||
python scripts/sdk_coverage.py
|
||||
|
||||
|
||||
29
.github/workflows/release-dotnet-native.yaml
vendored
29
.github/workflows/release-dotnet-native.yaml
vendored
@@ -1,29 +0,0 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release .NET Native NuGet Packages
|
||||
|
||||
jobs:
|
||||
release-runtimes:
|
||||
name: release-dotnet-native
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup .NET Core SDK
|
||||
uses: actions/setup-dotnet@v3.0.3
|
||||
with:
|
||||
dotnet-version: 7.x
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: release.yml
|
||||
name: release-artifacts
|
||||
- name: Extract Archive
|
||||
run: |
|
||||
tar -xvzf libextism-x86_64-pc-windows-msvc-v*.tar.gz --directory dotnet/nuget/runtimes
|
||||
mv dotnet/nuget/runtimes/extism.dll dotnet/nuget/runtimes/win-x64.dll
|
||||
- name: Publish win-x64
|
||||
run: |
|
||||
cd dotnet/nuget
|
||||
dotnet pack -o dist
|
||||
dotnet nuget push --source https://api.nuget.org/v3/index.json ./dist/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }}
|
||||
32
.github/workflows/release-dotnet.yaml
vendored
32
.github/workflows/release-dotnet.yaml
vendored
@@ -1,32 +0,0 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release .NET SDK
|
||||
|
||||
jobs:
|
||||
release-sdks:
|
||||
name: release-dotnet
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install extism shared library
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /home/runner/.local/bin/
|
||||
export PATH="/home/runner/.local/bin/:$PATH"
|
||||
curl https://raw.githubusercontent.com/extism/cli/main/install.sh | sh
|
||||
extism --sudo --prefix /usr/local install
|
||||
- name: Setup .NET Core SDK
|
||||
uses: actions/setup-dotnet@v3.0.3
|
||||
with:
|
||||
dotnet-version: 7.x
|
||||
- name: Test .NET Sdk
|
||||
run: |
|
||||
cd dotnet
|
||||
LD_LIBRARY_PATH=/usr/local/lib dotnet test ./Extism.sln
|
||||
- name: Publish .NET Sdk
|
||||
run: |
|
||||
cd dotnet/src/Extism.Sdk/
|
||||
dotnet pack -c Release
|
||||
dotnet nuget push --source https://api.nuget.org/v3/index.json ./bin/Release/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }}
|
||||
8
.github/workflows/release-elixir.yaml
vendored
8
.github/workflows/release-elixir.yaml
vendored
@@ -10,13 +10,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install extism shared library
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /home/runner/.local/bin/
|
||||
export PATH="/home/runner/.local/bin/:$PATH"
|
||||
curl https://raw.githubusercontent.com/extism/cli/main/install.sh | sh
|
||||
extism --sudo --prefix /usr/local install
|
||||
|
||||
- name: Setup Elixir Host SDK
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
|
||||
17
.github/workflows/release-haskell.yaml
vendored
17
.github/workflows/release-haskell.yaml
vendored
@@ -1,17 +0,0 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release Rust SDK
|
||||
|
||||
jobs:
|
||||
release-sdks:
|
||||
name: release-rust
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/haskell-release-action@v1
|
||||
with:
|
||||
- hackage-token: "${{ secrets.HACKAGE_TOKEN }}"
|
||||
- work-dir: ./haskell
|
||||
22
.github/workflows/release-java.yml
vendored
22
.github/workflows/release-java.yml
vendored
@@ -1,22 +0,0 @@
|
||||
name: Publish package to the Maven Central Repository
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Publish package
|
||||
env:
|
||||
JRELEASER_NEXUS2_USERNAME: ${{ secrets.JRELEASER_NEXUS2_USERNAME }}
|
||||
JRELEASER_NEXUS2_PASSWORD: ${{ secrets.JRELEASER_NEXUS2_PASSWORD }}
|
||||
JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }}
|
||||
JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }}
|
||||
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }}
|
||||
JRELEASER_GITHUB_TOKEN: "dummy"
|
||||
run: mvn -Prelease clean jreleaser:prepare deploy jreleaser:deploy -DaltDeploymentRepository=local::default::file:./target/staging-deploy
|
||||
3
.github/workflows/release-python.yaml
vendored
3
.github/workflows/release-python.yaml
vendored
@@ -23,8 +23,9 @@ jobs:
|
||||
run: |
|
||||
cd python
|
||||
cp ../LICENSE .
|
||||
cp ../README.md .
|
||||
make clean
|
||||
poetry install --no-dev
|
||||
make prepare
|
||||
poetry build
|
||||
|
||||
- name: Release Python Host SDK
|
||||
|
||||
2
.github/workflows/release-ruby.yaml
vendored
2
.github/workflows/release-ruby.yaml
vendored
@@ -21,5 +21,5 @@ jobs:
|
||||
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_TOKEN }}
|
||||
run: |
|
||||
cd ruby
|
||||
make publish RUBYGEMS_API_KEY=$RUBYGEMS_API_KEY
|
||||
make publish
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -7,7 +7,7 @@ name: Release
|
||||
|
||||
env:
|
||||
RUNTIME_MANIFEST: runtime/Cargo.toml
|
||||
RUNTIME_CRATE: libextism
|
||||
RUNTIME_CRATE: extism-runtime
|
||||
RUSTFLAGS: -C target-feature=-crt-static
|
||||
ARTIFACT_DIR: release-artifacts
|
||||
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -27,12 +27,9 @@ ruby/tmp/
|
||||
ruby/Gemfile.lock
|
||||
rust/target
|
||||
rust/test.log
|
||||
duniverse
|
||||
_build
|
||||
ocaml/duniverse
|
||||
ocaml/_build
|
||||
php/Extism.php
|
||||
dist-newstyle
|
||||
.stack-work
|
||||
vendor
|
||||
zig/zig-*
|
||||
zig/example-out/
|
||||
zig/*.log
|
||||
vendor
|
||||
@@ -1 +0,0 @@
|
||||
version = 0.24.1
|
||||
@@ -3,6 +3,8 @@ members = [
|
||||
"manifest",
|
||||
"runtime",
|
||||
"rust",
|
||||
"libextism",
|
||||
"libextism"
|
||||
]
|
||||
exclude = [
|
||||
"elixir/native/extism_nif"
|
||||
]
|
||||
|
||||
3
Makefile
3
Makefile
@@ -27,9 +27,6 @@ lint:
|
||||
build:
|
||||
cargo build --release $(FEATURE_FLAGS) --manifest-path libextism/Cargo.toml
|
||||
|
||||
debug:
|
||||
RUSTFLAGS=-g $(MAKE) build
|
||||
|
||||
install:
|
||||
install runtime/extism.h $(DEST)/include
|
||||
install target/release/libextism.$(SOEXT) $(DEST)/lib
|
||||
|
||||
13
README.md
13
README.md
@@ -10,21 +10,16 @@ The universal plug-in system. Run WebAssembly extensions inside your app. Use id
|
||||
[Ruby](https://extism.org/docs/integrate-into-your-codebase/ruby-host-sdk), [Python](https://extism.org/docs/integrate-into-your-codebase/python-host-sdk),
|
||||
[Node](https://extism.org/docs/integrate-into-your-codebase/node-host-sdk), [Rust](https://extism.org/docs/integrate-into-your-codebase/rust-host-sdk),
|
||||
[C](https://extism.org/docs/integrate-into-your-codebase/c-host-sdk), [C++](https://extism.org/docs/integrate-into-your-codebase/cpp-host-sdk),
|
||||
[OCaml](https://extism.org/docs/integrate-into-your-codebase/ocaml-host-sdk),
|
||||
[Haskell](https://extism.org/docs/integrate-into-your-codebase/haskell-host-sdk),
|
||||
[PHP](https://extism.org/docs/integrate-into-your-codebase/php-host-sdk),
|
||||
[Elixir/Erlang](https://extism.org/docs/integrate-into-your-codebase/elixir-or-erlang-host-sdk),
|
||||
[.NET](https://extism.org/docs/integrate-into-your-codebase/dotnet-host-sdk),
|
||||
[Java](https://extism.org/docs/integrate-into-your-codebase/java-host-sdk),
|
||||
[Zig](https://extism.org/docs/integrate-into-your-codebase/zig-host-sdk) & more (others coming soon).
|
||||
[OCaml](https://extism.org/docs/integrate-into-your-codebase/ocaml-host-sdk), [Haskell](https://extism.org/docs/integrate-into-your-codebase/haskell-host-sdk), [PHP](https://extism.org/docs/integrate-into-your-codebase/php-host-sdk), [Elixir/Erlang](https://extism.org/docs/integrate-into-your-codebase/elixir-or-erlang-host-sdk) & more (others coming soon).
|
||||
|
||||
Plug-in development kits (PDK) for plug-in authors supported in [Rust](https://github.com/extism/rust-pdk), [AssemblyScript](https://github.com/extism/assemblyscript-pdk), [Go](https://github.com/extism/go-pdk), [C/C++](https://github.com/extism/c-pdk), [Haskell](https://github.com/extism/haskell-pdk), and [Zig](https://github.com/extism/zig-pdk).
|
||||
Plug-in development kits (PDK) for plug-in authors supported in [Rust](https://github.com/extism/rust-pdk), [AssemblyScript](https://github.com/extism/assemblyscript-pdk), [Go](https://github.com/extism/go-pdk), [C/C++](https://github.com/extism/c-pdk).
|
||||
|
||||
<p align="center">
|
||||
<img style="width: 70%;" src="https://user-images.githubusercontent.com/7517515/210286900-39b144fd-1b26-4dd0-b7a9-2b5755bc174d.png" alt="Extism embedded SDK language support"/>
|
||||
<img style="width: 70%;" src="https://user-images.githubusercontent.com/7517515/200043015-ddfe5833-0252-43a8-bc9e-5b3f829c37d1.png" alt="Extism embedded SDK language support"/>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
Add a flexible, secure, and _bLaZiNg FaSt_ plug-in system to your project. Server, desktop, mobile, web, database -- you name it. Enable users to write and execute safe extensions to your software in **3 easy steps:**
|
||||
|
||||
### 1. Import
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
.PHONY: test
|
||||
|
||||
prepare:
|
||||
npm install
|
||||
|
||||
build:
|
||||
npm run build
|
||||
|
||||
test: prepare
|
||||
npm run test
|
||||
|
||||
clean:
|
||||
echo "No clean implemented"
|
||||
|
||||
publish: clean prepare build
|
||||
npm publish
|
||||
|
||||
format:
|
||||
npx prettier --write src
|
||||
|
||||
lint:
|
||||
npx prettier --check src
|
||||
|
||||
docs:
|
||||
npx typedoc --out doc src
|
||||
|
||||
show-docs: docs
|
||||
open doc/index.html
|
||||
@@ -1,12 +1,12 @@
|
||||
const { build } = require("esbuild");
|
||||
const { peerDependencies } = require('./package.json')
|
||||
const { dependencies, peerDependencies } = require('./package.json')
|
||||
|
||||
const sharedConfig = {
|
||||
entryPoints: ["src/index.ts"],
|
||||
bundle: true,
|
||||
minify: false,
|
||||
drop: [], // preseve debugger statements
|
||||
external: Object.keys(peerDependencies || {}),
|
||||
external: Object.keys(dependencies || {}).concat(Object.keys(peerDependencies || {})),
|
||||
};
|
||||
|
||||
build({
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
async loadFunctions(url) {
|
||||
let plugin = await this.extismContext.newPlugin({ "wasm": [ { "path": url } ] })
|
||||
let functions = Object.keys(await plugin.getExports())
|
||||
let functions = await plugin.getExportedFunctions()
|
||||
console.log("funcs ", functions)
|
||||
this.setState({functions})
|
||||
}
|
||||
|
||||
1040
browser/package-lock.json
generated
1040
browser/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@extism/runtime-browser",
|
||||
"version": "0.2.2",
|
||||
"version": "0.0.1-rc.12",
|
||||
"description": "Extism runtime in the browser",
|
||||
"scripts": {
|
||||
"build": "node build.js && tsc --emitDeclarationOnly --outDir dist",
|
||||
@@ -17,7 +17,7 @@
|
||||
],
|
||||
"module": "dist/index.esm.js",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/src/index.d.ts",
|
||||
"typings": "dist/index.d.ts",
|
||||
"author": "The Extism Authors <oss@extism.org>",
|
||||
"license": "BSD-3-Clause",
|
||||
"devDependencies": {
|
||||
@@ -30,8 +30,5 @@
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typedoc": "^0.23.20",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bjorn3/browser_wasi_shim": "^0.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('', () => {
|
||||
const ctx = new ExtismContext();
|
||||
const plugin = await ctx.newPlugin({ wasm: [{ data: data }] });
|
||||
const functions = await plugin.getExports();
|
||||
expect(Object.keys(functions).filter((x) => !x.startsWith('__') && x !== 'memory')).toEqual(['count_vowels']);
|
||||
expect(Object.keys(functions).filter(x => !x.startsWith("__") && x !== "memory")).toEqual(['count_vowels']);
|
||||
let output = await plugin.call('count_vowels', 'this is a test');
|
||||
expect(parse(output)).toEqual({ count: 4 });
|
||||
output = await plugin.call('count_vowels', 'this is a test again');
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import Allocator from './allocator';
|
||||
import { PluginConfig } from './manifest';
|
||||
//@ts-ignore TODO add types to this library
|
||||
import { WASI, File } from "@bjorn3/browser_wasi_shim";
|
||||
|
||||
export default class ExtismPlugin {
|
||||
moduleData: ArrayBuffer;
|
||||
@@ -63,19 +61,7 @@ export default class ExtismPlugin {
|
||||
return this.module;
|
||||
}
|
||||
const environment = this.makeEnv();
|
||||
const args: Array<string> = [];
|
||||
const envVars: Array<string> = [];
|
||||
let fds = [
|
||||
new File([]), // stdin
|
||||
new File([]), // stdout
|
||||
new File([]), // stderr
|
||||
];
|
||||
let wasi = new WASI(args, envVars, fds);
|
||||
let env = {
|
||||
wasi_snapshot_preview1: wasi.wasiImport,
|
||||
env: environment
|
||||
};
|
||||
this.module = await WebAssembly.instantiate(this.moduleData, env);
|
||||
this.module = await WebAssembly.instantiate(this.moduleData, { env: environment });
|
||||
return this.module;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
|
||||
1
c/main.c
1
c/main.c
@@ -21,7 +21,6 @@ uint8_t *read_file(const char *filename, size_t *len) {
|
||||
|
||||
uint8_t *data = malloc(length);
|
||||
if (data == NULL) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
},
|
||||
"files": [
|
||||
"php/src/Context.php",
|
||||
"php/src/Plugin.php"
|
||||
"php/src/Plugin.php",
|
||||
"php/src/extism.h"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
||||
@@ -53,10 +53,6 @@ public:
|
||||
Config config;
|
||||
std::vector<Wasm> wasm;
|
||||
std::vector<std::string> allowed_hosts;
|
||||
std::map<std::string, std::string> allowed_paths;
|
||||
uint64_t timeout_ms;
|
||||
|
||||
Manifest() : timeout_ms(30000) {}
|
||||
|
||||
static Manifest path(std::string s, std::string hash = std::string()) {
|
||||
Manifest m;
|
||||
@@ -98,16 +94,6 @@ public:
|
||||
doc["allowed_hosts"] = h;
|
||||
}
|
||||
|
||||
if (!this->allowed_paths.empty()) {
|
||||
Json::Value h;
|
||||
for (auto k : this->allowed_paths) {
|
||||
h[k.first] = k.second;
|
||||
}
|
||||
doc["allowed_paths"] = h;
|
||||
}
|
||||
|
||||
doc["timeout_ms"] = Json::Value(this->timeout_ms);
|
||||
|
||||
Json::FastWriter writer;
|
||||
return writer.write(doc);
|
||||
}
|
||||
@@ -129,15 +115,6 @@ public:
|
||||
|
||||
void allow_host(std::string host) { this->allowed_hosts.push_back(host); }
|
||||
|
||||
void allow_path(std::string src, std::string dest = std::string()) {
|
||||
if (dest.empty()) {
|
||||
dest = src;
|
||||
}
|
||||
this->allowed_paths[src] = dest;
|
||||
}
|
||||
|
||||
void set_timeout_ms(uint64_t ms) { this->timeout_ms = ms; }
|
||||
|
||||
void set_config(std::string k, std::string v) { this->config[k] = v; }
|
||||
};
|
||||
|
||||
@@ -289,7 +266,6 @@ public:
|
||||
class Context {
|
||||
public:
|
||||
std::shared_ptr<ExtismContext> pointer;
|
||||
|
||||
Context() {
|
||||
this->pointer = std::shared_ptr<ExtismContext>(extism_context_new(),
|
||||
extism_context_free);
|
||||
|
||||
479
dotnet/.gitignore
vendored
479
dotnet/.gitignore
vendored
@@ -1,479 +0,0 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
nuget/runtimes/win-x64.dll
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33110.190
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.Sdk", "src\Extism.Sdk\Extism.Sdk.csproj", "{1FAA7B6E-249C-4E4C-AE7A-A493A9D24475}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.Sdk.Tests", "test\Extism.Sdk\Extism.Sdk.Tests.csproj", "{DB440D61-C781-4C59-9223-9A79CC9FB4E7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.Sdk.Sample", "samples\Extism.Sdk.Sample\Extism.Sdk.Sample.csproj", "{2232E572-E8BA-46A1-AF31-E4168960DB75}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1FAA7B6E-249C-4E4C-AE7A-A493A9D24475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1FAA7B6E-249C-4E4C-AE7A-A493A9D24475}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1FAA7B6E-249C-4E4C-AE7A-A493A9D24475}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1FAA7B6E-249C-4E4C-AE7A-A493A9D24475}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB440D61-C781-4C59-9223-9A79CC9FB4E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB440D61-C781-4C59-9223-9A79CC9FB4E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB440D61-C781-4C59-9223-9A79CC9FB4E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB440D61-C781-4C59-9223-9A79CC9FB4E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2232E572-E8BA-46A1-AF31-E4168960DB75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2232E572-E8BA-46A1-AF31-E4168960DB75}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2232E572-E8BA-46A1-AF31-E4168960DB75}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2232E572-E8BA-46A1-AF31-E4168960DB75}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {2B6BF267-F2A5-4CB5-8DFD-F11CC8787E6B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,24 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<NoBuild>true</NoBuild>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.win-x64</PackageId>
|
||||
<Version>0.2.0</Version>
|
||||
<Authors>Extism Contributors</Authors>
|
||||
<Description>Internal implementation package for Extism to work on Windows x64</Description>
|
||||
<Tags>extism, wasm, plugin</Tags>
|
||||
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes/win-x64.dll"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\win-x64\native\extism.dll" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1 +0,0 @@
|
||||
win-x64.dll
|
||||
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\wasm\code.wasm" Link="code.wasm">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Extism.Sdk\Extism.Sdk.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -1,11 +0,0 @@
|
||||
using Extism.Sdk.Native;
|
||||
using System.Text;
|
||||
|
||||
var context = new Context();
|
||||
var wasm = await File.ReadAllBytesAsync("./code.wasm");
|
||||
using var plugin = context.CreatePlugin(wasm, withWasi: true);
|
||||
|
||||
var output = Encoding.UTF8.GetString(
|
||||
plugin.CallFunction("count_vowels", Encoding.UTF8.GetBytes("Hello World!"))
|
||||
);
|
||||
Console.WriteLine(output); // prints {"count": 3}
|
||||
@@ -1,5 +0,0 @@
|
||||
## Example 1
|
||||
|
||||
This example shows how you can use the library in the most basic way.
|
||||
It loads up the sample wasm plugin and lets you to pass inputs to it and show the ouput.
|
||||
**Please note that on Windows you have to manually copy the `extism.dll` file to the ouput directory.**
|
||||
@@ -1,24 +0,0 @@
|
||||
<!-- Recommended practices for publishing nuget packages from: https://devblogs.microsoft.com/dotnet/producing-packages-with-source-link/ -->
|
||||
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
|
||||
<!-- Embed source files that are not tracked by the source control manager in the PDB -->
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
|
||||
<!-- Recommended: Embed symbols containing Source Link in the main file (exe/dll) -->
|
||||
<DebugType>embedded</DebugType>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,184 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Extism.Sdk.Native;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Extism context through which you can load <see cref="Plugin"/>s.
|
||||
/// </summary>
|
||||
public class Context : IDisposable
|
||||
{
|
||||
private const int DisposedMarker = 1;
|
||||
|
||||
private int _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new Extism Context.
|
||||
/// </summary>
|
||||
public Context()
|
||||
{
|
||||
NativeHandle = LibExtism.extism_context_new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native pointer to the Extism Context.
|
||||
/// </summary>
|
||||
internal IntPtr NativeHandle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads an Extism <see cref="Plugin"/>.
|
||||
/// </summary>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="withWasi">Enable/Disable WASI.</param>
|
||||
public Plugin CreatePlugin(ReadOnlySpan<byte> wasm, bool withWasi)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* wasmPtr = wasm)
|
||||
{
|
||||
var plugin = LibExtism.extism_plugin_new(NativeHandle, wasmPtr, wasm.Length, withWasi);
|
||||
return new Plugin(this, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all plugins from this <see cref="Context"/>'s registry.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
LibExtism.extism_context_reset(NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get this this <see cref="Context"/>'s last error.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal string? GetError()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var result = LibExtism.extism_error(NativeHandle, -1);
|
||||
return Marshal.PtrToStringUTF8(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all resources held by this Context.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, DisposedMarker) == DisposedMarker)
|
||||
{
|
||||
// Already disposed.
|
||||
return;
|
||||
}
|
||||
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw an appropriate exception if the plugin has been disposed.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"></exception>
|
||||
protected void CheckNotDisposed()
|
||||
{
|
||||
Interlocked.MemoryBarrier();
|
||||
if (_disposed == DisposedMarker)
|
||||
{
|
||||
ThrowDisposedException();
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
private static void ThrowDisposedException()
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all resources held by this Context.
|
||||
/// </summary>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Free up any managed resources here
|
||||
}
|
||||
|
||||
// Free up unmanaged resources
|
||||
LibExtism.extism_context_free(NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructs the current Context and frees all resources used by it.
|
||||
/// </summary>
|
||||
~Context()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Extism version string.
|
||||
/// </summary>
|
||||
public static string GetExtismVersion()
|
||||
{
|
||||
var pointer = LibExtism.extism_version();
|
||||
return Marshal.PtrToStringUTF8(pointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set Extism's log file and level. This is applied for all <see cref="Context"/>s.
|
||||
/// </summary>
|
||||
/// <param name="logPath">Log file; can be 'stdout' or 'stderr' to write logs to the console.</param>
|
||||
/// <param name="level">The log level to write at.</param>
|
||||
public static bool SetExtismLogFile(string logPath, LogLevel level)
|
||||
{
|
||||
var logLevel = level switch
|
||||
{
|
||||
LogLevel.Error => LibExtism.LogLevels.Error,
|
||||
LogLevel.Warning => LibExtism.LogLevels.Warn,
|
||||
LogLevel.Info => LibExtism.LogLevels.Info,
|
||||
LogLevel.Debug => LibExtism.LogLevels.Debug,
|
||||
LogLevel.Trace => LibExtism.LogLevels.Trace,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return LibExtism.extism_log_file(logPath, logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extism Log Levels
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Designates very serious errors.
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// Designates hazardous situations.
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// Designates useful information.
|
||||
/// </summary>
|
||||
Info,
|
||||
|
||||
/// <summary>
|
||||
/// Designates lower priority information.
|
||||
/// </summary>
|
||||
Debug,
|
||||
|
||||
/// <summary>
|
||||
/// Designates very low priority, often extremely verbose, information.
|
||||
/// </summary>
|
||||
Trace
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
namespace Extism.Sdk.Native;
|
||||
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents errors that occur during calling Extism functions.
|
||||
/// </summary>
|
||||
public class ExtismException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtismException"/> class.
|
||||
/// </summary>
|
||||
public ExtismException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtismException"/> class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error .</param>
|
||||
public ExtismException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtismException"/> class
|
||||
/// with a specified error message and a reference to the inner exception
|
||||
/// that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error .</param>
|
||||
/// <param name="innerException">
|
||||
/// The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified.
|
||||
/// </param>
|
||||
public ExtismException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.Sdk</PackageId>
|
||||
<Version>0.2.0</Version>
|
||||
<Authors>Extism Contributors</Authors>
|
||||
<Description>Extism SDK that allows hosting Extism plugins in .NET apps.</Description>
|
||||
<Tags>extism, wasm, plugin</Tags>
|
||||
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="\"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,170 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Extism.Sdk.Native;
|
||||
|
||||
/// <summary>
|
||||
/// Functions exposed by the native Extism library.
|
||||
/// </summary>
|
||||
internal static class LibExtism
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new context.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the newly created context.</returns>
|
||||
[DllImport("extism")]
|
||||
public static extern IntPtr extism_context_new();
|
||||
|
||||
/// <summary>
|
||||
/// Remove a context from the registry and free associated memory.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[DllImport("extism")]
|
||||
public static extern void extism_context_free(IntPtr context);
|
||||
|
||||
/// <summary>
|
||||
/// Load a WASM plugin.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin will be associated with.</param>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="wasmSize">The length of the `wasm` parameter.</param>
|
||||
/// <param name="withWasi">Enables/disables WASI.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe public static extern IntPtr extism_plugin_new(IntPtr context, byte* wasm, int wasmSize, bool withWasi);
|
||||
|
||||
/// <summary>
|
||||
/// Update a plugin, keeping the existing ID.
|
||||
/// Similar to <see cref="extism_plugin_new"/> but takes an `plugin` argument to specify which plugin to update.
|
||||
/// Memory for this plugin will be reset upon update.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin is associated with.</param>
|
||||
/// <param name="plugin">Pointer to the plugin you want to update.</param>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="wasmLength">The length of the `wasm` parameter.</param>
|
||||
/// <param name="withWasi">Enables/disables WASI.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe public static extern bool extism_plugin_update(IntPtr context, IntPtr plugin, byte* wasm, int wasmLength, bool withWasi);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a plugin from the registry and free associated memory.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin is associated with.</param>
|
||||
/// <param name="plugin">Pointer to the plugin you want to free.</param>
|
||||
[DllImport("extism")]
|
||||
public static extern void extism_plugin_free(IntPtr context, IntPtr plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all plugins from the registry.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[DllImport("extism")]
|
||||
public static extern void extism_context_reset(IntPtr context);
|
||||
|
||||
/// <summary>
|
||||
/// Update plugin config values, this will merge with the existing values.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin is associated with.</param>
|
||||
/// <param name="plugin">Pointer to the plugin you want to update the configurations for.</param>
|
||||
/// <param name="json">The configuration JSON encoded in UTF8.</param>
|
||||
/// <param name="jsonLength">The length of the `json` parameter.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe public static extern bool extism_plugin_config(IntPtr context, IntPtr plugin, byte* json, int jsonLength);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if funcName exists.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <param name="funcName"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
public static extern bool extism_plugin_function_exists(IntPtr context, IntPtr plugin, string funcName);
|
||||
|
||||
/// <summary>
|
||||
/// Call a function.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <param name="funcName">The function to call.</param>
|
||||
/// <param name="data">Input data.</param>
|
||||
/// <param name="dataLen">The length of the `data` parameter.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe public static extern int extism_plugin_call(IntPtr context, IntPtr plugin, string funcName, byte* data, int dataLen);
|
||||
|
||||
/// <summary>
|
||||
/// Get the error associated with a Context or Plugin, if plugin is -1 then the context error will be returned.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin">A plugin pointer, or -1 for the context error.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
public static extern IntPtr extism_error(IntPtr context, nint plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Get the length of a plugin's output data.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
public static extern long extism_plugin_output_length(IntPtr context, IntPtr plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Get the plugin's output data.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
public static extern IntPtr extism_plugin_output_data(IntPtr context, IntPtr plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Set log file and level.
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <param name="logLevel"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
public static extern bool extism_log_file(string filename, string logLevel);
|
||||
|
||||
/// <summary>
|
||||
/// Get the Extism version string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism", EntryPoint = "extism_version")]
|
||||
public static extern IntPtr extism_version();
|
||||
|
||||
/// <summary>
|
||||
/// Extism Log Levels
|
||||
/// </summary>
|
||||
public static class LogLevels
|
||||
{
|
||||
/// <summary>
|
||||
/// Designates very serious errors.
|
||||
/// </summary>
|
||||
public const string Error = "Error";
|
||||
|
||||
/// <summary>
|
||||
/// Designates hazardous situations.
|
||||
/// </summary>
|
||||
public const string Warn = "Warn";
|
||||
|
||||
/// <summary>
|
||||
/// Designates useful information.
|
||||
/// </summary>
|
||||
public const string Info = "Info";
|
||||
|
||||
/// <summary>
|
||||
/// Designates lower priority information.
|
||||
/// </summary>
|
||||
public const string Debug = "Debug";
|
||||
|
||||
/// <summary>
|
||||
/// Designates very low priority, often extremely verbose, information.
|
||||
/// </summary>
|
||||
public const string Trace = "Trace";
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Extism.Sdk.Native;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a WASM Extism plugin.
|
||||
/// </summary>
|
||||
public class Plugin : IDisposable
|
||||
{
|
||||
private const int DisposedMarker = 1;
|
||||
|
||||
private readonly Context _context;
|
||||
private int _disposed;
|
||||
|
||||
internal Plugin(Context context, IntPtr handle)
|
||||
{
|
||||
_context = context;
|
||||
NativeHandle = handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A pointer to the native Plugin struct.
|
||||
/// </summary>
|
||||
internal IntPtr NativeHandle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Update a plugin, keeping the existing ID.
|
||||
/// </summary>
|
||||
/// <param name="wasm">The plugin WASM bytes.</param>
|
||||
/// <param name="withWasi">Enable/Disable WASI.</param>
|
||||
unsafe public bool Update(ReadOnlySpan<byte> wasm, bool withWasi)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
fixed (byte* wasmPtr = wasm)
|
||||
{
|
||||
return LibExtism.extism_plugin_update(_context.NativeHandle, NativeHandle, wasmPtr, wasm.Length, withWasi);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update plugin config values, this will merge with the existing values.
|
||||
/// </summary>
|
||||
/// <param name="json">The configuration JSON encoded in UTF8.</param>
|
||||
unsafe public bool SetConfig(ReadOnlySpan<byte> json)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
fixed (byte* jsonPtr = json)
|
||||
{
|
||||
return LibExtism.extism_plugin_config(_context.NativeHandle, NativeHandle, jsonPtr, json.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a specific function exists in the current plugin.
|
||||
/// </summary>
|
||||
public bool FunctionExists(string name)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
return LibExtism.extism_plugin_function_exists(_context.NativeHandle, NativeHandle, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls a function in the current plugin and returns a status.
|
||||
/// If the status represents an error, call <see cref="GetError"/> to get the error.
|
||||
/// Othewise, call <see cref="OutputData"/> to get the function's output data.
|
||||
/// </summary>
|
||||
/// <param name="functionName">Name of the function in the plugin to invoke.</param>
|
||||
/// <param name="data">A buffer to provide as input to the function.</param>
|
||||
/// <returns>The exit code of the function.</returns>
|
||||
/// <exception cref="ExtismException"></exception>
|
||||
unsafe public ReadOnlySpan<byte> CallFunction(string functionName, ReadOnlySpan<byte> data)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
fixed (byte* dataPtr = data)
|
||||
{
|
||||
int response = LibExtism.extism_plugin_call(_context.NativeHandle, NativeHandle, functionName, dataPtr, data.Length);
|
||||
if (response == 0) {
|
||||
return OutputData();
|
||||
} else {
|
||||
var errorMsg = GetError();
|
||||
if (errorMsg != null) {
|
||||
throw new ExtismException(errorMsg);
|
||||
} else {
|
||||
throw new ExtismException("Call to Extism failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the length of a plugin's output data.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal int OutputLength()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
return (int)LibExtism.extism_plugin_output_length(_context.NativeHandle, NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the plugin's output data.
|
||||
/// </summary>
|
||||
internal ReadOnlySpan<byte> OutputData()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var length = OutputLength();
|
||||
|
||||
unsafe
|
||||
{
|
||||
var ptr = LibExtism.extism_plugin_output_data(_context.NativeHandle, NativeHandle).ToPointer();
|
||||
return new Span<byte>(ptr, length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the error associated with the current plugin.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal string? GetError()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var result = LibExtism.extism_error(_context.NativeHandle, NativeHandle);
|
||||
return Marshal.PtrToStringUTF8(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all resources held by this Plugin.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, DisposedMarker) == DisposedMarker)
|
||||
{
|
||||
// Already disposed.
|
||||
return;
|
||||
}
|
||||
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw an appropriate exception if the plugin has been disposed.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"></exception>
|
||||
protected void CheckNotDisposed()
|
||||
{
|
||||
Interlocked.MemoryBarrier();
|
||||
if (_disposed == DisposedMarker)
|
||||
{
|
||||
ThrowDisposedException();
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
private static void ThrowDisposedException()
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Plugin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all resources held by this Plugin.
|
||||
/// </summary>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Free up any managed resources here
|
||||
}
|
||||
|
||||
// Free up unmanaged resources
|
||||
LibExtism.extism_plugin_free(_context.NativeHandle, NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructs the current Plugin and frees all resources used by it.
|
||||
/// </summary>
|
||||
~Plugin()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
## Extism.Sdk
|
||||
Extism SDK that allows hosting Extism plugins in .NET apps.
|
||||
@@ -1,24 +0,0 @@
|
||||
using Extism.Sdk.Native;
|
||||
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Extism.Sdk.Tests;
|
||||
|
||||
public class BasicTests
|
||||
{
|
||||
[Fact]
|
||||
public void CountHelloWorldVowels()
|
||||
{
|
||||
using var context = new Context();
|
||||
|
||||
var binDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
var wasm = File.ReadAllBytes(Path.Combine(binDirectory, "code.wasm"));
|
||||
using var plugin = context.CreatePlugin(wasm, withWasi: true);
|
||||
|
||||
var response = plugin.CallFunction("count_vowels", Encoding.UTF8.GetBytes("Hello World"));
|
||||
Assert.Equal("{\"count\": 3}", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\wasm\code.wasm" Link="code.wasm">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Extism.Sdk\Extism.Sdk.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
dune-project
24
dune-project
@@ -19,28 +19,6 @@
|
||||
(name extism)
|
||||
(synopsis "Extism bindings")
|
||||
(description "Bindings to Extism, the universal plugin system")
|
||||
(depends
|
||||
(ocaml (>= 4.14.1))
|
||||
(dune (>= 3.2))
|
||||
(ctypes-foreign (>= 0.18.0))
|
||||
(bigstringaf (>= 0.9.0))
|
||||
(ppx_yojson_conv (>= 0.15.0))
|
||||
extism-manifest
|
||||
(ppx_inline_test (>= 0.15.0))
|
||||
(cmdliner (>= 1.1.1))
|
||||
)
|
||||
(tags
|
||||
(topics wasm plugin)))
|
||||
|
||||
(package
|
||||
(name extism-manifest)
|
||||
(synopsis "Extism manifest bindings")
|
||||
(description "Bindings to Extism, the universal plugin system")
|
||||
(depends
|
||||
(ocaml (>= 4.14.1))
|
||||
(dune (>= 3.2))
|
||||
(ppx_yojson_conv (>= 0.15.0))
|
||||
(base64 (>= 3.5.0))
|
||||
)
|
||||
(depends ocaml dune ctypes-foreign bigstringaf ppx_yojson_conv base64 ppx_inline_test)
|
||||
(tags
|
||||
(topics wasm plugin)))
|
||||
|
||||
@@ -13,7 +13,7 @@ You can find this package on [hex.pm](https://hex.pm/packages/extism).
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:extism, "~> 0.1.0"}
|
||||
{:extism, "~> 0.0.1-rc.6"}
|
||||
]
|
||||
end
|
||||
```
|
||||
@@ -70,4 +70,4 @@ The key method to know here is [Extism.Plugin#call](Extism.Plugin.html#call/3) w
|
||||
```elixir
|
||||
{:ok, plugin} = Extism.Context.new_plugin(ctx, manifest, false)
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
```
|
||||
```
|
||||
@@ -4,8 +4,8 @@ defmodule Extism.MixProject do
|
||||
def project do
|
||||
[
|
||||
app: :extism,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.12",
|
||||
version: "0.0.1-rc.6",
|
||||
elixir: "~> 1.14",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
package: package(),
|
||||
@@ -43,7 +43,7 @@ defmodule Extism.MixProject do
|
||||
licenses: ["BSD-3-Clause"],
|
||||
description: "Extism Host SDK for Elixir and Erlang",
|
||||
name: "extism",
|
||||
files: ~w(lib native .formatter.exs mix.exs README.md LICENSE),
|
||||
files: ~w(lib native priv .formatter.exs mix.exs README.md LICENSE),
|
||||
links: %{"GitHub" => "https://github.com/extism/extism"}
|
||||
]
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "extism_nif"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1-rc.6"
|
||||
edition = "2021"
|
||||
authors = ["Benjamin Eckel <bhelx@simst.im>"]
|
||||
|
||||
@@ -11,5 +11,5 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
rustler = "0.26.0"
|
||||
extism = { version = "0.1.0", path = "../../../rust" }
|
||||
extism = { version = "0.0.1-rc.6" }
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use extism::{Context, Plugin};
|
||||
use rustler::{Atom, Env, ResourceArc, Term};
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use rustler::{Atom, Env, Term, ResourceArc};
|
||||
use extism::{Plugin, Context};
|
||||
use std::str;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::RwLock;
|
||||
use std::mem;
|
||||
|
||||
mod atoms {
|
||||
rustler::atoms! {
|
||||
@@ -15,12 +15,9 @@ mod atoms {
|
||||
}
|
||||
|
||||
struct ExtismContext {
|
||||
ctx: RwLock<Context>,
|
||||
ctx: RwLock<Context>
|
||||
}
|
||||
|
||||
unsafe impl Sync for ExtismContext {}
|
||||
unsafe impl Send for ExtismContext {}
|
||||
|
||||
fn load(env: Env, _: Term) -> bool {
|
||||
rustler::resource!(ExtismContext, env);
|
||||
true
|
||||
@@ -30,16 +27,15 @@ fn to_rustler_error(extism_error: extism::Error) -> rustler::Error {
|
||||
match extism_error {
|
||||
extism::Error::UnableToLoadPlugin(msg) => rustler::Error::Term(Box::new(msg)),
|
||||
extism::Error::Message(msg) => rustler::Error::Term(Box::new(msg)),
|
||||
extism::Error::Json(json_err) => rustler::Error::Term(Box::new(json_err.to_string())),
|
||||
extism::Error::Runtime(e) => rustler::Error::Term(Box::new(e.to_string())),
|
||||
extism::Error::Json(json_err) => rustler::Error::Term(Box::new(json_err.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn context_new() -> ResourceArc<ExtismContext> {
|
||||
ResourceArc::new(ExtismContext {
|
||||
ctx: RwLock::new(Context::new()),
|
||||
})
|
||||
ResourceArc::new(
|
||||
ExtismContext { ctx: RwLock::new(Context::new()) }
|
||||
)
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
@@ -55,11 +51,7 @@ fn context_free(ctx: ResourceArc<ExtismContext>) {
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_new_with_manifest(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
manifest_payload: String,
|
||||
wasi: bool,
|
||||
) -> Result<i32, rustler::Error> {
|
||||
fn plugin_new_with_manifest(ctx: ResourceArc<ExtismContext>, manifest_payload: String, wasi: bool) -> Result<i32, rustler::Error> {
|
||||
let context = &ctx.ctx.write().unwrap();
|
||||
let result = match Plugin::new(context, manifest_payload, wasi) {
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
@@ -75,22 +67,17 @@ fn plugin_new_with_manifest(
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_call(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
name: String,
|
||||
input: String,
|
||||
) -> Result<String, rustler::Error> {
|
||||
fn plugin_call(ctx: ResourceArc<ExtismContext>, plugin_id: i32, name: String, input: String) -> Result<String, rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let mut plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let result = match plugin.call(name, input) {
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
Ok(result) => match str::from_utf8(&result) {
|
||||
Ok(output) => Ok(output.to_string()),
|
||||
Err(_e) => Err(rustler::Error::Term(Box::new(
|
||||
"Could not read output from plugin",
|
||||
))),
|
||||
},
|
||||
Ok(result) => {
|
||||
match str::from_utf8(&result) {
|
||||
Ok(output) => Ok(output.to_string()),
|
||||
Err(_e) => Err(rustler::Error::Term(Box::new("Could not read output from plugin")))
|
||||
}
|
||||
}
|
||||
};
|
||||
// this forget should be safe because the context will clean up
|
||||
// all it's plugins when it is dropped
|
||||
@@ -99,17 +86,14 @@ fn plugin_call(
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_update_manifest(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
manifest_payload: String,
|
||||
wasi: bool,
|
||||
) -> Result<(), rustler::Error> {
|
||||
fn plugin_update_manifest(ctx: ResourceArc<ExtismContext>, plugin_id: i32, manifest_payload: String, wasi: bool) -> Result<(), rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let mut plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let result = match plugin.update(manifest_payload, wasi) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
Ok(()) => {
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(to_rustler_error(e))
|
||||
};
|
||||
// this forget should be safe because the context will clean up
|
||||
// all it's plugins when it is dropped
|
||||
@@ -129,28 +113,19 @@ fn plugin_free(ctx: ResourceArc<ExtismContext>, plugin_id: i32) -> Result<(), ru
|
||||
fn set_log_file(filename: String, log_level: String) -> Result<Atom, rustler::Error> {
|
||||
let path = Path::new(&filename);
|
||||
match log::Level::from_str(&log_level) {
|
||||
Err(_e) => Err(rustler::Error::Term(Box::new(format!(
|
||||
"{} not a valid log level",
|
||||
log_level
|
||||
)))),
|
||||
Err(_e) => Err(rustler::Error::Term(Box::new(format!("{} not a valid log level", log_level)))),
|
||||
Ok(level) => {
|
||||
if extism::set_log_file(path, Some(level)) {
|
||||
Ok(atoms::ok())
|
||||
} else {
|
||||
Err(rustler::Error::Term(Box::new(
|
||||
"Did not set log file, received false from the API.",
|
||||
)))
|
||||
Err(rustler::Error::Term(Box::new("Did not set log file, received false from the API.")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_has_function(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
function_name: String,
|
||||
) -> Result<bool, rustler::Error> {
|
||||
fn plugin_has_function(ctx: ResourceArc<ExtismContext>, plugin_id: i32, function_name: String) -> Result<bool, rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let has_function = plugin.has_function(function_name);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# This file is generated by dune, edit dune-project instead
|
||||
opam-version: "2.0"
|
||||
synopsis: "Extism manifest bindings"
|
||||
description: "Bindings to Extism, the universal plugin system"
|
||||
maintainer: ["Extism Authors <oss@extism.org>"]
|
||||
authors: ["Extism Authors <oss@extism.org>"]
|
||||
license: "BSD-3-Clause"
|
||||
tags: ["topics" "wasm" "plugin"]
|
||||
homepage: "https://github.com/extism/extism"
|
||||
doc: "https://github.com/extism/extism"
|
||||
bug-reports: "https://github.com/extism/extism/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.14.1"}
|
||||
"dune" {>= "3.2" & >= "3.2"}
|
||||
"ppx_yojson_conv" {>= "0.15.0"}
|
||||
"base64" {>= "3.5.0"}
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
"-p"
|
||||
name
|
||||
"-j"
|
||||
jobs
|
||||
"@install"
|
||||
"@runtest" {with-test}
|
||||
"@doc" {with-doc}
|
||||
]
|
||||
]
|
||||
dev-repo: "git+https://github.com/extism/extism.git"
|
||||
14
extism.go
14
extism.go
@@ -53,11 +53,11 @@ type WasmFile struct {
|
||||
}
|
||||
|
||||
type WasmUrl struct {
|
||||
Url string `json:"url"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Url string `json:"url"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
Header map[string]string `json:"header,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
}
|
||||
|
||||
type Wasm interface{}
|
||||
@@ -65,12 +65,10 @@ type Wasm interface{}
|
||||
type Manifest struct {
|
||||
Wasm []Wasm `json:"wasm"`
|
||||
Memory struct {
|
||||
MaxPages uint32 `json:"max_pages,omitempty"`
|
||||
Max uint32 `json:"max,omitempty"`
|
||||
} `json:"memory,omitempty"`
|
||||
Config map[string]string `json:"config,omitempty"`
|
||||
AllowedHosts []string `json:"allowed_hosts,omitempty"`
|
||||
AllowedPaths map[string]string `json:"allowed_paths,omitempty"`
|
||||
Timeout uint `json:"timeout_ms,omitempty"`
|
||||
}
|
||||
|
||||
func makePointer(data []byte) unsafe.Pointer {
|
||||
|
||||
15
extism.opam
15
extism.opam
@@ -10,14 +10,13 @@ homepage: "https://github.com/extism/extism"
|
||||
doc: "https://github.com/extism/extism"
|
||||
bug-reports: "https://github.com/extism/extism/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.14.1"}
|
||||
"dune" {>= "3.2" & >= "3.2"}
|
||||
"ctypes-foreign" {>= "0.18.0"}
|
||||
"bigstringaf" {>= "0.9.0"}
|
||||
"ppx_yojson_conv" {>= "0.15.0"}
|
||||
"extism-manifest"
|
||||
"ppx_inline_test" {>= "0.15.0"}
|
||||
"cmdliner" {>= "1.1.1"}
|
||||
"ocaml"
|
||||
"dune" {>= "3.2"}
|
||||
"ctypes-foreign"
|
||||
"bigstringaf"
|
||||
"ppx_yojson_conv"
|
||||
"base64"
|
||||
"ppx_inline_test"
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
module Main where
|
||||
|
||||
import System.Exit (exitFailure, exitSuccess)
|
||||
import qualified Data.ByteString as B
|
||||
import Extism
|
||||
import Extism.Manifest(manifest, wasmFile)
|
||||
import Extism.Manifest
|
||||
|
||||
unwrap (Right x) = x
|
||||
unwrap (Left (ExtismError msg)) = do
|
||||
error msg
|
||||
try f (Right x) = f x
|
||||
try f (Left (ErrorMessage msg)) = do
|
||||
_ <- putStrLn msg
|
||||
exitFailure
|
||||
|
||||
handlePlugin plugin = do
|
||||
res <- Extism.call plugin "count_vowels" (Extism.toByteString "this is a test")
|
||||
try (\bs -> do
|
||||
_ <- putStrLn (Extism.fromByteString bs)
|
||||
_ <- Extism.free plugin
|
||||
exitSuccess) res
|
||||
|
||||
main = do
|
||||
let m = manifest [wasmFile "../wasm/code.wasm"]
|
||||
context <- Extism.newContext
|
||||
plugin <- unwrap <$> Extism.pluginFromManifest context m False
|
||||
res <- unwrap <$> Extism.call plugin "count_vowels" (Extism.toByteString "this is a test")
|
||||
putStrLn (Extism.fromByteString res)
|
||||
Extism.free plugin
|
||||
context <- Extism.newContext ()
|
||||
plugin <- Extism.pluginFromManifest context (manifest [wasmFile "../wasm/code.wasm"]) False
|
||||
try handlePlugin plugin
|
||||
@@ -1,30 +0,0 @@
|
||||
.PHONY: test
|
||||
|
||||
prepare:
|
||||
cabal update
|
||||
|
||||
build: prepare
|
||||
cabal build
|
||||
|
||||
test: prepare
|
||||
cabal test
|
||||
|
||||
clean:
|
||||
cabal clean
|
||||
|
||||
publish: clean prepare
|
||||
cabal sdist
|
||||
# TODO: upload
|
||||
|
||||
format:
|
||||
# TODO
|
||||
|
||||
lint:
|
||||
cabal check
|
||||
hlint src manifest
|
||||
|
||||
docs:
|
||||
# TODO
|
||||
|
||||
show-docs: docs
|
||||
# TODO
|
||||
@@ -1 +0,0 @@
|
||||
packages: extism.cabal manifest/extism-manifest.cabal
|
||||
@@ -1,45 +1,54 @@
|
||||
cabal-version: 3.0
|
||||
cabal-version: 2.4
|
||||
name: extism
|
||||
version: 0.0.1
|
||||
license: BSD-3-Clause
|
||||
maintainer: oss@extism.org
|
||||
version: 0.0.1.0
|
||||
|
||||
-- A short (one-line) description of the package.
|
||||
synopsis: Extism bindings
|
||||
|
||||
-- A longer description of the package.
|
||||
description: Bindings to Extism, the universal plugin system
|
||||
|
||||
-- A URL where users can report bugs.
|
||||
bug-reports: https://github.com/extism/extism
|
||||
|
||||
-- The license under which the package is released.
|
||||
license: BSD-3-Clause
|
||||
|
||||
author: Extism authors
|
||||
bug-reports: https://github.com/extism/extism
|
||||
synopsis: Extism bindings
|
||||
description: Bindings to Extism, the universal plugin system
|
||||
category: Plugins, WebAssembly
|
||||
maintainer: oss@extism.org
|
||||
|
||||
-- A copyright notice.
|
||||
-- copyright:
|
||||
category: Plugins, WebAssembly
|
||||
extra-source-files: CHANGELOG.md
|
||||
|
||||
library
|
||||
exposed-modules: Extism
|
||||
reexported-modules: Extism.Manifest
|
||||
hs-source-dirs: src
|
||||
other-modules: Extism.Bindings
|
||||
default-language: Haskell2010
|
||||
extra-libraries: extism
|
||||
extra-lib-dirs: /usr/local/lib
|
||||
build-depends:
|
||||
base >= 4.16.1 && < 4.18.0,
|
||||
bytestring >= 0.11.3 && < 0.12,
|
||||
json >= 0.10 && < 0.11,
|
||||
extism-manifest >= 0.0.0 && < 0.1.0
|
||||
exposed-modules: Extism Extism.Manifest
|
||||
|
||||
test-suite extism-example
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Example.hs
|
||||
-- Modules included in this library but not exported.
|
||||
other-modules:
|
||||
|
||||
-- LANGUAGE extensions used by modules in this package.
|
||||
-- other-extensions:
|
||||
build-depends:
|
||||
base ^>=4.16.1.0
|
||||
, bytestring
|
||||
, base64-bytestring
|
||||
, json
|
||||
hs-source-dirs: src
|
||||
default-language: Haskell2010
|
||||
build-depends:
|
||||
base,
|
||||
extism,
|
||||
bytestring
|
||||
|
||||
test-suite extism-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Test.hs
|
||||
extra-libraries: extism
|
||||
extra-lib-dirs: /usr/local/lib
|
||||
|
||||
Test-Suite extism-example
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Example.hs
|
||||
build-depends: base, extism, bytestring
|
||||
default-language: Haskell2010
|
||||
|
||||
Test-Suite extism-test
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Test.hs
|
||||
hs-source-dirs: test
|
||||
build-depends: base, extism, bytestring, HUnit
|
||||
default-language: Haskell2010
|
||||
build-depends:
|
||||
base,
|
||||
extism,
|
||||
bytestring,
|
||||
HUnit
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Revision history for extism-manifest
|
||||
|
||||
## 0.1.0.0 -- YYYY-mm-dd
|
||||
|
||||
* First version. Released on an unsuspecting world.
|
||||
@@ -1,58 +0,0 @@
|
||||
module Extism.JSON (
|
||||
module Extism.JSON,
|
||||
module Text.JSON
|
||||
) where
|
||||
|
||||
import Text.JSON
|
||||
import qualified Data.ByteString as B
|
||||
import Data.ByteString.Internal (c2w, w2c)
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Char8 as BS (unpack)
|
||||
|
||||
data Nullable a = Null | NotNull a
|
||||
|
||||
makeArray x = JSArray [showJSON a | a <- x]
|
||||
isNull JSNull = True
|
||||
isNull _ = False
|
||||
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
|
||||
object x = makeObj $ filterNulls x
|
||||
objectWithNulls x = makeObj x
|
||||
nonNull x = NotNull x
|
||||
null' = Null
|
||||
(.=) a b = (a, showJSON b)
|
||||
toNullable (Just x) = NotNull x
|
||||
toNullable Nothing = Null
|
||||
fromNullable (NotNull x) = Just x
|
||||
fromNullable Null = Nothing
|
||||
mapNullable f Null = Null
|
||||
mapNullable f (NotNull x) = NotNull (f x)
|
||||
|
||||
(.?) (JSObject a) k =
|
||||
case valFromObj k a of
|
||||
Ok x -> NotNull x
|
||||
Error _ -> Null
|
||||
(.?) _ _ = Null
|
||||
(.??) a k = toNullable $ lookup k a
|
||||
|
||||
find :: JSON a => String -> JSValue -> Nullable a
|
||||
find k obj = obj .? k
|
||||
|
||||
update :: JSON a => String -> a -> JSValue -> JSValue
|
||||
update k v (JSObject obj) = object $ (fromJSObject obj) ++ [k .= v]
|
||||
|
||||
instance JSON a => JSON (Nullable a) where
|
||||
showJSON (NotNull x) = showJSON x
|
||||
showJSON Null = JSNull
|
||||
readJSON JSNull = Ok Null
|
||||
readJSON x = readJSON x
|
||||
|
||||
|
||||
newtype Base64 = Base64 B.ByteString
|
||||
|
||||
instance JSON Base64 where
|
||||
showJSON (Base64 bs) = showJSON (BS.unpack $ B64.encode bs)
|
||||
readJSON (JSString s) =
|
||||
let toByteString x = B.pack (Prelude.map c2w x) in
|
||||
case B64.decode (toByteString (fromJSString s)) of
|
||||
Left msg -> Error msg
|
||||
Right d -> Ok (Base64 d)
|
||||
@@ -1,247 +0,0 @@
|
||||
module Extism.Manifest where
|
||||
|
||||
import Extism.JSON
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Char8 as BS (unpack)
|
||||
|
||||
-- | Memory options
|
||||
newtype Memory = Memory
|
||||
{
|
||||
memoryMaxPages :: Nullable Int
|
||||
}
|
||||
|
||||
instance JSON Memory where
|
||||
showJSON (Memory max) =
|
||||
object [
|
||||
"max_pages" .= max
|
||||
]
|
||||
readJSON obj =
|
||||
let max = obj .? "max_pages" in
|
||||
Ok (Memory max)
|
||||
|
||||
-- | HTTP request
|
||||
data HTTPRequest = HTTPRequest
|
||||
{
|
||||
url :: String
|
||||
, headers :: Nullable [(String, String)]
|
||||
, method :: Nullable String
|
||||
}
|
||||
|
||||
makeKV x =
|
||||
object [(k, showJSON v) | (k, v) <- x]
|
||||
|
||||
requestObj (HTTPRequest url headers method) =
|
||||
[
|
||||
"url" .= url,
|
||||
"headers" .= mapNullable makeKV headers,
|
||||
"method" .= method
|
||||
]
|
||||
|
||||
instance JSON HTTPRequest where
|
||||
showJSON req = object $ requestObj req
|
||||
readJSON x =
|
||||
let url = x .? "url" in
|
||||
let headers = x .? "headers" in
|
||||
let method = x .? "method" in
|
||||
case url of
|
||||
Null -> Error "Missing 'url' field"
|
||||
NotNull url -> Ok (HTTPRequest url headers method)
|
||||
|
||||
|
||||
-- | WASM from file
|
||||
data WasmFile = WasmFile
|
||||
{
|
||||
filePath :: String
|
||||
, fileName :: Nullable String
|
||||
, fileHash :: Nullable String
|
||||
}
|
||||
|
||||
instance JSON WasmFile where
|
||||
showJSON (WasmFile path name hash) =
|
||||
object [
|
||||
"path" .= path,
|
||||
"name" .= name,
|
||||
"hash" .= hash
|
||||
]
|
||||
readJSON x =
|
||||
let path = x .? "url" in
|
||||
let name = x .? "name" in
|
||||
let hash = x .? "hash" in
|
||||
case path of
|
||||
Null -> Error "Missing 'path' field"
|
||||
NotNull path -> Ok (WasmFile path name hash)
|
||||
|
||||
|
||||
|
||||
-- | WASM from raw bytes
|
||||
data WasmData = WasmData
|
||||
{
|
||||
dataBytes :: Base64
|
||||
, dataName :: Nullable String
|
||||
, dataHash :: Nullable String
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
instance JSON WasmData where
|
||||
showJSON (WasmData bytes name hash) =
|
||||
object [
|
||||
"data" .= bytes,
|
||||
"name" .= name,
|
||||
"hash" .= hash
|
||||
]
|
||||
readJSON x =
|
||||
let d = x .? "data" in
|
||||
let name = x .? "name" in
|
||||
let hash = x .? "hash" in
|
||||
case d of
|
||||
Null -> Error "Missing 'path' field"
|
||||
NotNull d ->
|
||||
case readJSON d of
|
||||
Error msg -> Error msg
|
||||
Ok d' -> Ok (WasmData d' name hash)
|
||||
|
||||
|
||||
-- | WASM from a URL
|
||||
data WasmURL = WasmURL
|
||||
{
|
||||
req :: HTTPRequest
|
||||
, urlName :: Nullable String
|
||||
, urlHash :: Nullable String
|
||||
}
|
||||
|
||||
|
||||
instance JSON WasmURL where
|
||||
showJSON (WasmURL req name hash) =
|
||||
object (
|
||||
"name" .= name :
|
||||
"hash" .= hash :
|
||||
requestObj req)
|
||||
readJSON x =
|
||||
let req = x .? "req" in
|
||||
let name = x .? "name" in
|
||||
let hash = x .? "hash" in
|
||||
case fromNullable req of
|
||||
Nothing -> Error "Missing 'req' field"
|
||||
Just req -> Ok (WasmURL req name hash)
|
||||
|
||||
-- | Specifies where to get WASM module data
|
||||
data Wasm = File WasmFile | Data WasmData | URL WasmURL
|
||||
|
||||
instance JSON Wasm where
|
||||
showJSON x =
|
||||
case x of
|
||||
File f -> showJSON f
|
||||
Data d -> showJSON d
|
||||
URL u -> showJSON u
|
||||
readJSON x =
|
||||
let file = (readJSON x :: Result WasmFile) in
|
||||
case file of
|
||||
Ok x -> Ok (File x)
|
||||
Error _ ->
|
||||
let data' = (readJSON x :: Result WasmData) in
|
||||
case data' of
|
||||
Ok x -> Ok (Data x)
|
||||
Error _ ->
|
||||
let url = (readJSON x :: Result WasmURL) in
|
||||
case url of
|
||||
Ok x -> Ok (URL x)
|
||||
Error _ -> Error "JSON does not match any of the Wasm types"
|
||||
|
||||
wasmFile :: String -> Wasm
|
||||
wasmFile path =
|
||||
File WasmFile { filePath = path, fileName = null', fileHash = null'}
|
||||
|
||||
wasmURL :: String -> String -> Wasm
|
||||
wasmURL method url =
|
||||
let r = HTTPRequest { url = url, headers = null', method = nonNull method } in
|
||||
URL WasmURL { req = r, urlName = null', urlHash = null' }
|
||||
|
||||
wasmData :: B.ByteString -> Wasm
|
||||
wasmData d =
|
||||
Data WasmData { dataBytes = Base64 d, dataName = null', dataHash = null' }
|
||||
|
||||
withName :: Wasm -> String -> Wasm
|
||||
withName (Data d) name = Data d { dataName = nonNull name }
|
||||
withName (URL url) name = URL url { urlName = nonNull name }
|
||||
withName (File f) name = File f { fileName = nonNull name }
|
||||
|
||||
|
||||
withHash :: Wasm -> String -> Wasm
|
||||
withHash (Data d) hash = Data d { dataHash = nonNull hash }
|
||||
withHash (URL url) hash = URL url { urlHash = nonNull hash }
|
||||
withHash (File f) hash = File f { fileHash = nonNull hash }
|
||||
|
||||
-- | The 'Manifest' type is used to provide WASM data and configuration to the
|
||||
-- | Extism runtime
|
||||
data Manifest = Manifest
|
||||
{
|
||||
wasm :: [Wasm]
|
||||
, memory :: Nullable Memory
|
||||
, config :: Nullable [(String, String)]
|
||||
, allowedHosts :: Nullable [String]
|
||||
, allowedPaths :: Nullable [(String, String)]
|
||||
, timeout :: Nullable Int
|
||||
}
|
||||
|
||||
|
||||
instance JSON Manifest where
|
||||
showJSON (Manifest wasm memory config hosts paths timeout) =
|
||||
let w = makeArray wasm in
|
||||
object [
|
||||
"wasm" .= w,
|
||||
"memory" .= memory,
|
||||
"config" .= mapNullable makeKV config,
|
||||
"allowed_hosts" .= hosts,
|
||||
"allowed_paths" .= mapNullable makeKV paths,
|
||||
"timeout_ms" .= timeout
|
||||
]
|
||||
readJSON x =
|
||||
let wasm = x .? "wasm" in
|
||||
let memory = x .? "memory" in
|
||||
let config = x .? "config" in
|
||||
let hosts = x .? "allowed_hosts" in
|
||||
let paths = x .? "allowed_paths" in
|
||||
let timeout = x .? "timeout_ms" in
|
||||
case fromNullable wasm of
|
||||
Nothing -> Error "Missing 'wasm' field"
|
||||
Just wasm -> Ok (Manifest wasm memory config hosts paths timeout)
|
||||
|
||||
-- | Create a new 'Manifest' from a list of 'Wasm'
|
||||
manifest :: [Wasm] -> Manifest
|
||||
manifest wasm =
|
||||
Manifest {
|
||||
wasm = wasm,
|
||||
memory = null',
|
||||
config = null',
|
||||
allowedHosts = null',
|
||||
allowedPaths = null',
|
||||
timeout = null'
|
||||
}
|
||||
|
||||
-- | Update the config values
|
||||
withConfig :: Manifest -> [(String, String)] -> Manifest
|
||||
withConfig m config =
|
||||
m { config = nonNull config }
|
||||
|
||||
|
||||
-- | Update allowed hosts for `extism_http_request`
|
||||
withHosts :: Manifest -> [String] -> Manifest
|
||||
withHosts m hosts =
|
||||
m { allowedHosts = nonNull hosts }
|
||||
|
||||
|
||||
-- | Update allowed paths
|
||||
withPaths :: Manifest -> [(String, String)] -> Manifest
|
||||
withPaths m p =
|
||||
m { allowedPaths = nonNull p }
|
||||
|
||||
-- | Update plugin timeout (in milliseconds)
|
||||
withTimeout :: Manifest -> Int -> Manifest
|
||||
withTimeout m t =
|
||||
m { timeout = nonNull t }
|
||||
|
||||
toString :: (JSON a) => a -> String
|
||||
toString v =
|
||||
encode (showJSON v)
|
||||
@@ -1,21 +0,0 @@
|
||||
cabal-version: 3.0
|
||||
name: extism-manifest
|
||||
version: 0.0.1
|
||||
license: BSD-3-Clause
|
||||
maintainer: oss@extism.org
|
||||
author: Extism authors
|
||||
bug-reports: https://github.com/extism/extism
|
||||
synopsis: Extism manifest bindings
|
||||
description: Bindings to Extism WebAssembly manifest
|
||||
category: Plugins, WebAssembly
|
||||
extra-source-files: CHANGELOG.md
|
||||
|
||||
library
|
||||
exposed-modules: Extism.Manifest Extism.JSON
|
||||
hs-source-dirs: .
|
||||
default-language: Haskell2010
|
||||
build-depends:
|
||||
base >= 4.16.1 && < 4.18.0,
|
||||
bytestring >= 0.11.3 && < 0.12,
|
||||
json >= 0.10 && < 0.11,
|
||||
base64-bytestring >= 1.2.1 && < 1.3,
|
||||
@@ -1,68 +1,84 @@
|
||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||
|
||||
module Extism (module Extism, module Extism.Manifest) where
|
||||
import Data.Int
|
||||
import Data.Word
|
||||
import Control.Monad (void)
|
||||
import GHC.Int
|
||||
import GHC.Word
|
||||
import Foreign.C.Types
|
||||
import Foreign.Ptr
|
||||
import Foreign.ForeignPtr
|
||||
import Foreign.C.String
|
||||
import Foreign.Ptr
|
||||
import Control.Monad (void)
|
||||
import Data.ByteString as B
|
||||
import Data.ByteString.Internal (c2w, w2c)
|
||||
import Data.ByteString.Unsafe (unsafeUseAsCString)
|
||||
import Data.Bifunctor (second)
|
||||
import Text.JSON (encode, toJSObject, showJSON)
|
||||
import Text.JSON (JSON, toJSObject, toJSString, encode, JSValue(JSNull, JSString))
|
||||
import Extism.Manifest (Manifest, toString)
|
||||
import Extism.Bindings
|
||||
|
||||
-- | Context for managing plugins
|
||||
newtype ExtismContext = ExtismContext () deriving Show
|
||||
|
||||
foreign import ccall unsafe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext)
|
||||
foreign import ccall unsafe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ())
|
||||
foreign import ccall unsafe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> CBool -> IO Int32
|
||||
foreign import ccall unsafe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> CBool -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
|
||||
foreign import ccall unsafe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString
|
||||
foreign import ccall unsafe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64
|
||||
foreign import ccall unsafe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8)
|
||||
foreign import ccall unsafe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO ()
|
||||
foreign import ccall unsafe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO ()
|
||||
foreign import ccall unsafe "extism.h extism_version" extism_version :: IO CString
|
||||
|
||||
-- Context manages plugins
|
||||
newtype Context = Context (ForeignPtr ExtismContext)
|
||||
|
||||
-- | Plugins can be used to call WASM function
|
||||
-- Plugins can be used to call WASM function
|
||||
data Plugin = Plugin Context Int32
|
||||
|
||||
-- | Log level
|
||||
-- Log level
|
||||
data LogLevel = Error | Warn | Info | Debug | Trace deriving (Show)
|
||||
|
||||
-- | Extism error
|
||||
newtype Error = ExtismError String deriving Show
|
||||
-- Extism error
|
||||
newtype Error = ErrorMessage String deriving Show
|
||||
|
||||
-- | Result type
|
||||
type Result a = Either Error a
|
||||
|
||||
-- | Helper function to convert a 'String' to a 'ByteString'
|
||||
-- Helper function to convert a string to a bytestring
|
||||
toByteString :: String -> ByteString
|
||||
toByteString x = B.pack (Prelude.map c2w x)
|
||||
|
||||
-- | Helper function to convert a 'ByteString' to a 'String'
|
||||
-- Helper function to convert a bytestring to a string
|
||||
fromByteString :: ByteString -> String
|
||||
fromByteString bs = Prelude.map w2c $ B.unpack bs
|
||||
|
||||
-- | Get the Extism version string
|
||||
-- Get the Extism version string
|
||||
extismVersion :: () -> IO String
|
||||
extismVersion () = do
|
||||
v <- extism_version
|
||||
peekCString v
|
||||
|
||||
-- | Remove all registered plugins in a 'Context'
|
||||
-- Remove all registered plugins in a Context
|
||||
reset :: Context -> IO ()
|
||||
reset (Context ctx) =
|
||||
withForeignPtr ctx extism_context_reset
|
||||
|
||||
-- | Create a new 'Context'
|
||||
newContext :: IO Context
|
||||
newContext = do
|
||||
-- Create a new context
|
||||
newContext :: () -> IO Context
|
||||
newContext () = do
|
||||
ptr <- extism_context_new
|
||||
fptr <- newForeignPtr extism_context_free ptr
|
||||
return (Context fptr)
|
||||
|
||||
-- | Execute a function with a new 'Context' that is destroyed when it returns
|
||||
-- Execute a function with a new context that is destroyed when it returns
|
||||
withContext :: (Context -> IO a) -> IO a
|
||||
withContext f = do
|
||||
ctx <- newContext
|
||||
ctx <- newContext ()
|
||||
f ctx
|
||||
|
||||
-- | Create a 'Plugin' from a WASM module, `useWasi` determines if WASI should
|
||||
-- | be linked
|
||||
plugin :: Context -> B.ByteString -> Bool -> IO (Result Plugin)
|
||||
-- Create a plugin from a WASM module, `useWasi` determines if WASI should
|
||||
-- be linked
|
||||
plugin :: Context -> B.ByteString -> Bool -> IO (Either Error Plugin)
|
||||
plugin c wasm useWasi =
|
||||
let length = fromIntegral (B.length wasm) in
|
||||
let wasi = fromInteger (if useWasi then 1 else 0) in
|
||||
@@ -74,18 +90,18 @@ plugin c wasm useWasi =
|
||||
if p < 0 then do
|
||||
err <- extism_error ctx (-1)
|
||||
e <- peekCString err
|
||||
return $ Left (ExtismError e)
|
||||
return $ Left (ErrorMessage e)
|
||||
else
|
||||
return $ Right (Plugin c p))
|
||||
|
||||
-- | Create a 'Plugin' from a 'Manifest'
|
||||
pluginFromManifest :: Context -> Manifest -> Bool -> IO (Result Plugin)
|
||||
-- Create a plugin from a Manifest
|
||||
pluginFromManifest :: Context -> Manifest -> Bool -> IO (Either Error Plugin)
|
||||
pluginFromManifest ctx manifest useWasi =
|
||||
let wasm = toByteString $ toString manifest in
|
||||
plugin ctx wasm useWasi
|
||||
|
||||
-- | Update a 'Plugin' with a new WASM module
|
||||
update :: Plugin -> B.ByteString -> Bool -> IO (Result ())
|
||||
-- Update a plugin with a new WASM module
|
||||
update :: Plugin -> B.ByteString -> Bool -> IO (Either Error ())
|
||||
update (Plugin (Context ctx) id) wasm useWasi =
|
||||
let length = fromIntegral (B.length wasm) in
|
||||
let wasi = fromInteger (if useWasi then 1 else 0) in
|
||||
@@ -96,27 +112,30 @@ update (Plugin (Context ctx) id) wasm useWasi =
|
||||
if b <= 0 then do
|
||||
err <- extism_error ctx (-1)
|
||||
e <- peekCString err
|
||||
return $ Left (ExtismError e)
|
||||
return $ Left (ErrorMessage e)
|
||||
else
|
||||
return (Right ()))
|
||||
|
||||
-- | Update a 'Plugin' with a new 'Manifest'
|
||||
updateManifest :: Plugin -> Manifest -> Bool -> IO (Result ())
|
||||
-- Update a plugin with a new Manifest
|
||||
updateManifest :: Plugin -> Manifest -> Bool -> IO (Either Error ())
|
||||
updateManifest plugin manifest useWasi =
|
||||
let wasm = toByteString $ toString manifest in
|
||||
update plugin wasm useWasi
|
||||
|
||||
-- | Check if a 'Plugin' is valid
|
||||
-- Check if a plugin is value
|
||||
isValid :: Plugin -> Bool
|
||||
isValid (Plugin _ p) = p >= 0
|
||||
|
||||
-- | Set configuration values for a plugin
|
||||
convertMaybeString Nothing = JSNull
|
||||
convertMaybeString (Just s) = JSString (toJSString s)
|
||||
|
||||
-- Set configuration values for a plugin
|
||||
setConfig :: Plugin -> [(String, Maybe String)] -> IO Bool
|
||||
setConfig (Plugin (Context ctx) plugin) x =
|
||||
if plugin < 0
|
||||
then return False
|
||||
else
|
||||
let obj = toJSObject [(k, showJSON v) | (k, v) <- x] in
|
||||
let obj = toJSObject [(k, convertMaybeString v) | (k, v) <- x] in
|
||||
let bs = toByteString (encode obj) in
|
||||
let length = fromIntegral (B.length bs) in
|
||||
unsafeUseAsCString bs (\s -> do
|
||||
@@ -130,7 +149,7 @@ levelStr Warn = "warn"
|
||||
levelStr Trace = "trace"
|
||||
levelStr Info = "info"
|
||||
|
||||
-- | Set the log file and level, this is a global configuration
|
||||
-- Set the log file and level, this is a global configuration
|
||||
setLogFile :: String -> LogLevel -> IO Bool
|
||||
setLogFile filename level =
|
||||
let s = levelStr level in
|
||||
@@ -139,15 +158,15 @@ setLogFile filename level =
|
||||
b <- extism_log_file f l
|
||||
return $ b /= 0))
|
||||
|
||||
-- | Check if a function exists in the given plugin
|
||||
-- Check if a function exists in the given plugin
|
||||
functionExists :: Plugin -> String -> IO Bool
|
||||
functionExists (Plugin (Context ctx) plugin) name = do
|
||||
withForeignPtr ctx (\ctx -> do
|
||||
b <- withCString name (extism_plugin_function_exists ctx plugin)
|
||||
if b == 1 then return True else return False)
|
||||
|
||||
--- | Call a function provided by the given plugin
|
||||
call :: Plugin -> String -> B.ByteString -> IO (Result B.ByteString)
|
||||
--- Call a function provided by the given plugin
|
||||
call :: Plugin -> String -> B.ByteString -> IO (Either Error B.ByteString)
|
||||
call (Plugin (Context ctx) plugin) name input =
|
||||
let length = fromIntegral (B.length input) in
|
||||
do
|
||||
@@ -158,17 +177,16 @@ call (Plugin (Context ctx) plugin) name input =
|
||||
err <- extism_error ctx plugin
|
||||
if err /= nullPtr
|
||||
then do e <- peekCString err
|
||||
return $ Left (ExtismError e)
|
||||
return $ Left (ErrorMessage e)
|
||||
else if rc == 0
|
||||
then do
|
||||
length <- extism_plugin_output_length ctx plugin
|
||||
ptr <- extism_plugin_output_data ctx plugin
|
||||
buf <- packCStringLen (castPtr ptr, fromIntegral length)
|
||||
return $ Right buf
|
||||
else return $ Left (ExtismError "Call failed"))
|
||||
else return $ Left (ErrorMessage "Call failed"))
|
||||
|
||||
-- | Free a 'Plugin', this will automatically be called for every plugin
|
||||
-- | associated with a 'Context' when that 'Context' is freed
|
||||
-- Free a plugin
|
||||
free :: Plugin -> IO ()
|
||||
free (Plugin (Context ctx) plugin) =
|
||||
withForeignPtr ctx (`extism_plugin_free` plugin)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||
|
||||
module Extism.Bindings where
|
||||
|
||||
import Foreign.C.Types
|
||||
import Foreign.Ptr
|
||||
import Foreign.C.String
|
||||
import Data.Int
|
||||
import Data.Word
|
||||
|
||||
newtype ExtismContext = ExtismContext () deriving Show
|
||||
|
||||
foreign import ccall unsafe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext)
|
||||
foreign import ccall unsafe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ())
|
||||
foreign import ccall unsafe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> CBool -> IO Int32
|
||||
foreign import ccall unsafe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> CBool -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
|
||||
foreign import ccall unsafe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString
|
||||
foreign import ccall unsafe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64
|
||||
foreign import ccall unsafe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8)
|
||||
foreign import ccall unsafe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool
|
||||
foreign import ccall unsafe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO ()
|
||||
foreign import ccall unsafe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO ()
|
||||
foreign import ccall unsafe "extism.h extism_version" extism_version :: IO CString
|
||||
183
haskell/src/Extism/Manifest.hs
Normal file
183
haskell/src/Extism/Manifest.hs
Normal file
@@ -0,0 +1,183 @@
|
||||
module Extism.Manifest where
|
||||
|
||||
import Text.JSON
|
||||
(
|
||||
JSValue(JSNull, JSString, JSArray),
|
||||
toJSString, showJSON, makeObj, encode
|
||||
)
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Char8 as BS (unpack)
|
||||
|
||||
valueOrNull f Nothing = JSNull
|
||||
valueOrNull f (Just x) = f x
|
||||
makeString s = JSString (toJSString s)
|
||||
stringOrNull = valueOrNull makeString
|
||||
makeArray f [] = JSNull
|
||||
makeArray f x = JSArray [f a | a <- x]
|
||||
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
|
||||
mapObj f x = makeObj (filterNulls [(a, f b) | (a, b) <- x])
|
||||
isNull JSNull = True
|
||||
isNull _ = False
|
||||
|
||||
newtype Memory = Memory
|
||||
{
|
||||
memoryMax :: Maybe Int
|
||||
}
|
||||
|
||||
class JSONValue a where
|
||||
toJSONValue :: a -> JSValue
|
||||
|
||||
instance JSONValue Memory where
|
||||
toJSONValue x =
|
||||
case memoryMax x of
|
||||
Nothing -> makeObj []
|
||||
Just max -> makeObj [("max", showJSON max)]
|
||||
|
||||
data HttpRequest = HttpRequest
|
||||
{
|
||||
url :: String
|
||||
, header :: [(String, String)]
|
||||
, method :: Maybe String
|
||||
}
|
||||
|
||||
requestObj x =
|
||||
let meth = stringOrNull $ method x in
|
||||
let h = mapObj makeString $ header x in
|
||||
filterNulls [
|
||||
("url", makeString $ url x),
|
||||
("header", h),
|
||||
("method", meth)
|
||||
]
|
||||
|
||||
instance JSONValue HttpRequest where
|
||||
toJSONValue x =
|
||||
makeObj $ requestObj x
|
||||
|
||||
data WasmFile = WasmFile
|
||||
{
|
||||
filePath :: String
|
||||
, fileName :: Maybe String
|
||||
, fileHash :: Maybe String
|
||||
}
|
||||
|
||||
instance JSONValue WasmFile where
|
||||
toJSONValue x =
|
||||
let path = makeString $ filePath x in
|
||||
let name = stringOrNull $ fileName x in
|
||||
let hash = stringOrNull $ fileHash x in
|
||||
makeObj $ filterNulls [
|
||||
("path", path),
|
||||
("name", name),
|
||||
("hash", hash)
|
||||
]
|
||||
|
||||
data WasmCode = WasmCode
|
||||
{
|
||||
codeBytes :: B.ByteString
|
||||
, codeName :: Maybe String
|
||||
, codeHash :: Maybe String
|
||||
}
|
||||
|
||||
|
||||
instance JSONValue WasmCode where
|
||||
toJSONValue x =
|
||||
let bytes = makeString $ BS.unpack $ B64.encode $ codeBytes x in
|
||||
let name = stringOrNull $ codeName x in
|
||||
let hash = stringOrNull $ codeHash x in
|
||||
makeObj $ filterNulls [
|
||||
("data", bytes),
|
||||
("name", name),
|
||||
("hash", hash)
|
||||
]
|
||||
|
||||
data WasmURL = WasmURL
|
||||
{
|
||||
req :: HttpRequest
|
||||
, urlName :: Maybe String
|
||||
, urlHash :: Maybe String
|
||||
}
|
||||
|
||||
|
||||
instance JSONValue WasmURL where
|
||||
toJSONValue x =
|
||||
let request = requestObj $ req x in
|
||||
let name = stringOrNull $ urlName x in
|
||||
let hash = stringOrNull $ urlHash x in
|
||||
makeObj $ filterNulls $ ("name", name) : ("hash", hash) : request
|
||||
|
||||
data Wasm = File WasmFile | Code WasmCode | URL WasmURL
|
||||
|
||||
instance JSONValue Wasm where
|
||||
toJSONValue x =
|
||||
case x of
|
||||
File f -> toJSONValue f
|
||||
Code d -> toJSONValue d
|
||||
URL u -> toJSONValue u
|
||||
|
||||
wasmFile :: String -> Wasm
|
||||
wasmFile path =
|
||||
File WasmFile { filePath = path, fileName = Nothing, fileHash = Nothing}
|
||||
|
||||
wasmURL :: String -> String -> Wasm
|
||||
wasmURL method url =
|
||||
let r = HttpRequest { url = url, header = [], method = Just method } in
|
||||
URL WasmURL { req = r, urlName = Nothing, urlHash = Nothing }
|
||||
|
||||
wasmCode :: B.ByteString -> Wasm
|
||||
wasmCode code =
|
||||
Code WasmCode { codeBytes = code, codeName = Nothing, codeHash = Nothing }
|
||||
|
||||
withName :: Wasm -> String -> Wasm
|
||||
withName (Code code) name = Code code { codeName = Just name }
|
||||
withName (URL url) name = URL url { urlName = Just name }
|
||||
withName (File f) name = File f { fileName = Just name }
|
||||
|
||||
|
||||
withHash :: Wasm -> String -> Wasm
|
||||
withHash (Code code) hash = Code code { codeHash = Just hash }
|
||||
withHash (URL url) hash = URL url { urlHash = Just hash }
|
||||
withHash (File f) hash = File f { fileHash = Just hash }
|
||||
|
||||
data Manifest = Manifest
|
||||
{
|
||||
wasm :: [Wasm]
|
||||
, memory :: Maybe Memory
|
||||
, config :: [(String, String)]
|
||||
, allowed_hosts :: [String]
|
||||
}
|
||||
|
||||
manifest :: [Wasm] -> Manifest
|
||||
manifest wasm =
|
||||
Manifest {
|
||||
wasm = wasm,
|
||||
memory = Nothing,
|
||||
config = [],
|
||||
allowed_hosts = []
|
||||
}
|
||||
|
||||
withConfig :: Manifest -> [(String, String)] -> Manifest
|
||||
withConfig m config =
|
||||
m { config = config }
|
||||
|
||||
|
||||
withHosts :: Manifest -> [String] -> Manifest
|
||||
withHosts m hosts =
|
||||
m { allowed_hosts = hosts }
|
||||
|
||||
instance JSONValue Manifest where
|
||||
toJSONValue x =
|
||||
let w = makeArray toJSONValue $ wasm x in
|
||||
let mem = valueOrNull toJSONValue $ memory x in
|
||||
let c = mapObj makeString $ config x in
|
||||
let hosts = makeArray makeString $ allowed_hosts x in
|
||||
makeObj $ filterNulls [
|
||||
("wasm", w),
|
||||
("memory", mem),
|
||||
("config", c),
|
||||
("allowed_hosts", hosts)
|
||||
]
|
||||
|
||||
toString :: Manifest -> String
|
||||
toString manifest =
|
||||
encode (toJSONValue manifest)
|
||||
67
haskell/stack.yaml
Normal file
67
haskell/stack.yaml
Normal file
@@ -0,0 +1,67 @@
|
||||
# This file was automatically generated by 'stack init'
|
||||
#
|
||||
# Some commonly used options have been documented as comments in this file.
|
||||
# For advanced use and comprehensive documentation of the format, please see:
|
||||
# https://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||
|
||||
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||
# A snapshot resolver dictates the compiler version and the set of packages
|
||||
# to be used for project dependencies. For example:
|
||||
#
|
||||
# resolver: lts-3.5
|
||||
# resolver: nightly-2015-09-21
|
||||
# resolver: ghc-7.10.2
|
||||
#
|
||||
# The location of a snapshot can be provided as a file or url. Stack assumes
|
||||
# a snapshot provided as a file might change, whereas a url resource does not.
|
||||
#
|
||||
# resolver: ./custom-snapshot.yaml
|
||||
# resolver: https://example.com/snapshots/2018-01-01.yaml
|
||||
resolver:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/8/30.yaml
|
||||
|
||||
# User packages to be built.
|
||||
# Various formats can be used as shown in the example below.
|
||||
#
|
||||
# packages:
|
||||
# - some-directory
|
||||
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||
# subdirs:
|
||||
# - auto-update
|
||||
# - wai
|
||||
packages:
|
||||
- .
|
||||
# Dependency packages to be pulled from upstream that are not in the resolver.
|
||||
# These entries can reference officially published versions as well as
|
||||
# forks / in-progress versions pinned to a git hash. For example:
|
||||
#
|
||||
# extra-deps:
|
||||
# - acme-missiles-0.3
|
||||
# - git: https://github.com/commercialhaskell/stack.git
|
||||
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||
#
|
||||
# extra-deps: []
|
||||
|
||||
# Override default flag values for local packages and extra-deps
|
||||
# flags: {}
|
||||
|
||||
# Extra package databases containing global packages
|
||||
# extra-package-dbs: []
|
||||
|
||||
# Control whether we use the GHC we find on the path
|
||||
# system-ghc: true
|
||||
#
|
||||
# Require a specific version of stack, using version ranges
|
||||
# require-stack-version: -any # Default
|
||||
# require-stack-version: ">=2.7"
|
||||
#
|
||||
# Override the architecture used by stack, especially useful on Windows
|
||||
# arch: i386
|
||||
# arch: x86_64
|
||||
#
|
||||
# Extra directories used by stack for building
|
||||
# extra-include-dirs: [/path/to/dir]
|
||||
# extra-lib-dirs: [/path/to/dir]
|
||||
#
|
||||
# Allow a newer minor version of GHC than the snapshot specifies
|
||||
# compiler-check: newer-minor
|
||||
13
haskell/stack.yaml.lock
Normal file
13
haskell/stack.yaml.lock
Normal file
@@ -0,0 +1,13 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages: []
|
||||
snapshots:
|
||||
- completed:
|
||||
size: 632828
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/8/30.yaml
|
||||
sha256: 5b02c2ce430ac62843fb884126765628da2ca2280bb9de0c6635c723e32a9f6b
|
||||
original:
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/8/30.yaml
|
||||
@@ -3,15 +3,18 @@ import Extism
|
||||
import Extism.Manifest
|
||||
|
||||
|
||||
unwrap (Right x) = return x
|
||||
unwrap (Left (ExtismError msg)) =
|
||||
unwrap' (Right x) = return x
|
||||
unwrap' (Left (ErrorMessage msg)) =
|
||||
assertFailure msg
|
||||
|
||||
unwrap io = do
|
||||
x <- io
|
||||
unwrap' x
|
||||
|
||||
defaultManifest = manifest [wasmFile "test/code.wasm"]
|
||||
|
||||
initPlugin :: Context -> IO Plugin
|
||||
initPlugin context =
|
||||
Extism.pluginFromManifest context defaultManifest False >>= unwrap
|
||||
unwrap (Extism.pluginFromManifest context defaultManifest False)
|
||||
|
||||
pluginFunctionExists = do
|
||||
withContext (\ctx -> do
|
||||
@@ -22,7 +25,7 @@ pluginFunctionExists = do
|
||||
assertBool "function doesn't exist" (not exists'))
|
||||
|
||||
checkCallResult p = do
|
||||
res <- call p "count_vowels" (toByteString "this is a test") >>= unwrap
|
||||
res <- unwrap (call p "count_vowels" (toByteString "this is a test"))
|
||||
assertEqual "count vowels output" "{\"count\": 4}" (fromByteString res)
|
||||
|
||||
pluginCall = do
|
||||
@@ -42,7 +45,7 @@ pluginMultiple = do
|
||||
pluginUpdate = do
|
||||
withContext (\ctx -> do
|
||||
p <- initPlugin ctx
|
||||
updateManifest p defaultManifest True >>= unwrap
|
||||
unwrap (updateManifest p defaultManifest True)
|
||||
checkCallResult p)
|
||||
|
||||
pluginConfig = do
|
||||
@@ -52,7 +55,7 @@ pluginConfig = do
|
||||
assertBool "set config" b)
|
||||
|
||||
testSetLogFile = do
|
||||
b <- setLogFile "stderr" Extism.Error
|
||||
b <- setLogFile "stderr" Error
|
||||
assertBool "set log file" b
|
||||
|
||||
t name f = TestLabel name (TestCase f)
|
||||
|
||||
190
java/pom.xml
190
java/pom.xml
@@ -1,190 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.extism.sdk</groupId>
|
||||
<artifactId>extism</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1.0</version>
|
||||
<name>extism</name>
|
||||
<url>https://github.com/extism/extism</url>
|
||||
<description>Java-SDK for Extism to use webassembly from Java</description>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>BSD 3-Clause</name>
|
||||
<url>https://opensource.org/licenses/BSD-3-Clause</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<organization>
|
||||
<name>Dylibso, Inc.</name>
|
||||
<url>https://dylib.so</url>
|
||||
</organization>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>The Extism Authors</name>
|
||||
<email>oss@extism.org</email>
|
||||
<roles>
|
||||
<role>Maintainer</role>
|
||||
</roles>
|
||||
<organization>Dylibso, Inc.</organization>
|
||||
<organizationUrl>https://dylib.so</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/extism/extism.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/extism/extism.git</developerConnection>
|
||||
<url>https://github.com/extism/extism/java</url>
|
||||
<tag>main</tag>
|
||||
</scm>
|
||||
|
||||
<issueManagement>
|
||||
<system>Github</system>
|
||||
<url>https://github.com/extism/extism/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<!-- dependencies -->
|
||||
<jna.version>5.12.1</jna.version>
|
||||
<gson.version>2.10</gson.version>
|
||||
|
||||
<!-- testing -->
|
||||
<junit-jupiter-engine.version>5.9.1</junit-jupiter-engine.version>
|
||||
<assertj-core.version>3.23.1</assertj-core.version>
|
||||
|
||||
<!-- maven plugins -->
|
||||
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
|
||||
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
|
||||
|
||||
<!-- jreleaser -->
|
||||
<jreleaser.git.root.search>true</jreleaser.git.root.search>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadoc</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-source</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jreleaser</groupId>
|
||||
<artifactId>jreleaser-maven-plugin</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<configuration>
|
||||
<jreleaser>
|
||||
<release>
|
||||
<github>
|
||||
<skipRelease>true</skipRelease>
|
||||
</github>
|
||||
</release>
|
||||
<signing>
|
||||
<active>ALWAYS</active>
|
||||
<armored>true</armored>
|
||||
</signing>
|
||||
<deploy>
|
||||
<maven>
|
||||
<nexus2>
|
||||
<maven-central>
|
||||
<active>ALWAYS</active>
|
||||
<url>https://s01.oss.sonatype.org/service/local</url>
|
||||
<!--
|
||||
<closeRepository>false</closeRepository>
|
||||
<releaseRepository>false</releaseRepository>
|
||||
-->
|
||||
<stagingRepositories>target/staging-deploy</stagingRepositories>
|
||||
</maven-central>
|
||||
</nexus2>
|
||||
</maven>
|
||||
</deploy>
|
||||
</jreleaser>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<jna.library.path>../target/release</jna.library.path>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>${jna.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit-jupiter-engine.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>uk.org.webcompere</groupId>
|
||||
<artifactId>model-assert</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,39 +0,0 @@
|
||||
Extism Java-SDK
|
||||
---
|
||||
|
||||
Java SDK for the [extism](https://extism.org/) WebAssembly Plugin-System.
|
||||
|
||||
# Build
|
||||
|
||||
To build the extism java-sdk run the following command:
|
||||
|
||||
```
|
||||
mvn clean verify
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
To use the extism java-sdk you need to add the `org.extism.sdk` dependency to your dependency management and ensure that
|
||||
the native extism library is installed on your system. For installing the native library refer to the [extism documentation](https://extism.org/docs/install).
|
||||
|
||||
Instead of installing the native library on your system, you can also download the appropriate library for your platform
|
||||
yourself.
|
||||
To do that simply download the [extism release](https://github.com/extism/extism/releases) to a `folder` and run your java application with the system property `-Djna.library.path=/path/to/folder`.
|
||||
|
||||
## Maven
|
||||
To use the extism java-sdk with maven you need to add the following dependency to your `pom.xml` file:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.extism.sdk</groupId>
|
||||
<artifactId>extism</artifactId>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Gradle
|
||||
To use the extism java-sdk with maven you need to add the following dependency to your `build.gradle` file:
|
||||
|
||||
```
|
||||
implementation 'org.extism.sdk:extism:0.1.0'
|
||||
```
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import org.extism.sdk.manifest.Manifest;
|
||||
|
||||
/**
|
||||
* Extism Context is used to store and manage plugins.
|
||||
*/
|
||||
public class Context implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* Holds a pointer to the native ExtismContext struct.
|
||||
*/
|
||||
private final Pointer contextPointer;
|
||||
|
||||
/**
|
||||
* Creates a new context.
|
||||
* <p>
|
||||
* A Context is used to manage Plugins
|
||||
* and make sure they are cleaned up when you are done with them.
|
||||
*/
|
||||
public Context() {
|
||||
this.contextPointer = LibExtism.INSTANCE.extism_context_new();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin managed by this context.
|
||||
*
|
||||
* @param manifest The manifest for the plugin
|
||||
* @param withWASI Set to true to enable WASI
|
||||
* @return the plugin instance
|
||||
*/
|
||||
public Plugin newPlugin(Manifest manifest, boolean withWASI) {
|
||||
return new Plugin(this, manifest, withWASI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the context *and* frees all its Plugins. Use {@link #reset()}, if you just want to
|
||||
* free the plugins but keep the context. You should ensure this is called when you are done
|
||||
* with the context.
|
||||
*/
|
||||
public void free() {
|
||||
LibExtism.INSTANCE.extism_context_free(this.contextPointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the context by freeing all its Plugins. Unlike {@link #free()}, it does not
|
||||
* free the context itself.
|
||||
*/
|
||||
public void reset() {
|
||||
LibExtism.INSTANCE.extism_context_reset(this.contextPointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version string of the linked Extism Runtime.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public String getVersion() {
|
||||
return LibExtism.INSTANCE.extism_version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error associated with a context, if plugin is {@literal null} then the context error will be returned.
|
||||
*
|
||||
* @param plugin
|
||||
* @return the error message
|
||||
*/
|
||||
protected String error(Plugin plugin) {
|
||||
return LibExtism.INSTANCE.extism_error(this.contextPointer, plugin == null ? -1 : plugin.getIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the raw pointer to this context.
|
||||
*
|
||||
* @return the pointer
|
||||
*/
|
||||
public Pointer getPointer() {
|
||||
return this.contextPointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #free()} if used in the context of a TWR block.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
this.free();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import org.extism.sdk.manifest.Manifest;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Extism convenience functions.
|
||||
*/
|
||||
public class Extism {
|
||||
|
||||
/**
|
||||
* Configure a log file with the given {@link Path} and configure the given {@link LogLevel}.
|
||||
*
|
||||
* @param path
|
||||
* @param level
|
||||
*
|
||||
* @deprecated will be replaced with better logging API.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void setLogFile(Path path, LogLevel level) {
|
||||
|
||||
Objects.requireNonNull(path, "path");
|
||||
Objects.requireNonNull(level, "level");
|
||||
|
||||
var result = LibExtism.INSTANCE.extism_log_file(path.toString(), level.getLevel());
|
||||
if (!result) {
|
||||
var error = String.format("Could not set extism logger to %s with level %s", path, level);
|
||||
throw new ExtismException(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the named {@code function} from the {@link Manifest} with the given {@code input}.
|
||||
*
|
||||
* @param manifest the manifest containing the function
|
||||
* @param function the name of the function to call
|
||||
* @param input the input as string
|
||||
* @return the output as string
|
||||
* @throws ExtismException if the call fails
|
||||
*/
|
||||
public static String invokeFunction(Manifest manifest, String function, String input) throws ExtismException {
|
||||
try (var ctx = new Context()) {
|
||||
try (var plugin = ctx.newPlugin(manifest, false)) {
|
||||
return plugin.call(function, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error levels for the Extism logging facility.
|
||||
*
|
||||
* @see Extism#setLogFile(Path, LogLevel)
|
||||
*/
|
||||
public enum LogLevel {
|
||||
|
||||
INFO("info"), //
|
||||
|
||||
DEBUG("debug"), //
|
||||
|
||||
WARN("warn"), //
|
||||
|
||||
TRACE("trace");
|
||||
|
||||
private final String level;
|
||||
|
||||
LogLevel(String level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public String getLevel() {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
/**
|
||||
* Thrown when an exceptional condition has occurred.
|
||||
*/
|
||||
public class ExtismException extends RuntimeException {
|
||||
|
||||
public ExtismException() {
|
||||
}
|
||||
|
||||
public ExtismException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExtismException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
/**
|
||||
* Wrapper around the Extism library.
|
||||
*/
|
||||
public interface LibExtism extends Library {
|
||||
|
||||
/**
|
||||
* Holds the extism library instance.
|
||||
* Resolves the extism library based on the resolution algorithm defined in {@link com.sun.jna.NativeLibrary}.
|
||||
*/
|
||||
LibExtism INSTANCE = Native.load("extism", LibExtism.class);
|
||||
|
||||
/**
|
||||
* Create a new context
|
||||
*/
|
||||
Pointer extism_context_new();
|
||||
|
||||
/**
|
||||
* Free a context
|
||||
*/
|
||||
void extism_context_free(Pointer contextPointer);
|
||||
|
||||
/**
|
||||
* Remove all plugins from the registry.
|
||||
*
|
||||
* @param contextPointer
|
||||
*/
|
||||
void extism_context_reset(Pointer contextPointer);
|
||||
|
||||
/**
|
||||
* Sets the logger to the given path with the given level of verbosity
|
||||
*
|
||||
* @param path The file path of the logger
|
||||
* @param logLevel The level of the logger
|
||||
* @return true if successful
|
||||
*/
|
||||
boolean extism_log_file(String path, String logLevel);
|
||||
|
||||
/**
|
||||
* Returns the error associated with a @{@link Context} or @{@link Plugin}, if {@code pluginId} is {@literal -1} then the context error will be returned
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginId
|
||||
* @return
|
||||
*/
|
||||
String extism_error(Pointer contextPointer, int pluginId);
|
||||
|
||||
/**
|
||||
* Create a new plugin.
|
||||
*
|
||||
* @param contextPointer pointer to the {@link Context}.
|
||||
* @param wasm is a WASM module (wat or wasm) or a JSON encoded manifest
|
||||
* @param wasmSize the length of the `wasm` parameter
|
||||
* @param withWASI enables/disables WASI
|
||||
* @return id of the plugin or {@literal -1} in case of error
|
||||
*/
|
||||
int extism_plugin_new(long contextPointer, byte[] wasm, long wasmSize, boolean withWASI);
|
||||
|
||||
/**
|
||||
* Returns the Extism version string
|
||||
*/
|
||||
String extism_version();
|
||||
|
||||
/**
|
||||
* Create a new plugin.
|
||||
*
|
||||
* @param contextPointer pointer to the {@link Context}.
|
||||
* @param wasm is a WASM module (wat or wasm) or a JSON encoded manifest
|
||||
* @param length the length of the `wasm` parameter
|
||||
* @param withWASI enables/disables WASI
|
||||
* @return id of the plugin or {@literal -1} in case of error
|
||||
* @see #extism_plugin_new(long, byte[], long, boolean)
|
||||
*/
|
||||
int extism_plugin_new(Pointer contextPointer, byte[] wasm, int length, boolean withWASI);
|
||||
|
||||
/**
|
||||
* Calls a function from the @{@link Plugin} at the given {@code pluginIndex}.
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginIndex
|
||||
* @param function_name is the function to call
|
||||
* @param data is the data input data
|
||||
* @param dataLength is the data input data length
|
||||
* @return the result code of the plugin call. {@literal -1} in case of error, {@literal 0} otherwise.
|
||||
*/
|
||||
int extism_plugin_call(Pointer contextPointer, int pluginIndex, String function_name, byte[] data, int dataLength);
|
||||
|
||||
/**
|
||||
* Returns the length of a plugin's output data.
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginIndex
|
||||
* @return the length of the output data in bytes.
|
||||
*/
|
||||
int extism_plugin_output_length(Pointer contextPointer, int pluginIndex);
|
||||
|
||||
/**
|
||||
* Returns the plugin's output data.
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginIndex
|
||||
* @return
|
||||
*/
|
||||
Pointer extism_plugin_output_data(Pointer contextPointer, int pluginIndex);
|
||||
|
||||
/**
|
||||
* Update a plugin, keeping the existing ID.
|
||||
* Similar to {@link #extism_plugin_new(long, byte[], long, boolean)} but takes an {@code pluginIndex} argument to specify which plugin to update.
|
||||
* Note: Memory for this plugin will be reset upon update.
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginIndex
|
||||
* @param wasm
|
||||
* @param length
|
||||
* @param withWASI
|
||||
* @return {@literal true} if update was successful
|
||||
*/
|
||||
boolean extism_plugin_update(Pointer contextPointer, int pluginIndex, byte[] wasm, int length, boolean withWASI);
|
||||
|
||||
/**
|
||||
* Remove a plugin from the registry and free associated memory.
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginIndex
|
||||
*/
|
||||
void extism_plugin_free(Pointer contextPointer, int pluginIndex);
|
||||
|
||||
/**
|
||||
* Update plugin config values, this will merge with the existing values.
|
||||
*
|
||||
* @param contextPointer
|
||||
* @param pluginIndex
|
||||
* @param json
|
||||
* @param jsonLength
|
||||
* @return {@literal true} if update was successful
|
||||
*/
|
||||
boolean extism_plugin_config(Pointer contextPointer, int pluginIndex, byte[] json, int jsonLength);
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import org.extism.sdk.manifest.Manifest;
|
||||
import org.extism.sdk.support.JsonSerde;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a Extism plugin.
|
||||
*/
|
||||
public class Plugin implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* Holds the Extism {@link Context} that the plugin belongs to.
|
||||
*/
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Holds the index of the plugin
|
||||
*/
|
||||
private final int index;
|
||||
|
||||
/**
|
||||
* Constructor for a Plugin. Only expose internally. Plugins should be created and
|
||||
* managed from {@link org.extism.sdk.Context}.
|
||||
*
|
||||
* @param context The context to manage the plugin
|
||||
* @param manifestBytes The manifest for the plugin
|
||||
* @param withWASI Set to true to enable WASI
|
||||
*/
|
||||
public Plugin(Context context, byte[] manifestBytes, boolean withWASI) {
|
||||
|
||||
Objects.requireNonNull(context, "context");
|
||||
Objects.requireNonNull(manifestBytes, "manifestBytes");
|
||||
|
||||
Pointer contextPointer = context.getPointer();
|
||||
int index = LibExtism.INSTANCE.extism_plugin_new(contextPointer, manifestBytes, manifestBytes.length, withWASI);
|
||||
if (index == -1) {
|
||||
String error = context.error(this);
|
||||
throw new ExtismException(error);
|
||||
}
|
||||
|
||||
this.index= index;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public Plugin(Context context, Manifest manifest, boolean withWASI) {
|
||||
this(context, serialize(manifest), withWASI);
|
||||
}
|
||||
|
||||
private static byte[] serialize(Manifest manifest) {
|
||||
Objects.requireNonNull(manifest, "manifest");
|
||||
return JsonSerde.toJson(manifest).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the internal index pointer to this plugin.
|
||||
*
|
||||
* @return the plugin index
|
||||
*/
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a function with the given name and input.
|
||||
*
|
||||
* @param functionName The name of the exported function to invoke
|
||||
* @param inputData The raw bytes representing any input data
|
||||
* @return A byte array representing the raw output data
|
||||
* @throws ExtismException if the call fails
|
||||
*/
|
||||
public byte[] call(String functionName, byte[] inputData) {
|
||||
|
||||
Objects.requireNonNull(functionName, "functionName");
|
||||
|
||||
Pointer contextPointer = context.getPointer();
|
||||
int inputDataLength = inputData == null ? 0 : inputData.length;
|
||||
int exitCode = LibExtism.INSTANCE.extism_plugin_call(contextPointer, index, functionName, inputData, inputDataLength);
|
||||
if (exitCode == -1) {
|
||||
String error = context.error(this);
|
||||
throw new ExtismException(error);
|
||||
}
|
||||
|
||||
int length = LibExtism.INSTANCE.extism_plugin_output_length(contextPointer, index);
|
||||
Pointer output = LibExtism.INSTANCE.extism_plugin_output_data(contextPointer, index);
|
||||
return output.getByteArray(0, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a function with the given name and input.
|
||||
*
|
||||
* @param functionName The name of the exported function to invoke
|
||||
* @param input The string representing the input data
|
||||
* @return A string representing the output data
|
||||
*/
|
||||
public String call(String functionName, String input) {
|
||||
|
||||
Objects.requireNonNull(functionName, "functionName");
|
||||
|
||||
var inputBytes = input == null ? null : input.getBytes(StandardCharsets.UTF_8);
|
||||
var outputBytes = call(functionName, inputBytes);
|
||||
return new String(outputBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the plugin code given manifest changes
|
||||
*
|
||||
* @param manifest The manifest for the plugin
|
||||
* @param withWASI Set to true to enable WASI
|
||||
* @return {@literal true} if update was successful
|
||||
*/
|
||||
public boolean update(Manifest manifest, boolean withWASI) {
|
||||
return update(serialize(manifest), withWASI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the plugin code given manifest changes
|
||||
*
|
||||
* @param manifestBytes The manifest for the plugin
|
||||
* @param withWASI Set to true to enable WASI
|
||||
* @return {@literal true} if update was successful
|
||||
*/
|
||||
public boolean update(byte[] manifestBytes, boolean withWASI) {
|
||||
Objects.requireNonNull(manifestBytes, "manifestBytes");
|
||||
return LibExtism.INSTANCE.extism_plugin_update(context.getPointer(), index, manifestBytes, manifestBytes.length, withWASI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees a plugin from memory. Plugins will be automatically cleaned up
|
||||
* if you free their parent Context using {@link org.extism.sdk.Context#free() free()} or {@link org.extism.sdk.Context#reset() reset()}
|
||||
*/
|
||||
public void free() {
|
||||
LibExtism.INSTANCE.extism_plugin_free(context.getPointer(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update plugin config values, this will merge with the existing values.
|
||||
*
|
||||
* @param json
|
||||
* @return
|
||||
*/
|
||||
public boolean updateConfig(String json) {
|
||||
Objects.requireNonNull(json, "json");
|
||||
return updateConfig(json.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update plugin config values, this will merge with the existing values.
|
||||
*
|
||||
* @param jsonBytes
|
||||
* @return {@literal true} if update was successful
|
||||
*/
|
||||
public boolean updateConfig(byte[] jsonBytes) {
|
||||
Objects.requireNonNull(jsonBytes, "jsonBytes");
|
||||
return LibExtism.INSTANCE.extism_plugin_config(context.getPointer(), index, jsonBytes, jsonBytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #free()} if used in the context of a TWR block.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
free();
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.extism.sdk.manifest;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.extism.sdk.wasm.WasmSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Manifest {
|
||||
|
||||
@SerializedName("wasm")
|
||||
private final List<WasmSource> sources;
|
||||
|
||||
@SerializedName("memory")
|
||||
private final MemoryOptions memoryOptions;
|
||||
|
||||
// FIXME remove this and related stuff if not supported in java-sdk
|
||||
@SerializedName("allowed_hosts")
|
||||
private final List<String> allowedHosts;
|
||||
|
||||
@SerializedName("config")
|
||||
private final Map<String, String> config;
|
||||
|
||||
public Manifest() {
|
||||
this(new ArrayList<>(), null, null, null);
|
||||
}
|
||||
|
||||
public Manifest(WasmSource source) {
|
||||
this(List.of(source));
|
||||
}
|
||||
|
||||
public Manifest(List<WasmSource> sources) {
|
||||
this(sources, null, null, null);
|
||||
}
|
||||
|
||||
public Manifest(List<WasmSource> sources, MemoryOptions memoryOptions) {
|
||||
this(sources, memoryOptions, null, null);
|
||||
}
|
||||
|
||||
public Manifest(List<WasmSource> sources, MemoryOptions memoryOptions, Map<String, String> config) {
|
||||
this(sources, memoryOptions, config, null);
|
||||
}
|
||||
|
||||
public Manifest(List<WasmSource> sources, MemoryOptions memoryOptions, Map<String, String> config, List<String> allowedHosts) {
|
||||
this.sources = sources;
|
||||
this.memoryOptions = memoryOptions;
|
||||
this.config = config;
|
||||
this.allowedHosts = allowedHosts;
|
||||
}
|
||||
|
||||
public void addSource(WasmSource source) {
|
||||
this.sources.add(source);
|
||||
}
|
||||
|
||||
public List<WasmSource> getSources() {
|
||||
return Collections.unmodifiableList(sources);
|
||||
}
|
||||
|
||||
public MemoryOptions getMemoryOptions() {
|
||||
return memoryOptions;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
if (config == null || config.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return Collections.unmodifiableMap(config);
|
||||
}
|
||||
|
||||
public List<String> getAllowedHosts() {
|
||||
if (allowedHosts == null || allowedHosts.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(allowedHosts);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package org.extism.sdk.manifest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
// FIXME remove this and related stuff if not supported in java-sdk
|
||||
public record ManifestHttpRequest(String url, Map<String, String> header, String method) {
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.extism.sdk.manifest;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Configures memory for the Wasm runtime.
|
||||
* Memory is described in units of pages (64KB) and represent contiguous chunks of addressable memory.
|
||||
*
|
||||
* @param max Max number of pages.
|
||||
*/
|
||||
public record MemoryOptions(@SerializedName("max") Integer max) {
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.extism.sdk.support;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class Hashing {
|
||||
|
||||
public static String sha256HexDigest(byte[] input) {
|
||||
try {
|
||||
var messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
var messageDigestBytes = messageDigest.digest(input);
|
||||
|
||||
var hexString = new StringBuilder();
|
||||
for (var b : messageDigestBytes) {
|
||||
hexString.append(String.format("%02x", b));
|
||||
}
|
||||
return hexString.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.extism.sdk.support;
|
||||
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import org.extism.sdk.manifest.Manifest;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Base64;
|
||||
|
||||
public class JsonSerde {
|
||||
|
||||
private static final Gson GSON;
|
||||
|
||||
static {
|
||||
GSON = new GsonBuilder() //
|
||||
.disableHtmlEscaping() //
|
||||
// needed to convert the byte[] to a base64 encoded String
|
||||
.registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64TypeAdapter()) //
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) //
|
||||
.setPrettyPrinting() //
|
||||
.create();
|
||||
}
|
||||
|
||||
public static String toJson(Manifest manifest) {
|
||||
return GSON.toJson(manifest);
|
||||
}
|
||||
|
||||
private static class ByteArrayToBase64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
|
||||
|
||||
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return Base64.getDecoder().decode(json.getAsString());
|
||||
}
|
||||
|
||||
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(Base64.getEncoder().withoutPadding().encodeToString(src));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.extism.sdk.wasm;
|
||||
|
||||
/**
|
||||
* WASM Source represented by raw bytes.
|
||||
*
|
||||
* @param name
|
||||
* @param data the byte array representing the WASM code
|
||||
* @param hash
|
||||
*/
|
||||
public record ByteArrayWasmSource(String name, byte[] data, String hash) implements WasmSource {
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.extism.sdk.wasm;
|
||||
|
||||
/**
|
||||
* WASM Source represented by a file referenced by a path.
|
||||
*
|
||||
* @param name
|
||||
* @param path
|
||||
* @param hash
|
||||
*/
|
||||
public record PathWasmSource(String name, String path, String hash) implements WasmSource {
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.extism.sdk.wasm;
|
||||
|
||||
/**
|
||||
* A named WASM source.
|
||||
*/
|
||||
public interface WasmSource {
|
||||
|
||||
/**
|
||||
* Logical name of the WASM source
|
||||
* @return
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Hash of the WASM source
|
||||
* @return
|
||||
*/
|
||||
String hash();
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.extism.sdk.wasm;
|
||||
|
||||
import org.extism.sdk.ExtismException;
|
||||
import org.extism.sdk.support.Hashing;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Resolves {@link WasmSource} from {@link Path Path's} or raw bytes.
|
||||
*/
|
||||
public class WasmSourceResolver {
|
||||
|
||||
public PathWasmSource resolve(Path path) {
|
||||
return resolve(null, path);
|
||||
}
|
||||
|
||||
public PathWasmSource resolve(String name, Path path) {
|
||||
|
||||
Objects.requireNonNull(path, "path");
|
||||
|
||||
var wasmFile = path.toFile();
|
||||
var hash = hash(path);
|
||||
var wasmName = name == null ? wasmFile.getName() : name;
|
||||
|
||||
return new PathWasmSource(wasmName, wasmFile.getAbsolutePath(), hash);
|
||||
}
|
||||
|
||||
public ByteArrayWasmSource resolve(String name, byte[] bytes) {
|
||||
return new ByteArrayWasmSource(name, bytes, hash(bytes));
|
||||
}
|
||||
|
||||
protected String hash(Path wasmFile) {
|
||||
try {
|
||||
return hash(Files.readAllBytes(wasmFile));
|
||||
} catch (IOException ioe) {
|
||||
throw new ExtismException("Could not compute hash from path: " + wasmFile, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
protected String hash(byte[] bytes) {
|
||||
return Hashing.sha256HexDigest(bytes);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ContextTests {
|
||||
|
||||
@Test
|
||||
public void shouldReturnVersionString() {
|
||||
try (var ctx = new Context()) {
|
||||
var version = ctx.getVersion();
|
||||
assertThat(version).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowResetOnEmptyContext() {
|
||||
try (var ctx = new Context()) {
|
||||
ctx.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import org.extism.sdk.manifest.Manifest;
|
||||
import org.extism.sdk.manifest.MemoryOptions;
|
||||
import org.extism.sdk.support.JsonSerde;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.extism.sdk.TestWasmSources.CODE;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static uk.org.webcompere.modelassert.json.JsonAssertions.assertJson;
|
||||
|
||||
public class ManifestTests {
|
||||
|
||||
@Test
|
||||
public void shouldSerializeManifestWithWasmSourceToJson() {
|
||||
|
||||
var manifest = new Manifest(CODE.pathWasmSource());
|
||||
var json = JsonSerde.toJson(manifest);
|
||||
assertNotNull(json);
|
||||
|
||||
assertJson(json).at("/wasm").isArray();
|
||||
assertJson(json).at("/wasm").hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSerializeManifestWithWasmSourceAndMemoryOptionsToJson() {
|
||||
|
||||
var manifest = new Manifest(List.of(CODE.pathWasmSource()), new MemoryOptions(4));
|
||||
var json = JsonSerde.toJson(manifest);
|
||||
assertNotNull(json);
|
||||
|
||||
assertJson(json).at("/wasm").isArray();
|
||||
assertJson(json).at("/wasm").hasSize(1);
|
||||
assertJson(json).at("/memory/max").isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void codeWasmFromFileAndBytesShouldProduceTheSameHash() {
|
||||
|
||||
var byteHash = CODE.byteArrayWasmSource().hash();
|
||||
var fileHash = CODE.pathWasmSource().hash();
|
||||
|
||||
assertThat(byteHash).isEqualTo(fileHash);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import org.extism.sdk.manifest.Manifest;
|
||||
import org.extism.sdk.manifest.MemoryOptions;
|
||||
import org.extism.sdk.wasm.WasmSourceResolver;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.extism.sdk.TestWasmSources.CODE;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class PluginTests {
|
||||
|
||||
// static {
|
||||
// Extism.setLogFile(Paths.get("/tmp/extism.log"), Extism.LogLevel.TRACE);
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void shouldInvokeFunctionWithMemoryOptions() {
|
||||
//FIXME check whether memory options are effective
|
||||
var manifest = new Manifest(List.of(CODE.pathWasmSource()), new MemoryOptions(0));
|
||||
var output = Extism.invokeFunction(manifest, "count_vowels", "Hello World");
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInvokeFunctionWithConfig() {
|
||||
//FIXME check if config options are available in wasm call
|
||||
var config = Map.of("key1", "value1");
|
||||
var manifest = new Manifest(List.of(CODE.pathWasmSource()), null, config);
|
||||
var output = Extism.invokeFunction(manifest, "count_vowels", "Hello World");
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInvokeFunctionFromFileWasmSource() {
|
||||
var manifest = new Manifest(CODE.pathWasmSource());
|
||||
var output = Extism.invokeFunction(manifest, "count_vowels", "Hello World");
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
}
|
||||
|
||||
// TODO This test breaks on CI with error:
|
||||
// data did not match any variant of untagged enum Wasm at line 8 column 3
|
||||
// @Test
|
||||
// public void shouldInvokeFunctionFromByteArrayWasmSource() {
|
||||
// var manifest = new Manifest(CODE.byteArrayWasmSource());
|
||||
// var output = Extism.invokeFunction(manifest, "count_vowels", "Hello World");
|
||||
// assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void shouldFailToInvokeUnknownFunction() {
|
||||
assertThrows(ExtismException.class, () -> {
|
||||
var manifest = new Manifest(CODE.pathWasmSource());
|
||||
Extism.invokeFunction(manifest, "unknown", "dummy");
|
||||
}, "Function not found: unknown");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowInvokeFunctionFromFileWasmSourceMultipleTimes() {
|
||||
var wasmSource = CODE.pathWasmSource();
|
||||
var manifest = new Manifest(wasmSource);
|
||||
var output = Extism.invokeFunction(manifest, "count_vowels", "Hello World");
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
|
||||
output = Extism.invokeFunction(manifest, "count_vowels", "Hello World");
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowInvokeFunctionFromFileWasmSourceApiUsageExample() {
|
||||
|
||||
var wasmSourceResolver = new WasmSourceResolver();
|
||||
var manifest = new Manifest(wasmSourceResolver.resolve(CODE.getWasmFilePath()));
|
||||
|
||||
var functionName = "count_vowels";
|
||||
var input = "Hello World";
|
||||
|
||||
try (var ctx = new Context()) {
|
||||
try (var plugin = ctx.newPlugin(manifest, false)) {
|
||||
var output = plugin.call(functionName, input);
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowInvokeFunctionFromFileWasmSourceMultipleTimesByReusingContext() {
|
||||
var manifest = new Manifest(CODE.pathWasmSource());
|
||||
var functionName = "count_vowels";
|
||||
var input = "Hello World";
|
||||
|
||||
try (var ctx = new Context()) {
|
||||
try (var plugin = ctx.newPlugin(manifest, false)) {
|
||||
var output = plugin.call(functionName, input);
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
|
||||
output = plugin.call(functionName, input);
|
||||
assertThat(output).isEqualTo("{\"count\": 3}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.extism.sdk;
|
||||
|
||||
import org.extism.sdk.wasm.ByteArrayWasmSource;
|
||||
import org.extism.sdk.wasm.PathWasmSource;
|
||||
import org.extism.sdk.wasm.WasmSourceResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum TestWasmSources {
|
||||
|
||||
CODE {
|
||||
public Path getWasmFilePath() {
|
||||
return Paths.get(WASM_LOCATION, "code.wasm");
|
||||
}
|
||||
};
|
||||
|
||||
public static final String WASM_LOCATION = "src/test/resources";
|
||||
|
||||
public abstract Path getWasmFilePath();
|
||||
|
||||
public PathWasmSource pathWasmSource() {
|
||||
return resolvePathWasmSource(getWasmFilePath());
|
||||
}
|
||||
|
||||
public ByteArrayWasmSource byteArrayWasmSource() {
|
||||
try {
|
||||
var wasmBytes = Files.readAllBytes(getWasmFilePath());
|
||||
return new WasmSourceResolver().resolve("wasm@" + Arrays.hashCode(wasmBytes), wasmBytes);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
public static PathWasmSource resolvePathWasmSource(Path path) {
|
||||
return new WasmSourceResolver().resolve(path);
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -5,6 +5,6 @@ libdir=${exec_prefix}/lib
|
||||
|
||||
Name: extism
|
||||
Description: The Extism universal plug-in system.
|
||||
Version: 0.1.0
|
||||
Version: 0.0.1
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -lextism
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user