Compare commits

...

40 Commits

Author SHA1 Message Date
Benjamin Eckel
37b8e5bd12 release: v0.1.0 (#110) 2022-11-30 14:52:46 -06:00
Benjamin Eckel
c6b8429c67 chore: Add Makefile for browser runtime (#103)
I accidentally published without building. this should help make sure
releases are valid
2022-11-30 13:20:23 -06:00
Benjamin Eckel
ad114f44d2 fix(ruby-sdk): Add FFI gem as dependency (#106) 2022-11-30 13:19:54 -06:00
Benjamin Eckel
45180ad473 docs(node-sdk): Fix example to use CJS imports (#107) 2022-11-30 13:19:45 -06:00
Benjamin Eckel
9546dac689 docs(elixir-sdk): Reduce elixir min version (#108)
I've tested it in at least as low as 1.12. It just prints out a warning
so should be fine if someone wants to use it on an older version.
2022-11-30 13:19:28 -06:00
zach
197e934258 Fix elixir bindings (#109) 2022-11-30 10:42:43 -08:00
Benjamin Eckel
339556597b chore(python-sdk): Put placeholder readme in python dir (#104) 2022-11-29 12:39:01 -06:00
Benjamin Eckel
64927d9bcd fix(rust-sdk): Fix dep versions (#102) 2022-11-29 12:17:49 -06:00
Benjamin Eckel
86f1117ad5 chore: Fix bad publish, bump to 0.0.2 2022-11-29 11:52:55 -06:00
Benjamin Eckel
34be80a7ad chore: Fix elixir release workflow (#77) 2022-11-29 11:47:30 -06:00
Benjamin Eckel
3e6a0071e9 fix: Fix the release action (#101) 2022-11-29 10:33:26 -06:00
Benjamin Eckel
d17b693c4b release: Bump to 0.0.1 (#97) 2022-11-29 09:48:40 -06:00
zach
18fceec8f8 Cleanup Haskell SDK, split out manifest sublibrary (#99) 2022-11-28 21:03:13 -08:00
Benjamin Eckel
f5cf4f184e chore: fix ruby release publish (#78) 2022-11-28 19:31:26 -06:00
Steve Manuel
821661d391 fix: resolve php sdk issues (#95) 2022-11-28 18:04:27 -07:00
zach
f28e01125e Updates for wasmtime/wasmtime-wasi/wasmtime-wasi-nn, basic support for calling _start functions (#94)
- Updates codebase to use the latest version of wasmtime, wasmtime-wasi
and wasmtime-wasi-nn
- Allows functions with no return values to be called, this provides
basic support for `_start` functions
- For now `_start` functions called by extism still need to use the
extism input/output functions instead of the command line arguments
2022-11-28 14:08:52 -08:00
dependabot[bot]
c0faa53df5 chore(deps-dev): bump prettier from 2.7.1 to 2.8.0 in /node (#90)
Bumps [prettier](https://github.com/prettier/prettier) from 2.7.1 to
2.8.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>2.8.0</h2>
<p><a
href="https://github.com/prettier/prettier/compare/2.7.1...2.8.0">diff</a></p>
<p>🔗 <a href="https://prettier.io/blog/2022/11/23/2.8.0.html">Release
note</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>2.8.0</h1>
<p><a
href="https://github.com/prettier/prettier/compare/2.7.1...2.8.0">diff</a></p>
<p>🔗 <a href="https://prettier.io/blog/2022/11/23/2.8.0.html">Release
Notes</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="dcc0623911"><code>dcc0623</code></a>
Release 2.8.0</li>
<li><a
href="7f7498109b"><code>7f74981</code></a>
Remove version validattion temp</li>
<li><a
href="876c297589"><code>876c297</code></a>
Update changelog for <code>satisfies</code> (<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13879">#13879</a>)</li>
<li><a
href="fd376000e9"><code>fd37600</code></a>
Update <code>@typescript-eslint/typescript-estree</code> to support
<code>satisfies</code> (<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13872">#13872</a>)</li>
<li><a
href="fac87619a4"><code>fac8761</code></a>
Build(deps): Bump minimatch from 3.0.4 to 3.1.2 in /scripts/release (<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13854">#13854</a>)</li>
<li><a
href="68ea881498"><code>68ea881</code></a>
Update typescript to v4.9 (<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13852">#13852</a>)</li>
<li><a
href="5e0b88329e"><code>5e0b883</code></a>
Fix generate truncate comment (backport <a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13437">#13437</a>)
(<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13814">#13814</a>)</li>
<li><a
href="a980caa7db"><code>a980caa</code></a>
Add missing changelog for <a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13016">#13016</a>
(<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13812">#13812</a>)</li>
<li><a
href="520dbcd36e"><code>520dbcd</code></a>
Add changelog for <a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13783">#13783</a>
(<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13811">#13811</a>)</li>
<li><a
href="ae4d85ab3c"><code>ae4d85a</code></a>
Update description of <code>vueIndentScriptAndStyle</code> option (<a
href="https://github-redirect.dependabot.com/prettier/prettier/issues/13781">#13781</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/2.7.1...2.8.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=prettier&package-manager=npm_and_yarn&previous-version=2.7.1&new-version=2.8.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-27 19:46:47 -06:00
dependabot[bot]
be7961bbf6 chore(deps-dev): bump ex_doc from 0.29.0 to 0.29.1 in /elixir (#89)
Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.29.0 to
0.29.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md">ex_doc's
changelog</a>.</em></p>
<blockquote>
<h2>v0.29.1 (2022-11-21)</h2>
<ul>
<li>
<p>Enhancements</p>
<ul>
<li>Add optional function annotations</li>
<li>Support media print on stylesheets</li>
<li>Add download ePub link to footer</li>
<li>Support extras for Erlang</li>
<li>Add tooltip to functions on sidebar</li>
<li>Disable spellcheck and autocorrect on search input</li>
</ul>
</li>
<li>
<p>Bug fix</p>
<ul>
<li>Special handle functions called <code>record/*</code> in Erlang</li>
</ul>
</li>
<li>
<p>Deprecations</p>
<ul>
<li>Rename <code>:groups_for_functions</code> to
<code>:groups_for_docs</code></li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1439375934"><code>1439375</code></a>
Release v0.29.1</li>
<li><a
href="e4ae7ce037"><code>e4ae7ce</code></a>
Rename groups_for_functions as groups_for_docs</li>
<li><a
href="b17fcd278a"><code>b17fcd2</code></a>
Add optional function annotations (<a
href="https://github-redirect.dependabot.com/elixir-lang/ex_doc/issues/1627">#1627</a>)</li>
<li><a
href="69a74c029c"><code>69a74c0</code></a>
Firefox H2 print rendering fix (<a
href="https://github-redirect.dependabot.com/elixir-lang/ex_doc/issues/1630">#1630</a>)</li>
<li><a
href="623ac03fea"><code>623ac03</code></a>
mix format</li>
<li><a
href="a9284d50e3"><code>a9284d5</code></a>
Consistenly use <strong>doc</strong> for metadata</li>
<li><a
href="a4ea93cf26"><code>a4ea93c</code></a>
Rename internal nodes for consistency</li>
<li><a
href="5a0aa08550"><code>5a0aa08</code></a>
Simplify handling of empty nodes in module summary</li>
<li><a
href="148ad54a6d"><code>148ad54</code></a>
Use v1.13 formatting rules</li>
<li><a
href="4b05286003"><code>4b05286</code></a>
mix format</li>
<li>Additional commits viewable in <a
href="https://github.com/elixir-lang/ex_doc/compare/v0.29.0...v0.29.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ex_doc&package-manager=hex&previous-version=0.29.0&new-version=0.29.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-27 19:46:38 -06:00
Benjamin Eckel
4af4f7c41a fix(php-sdk): fix the composer file (#88)
Need to include new Context file
2022-11-22 16:51:19 -06:00
Benjamin Eckel
0003246ff7 chore: release browser version rc.13 2022-11-21 16:04:21 -06:00
Benjamin Eckel
886e01b959 Merge branch 'main' of github.com:extism/extism 2022-11-21 16:03:05 -06:00
Benjamin Eckel
b30bcc3601 chore: release browser version rc.11 2022-11-21 16:02:25 -06:00
zach
b57d54e63e feat: add var/config functions, merge ExtismPlugin and ExtismPluginCall (#87)
This also only instantiates the module once and combines `ExtismPlugin`
with `ExtismPluginCall`, I can pull out just the new function
implementations if that's an issue.
2022-11-21 13:34:27 -08:00
dependabot[bot]
ba2516650d chore(deps-dev): bump typedoc from 0.23.20 to 0.23.21 in /node (#86)
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.23.20 to
0.23.21.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/TypeStrong/TypeDoc/releases">typedoc's
releases</a>.</em></p>
<blockquote>
<h2>v0.23.21</h2>
<h3>Features</h3>
<ul>
<li>Added support for a catch-all wildcard in
<code>externalSymbolLinkMappings</code>, <a
href="https://github-redirect.dependabot.com/TypeStrong/TypeDoc/issues/2102">#2102</a>.</li>
<li>Added support for TypeScript 4.9.</li>
</ul>
<h3>Thanks!</h3>
<ul>
<li><a
href="https://github.com/mistic100"><code>@​mistic100</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/TypeStrong/typedoc/blob/master/CHANGELOG.md">typedoc's
changelog</a>.</em></p>
<blockquote>
<h2>v0.23.21 (2022-11-14)</h2>
<h3>Features</h3>
<ul>
<li>Added support for a catch-all wildcard in
<code>externalSymbolLinkMappings</code>, <a
href="https://github-redirect.dependabot.com/TypeStrong/TypeDoc/issues/2102">#2102</a>.</li>
<li>Added support for TypeScript 4.9.</li>
</ul>
<h3>Thanks!</h3>
<ul>
<li><a
href="https://github.com/mistic100"><code>@​mistic100</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8a3e195d38"><code>8a3e195</code></a>
Update changelog for release</li>
<li><a
href="530eab35be"><code>530eab3</code></a>
Bump version to 0.23.21</li>
<li><a
href="893c86c6a8"><code>893c86c</code></a>
Fix lint</li>
<li><a
href="38fe129856"><code>38fe129</code></a>
CI fix?</li>
<li><a
href="9b62e0ce6e"><code>9b62e0c</code></a>
Add support for TS 4.9</li>
<li><a
href="8e190487b1"><code>8e19048</code></a>
Tweak changelog notes</li>
<li><a
href="0ba5af5204"><code>0ba5af5</code></a>
Merge pull request <a
href="https://github-redirect.dependabot.com/TypeStrong/TypeDoc/issues/2102">#2102</a>
from mistic100/external-wildcard</li>
<li><a
href="4b0a9b0a90"><code>4b0a9b0</code></a>
Fix <a
href="https://github-redirect.dependabot.com/TypeStrong/TypeDoc/issues/2101">#2101</a>
support externalSymbolLinkMappings wildcard</li>
<li><a
href="1ab233b1cf"><code>1ab233b</code></a>
reg-suit begone! -300 dev dependencies</li>
<li><a
href="ab51894154"><code>ab51894</code></a>
Clean up some unnecessary any</li>
<li>Additional commits viewable in <a
href="https://github.com/TypeStrong/TypeDoc/compare/v0.23.20...v0.23.21">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typedoc&package-manager=npm_and_yarn&previous-version=0.23.20&new-version=0.23.21)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-21 10:01:14 -06:00
dependabot[bot]
97b4582aa4 chore(deps-dev): bump typescript from 4.8.4 to 4.9.3 in /node (#85)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.8.4
to 4.9.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Microsoft/TypeScript/releases">typescript's
releases</a>.</em></p>
<blockquote>
<h2>TypeScript 4.9</h2>
<p>For release notes, check out the <a
href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/">release
announcement</a>.</p>
<p>Downloads are available on:</p>
<ul>
<li><a href="https://www.npmjs.com/package/typescript">npm</a></li>
<li><a
href="https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild">NuGet
package</a></li>
</ul>
<h2>Changes:</h2>
<ul>
<li>93bd577458d55cd720b2677705feab5c91eb12ce Bump version to 4.9.3 and
LKG.</li>
<li>107f832b80df2dc97748021cb00af2b6813db75b Update LKG.</li>
<li>31bee5682df130a14ffdd5742f994dbe7313dd0e Cherry-pick PR <a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/50977">#50977</a>
into release-4.9 (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51363">#51363</a>)
[ <a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/50872">#50872</a>
]</li>
<li>1e2fa7ae15f8530910fef8b916ec8a4ed0b59c45 Update version to 4.9.2-rc
and LKG.</li>
<li>7ab89e5c6e401d161f31f28a6c555a3ba530910e Merge remote-tracking
branch 'origin/main' into release-4.9</li>
<li>e5cd686defb1a4cbdb36bd012357ba5bed28f371 Update
package-lock.json</li>
<li>8d40dc15d1b9945837e7860320fdccfe27c40cad Update
package-lock.json</li>
<li>5cfb3a2fe344a5350734305193e6cc99516285ca Only call return() for an
abrupt completion in user code (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51297">#51297</a>)</li>
<li>a7a9d158e817fcb0e94dc1c24e0a401b21be0cc9 Fix for broken baseline in
yieldInForInInDownlevelGenerator (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51345">#51345</a>)</li>
<li>7f8426f4df0d0a7dd8b72079dafc3e60164a23b1 fix for-in enumeration
containing yield in generator (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51295">#51295</a>)</li>
</ul>
<!-- raw HTML omitted -->
<ul>
<li>3d2b4017eb6b9a2b94bc673291e56ae95e8beddd Fix assertion functions
accessed via wildcard imports (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51324">#51324</a>)</li>
<li>64d0d5ae140b7b26a09e75114517b418d6bcaa9f fix(51301): Fixing an
unused import at the end of a line removes the newline (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51320">#51320</a>)</li>
<li>754eeb2986bde30d5926e0fa99c87dda9266d01b Update CodeQL workflow and
configuration, fix found bugs (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51263">#51263</a>)</li>
<li>d8aad262006ad2d2c91aa7a0e4449b4b83c57f7b Update
package-lock.json</li>
<li>d4f26c840b1db76c0b25a405c8e73830a2b45cbc fix(51245): Class with
parameter decorator in arrow function causes &quot;convert to default
export&quot; refactoring failure (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51256">#51256</a>)</li>
<li>16faf45682173ea437a50330feb4785578923d7f Update
package-lock.json</li>
<li>8b1ecdb701e2a2e19e9f8bcdd6b2beac087eabee fix(50654): &quot;Move to a
new file&quot; breaks the declaration of referenced variable (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/50681">#50681</a>)</li>
<li>170a17fad57eae619c5ef2b7bdb3ac00d6c32c47 Dom update 2022-10-25 (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51300">#51300</a>)</li>
<li>9c4e14d75174432f6a4dc5967a09712a6784ab88 Remove &quot;No type
information for this code&quot; from baseline (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51311">#51311</a>)</li>
<li>88d25b4f232929df59729156dfda6b65277affec fix(50068): Refactors
trigger debug failure when JSX text has a ' and a tag on the same line.
(<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51299">#51299</a>)</li>
<li>8bee69acf410d4986cb0cc102b949e2d133d5380 Update
package-lock.json</li>
<li>702de1eeaaef88a189e4d06e5a2aae287853790a Fix early call to
return/throw on generator (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51294">#51294</a>)</li>
<li>2c12b1499908ad7718e65d20e264561207c22375 Add a GH Action to file a
new issue if we go a week without seeing a typescript-error-deltas issue
(<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51271">#51271</a>)</li>
<li>6af270dee09d62516f6dc02ec102a745ffebc037 Update
package-lock.json</li>
<li>2cc4c16a26672a7ba6c97ba16309fcf334db7cae Update
package-lock.json</li>
<li>60934915d9ccc4ca9c0fb2cd060d7ec81601942b Fix apparent typo in
getStringMappingType (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51248">#51248</a>)</li>
<li>61c26096e3373719ece686b84c698423890e9a5f Update
package-lock.json</li>
<li>ef69116c41cb6805f89e6592eacb0ccb7f02207d Generate shortest
<code>rootDirs</code> module specifier instead of first possible (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51244">#51244</a>)</li>
<li>bbb42f453dc684e03d977c5b70391124d57543a9 Fix typo in
canWatchDirectoryOrFile found by CodeQL (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51262">#51262</a>)</li>
<li>a56b254ad3c52b598bc5d44f83f3d0a1cf806068 Include 'this' type
parameter in isRelatedTo fast path (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51230">#51230</a>)</li>
<li>3abd351c0eea55758f27ee5558a4a1525b77f45b Fix super property
transform in async arrow in method (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51240">#51240</a>)</li>
<li>eed05112180e0d94f78aa02d676d49468f15dc31 Update
package-lock.json</li>
<li>2625c1feae25aede35465ca835440fc57bf13d52 Make the init config
category order predictable (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51247">#51247</a>)</li>
<li>1ca99b34029dafad2c18af7bdc0711f4abf7e522 fix(50551): Destructuring
assignment with var bypasses &quot;variable is used before being
assigned&quot; check (2454) (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/50560">#50560</a>)</li>
<li>3f28fa12dfecb8dfd66ce4684bf26f64e1f092f1 Update
package-lock.json</li>
<li>906ebe49334a3a9c2dbd73cd3c902898bc712b66 Revert
structuredTypeRelatedTo change and fix isUnitLikeType (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51076">#51076</a>)</li>
<li>8ac465239f52de1da3ada8cdc4c3f107f4d62e45 change type (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51231">#51231</a>)</li>
<li>245a02cbed7ad50a21289730159abc8d19a66f40 fix(51222):
Go-to-definition on return statements should jump to the containing
function declaration (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51227">#51227</a>)</li>
<li>2dff34e8c4a91c0005ca9ccfb7e045e225b6f2e4 markAliasReferenced should
include ExportValue as well (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51219">#51219</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="93bd577458"><code>93bd577</code></a>
Bump version to 4.9.3 and LKG.</li>
<li><a
href="107f832b80"><code>107f832</code></a>
Update LKG.</li>
<li><a
href="31bee5682d"><code>31bee56</code></a>
Cherry-pick PR <a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/50977">#50977</a>
into release-4.9 (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51363">#51363</a>)</li>
<li><a
href="1e2fa7ae15"><code>1e2fa7a</code></a>
Update version to 4.9.2-rc and LKG.</li>
<li><a
href="7ab89e5c6e"><code>7ab89e5</code></a>
Merge remote-tracking branch 'origin/main' into release-4.9</li>
<li><a
href="e5cd686def"><code>e5cd686</code></a>
Update package-lock.json</li>
<li><a
href="8d40dc15d1"><code>8d40dc1</code></a>
Update package-lock.json</li>
<li><a
href="5cfb3a2fe3"><code>5cfb3a2</code></a>
Only call return() for an abrupt completion in user code (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51297">#51297</a>)</li>
<li><a
href="a7a9d158e8"><code>a7a9d15</code></a>
Fix for broken baseline in yieldInForInInDownlevelGenerator (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51345">#51345</a>)</li>
<li><a
href="7f8426f4df"><code>7f8426f</code></a>
fix for-in enumeration containing yield in generator (<a
href="https://github-redirect.dependabot.com/Microsoft/TypeScript/issues/51295">#51295</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/Microsoft/TypeScript/compare/v4.8.4...v4.9.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typescript&package-manager=npm_and_yarn&previous-version=4.8.4&new-version=4.9.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-21 10:01:03 -06:00
dependabot[bot]
762feb1056 chore(deps-dev): bump jest from 29.2.2 to 29.3.1 in /node (#84)
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest)
from 29.2.2 to 29.3.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/facebook/jest/releases">jest's
releases</a>.</em></p>
<blockquote>
<h2>v29.3.1</h2>
<h2>Fixes</h2>
<ul>
<li><code>[jest-config]</code> Do not warn about <code>preset</code> in
<code>ProjectConfig</code> <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13583">#13583</a></li>
</ul>
<h2>Performance</h2>
<ul>
<li><code>[jest-transform]</code> Defer creation of cache directory <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13420">#13420</a></li>
</ul>
<h2>v29.3.0</h2>
<h2>Features</h2>
<ul>
<li><code>[jest-runtime]</code> Support WebAssembly (Wasm) imports in
ESM modules (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13505">#13505</a>)</li>
</ul>
<h2>Fixes</h2>
<ul>
<li><code>[jest-config]</code> Add config validation for
<code>projects</code> option (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13565">#13565</a>)</li>
<li><code>[jest-mock]</code> Treat cjs modules as objects so they can be
mocked (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13513">#13513</a>)</li>
<li><code>[jest-worker]</code> Throw an error instead of hanging when
jest workers terminate unexpectedly (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13566">#13566</a>)</li>
</ul>
<h2>Chore &amp; Maintenance</h2>
<ul>
<li><code>[@jest/transform]</code> Update
<code>convert-source-map</code> (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13509">#13509</a>)</li>
<li><code>[docs]</code> Mention <code>toStrictEqual</code> in
UsingMatchers docs. (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13560">#13560</a>)</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Tofandel"><code>@​Tofandel</code></a>
made their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13513">facebook/jest#13513</a></li>
<li><a
href="https://github.com/RyWilliams"><code>@​RyWilliams</code></a> made
their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13520">facebook/jest#13520</a></li>
<li><a href="https://github.com/waikoo"><code>@​waikoo</code></a> made
their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13447">facebook/jest#13447</a></li>
<li><a href="https://github.com/kachkaev"><code>@​kachkaev</code></a>
made their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13505">facebook/jest#13505</a></li>
<li><a href="https://github.com/ibuibu"><code>@​ibuibu</code></a> made
their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13565">facebook/jest#13565</a></li>
<li><a
href="https://github.com/necipallef"><code>@​necipallef</code></a> made
their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13560">facebook/jest#13560</a></li>
<li><a
href="https://github.com/ravshansbox"><code>@​ravshansbox</code></a>
made their first contribution in <a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13533">facebook/jest#13533</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/facebook/jest/compare/v29.2.2...v29.3.0">https://github.com/facebook/jest/compare/v29.2.2...v29.3.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/facebook/jest/blob/main/CHANGELOG.md">jest's
changelog</a>.</em></p>
<blockquote>
<h2>29.3.1</h2>
<h3>Fixes</h3>
<ul>
<li><code>[jest-config]</code> Do not warn about <code>preset</code> in
<code>ProjectConfig</code> (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13583">#13583</a>)</li>
</ul>
<h3>Performance</h3>
<ul>
<li><code>[jest-transform]</code> Defer creation of cache directory (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13420">#13420</a>)</li>
</ul>
<h2>29.3.0</h2>
<h3>Features</h3>
<ul>
<li><code>[jest-runtime]</code> Support WebAssembly (Wasm) imports in
ESM modules (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13505">#13505</a>)</li>
</ul>
<h3>Fixes</h3>
<ul>
<li><code>[jest-config]</code> Add config validation for
<code>projects</code> option (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13565">#13565</a>)</li>
<li><code>[jest-mock]</code> Treat cjs modules as objects so they can be
mocked (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13513">#13513</a>)</li>
<li><code>[jest-worker]</code> Throw an error instead of hanging when
jest workers terminate unexpectedly (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13566">#13566</a>)</li>
</ul>
<h3>Chore &amp; Maintenance</h3>
<ul>
<li><code>[@jest/transform]</code> Update
<code>convert-source-map</code> (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13509">#13509</a>)</li>
<li><code>[docs]</code> Mention <code>toStrictEqual</code> in
UsingMatchers docs. (<a
href="https://github-redirect.dependabot.com/facebook/jest/pull/13560">#13560</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="05deb8393c"><code>05deb83</code></a>
v29.3.1</li>
<li><a
href="84b8de987b"><code>84b8de9</code></a>
v29.3.0</li>
<li>See full diff in <a
href="https://github.com/facebook/jest/commits/v29.3.1/packages/jest">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jest&package-manager=npm_and_yarn&previous-version=29.2.2&new-version=29.3.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-21 10:00:53 -06:00
Benjamin Eckel
6267682266 fix(node-sdk): Properly calculate length of utf-8 inputs (#82)
Properly calculate the length of utf-8 inputs. I'll work on getting
unicode chars into our other tests as well and see if we need to fix any
other SDKs
2022-11-20 13:23:01 -06:00
Benjamin Eckel
b1ca2f398d test: Make sure we have unicode in the SDK tests (#83)
Related to https://github.com/extism/extism/pull/82

Ensures we have unicode inputs in our SDK tests. We should probably get
unicode in the output as well
2022-11-20 13:22:48 -06:00
zach
58ad4ce6e2 feat: add extism_http_status_code to get the status code of the last HTTP request + fixes for clippy (#81)
This is kind of hacky but until we have an established way of encoding
complex types in memory it seems good enough
2022-11-18 11:15:17 -08:00
zach
d0296de9a7 fix: python README path (#80) 2022-11-18 09:02:17 -08:00
zach
4db1303273 cleanup: remove extism_load_u32 and extism_store_u32 (#79) 2022-11-16 10:56:20 -08:00
Benjamin Eckel
28d16f2fa8 fix: Fix the python release workflow (#76) 2022-11-14 16:27:01 -06:00
Benjamin Eckel
e3dd34e59f feat(elixir): use result of set_log_file (#39)
We can merge this after we release `rc.6`
2022-11-14 13:58:29 -06:00
Benjamin Eckel
bb6026976c fix: fix test import syntax in browser runtime (#75) 2022-11-10 16:28:01 -06:00
Benjamin Eckel
33c0f8a4c8 chore: bump browser runtime to rc.10 and refactor (#74) 2022-11-10 15:57:38 -06:00
Benjamin Eckel
b57acde149 test(browser-runtime): Add a simple test and runner (#73) 2022-11-09 12:11:59 -06:00
Benjamin Eckel
7e8031fcdc feat: [Experimental] Extism browser runtime (#70)
Experimental browser runtime. Will allow you to load and run simple
Extism plugins in the browser.

Still a work-in-progress. I've currently implemented just enough to run
the `count_vowels` plugin. To try it out:

```
cd browser
npm install
npm run build
python3 -m http.server
```

Open http://localhost:8000

There is a small playground style app. Set the url, and the function
name. Paste in some input. Hit `Run`. The result of the plugin call
should appear on the right.

<img width="964" alt="Screen Shot 2022-11-05 at 2 01 24 PM"
src="https://user-images.githubusercontent.com/185919/200136657-80e90a77-0b79-4f9d-a5dc-f5e1f340d143.png">

Co-authored-by: zach <zachshipko@gmail.com>
2022-11-09 11:02:27 -06:00
zach
e6499cab72 Make Rust SDK depend directly on extism-runtime (#65) 2022-11-07 12:45:56 -08:00
dependabot[bot]
e44800f7f6 chore(deps-dev): bump typedoc from 0.23.19 to 0.23.20 in /node (#71)
Bumps [typedoc](https://github.com/TypeStrong/TypeDoc) from 0.23.19 to
0.23.20.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/TypeStrong/TypeDoc/releases">typedoc's
releases</a>.</em></p>
<blockquote>
<h2>v0.23.20</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Fixed comment discovery for <code>@inheritDoc</code> if inheriting
from a function type alias, <a
href="https://github-redirect.dependabot.com/TypeStrong/TypeDoc/issues/2087">#2087</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/TypeStrong/typedoc/blob/master/CHANGELOG.md">typedoc's
changelog</a>.</em></p>
<blockquote>
<h2>v0.23.20 (2022-11-03)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>Fixed comment discovery for <code>@inheritDoc</code> if inheriting
from a function type alias, <a
href="https://github-redirect.dependabot.com/TypeStrong/TypeDoc/issues/2087">#2087</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bfa9dbdf51"><code>bfa9dbd</code></a>
Update changelog for release</li>
<li><a
href="ea185d119a"><code>ea185d1</code></a>
Bump version to 0.23.20</li>
<li><a
href="b0012e577d"><code>b0012e5</code></a>
Doc comment</li>
<li><a
href="30555f1776"><code>30555f1</code></a>
Fix comment discovery for <code>@inheritDoc</code></li>
<li>See full diff in <a
href="https://github.com/TypeStrong/TypeDoc/compare/v0.23.19...v0.23.20">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typedoc&package-manager=npm_and_yarn&previous-version=0.23.19&new-version=0.23.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 10:35:17 -06:00
dependabot[bot]
d6b403e112 chore(deps-dev): bump @types/jest from 29.2.1 to 29.2.2 in /node (#72)
Bumps
[@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest)
from 29.2.1 to 29.2.2.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@types/jest&package-manager=npm_and_yarn&previous-version=29.2.1&new-version=29.2.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 10:34:55 -06:00
68 changed files with 11579 additions and 1105 deletions

View File

@@ -3,8 +3,8 @@ on: [pull_request, workflow_dispatch]
name: CI
env:
RUNTIME_MANIFEST: runtime/Cargo.toml
RUNTIME_CRATE: extism-runtime
LIBEXTISM_CRATE: libextism
RUST_SDK_CRATE: extism
jobs:
@@ -41,7 +41,7 @@ jobs:
- name: Build
if: steps.cache-libextism.outputs.cache-hit != 'true'
shell: bash
run: cargo build --release -p ${{ env.RUNTIME_CRATE }}
run: cargo build --release -p ${{ env.LIBEXTISM_CRATE }}
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
@@ -170,6 +170,7 @@ jobs:
- name: Test Python Host SDK
run: |
cd python
cp ../README.md .
poetry install
poetry run python example.py
poetry run python -m unittest discover
@@ -225,6 +226,12 @@ jobs:
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

View File

@@ -10,7 +10,13 @@ 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:

View File

@@ -16,19 +16,19 @@ jobs:
with:
python-version: "3.9"
check-latest: true
- name: Run image
uses: abatilo/actions-poetry@v2
- name: Build Python Host SDK
run: |
cd python
cp ../LICENSE .
cp ../README.md .
make clean
make build
make prepare
poetry build
- name: Release Python Host SDK
uses: pypa/gh-action-pypi-publish@release/v1
env:
INPUT_VERIFY_METADATA: false
with:
user: ${{ secrets.PYPI_API_USER }}
password: ${{ secrets.PYPI_API_TOKEN }}

View File

@@ -21,5 +21,5 @@ jobs:
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_TOKEN }}
run: |
cd ruby
make publish
make publish RUBYGEMS_API_KEY=$RUBYGEMS_API_KEY

View File

@@ -7,7 +7,7 @@ name: Release
env:
RUNTIME_MANIFEST: runtime/Cargo.toml
RUNTIME_CRATE: extism-runtime
RUNTIME_CRATE: libextism
RUSTFLAGS: -C target-feature=-crt-static
ARTIFACT_DIR: release-artifacts

View File

@@ -3,8 +3,8 @@ members = [
"manifest",
"runtime",
"rust",
"libextism"
]
exclude = [
"elixir/native/extism_nif"
]

View File

@@ -25,7 +25,7 @@ lint:
cargo clippy --release --no-deps --manifest-path runtime/Cargo.toml
build:
cargo build --release $(FEATURE_FLAGS) --manifest-path runtime/Cargo.toml
cargo build --release $(FEATURE_FLAGS) --manifest-path libextism/Cargo.toml
install:
install runtime/extism.h $(DEST)/include

130
browser/.gitignore vendored Normal file
View File

@@ -0,0 +1,130 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

5
browser/.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"printWidth": 120,
"trailingComma": "all",
"singleQuote": true
}

25
browser/Makefile Normal file
View File

@@ -0,0 +1,25 @@
.PHONY: test
prepare:
npm install
test: prepare
npm run test
clean:
echo "No clean implemented"
publish: clean prepare
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

23
browser/build.js Normal file
View File

@@ -0,0 +1,23 @@
const { build } = require("esbuild");
const { dependencies, peerDependencies } = require('./package.json')
const sharedConfig = {
entryPoints: ["src/index.ts"],
bundle: true,
minify: false,
drop: [], // preseve debugger statements
external: Object.keys(dependencies || {}).concat(Object.keys(peerDependencies || {})),
};
build({
...sharedConfig,
platform: 'node', // for CJS
outfile: "dist/index.js",
});
build({
...sharedConfig,
outfile: "dist/index.esm.js",
platform: 'neutral', // for ESM
format: "esm",
});

BIN
browser/data/code.wasm Executable file

Binary file not shown.

238
browser/index.html Normal file
View File

@@ -0,0 +1,238 @@
<html>
<head>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<style>
#main {
width: 100%;
}
.manifest {
display: flex; /* or inline-flex */
flex-direction: row;
flex-wrap: nowrap;
width: 100%;
}
.urlInput {
width: 600px;
}
.funcName {
width: 150px;
}
.textAreas {
display: flex; /* or inline-flex */
flex-direction: row;
flex-wrap: nowrap;
width: 100%;
height: 300px;
}
.inputBox {
width: 100%;
height: 100%;
}
.inputBox > textarea {
width: 100%;
height: 100%;
}
.outputBox {
width: 100%;
height: 100%;
}
.outputBox > textarea {
width: 100%;
height: 100%;
}
.space {
height: 80px;
}
.dragAreas {
display: flex; /* or inline-flex */
flex-direction: row;
flex-wrap: nowrap;
width: 100%;
height: 200px;
}
.dragInput {
width: 100%;
height: 100%;
border-style: dotted;
border-color: #000;
}
.dragOutput {
width: 100%;
height: 100%;
}
.dropZone {
width: 100%;
height: 100%;
}
.outputImage {
width: 100%;
height: 100%;
}
</style>
<script type="text/babel">
function getBase64(file, cb) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log("error")
};
}
function arrayTob64(buffer) {
var binary = '';
var bytes = [].slice.call(buffer);
bytes.forEach((b) => binary += String.fromCharCode(b));
return window.btoa(binary);
}
class App extends React.Component {
state = {
url: "https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm",
input: new Uint8Array(),
output: new Uint8Array(),
func_name: "count_vowels",
functions: []
}
async loadFunctions(url) {
let plugin = await this.extismContext.newPlugin({ "wasm": [ { "path": url } ] })
let functions = await plugin.getExportedFunctions()
console.log("funcs ", functions)
this.setState({functions})
}
componentDidMount() {
this.loadFunctions(this.state.url)
}
constructor(props) {
super(props)
this.extismContext = props.extismContext
}
handleInputChange(e) {
e.preventDefault();
this.setState({ [e.target.name]: e.target.value })
if (e.target.name === "url") {
this.loadFunctions(e.target.value)
}
}
onInputKeyPress(e) {
if (e.keyCode == 13 && e.shiftKey == true) {
e.preventDefault()
this.handleOnRun()
}
}
async handleOnRun(e) {
e && e.preventDefault && e.preventDefault();
let plugin = await this.extismContext.newPlugin({ "wasm": [ { "path": this.state.url } ] })
let result = await plugin.call(this.state.func_name, this.state.input)
let output = result
this.setState({output})
}
nop = (e) => {
e.preventDefault();
e.stopPropagation();
};
handleDrop = e => {
e.preventDefault();
e.stopPropagation();
let files = [...e.dataTransfer.files];
if (files && files.length == 1) {
let file = files[0]
console.log(file)
file.arrayBuffer().then(b => {
this.setState({input: new Uint8Array(b)})
this.handleOnRun()
})
} else {
throw Error("Only one file please")
}
};
render() {
const funcOptions = this.state.functions.map(f => <option value={f}>{f}</option>)
let image = null
if (this.state.output) {
image = <img src={`data:image/png;base64,${arrayTob64(this.state.output)}`}/>
}
return <div className="app">
<div className="manifest">
<div>
<label>WASM Url: </label>
<input type="text" name="url" className="urlInput" value={this.state.url} onChange={this.handleInputChange.bind(this)} />
</div>
<div>
<label>Function: </label>
<select type="text" name="func_name" className="funcName" value={this.state.func_name} onChange={this.handleInputChange.bind(this)}>
{funcOptions}
</select>
</div>
<div>
<button onClick={this.handleOnRun.bind(this)}>Run</button>
</div>
</div>
<div className="textAreas">
<div className="inputBox">
<h3>Text Input</h3>
<textarea name="input" value={this.state.input} onChange={this.handleInputChange.bind(this)} onKeyDown={this.onInputKeyPress.bind(this)}></textarea>
</div>
<div className="outputBox">
<h3>Text Output</h3>
<textarea name="output" value={new TextDecoder().decode(this.state.output)} ></textarea>
</div>
</div>
<div className="space" />
<div className="dragAreas">
<div className="dragInput">
<h3>Image Input</h3>
<div className="dropZone"
onDrop={this.handleDrop.bind(this)}
onDragOver={this.nop.bind(this)}
onDragEnter={this.nop.bind(this)}
onDragLeave={this.nop.bind(this)}
>
</div>
</div>
<div className="dragOutput">
<h3>Image Output</h3>
<div className="outputImage">
{image}
</div>
</div>
</div>
</div>
}
}
window.App = App
</script>
<script type="module">
import {ExtismContext} from './dist/index.esm.js'
const e = React.createElement;
window.onload = () => {
const domContainer = document.getElementById('main');
console.log(domContainer)
const root = ReactDOM.createRoot(domContainer);
const extismContext = new ExtismContext()
root.render(e(App, {extismContext}));
}
</script>
</head>
<body>
<div id="main"></div>
</body>
</html>

5
browser/jest.config.js Normal file
View File

@@ -0,0 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

9554
browser/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

34
browser/package.json Normal file
View File

@@ -0,0 +1,34 @@
{
"name": "@extism/runtime-browser",
"version": "0.1.0",
"description": "Extism runtime in the browser",
"scripts": {
"build": "node build.js && tsc --emitDeclarationOnly --outDir dist",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "tslint -p tsconfig.json",
"test": "jest --config jest.config.js"
},
"private": false,
"publishConfig": {
"access": "public"
},
"files": [
"dist/*"
],
"module": "dist/index.esm.js",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"author": "The Extism Authors <oss@extism.org>",
"license": "BSD-3-Clause",
"devDependencies": {
"@types/jest": "^29.2.2",
"esbuild": "^0.15.13",
"jest": "^29.2.2",
"prettier": "^2.7.1",
"ts-jest": "^29.0.3",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typedoc": "^0.23.20",
"typescript": "^4.8.4"
}
}

104
browser/src/allocator.ts Normal file
View File

@@ -0,0 +1,104 @@
type MemoryBlock = { offset: bigint; length: bigint };
export default class Allocator {
currentIndex: bigint;
active: Record<number, MemoryBlock>;
freed: MemoryBlock[];
memory: Uint8Array;
constructor(n: number) {
this.currentIndex = BigInt(1);
this.active = {};
this.freed = [];
this.memory = new Uint8Array(n);
}
reset() {
this.currentIndex = BigInt(1);
this.active = {};
this.freed = [];
}
alloc(length: bigint): bigint {
for (var i = 0; i < this.freed.length; i++) {
let block = this.freed[i];
if (block.length === length) {
this.active[Number(block.offset)] = block;
this.freed.splice(i, 1);
return block.offset;
} else if (block.length > length + BigInt(64)) {
const newBlock = { offset: block.offset, length };
block.offset += length;
block.length -= length;
return newBlock.offset;
}
}
// Resize memory if needed
// TODO: put a limit on the memory size
if (BigInt(this.memory.length) < this.currentIndex + length) {
const tmp = new Uint8Array(Number(this.currentIndex + length + BigInt(64)));
tmp.set(this.memory);
this.memory = tmp;
}
const offset = this.currentIndex;
this.currentIndex += length;
this.active[Number(offset)] = { offset, length };
return offset;
}
getBytes(offset: bigint): Uint8Array | null {
const block = this.active[Number(offset)];
if (!block) {
return null;
}
return new Uint8Array(this.memory.buffer, Number(offset), Number(block.length));
}
getString(offset: bigint): string | null {
const bytes = this.getBytes(offset);
if (bytes === null) {
return null;
}
return new TextDecoder().decode(bytes);
}
allocBytes(data: Uint8Array): bigint {
const offs = this.alloc(BigInt(data.length));
const bytes = this.getBytes(offs);
if (bytes === null) {
this.free(offs);
return BigInt(0);
}
bytes.set(data);
return offs;
}
allocString(data: string): bigint {
const bytes = new TextEncoder().encode(data);
return this.allocBytes(bytes);
}
getLength(offset: bigint): bigint {
const block = this.active[Number(offset)];
if (!block) {
return BigInt(0);
}
return block.length;
}
free(offset: bigint) {
const block = this.active[Number(offset)];
if (!block) {
return;
}
delete this.active[Number(offset)];
this.freed.push(block);
}
}

45
browser/src/context.ts Normal file
View File

@@ -0,0 +1,45 @@
import { Manifest, PluginConfig, ManifestWasmFile, ManifestWasmData } from './manifest';
import ExtismPlugin from './plugin';
/**
* Can be a {@link Manifest} or just the raw bytes of the WASM module as an ArrayBuffer.
* We recommend using {@link Manifest}
*/
type ManifestData = Manifest | ArrayBuffer;
/**
* A Context is needed to create plugins. The Context
* is where your plugins live. Freeing the context
* frees all of the plugins in its scope.
*/
export default class ExtismContext {
/**
* Create a plugin managed by this context
*
* @param manifest - The {@link ManifestData} describing the plugin code and config
* @param config - Config details for the plugin
* @returns A new Plugin scoped to this Context
*/
async newPlugin(manifest: ManifestData, config?: PluginConfig) {
let moduleData: ArrayBuffer | null = null;
if (manifest instanceof ArrayBuffer) {
moduleData = manifest;
} else if ((manifest as Manifest).wasm) {
const wasmData = (manifest as Manifest).wasm;
if (wasmData.length > 1) throw Error('This runtime only supports one module in Manifest.wasm');
const wasm = wasmData[0];
if ((wasm as ManifestWasmData).data) {
moduleData = (wasm as ManifestWasmData).data;
} else if ((wasm as ManifestWasmFile).path) {
const response = await fetch((wasm as ManifestWasmFile).path);
moduleData = await response.arrayBuffer();
console.dir(moduleData);
}
}
if (!moduleData) {
throw Error(`Unsure how to interpret manifest ${manifest}`);
}
return new ExtismPlugin(moduleData, config);
}
}

23
browser/src/index.test.ts Normal file
View File

@@ -0,0 +1,23 @@
import { ExtismContext } from './';
import fs from 'fs';
import path from 'path';
function parse(bytes: Uint8Array): any {
return JSON.parse(new TextDecoder().decode(bytes));
}
describe('', () => {
it('can load and call a plugin', async () => {
const data = fs.readFileSync(path.join(__dirname, '..', 'data', 'code.wasm'));
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']);
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');
expect(parse(output)).toEqual({ count: 7 });
output = await plugin.call('count_vowels', 'this is a test thrice');
expect(parse(output)).toEqual({ count: 6 });
});
});

3
browser/src/index.ts Normal file
View File

@@ -0,0 +1,3 @@
import ExtismContext from './context';
export { ExtismContext };

40
browser/src/manifest.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* Represents a path or url to a WASM module
*/
export type ManifestWasmFile = {
path: string;
name?: string;
hash?: string;
};
/**
* Represents the raw bytes of a WASM file loaded into memory
*/
export type ManifestWasmData = {
data: Uint8Array;
name?: string;
hash?: string;
};
/**
* {@link ExtismPlugin} Config
*/
export type PluginConfig = Map<string, string>;
/**
* The WASM to load as bytes or a path
*/
export type ManifestWasm = ManifestWasmFile | ManifestWasmData;
/**
* The manifest which describes the {@link ExtismPlugin} code and
* runtime constraints.
*
* @see [Extism > Concepts > Manifest](https://extism.org/docs/concepts/manifest)
*/
export type Manifest = {
wasm: Array<ManifestWasm>;
//memory?: ManifestMemory;
config?: PluginConfig;
allowed_hosts?: Array<string>;
};

170
browser/src/plugin.ts Normal file
View File

@@ -0,0 +1,170 @@
import Allocator from './allocator';
import { PluginConfig } from './manifest';
export default class ExtismPlugin {
moduleData: ArrayBuffer;
allocator: Allocator;
config?: PluginConfig;
vars: Record<string, Uint8Array>;
input: Uint8Array;
output: Uint8Array;
module?: WebAssembly.WebAssemblyInstantiatedSource;
constructor(moduleData: ArrayBuffer, config?: PluginConfig) {
this.moduleData = moduleData;
this.allocator = new Allocator(1024 * 1024);
this.config = config;
this.vars = {};
this.input = new Uint8Array();
this.output = new Uint8Array();
}
async getExports(): Promise<WebAssembly.Exports> {
const module = await this._instantiateModule();
return module.instance.exports;
}
async getImports(): Promise<WebAssembly.ModuleImportDescriptor[]> {
const module = await this._instantiateModule();
return WebAssembly.Module.imports(module.module);
}
async getInstance(): Promise<WebAssembly.Instance> {
const module = await this._instantiateModule();
return module.instance;
}
async call(func_name: string, input: Uint8Array | string): Promise<Uint8Array> {
const module = await this._instantiateModule();
if (typeof input === 'string') {
this.input = new TextEncoder().encode(input);
} else if (input instanceof Uint8Array) {
this.input = input;
} else {
throw new Error('input should be string or Uint8Array');
}
this.allocator.reset();
let func = module.instance.exports[func_name];
if (!func) {
throw Error(`function does not exist ${func_name}`);
}
//@ts-ignore
func();
return this.output;
}
async _instantiateModule(): Promise<WebAssembly.WebAssemblyInstantiatedSource> {
if (this.module) {
return this.module;
}
const environment = this.makeEnv();
this.module = await WebAssembly.instantiate(this.moduleData, { env: environment });
return this.module;
}
makeEnv(): any {
const plugin = this;
return {
extism_alloc(n: bigint): bigint {
return plugin.allocator.alloc(n);
},
extism_free(n: bigint) {
plugin.allocator.free(n);
},
extism_load_u8(n: bigint): number {
return plugin.allocator.memory[Number(n)];
},
extism_load_u64(n: bigint): bigint {
let cast = new DataView(plugin.allocator.memory.buffer, Number(n));
return cast.getBigUint64(0, true);
},
extism_store_u8(offset: bigint, n: number) {
plugin.allocator.memory[Number(offset)] = Number(n);
},
extism_store_u64(offset: bigint, n: bigint) {
const tmp = new DataView(plugin.allocator.memory.buffer, Number(offset));
tmp.setBigUint64(0, n, true);
},
extism_input_length(): bigint {
return BigInt(plugin.input.length);
},
extism_input_load_u8(i: bigint): number {
return plugin.input[Number(i)];
},
extism_input_load_u64(idx: bigint): bigint {
let cast = new DataView(plugin.input.buffer, Number(idx));
return cast.getBigUint64(0, true);
},
extism_output_set(offset: bigint, length: bigint) {
const offs = Number(offset);
const len = Number(length);
plugin.output = plugin.allocator.memory.slice(offs, offs + len);
},
extism_error_set(i: bigint) {
throw plugin.allocator.getString(i);
},
extism_config_get(i: bigint): bigint {
if (typeof plugin.config === 'undefined') {
return BigInt(0);
}
const key = plugin.allocator.getString(i);
if (key === null) {
return BigInt(0);
}
const value = plugin.config.get(key);
if (typeof value === 'undefined') {
return BigInt(0);
}
return plugin.allocator.allocString(value);
},
extism_var_get(i: bigint): bigint {
const key = plugin.allocator.getString(i);
if (key === null) {
return BigInt(0);
}
const value = plugin.vars[key];
if (typeof value === 'undefined') {
return BigInt(0);
}
return plugin.allocator.allocBytes(value);
},
extism_var_set(n: bigint, i: bigint) {
const key = plugin.allocator.getString(n);
if (key === null) {
return;
}
const value = plugin.allocator.getBytes(i);
if (value === null) {
return;
}
plugin.vars[key] = value;
},
extism_http_request(n: bigint, i: bigint): number {
debugger;
return 0;
},
extism_length(i: bigint): bigint {
return plugin.allocator.getLength(i);
},
extism_log_warn(i: bigint) {
const s = plugin.allocator.getString(i);
console.warn(s);
},
extism_log_info(i: bigint) {
const s = plugin.allocator.getString(i);
console.log(s);
},
extism_log_debug(i: bigint) {
const s = plugin.allocator.getString(i);
console.debug(s);
},
extism_log_error(i: bigint) {
const s = plugin.allocator.getString(i);
console.error(s);
},
};
}
}

12
browser/tsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"strict": true,
"skipLibCheck": true
},
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}

6
browser/tslint.json Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": [
"tslint:recommended",
"tslint-config-prettier"
]
}

View File

@@ -1,50 +1,50 @@
{
"name": "extism/extism",
"description": "Make your software programmable. Run WebAssembly extensions in your app using the first off-the-shelf, universal plug-in system.",
"license": "BSD-3-Clause",
"type": "library",
"keywords": [
"WebAssembly",
"plugin-system",
"runtime",
"plug-in"
],
"authors": [
{
"name": "The Extism Authors",
"email": "oss@extism.org",
"homepage": "https://extism.org"
},
{
"name": "Dylibso, Inc.",
"email": "oss@dylib.so",
"homepage": "https://dylib.so"
}
],
"require": {
"php": "^7.4 || ^8",
"ircmaxell/ffime": "dev-master"
"name": "extism/extism",
"description": "Make your software programmable. Run WebAssembly extensions in your app using the first off-the-shelf, universal plug-in system.",
"license": "BSD-3-Clause",
"type": "library",
"keywords": [
"WebAssembly",
"plugin-system",
"runtime",
"plug-in"
],
"authors": [
{
"name": "The Extism Authors",
"email": "oss@extism.org",
"homepage": "https://extism.org"
},
"suggest": {},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Extism\\": "php/src/"
},
"files": [
"php/src/Plugin.php",
"php/src/generate.php",
"php/src/extism.h"
]
{
"name": "Dylibso, Inc.",
"email": "oss@dylib.so",
"homepage": "https://dylib.so"
}
],
"require": {
"php": "^7.4 || ^8",
"ircmaxell/ffime": "dev-master"
},
"suggest": {},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Extism\\": "php/src/"
},
"autoload-dev": {
"psr-4": {}
},
"config": {
"sort-packages": true
},
"extra": {},
"scripts": {},
"scripts-descriptions": {}
"files": [
"php/src/Context.php",
"php/src/Plugin.php",
"php/src/extism.h"
]
},
"autoload-dev": {
"psr-4": {}
},
"config": {
"sort-packages": true
},
"extra": {},
"scripts": {},
"scripts-descriptions": {}
}

View File

@@ -4,8 +4,8 @@ defmodule Extism.MixProject do
def project do
[
app: :extism,
version: "0.0.1-rc.6",
elixir: "~> 1.14",
version: "0.1.0",
elixir: "~> 1.12",
start_permanent: Mix.env() == :prod,
deps: deps(),
package: package(),

View File

@@ -1,6 +1,6 @@
%{
"earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"ex_doc": {:hex, :ex_doc, "0.29.0", "4a1cb903ce746aceef9c1f9ae8a6c12b742a5461e6959b9d3b24d813ffbea146", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "f096adb8bbca677d35d278223361c7792d496b3fc0d0224c9d4bc2f651af5db1"},
"ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"json": {:hex, :json, "1.4.1", "8648f04a9439765ad449bc56a3ff7d8b11dd44ff08ffcdefc4329f7c93843dfa", [:mix], [], "hexpm", "9abf218dbe4ea4fcb875e087d5f904ef263d012ee5ed21d46e9dbca63f053d16"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},

View File

@@ -1,6 +1,6 @@
[package]
name = "extism_nif"
version = "0.0.1-rc.6"
version = "0.1.0"
edition = "2021"
authors = ["Benjamin Eckel <bhelx@simst.im>"]
@@ -11,5 +11,5 @@ crate-type = ["cdylib"]
[dependencies]
rustler = "0.26.0"
extism = { version = "0.0.1-rc.5" }
extism = { version = "0.1.0" }
log = "0.4"

View File

@@ -1,10 +1,10 @@
use rustler::{Atom, Env, Term, ResourceArc};
use extism::{Plugin, Context};
use std::str;
use extism::{Context, Plugin};
use rustler::{Atom, Env, ResourceArc, Term};
use std::mem;
use std::path::Path;
use std::str;
use std::str::FromStr;
use std::sync::RwLock;
use std::mem;
mod atoms {
rustler::atoms! {
@@ -15,9 +15,12 @@ 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
@@ -27,15 +30,16 @@ 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::Json(json_err) => rustler::Error::Term(Box::new(json_err.to_string())),
extism::Error::Runtime(e) => rustler::Error::Term(Box::new(e.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]
@@ -51,7 +55,11 @@ 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)),
@@ -67,17 +75,22 @@ fn plugin_new_with_manifest(ctx: ResourceArc<ExtismContext>, manifest_payload: S
}
#[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 plugin = unsafe { Plugin::from_id(plugin_id, context) };
let mut 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
@@ -86,14 +99,17 @@ fn plugin_call(ctx: ResourceArc<ExtismContext>, plugin_id: i32, name: String, in
}
#[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
@@ -113,16 +129,28 @@ 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) => {
extism::set_log_file(path, Some(level));
Ok(atoms::ok())
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.",
)))
}
}
}
}
#[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);

View File

@@ -35,6 +35,8 @@ defmodule ExtismTest do
assert JSON.decode(output) == {:ok, %{"count" => 7}}
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test thrice")
assert JSON.decode(output) == {:ok, %{"count" => 6}}
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "🌎hello🌎world🌎")
assert JSON.decode(output) == {:ok, %{"count" => 3}}
Extism.Context.free(ctx)
end

View File

@@ -18,6 +18,6 @@ handlePlugin plugin = do
exitSuccess) res
main = do
context <- Extism.newContext ()
context <- Extism.newContext
plugin <- Extism.pluginFromManifest context (manifest [wasmFile "../wasm/code.wasm"]) False
try handlePlugin plugin

View File

@@ -1,6 +1,6 @@
cabal-version: 2.4
cabal-version: 3.0
name: extism
version: 0.0.1.0
version: 0.0.1
-- A short (one-line) description of the package.
synopsis: Extism bindings
@@ -23,22 +23,41 @@ category: Plugins, WebAssembly
extra-source-files: CHANGELOG.md
library
exposed-modules: Extism Extism.Manifest
exposed-modules: Extism
reexported-modules: Extism.Manifest
-- Modules included in this library but not exported.
other-modules: Extism.Bindings
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends:
base >= 1.6.0
, bytestring
, json
, manifest
hs-source-dirs: src
default-language: Haskell2010
extra-libraries: extism
extra-lib-dirs: /usr/local/lib
library manifest
exposed-modules: Extism.Manifest
visibility: public
-- 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
base
, bytestring
, base64-bytestring
, json
hs-source-dirs: src
hs-source-dirs: manifest
default-language: Haskell2010
extra-libraries: extism
extra-lib-dirs: /usr/local/lib
Test-Suite extism-example
type: exitcode-stdio-1.0

View File

@@ -1,7 +1,10 @@
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module Extism.Manifest where
import Text.JSON
(
JSON,
JSValue(JSNull, JSString, JSArray),
toJSString, showJSON, makeObj, encode
)
@@ -9,16 +12,12 @@ 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])
makeArray x = JSArray [toJSONValue a | a <- x]
isNull JSNull = True
isNull _ = False
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
object x = makeObj $ filterNulls x
(.=) a b = (a, toJSONValue b)
newtype Memory = Memory
{
@@ -27,32 +26,37 @@ newtype Memory = Memory
class JSONValue a where
toJSONValue :: a -> JSValue
instance {-# OVERLAPS #-} (JSON a) => (JSONValue a) where
toJSONValue j = showJSON j
instance {-# OVERLAPS #-} (JSONValue a) => (JSONValue (Maybe a)) where
toJSONValue Nothing = JSNull
toJSONValue (Just x) = toJSONValue x
instance JSONValue Memory where
toJSONValue x =
case memoryMax x of
Nothing -> makeObj []
Just max -> makeObj [("max", showJSON max)]
toJSONValue (Memory max) =
object [
"max" .= max
]
data HttpRequest = HttpRequest
data HTTPRequest = HTTPRequest
{
url :: String
, header :: [(String, String)]
, header :: Maybe [(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)
requestObj (HTTPRequest url header method) =
[
"url" .= url ,
"header" .= header,
"method" .= method
]
instance JSONValue HttpRequest where
instance JSONValue HTTPRequest where
toJSONValue x =
makeObj $ requestObj x
object $ requestObj x
data WasmFile = WasmFile
{
@@ -62,14 +66,11 @@ data WasmFile = WasmFile
}
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)
toJSONValue (WasmFile path name hash) =
object [
"path" .= path,
"name" .= name,
"hash" .= hash
]
data WasmCode = WasmCode
@@ -81,30 +82,26 @@ data WasmCode = WasmCode
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)
toJSONValue (WasmCode x name hash) =
let bytes = BS.unpack $ B64.encode x in
object [
"data" .= bytes,
"name" .= name,
"hash" .= hash
]
data WasmURL = WasmURL
{
req :: HttpRequest
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
toJSONValue (WasmURL req name hash) =
let request = requestObj $ req in
object $ "name" .= name : "hash" .= hash : request
data Wasm = File WasmFile | Code WasmCode | URL WasmURL
@@ -121,7 +118,7 @@ wasmFile path =
wasmURL :: String -> String -> Wasm
wasmURL method url =
let r = HttpRequest { url = url, header = [], method = Just method } in
let r = HTTPRequest { url = url, header = Nothing, method = Just method } in
URL WasmURL { req = r, urlName = Nothing, urlHash = Nothing }
wasmCode :: B.ByteString -> Wasm
@@ -143,8 +140,8 @@ data Manifest = Manifest
{
wasm :: [Wasm]
, memory :: Maybe Memory
, config :: [(String, String)]
, allowed_hosts :: [String]
, config :: Maybe [(String, String)]
, allowedHosts :: Maybe [String]
}
manifest :: [Wasm] -> Manifest
@@ -152,32 +149,29 @@ manifest wasm =
Manifest {
wasm = wasm,
memory = Nothing,
config = [],
allowed_hosts = []
config = Nothing,
allowedHosts = Nothing
}
withConfig :: Manifest -> [(String, String)] -> Manifest
withConfig m config =
m { config = config }
m { config = Just config }
withHosts :: Manifest -> [String] -> Manifest
withHosts m hosts =
m { allowed_hosts = hosts }
m { allowedHosts = Just 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)
toJSONValue (Manifest wasm memory config hosts) =
let w = makeArray wasm in
object [
"wasm" .= w,
"memory" .= memory,
"config" .= config,
"allowed_hosts" .= hosts
]
toString :: Manifest -> String
toString manifest =
encode (toJSONValue manifest)
toString :: (JSONValue a) => a -> String
toString v =
encode (toJSONValue v)

View File

@@ -1,36 +1,17 @@
{-# LANGUAGE ForeignFunctionInterface #-}
module Extism (module Extism, module Extism.Manifest) where
import GHC.Int
import GHC.Word
import Foreign.C.Types
import Foreign.Ptr
import Data.Int
import Data.Word
import Control.Monad (void)
import Foreign.ForeignPtr
import Foreign.C.String
import Control.Monad (void)
import Foreign.Ptr
import Data.ByteString as B
import Data.ByteString.Internal (c2w, w2c)
import Data.ByteString.Unsafe (unsafeUseAsCString)
import Data.Bifunctor (second)
import Text.JSON (JSON, toJSObject, toJSString, encode, JSValue(JSNull, JSString))
import Extism.Manifest (Manifest, toString)
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
import Text.JSON (encode, toJSObject)
import Extism.Manifest (Manifest, toString, toJSONValue)
import Extism.Bindings
-- Context manages plugins
newtype Context = Context (ForeignPtr ExtismContext)
@@ -64,8 +45,8 @@ reset (Context ctx) =
withForeignPtr ctx extism_context_reset
-- Create a new context
newContext :: () -> IO Context
newContext () = do
newContext :: IO Context
newContext = do
ptr <- extism_context_new
fptr <- newForeignPtr extism_context_free ptr
return (Context fptr)
@@ -73,7 +54,7 @@ newContext () = do
-- 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
@@ -126,16 +107,13 @@ updateManifest plugin manifest useWasi =
isValid :: Plugin -> Bool
isValid (Plugin _ p) = p >= 0
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, convertMaybeString v) | (k, v) <- x] in
let obj = toJSObject [(k, toJSONValue v) | (k, v) <- x] in
let bs = toByteString (encode obj) in
let length = fromIntegral (B.length bs) in
unsafeUseAsCString bs (\s -> do

View File

@@ -0,0 +1,26 @@
{-# 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

View File

@@ -5,6 +5,6 @@ libdir=${exec_prefix}/lib
Name: extism
Description: The Extism universal plug-in system.
Version: 0.0.1
Version: 0.1.0
Cflags: -I${includedir}
Libs: -L${libdir} -lextism

23
libextism/Cargo.toml Normal file
View File

@@ -0,0 +1,23 @@
[package]
name = "libextism"
version = "0.1.0"
edition = "2021"
authors = ["The Extism Authors", "oss@extism.org"]
license = "BSD-3-Clause"
homepage = "https://extism.org"
repository = "https://github.com/extism/extism"
description = "libextism"
[lib]
name = "extism"
crate-type = ["cdylib"]
[dependencies]
extism-runtime = {path = "../runtime"}
[features]
default = ["http", "register-http", "register-filesystem"]
nn = ["extism-runtime/nn"]
register-http = ["extism-runtime/register-http"] # enables wasm to be downloaded using http
register-filesystem = ["extism-runtime/register-filesystem"] # enables wasm to be loaded from disk
http = ["extism-runtime/http"] # enables extism_http_request

10
libextism/src/lib.rs Normal file
View File

@@ -0,0 +1,10 @@
//! This crate is used to generate `libextism` using `extism-runtime`
pub use extism_runtime::sdk::*;
#[cfg(test)]
#[test]
fn test_version() {
let s = unsafe { std::ffi::CStr::from_ptr(extism_version()) };
assert!(s.to_bytes() != b"0.0.0");
}

View File

@@ -1,6 +1,6 @@
[package]
name = "extism-manifest"
version = "0.0.1-rc.6"
version = "0.1.0"
edition = "2021"
authors = ["The Extism Authors", "oss@extism.org"]
license = "BSD-3-Clause"
@@ -9,7 +9,7 @@ repository = "https://github.com/extism/extism"
description = "Extism plug-in manifest crate"
[dependencies]
serde = {version = "1", features=["derive"]}
serde = {version = "1", features = ["derive"]}
base64 = "0.20.0-alpha"
schemars = {version = "0.8", optional=true}

View File

@@ -1,5 +1,5 @@
import { withContext, Context } from "./dist/index.js";
import { readFileSync } from "fs";
const { withContext, Context } = require('./dist/index.js');
const { readFileSync } = require('fs');
withContext(async function (context) {
let wasm = readFileSync("../wasm/code.wasm");

1170
node/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@extism/extism",
"version": "0.0.1-rc.6",
"version": "0.1.0",
"description": "Extism Host SDK for Node",
"keywords": [
"extism",
@@ -21,7 +21,7 @@
},
"scripts": {
"prepare": "npm run build",
"example": "node example.mjs",
"example": "node example.js",
"build": "tsc",
"test": "jest --coverage"
},
@@ -36,7 +36,7 @@
"@types/jest": "^29.2.0",
"@types/node": "^18.11.4",
"jest": "^29.2.2",
"prettier": "2.7.1",
"prettier": "2.8.0",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"typedoc": "^0.23.18",

View File

@@ -299,7 +299,7 @@ export class Plugin {
let plugin = lib.extism_plugin_new(
ctx.pointer,
dataRaw,
dataRaw.length,
Buffer.byteLength(dataRaw, 'utf-8'),
wasi
);
if (plugin < 0) {
@@ -319,7 +319,7 @@ export class Plugin {
if (config != null) {
let s = JSON.stringify(config);
lib.extism_plugin_config(ctx.pointer, this.id, s, s.length);
lib.extism_plugin_config(ctx.pointer, this.id, s, Buffer.byteLength(s, 'utf-8'),);
}
}
@@ -344,7 +344,7 @@ export class Plugin {
this.ctx.pointer,
this.id,
dataRaw,
dataRaw.length,
Buffer.byteLength(dataRaw, 'utf-8'),
wasi
);
if (!ok) {
@@ -357,7 +357,7 @@ export class Plugin {
if (config != null) {
let s = JSON.stringify(config);
lib.extism_plugin_config(this.ctx.pointer, this.id, s, s.length);
lib.extism_plugin_config(this.ctx.pointer, this.id, s, Buffer.byteLength(s, 'utf-8'),);
}
}
@@ -401,7 +401,7 @@ export class Plugin {
this.id,
functionName,
input.toString(),
input.length
Buffer.byteLength(input, 'utf-8'),
);
if (rc !== 0) {
var err = lib.extism_error(this.ctx.pointer, this.id);

View File

@@ -31,6 +31,9 @@ describe("test extism", () => {
output = await plugin.call("count_vowels", "this is a test thrice");
result = JSON.parse(output.toString());
expect(result["count"]).toBe(6);
output = await plugin.call("count_vowels", "🌎hello🌎world🌎");
result = JSON.parse(output.toString());
expect(result["count"]).toBe(3);
});
});

View File

@@ -1,22 +1,22 @@
{
"name": "extism/example",
"description": "Example running the Extism PHP Host SDK",
"license": "BSD-3-Clause",
"type": "project",
"authors": [
{
"name": "The Extism Authors",
"email": "oss@extism.org"
}
],
"require": {
"extism/extism": "*"
},
"repositories": [
{
"type": "path",
"url": "../../"
}
],
"minimum-stability": "dev"
"name": "extism/example",
"description": "Example running the Extism PHP Host SDK",
"license": "BSD-3-Clause",
"type": "project",
"authors": [
{
"name": "The Extism Authors",
"email": "oss@extism.org"
}
],
"require": {
"extism/extism": "*"
},
"repositories": [
{
"type": "path",
"url": "../../"
}
],
"minimum-stability": "dev"
}

View File

@@ -8,11 +8,11 @@
"packages": [
{
"name": "extism/extism",
"version": "dev-feat-reuse-plugins",
"version": "dev-php-sdk-fix",
"dist": {
"type": "path",
"url": "../..",
"reference": "9e5f576acff0aa9c8720fdf6d1103b7c1996bf14"
"reference": "6119eae37bcb2e02f7c7b5b203ab9f959819e83d"
},
"require": {
"ircmaxell/ffime": "dev-master",
@@ -22,7 +22,12 @@
"autoload": {
"psr-4": {
"Extism\\": "php/src/"
}
},
"files": [
"php/src/Context.php",
"php/src/Plugin.php",
"php/src/extism.h"
]
},
"autoload-dev": {
"psr-4": []
@@ -59,12 +64,12 @@
"source": {
"type": "git",
"url": "https://github.com/ircmaxell/FFIMe.git",
"reference": "7b9e0bf23adceddd5fde3130d30275a45cfc1867"
"reference": "f6911d7a6a7090a9782a21a946819a2efa9a2ff7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ircmaxell/FFIMe/zipball/7b9e0bf23adceddd5fde3130d30275a45cfc1867",
"reference": "7b9e0bf23adceddd5fde3130d30275a45cfc1867",
"url": "https://api.github.com/repos/ircmaxell/FFIMe/zipball/f6911d7a6a7090a9782a21a946819a2efa9a2ff7",
"reference": "f6911d7a6a7090a9782a21a946819a2efa9a2ff7",
"shasum": ""
},
"require": {
@@ -97,7 +102,7 @@
"issues": "https://github.com/ircmaxell/FFIMe/issues",
"source": "https://github.com/ircmaxell/FFIMe/tree/master"
},
"time": "2022-09-07T19:50:56+00:00"
"time": "2022-09-25T18:13:59+00:00"
},
{
"name": "ircmaxell/php-c-parser",
@@ -105,12 +110,12 @@
"source": {
"type": "git",
"url": "https://github.com/ircmaxell/php-c-parser.git",
"reference": "55e0a4fdf88d6e955d928860e1e107a68492c1cf"
"reference": "fd8f5efefd0fcc6c5119d945694acaa3a6790ada"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ircmaxell/php-c-parser/zipball/55e0a4fdf88d6e955d928860e1e107a68492c1cf",
"reference": "55e0a4fdf88d6e955d928860e1e107a68492c1cf",
"url": "https://api.github.com/repos/ircmaxell/php-c-parser/zipball/fd8f5efefd0fcc6c5119d945694acaa3a6790ada",
"reference": "fd8f5efefd0fcc6c5119d945694acaa3a6790ada",
"shasum": ""
},
"require": {
@@ -146,7 +151,7 @@
"issues": "https://github.com/ircmaxell/php-c-parser/issues",
"source": "https://github.com/ircmaxell/php-c-parser/tree/master"
},
"time": "2022-08-27T17:37:14+00:00"
"time": "2022-09-23T19:39:35+00:00"
},
{
"name": "ircmaxell/php-object-symbolresolver",
@@ -154,12 +159,12 @@
"source": {
"type": "git",
"url": "https://github.com/ircmaxell/php-object-symbolresolver.git",
"reference": "88c918a0f4621ef59dc4a4c21ead7f39bd720337"
"reference": "dfe1b1aa6c15b198bdef50fff8485e98e89f2a09"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ircmaxell/php-object-symbolresolver/zipball/88c918a0f4621ef59dc4a4c21ead7f39bd720337",
"reference": "88c918a0f4621ef59dc4a4c21ead7f39bd720337",
"url": "https://api.github.com/repos/ircmaxell/php-object-symbolresolver/zipball/dfe1b1aa6c15b198bdef50fff8485e98e89f2a09",
"reference": "dfe1b1aa6c15b198bdef50fff8485e98e89f2a09",
"shasum": ""
},
"require": {
@@ -191,7 +196,7 @@
"issues": "https://github.com/ircmaxell/php-object-symbolresolver/issues",
"source": "https://github.com/ircmaxell/php-object-symbolresolver/tree/master"
},
"time": "2022-09-07T19:47:04+00:00"
"time": "2022-09-15T18:21:50+00:00"
}
],
"packages-dev": [],

View File

@@ -3,12 +3,37 @@ declare(strict_types=1);
namespace Extism;
require_once "vendor/autoload.php";
require_once "generate.php";
function generate_extism_lib() {
return (new \FFIMe\FFIMe("libextism.".soext()))
->include("extism.h")
->showWarnings(false)
->codeGen('ExtismLib', __DIR__.'/ExtismLib.php');
}
function soext() {
$platform = php_uname("s");
switch ($platform) {
case "Darwin":
return "dylib";
case "Linux":
return "so";
case "Windows":
return "dll";
default:
throw new \Exception("Extism: unsupported platform ".$platform);
}
}
if (!file_exists(__DIR__."/ExtismLib.php")) {
generate_extism_lib();
}
require_once "ExtismLib.php";
$lib = new \ExtismLib(\ExtismLib::SOFILE);
if ($lib == null) {
throw new Exception("Extism: failed to create new runtime instance");
throw new \Exception("Extism: failed to create new runtime instance");
}
class Context

View File

@@ -2,8 +2,6 @@
declare(strict_types=1);
namespace Extism;
require_once "vendor/autoload.php";
require_once "generate.php";
require_once "ExtismLib.php";
class Plugin
@@ -34,13 +32,13 @@ class Plugin
$id = $this->lib->extism_plugin_new($ctx->pointer, $data, count($data), (int)$wasi);
if ($id < 0) {
$err = $this->lib->extism_error($ctx->pointer, -1);
throw new Exception("Extism: unable to load plugin: " . $err);
throw new \Exception("Extism: unable to load plugin: " . $err);
}
$this->id = $id;
$this->context = $ctx;
if ($config != null) {
$cfg = string_to_bytes(json_encode(config));
if ($this->config != null) {
$cfg = string_to_bytes(json_encode($config));
$this->lib->extism_plugin_config($ctx->pointer, $this->id, $cfg, count($cfg));
}
}
@@ -73,14 +71,14 @@ class Plugin
if ($err) {
$msg = $msg . ", error = " . $err;
}
throw new Execption("Extism: call to '".$name."' failed with " . $msg);
throw new \Exception("Extism: call to '".$name."' failed with " . $msg);
}
$length = $this->lib->extism_plugin_output_length($this->context->pointer, $this->id);
$buf = $this->lib->extism_plugin_output_data($this->context->pointer, $this->id);
$ouput = [];
$output = [];
$data = $buf->getData();
for ($i = 0; $i < $length; $i++) {
$output[$i] = $data[$i];
@@ -101,7 +99,7 @@ class Plugin
$ok = $this->lib->extism_plugin_update($this->context->pointer, $this->id, $data, count($data), (int)$wasi);
if (!$ok) {
$err = $this->lib->extism_error($this->context->pointer, -1);
throw new Exception("Extism: unable to update plugin: " . $err);
throw new \Exception("Extism: unable to update plugin: " . $err);
}
if ($config != null) {

View File

@@ -1,29 +0,0 @@
<?php
require_once "vendor/autoload.php";
function generate() {
return (new FFIMe\FFIMe("libextism.".soext()))
->include("extism.h")
->showWarnings(false)
->codeGen('ExtismLib', __DIR__.'/ExtismLib.php');
}
function soext() {
$platform = php_uname("s");
switch ($platform) {
case "Darwin":
return "dylib";
case "Linux":
return "so";
case "Windows":
return "dll";
default:
throw new Exeception("Extism: unsupported platform ".$platform);
}
}
if (!file_exists(__DIR__."/ExtismLib.php")) {
generate();
}

3
python/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Extism Python Host SDK
See [https://extism.org/docs/integrate-into-your-codebase/python-host-sdk/](https://extism.org/docs/integrate-into-your-codebase/python-host-sdk/).

View File

@@ -1,9 +1,10 @@
[tool.poetry]
name = "extism"
version = "0.0.1-rc.6"
description = ""
version = "0.1.0"
description = "Extism Host SDK for python"
authors = ["The Extism Authors <oss@extism.org>"]
license = "BSD-3-Clause"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.7"

View File

@@ -20,6 +20,8 @@ class TestExtism(unittest.TestCase):
self.assertEqual(j["count"], 7)
j = json.loads(plugin.call("count_vowels", "this is a test thrice"))
self.assertEqual(j["count"], 6)
j = json.loads(plugin.call("count_vowels", "🌎hello🌎world🌎"))
self.assertEqual(j["count"], 3)
def test_update_plugin_manifest(self):
with extism.Context() as ctx:

View File

@@ -1,3 +1,4 @@
RUBYGEMS_API_KEY ?=
.PHONY: prepare test
@@ -9,11 +10,15 @@ test: prepare
bundle exec rake test
clean:
rm extism-*.gem
rm -f extism-*.gem
publish-local: clean prepare
gem build extism.gemspec
gem push extism-*.gem
publish: clean prepare
gem build extism.gemspec
gem push extism-*.gem
GEM_HOST_API_KEY=$(RUBYGEMS_API_KEY) gem push extism-*.gem
lint:
bundle exec rufo --check .

View File

@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
# Uncomment to register a new dependency of your gem
# spec.add_dependency "example-gem", "~> 1.0"
spec.add_dependency "ffi", ">= 1.0.0"
# For more information and examples about making a new gem, check out our
# guide at: https://bundler.io/guides/creating_gem.html

View File

@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Extism
VERSION = "0.0.1.rc6"
VERSION = "0.1.0"
end

View File

@@ -20,6 +20,8 @@ class TestExtism < Minitest::Test
assert_equal res["count"], 7
res = JSON.parse(plugin.call("count_vowels", "this is a test thrice"))
assert_equal res["count"], 6
res = JSON.parse(plugin.call("count_vowels", "🌎hello🌎world🌎"))
assert_equal res["count"], 3
end
end

View File

@@ -1,6 +1,6 @@
[package]
name = "extism-runtime"
version = "0.0.1-rc.6"
version = "0.1.0"
edition = "2021"
authors = ["The Extism Authors", "oss@extism.org"]
license = "BSD-3-Clause"
@@ -8,27 +8,21 @@ homepage = "https://extism.org"
repository = "https://github.com/extism/extism"
description = "Extism runtime component"
[lib]
name = "extism"
crate-type = ["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasmtime = "2.0.1"
wasmtime-wasi = "2.0.1"
wasmtime-wasi-nn = {version = "2.0.1", optional=true}
wasmtime = "3.0.0"
wasmtime-wasi = "3.0.0"
wasmtime-wasi-nn = {version = "3.0.0", optional=true}
anyhow = "1"
serde = { version = "1", features=["derive"] }
toml = "0.5"
serde = {version = "1", features = ["derive"]}
serde_json = "1"
toml = "0.5"
sha2 = "0.10"
log = "0.4"
log4rs = "1.1"
url = "2.3"
url = "2"
glob = "0.3"
ureq = {version = "2.5", optional=true}
extism-manifest = { version = "0.0.1-rc.6", path = "../manifest" }
extism-manifest = { version = "0.1.0", path = "../manifest" }
pretty-hex = { version = "0.3" }
[features]

View File

@@ -1,10 +1,6 @@
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
if let Ok(bindings) = cbindgen::Builder::new()
.with_crate(crate_dir)
.with_crate(".")
.with_language(cbindgen::Language::C)
.with_no_includes()
.with_sys_include("stdint.h")

View File

@@ -29,7 +29,7 @@ impl Context {
/// Get the next valid plugin ID
pub fn next_id(&mut self) -> Result<PluginIndex, Error> {
// Make sure we haven't exhausted all plugin IDs, it reach this it would require the machine
// Make sure we haven't exhausted all plugin IDs, to reach this it would require the machine
// running this code to have a lot of memory - no computer I tested on was able to allocate
// the max number of plugins.
//
@@ -37,7 +37,7 @@ impl Context {
// try to use one of those before returning an error
let exhausted = self.next_id.load(std::sync::atomic::Ordering::SeqCst) == PluginIndex::MAX;
// If there is a significant number of old IDs we can start to re-use them
// If there are a significant number of old IDs we can start to re-use them
if self.reclaimed_ids.len() >= START_REUSING_IDS || exhausted {
if let Some(x) = self.reclaimed_ids.pop_front() {
return Ok(x);
@@ -55,6 +55,32 @@ impl Context {
.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
}
pub fn insert(&mut self, plugin: Plugin) -> PluginIndex {
// Generate a new plugin ID
let id: i32 = match self.next_id() {
Ok(id) => id,
Err(e) => {
error!("Error creating Plugin: {:?}", e);
self.set_error(e);
return -1;
}
};
self.plugins.insert(id, plugin);
id
}
pub fn new_plugin(&mut self, data: impl AsRef<[u8]>, with_wasi: bool) -> PluginIndex {
let plugin = match Plugin::new(data, with_wasi) {
Ok(x) => x,
Err(e) => {
error!("Error creating Plugin: {:?}", e);
self.set_error(e);
return -1;
}
};
self.insert(plugin)
}
/// Set the context error
pub fn set_error(&mut self, e: impl std::fmt::Debug) {
trace!("Set context error: {:?}", e);
@@ -78,9 +104,9 @@ impl Context {
/// Remove a plugin from the context
pub fn remove(&mut self, id: PluginIndex) {
self.plugins.remove(&id);
// Collect old IDs in case we need to re-use them
self.reclaimed_ids.push_back(id);
if self.plugins.remove(&id).is_some() {
// Collect old IDs in case we need to re-use them
self.reclaimed_ids.push_back(id);
}
}
}

View File

@@ -84,30 +84,6 @@ impl PluginMemory {
Ok(self.memory.data(&self.store)[offs])
}
/// Write u32 to memory
pub(crate) fn store_u32(&mut self, offs: usize, data: u32) -> Result<(), Error> {
trace!("store_u32: {data:x} at offset {offs}");
let handle = MemoryBlock {
offset: offs,
length: 4,
};
self.write(handle, data.to_ne_bytes())?;
Ok(())
}
/// Read u32 from memory
pub(crate) fn load_u32(&self, offs: usize) -> Result<u32, Error> {
trace!("load_u32: offset {offs}");
let mut buf = [0; 4];
let handle = MemoryBlock {
offset: offs,
length: 4,
};
self.read(handle, &mut buf)?;
Ok(u32::from_ne_bytes(buf))
}
/// Write u64 to memory
pub(crate) fn store_u64(&mut self, offs: usize, data: u64) -> Result<(), Error> {
trace!("store_u64: {data:x} at offset {offs}");

View File

@@ -7,7 +7,7 @@ macro_rules! args {
($input:expr, $index:expr, $ty:ident) => {
match $input[$index].$ty() {
Some(x) => x,
None => return Err(Trap::new("Invalid input type"))
None => return Err(Error::msg("Invalid input type"))
}
};
($input:expr, $(($index:expr, $ty:ident)),*$(,)?) => {
@@ -24,7 +24,7 @@ pub(crate) fn input_length(
caller: Caller<Internal>,
_input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &Internal = caller.data();
output[0] = Val::I64(data.input_length as i64);
Ok(())
@@ -37,7 +37,7 @@ pub(crate) fn input_load_u8(
caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &Internal = caller.data();
if data.input.is_null() {
return Ok(());
@@ -53,7 +53,7 @@ pub(crate) fn input_load_u64(
caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &Internal = caller.data();
if data.input.is_null() {
return Ok(());
@@ -72,12 +72,10 @@ pub(crate) fn store_u8(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let (offset, byte) = args!(input, (0, i64), (1, i32));
data.memory_mut()
.store_u8(offset as usize, byte as u8)
.map_err(|_| Trap::new("Write error"))?;
data.memory_mut().store_u8(offset as usize, byte as u8)?;
Ok(())
}
@@ -88,51 +86,14 @@ pub(crate) fn load_u8(
caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &Internal = caller.data();
let offset = args!(input, 0, i64) as usize;
let byte = data
.memory()
.load_u8(offset)
.map_err(|_| Trap::new("Read error"))?;
let byte = data.memory().load_u8(offset)?;
output[0] = Val::I32(byte as i32);
Ok(())
}
/// Store an unsigned 32 bit integer in memory
/// Params: i64 (offset), i32 (int)
/// Returns: none
pub(crate) fn store_u32(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
let data: &mut Internal = caller.data_mut();
let (offset, b) = args!(input, (0, i64), (1, i32));
data.memory_mut()
.store_u32(offset as usize, b as u32)
.map_err(|_| Trap::new("Write error"))?;
Ok(())
}
/// Load an unsigned 32 bit integer from memory
/// Params: i64 (offset)
/// Returns: i32 (int)
pub(crate) fn load_u32(
caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
let data: &Internal = caller.data();
let offset = args!(input, 0, i64) as usize;
let b = data
.memory()
.load_u32(offset)
.map_err(|_| Trap::new("Read error"))?;
output[0] = Val::I32(b as i32);
Ok(())
}
/// Store an unsigned 64 bit integer in memory
/// Params: i64 (offset), i64 (int)
/// Returns: none
@@ -140,12 +101,10 @@ pub(crate) fn store_u64(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let (offset, b) = args!(input, (0, i64), (1, i64));
data.memory_mut()
.store_u64(offset as usize, b as u64)
.map_err(|_| Trap::new("Write error"))?;
data.memory_mut().store_u64(offset as usize, b as u64)?;
Ok(())
}
@@ -156,13 +115,10 @@ pub(crate) fn load_u64(
caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &Internal = caller.data();
let offset = args!(input, 0, i64) as usize;
let byte = data
.memory()
.load_u64(offset)
.map_err(|_| Trap::new("Read error"))?;
let byte = data.memory().load_u64(offset)?;
output[0] = Val::I64(byte as i64);
Ok(())
}
@@ -174,7 +130,7 @@ pub(crate) fn output_set(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let (offset, length) = args!(input, (0, i64), (1, i64));
data.output_offset = offset as usize;
@@ -189,7 +145,7 @@ pub(crate) fn alloc(
mut caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let offs = data.memory_mut().alloc(input[0].unwrap_i64() as _)?;
output[0] = Val::I64(offs.offset as i64);
@@ -204,7 +160,7 @@ pub(crate) fn free(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let offset = args!(input, 0, i64) as usize;
data.memory_mut().free(offset);
@@ -218,7 +174,7 @@ pub(crate) fn error_set(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let offset = args!(input, 0, i64) as usize;
@@ -240,7 +196,7 @@ pub(crate) fn config_get(
mut caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let plugin = data.plugin_mut();
@@ -265,7 +221,7 @@ pub(crate) fn var_get(
mut caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let plugin = data.plugin_mut();
@@ -292,7 +248,7 @@ pub(crate) fn var_set(
mut caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let plugin = data.plugin_mut();
@@ -305,7 +261,7 @@ pub(crate) fn var_set(
// If the store is larger than 100MB then stop adding things
if size > 1024 * 1024 * 100 && voffset != 0 {
return Err(Trap::new("Variable store is full"));
return Err(Error::msg("Variable store is full"));
}
let key_offs = args!(input, 0, i64) as usize;
@@ -332,12 +288,12 @@ pub(crate) fn http_request(
#[allow(unused_mut)] mut caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
#[cfg(not(feature = "http"))]
{
let _ = (caller, input);
output[0] = Val::I64(0 as i64);
output[0] = Val::I64(0);
error!("http_request is not enabled");
return Ok(());
}
@@ -349,14 +305,13 @@ pub(crate) fn http_request(
let http_req_offset = args!(input, 0, i64) as usize;
let req: extism_manifest::HttpRequest =
serde_json::from_slice(data.memory().get(http_req_offset)?)
.map_err(|_| Trap::new("Invalid http request"))?;
serde_json::from_slice(data.memory().get(http_req_offset)?)?;
let body_offset = args!(input, 1, i64) as usize;
let url = match url::Url::parse(&req.url) {
Ok(u) => u,
Err(e) => return Err(Trap::new(format!("Invalid URL: {e:?}"))),
Err(e) => return Err(Error::msg(format!("Invalid URL: {e:?}"))),
};
let allowed_hosts = &data.plugin().manifest.as_ref().allowed_hosts;
let host_str = url.host_str().unwrap_or_default();
@@ -370,7 +325,7 @@ pub(crate) fn http_request(
pat.matches(host_str)
});
if !allowed_hosts.is_empty() && !host_matches_allowed {
return Err(Trap::new(format!(
return Err(Error::msg(format!(
"HTTP request to {} is not allowed",
req.url
)));
@@ -383,20 +338,20 @@ pub(crate) fn http_request(
r = r.set(k, v);
}
let mut res = if body_offset > 0 {
let res = if body_offset > 0 {
let buf = data.memory().get(body_offset)?;
r.send_bytes(buf)
.map_err(|e| Trap::new(&format!("Request error: {e:?}")))?
.into_reader()
let res = r.send_bytes(buf)?;
data.http_status = res.status();
res.into_reader()
} else {
r.call()
.map_err(|e| Trap::new(format!("{:?}", e)))?
.into_reader()
let res = r.call()?;
data.http_status = res.status();
res.into_reader()
};
let mut buf = Vec::new();
res.read_to_end(&mut buf)
.map_err(|e| Trap::new(format!("{:?}", e)))?;
res.take(1024 * 1024 * 50) // TODO: make this limit configurable
.read_to_end(&mut buf)?;
let mem = data.memory_mut().alloc_bytes(buf)?;
@@ -405,6 +360,19 @@ pub(crate) fn http_request(
}
}
/// Get the status code of the last HTTP request
/// Params: none
/// Returns: i32 (status code)
pub(crate) fn http_status_code(
mut caller: Caller<Internal>,
_input: &[Val],
output: &mut [Val],
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
output[0] = Val::I32(data.http_status as i32);
Ok(())
}
/// Get the length of an allocated block given the offset
/// Params: i64 (offset)
/// Returns: i64 (length or 0)
@@ -412,7 +380,7 @@ pub(crate) fn length(
mut caller: Caller<Internal>,
input: &[Val],
output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &mut Internal = caller.data_mut();
let offset = args!(input, 0, i64) as usize;
if offset == 0 {
@@ -421,7 +389,7 @@ pub(crate) fn length(
}
let length = match data.memory().block_length(offset) {
Some(x) => x,
None => return Err(Trap::new("Unable to find length for offset")),
None => return Err(Error::msg("Unable to find length for offset")),
};
output[0] = Val::I64(length as i64);
Ok(())
@@ -432,7 +400,7 @@ pub fn log(
caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
let data: &Internal = caller.data();
let offset = args!(input, 0, i64) as usize;
let buf = data.memory().get(offset)?;
@@ -451,7 +419,7 @@ pub(crate) fn log_warn(
caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
log(log::Level::Warn, caller, input, _output)
}
@@ -462,7 +430,7 @@ pub(crate) fn log_info(
caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
log(log::Level::Info, caller, input, _output)
}
@@ -473,7 +441,7 @@ pub(crate) fn log_debug(
caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
log(log::Level::Debug, caller, input, _output)
}
@@ -484,6 +452,6 @@ pub(crate) fn log_error(
caller: Caller<Internal>,
input: &[Val],
_output: &mut [Val],
) -> Result<(), Trap> {
) -> Result<(), Error> {
log(log::Level::Error, caller, input, _output)
}

View File

@@ -20,6 +20,7 @@ pub struct Internal {
pub output_length: usize,
pub plugin: *mut Plugin,
pub wasi: Option<Wasi>,
pub http_status: u16,
}
pub struct Wasi {
@@ -42,6 +43,7 @@ impl Internal {
let nn = wasmtime_wasi_nn::WasiNnCtx::new()?;
#[cfg(not(feature = "nn"))]
#[allow(clippy::let_unit_value)]
let nn = ();
Some(Wasi {
@@ -59,6 +61,7 @@ impl Internal {
input: std::ptr::null(),
wasi,
plugin: std::ptr::null_mut(),
http_status: 0,
})
}
@@ -137,10 +140,8 @@ impl Plugin {
alloc(I64) -> I64;
free(I64);
load_u8(I64) -> I32;
load_u32(I64) -> I32;
load_u64(I64) -> I64;
store_u8(I64, I32);
store_u32(I64, I32);
store_u64(I64, I64);
input_length() -> I64;
input_load_u8(I64) -> I32;
@@ -151,6 +152,7 @@ impl Plugin {
var_get(I64) -> I64;
var_set(I64, I64);
http_request(I64, I64) -> I64;
http_status_code() -> I32;
length(I64) -> I64;
log_warn(I64);
log_info(I64);

View File

@@ -36,29 +36,8 @@ pub unsafe extern "C" fn extism_plugin_new(
) -> PluginIndex {
trace!("Call to extism_plugin_new with wasm pointer {:?}", wasm);
let ctx = &mut *ctx;
let data = std::slice::from_raw_parts(wasm, wasm_size as usize);
let plugin = match Plugin::new(data, with_wasi) {
Ok(x) => x,
Err(e) => {
error!("Error creating Plugin: {:?}", e);
ctx.set_error(e);
return -1;
}
};
// Allocate a new plugin ID
let id: i32 = match ctx.next_id() {
Ok(id) => id,
Err(e) => {
error!("Error creating Plugin: {:?}", e);
ctx.set_error(e);
return -1;
}
};
ctx.plugins.insert(id, plugin);
info!("New plugin added: {id}");
id
ctx.new_plugin(data, with_wasi)
}
/// Update a plugin, keeping the existing ID
@@ -242,12 +221,29 @@ pub unsafe extern "C" fn extism_plugin_call(
None => return plugin.error(format!("Function not found: {name}"), -1),
};
// Call function with offset+length pointing to input data.
let mut results = vec![Val::I32(0)];
// Check the number of results, reject functions with more than 1 result
let n_results = func.ty(&plugin.memory.store).results().len();
if n_results > 1 {
return plugin.error(
format!("Function {name} has {n_results} results, expected 0 or 1"),
-1,
);
}
let mut results = vec![Val::null(); n_results];
match func.call(&mut plugin.memory.store, &[], results.as_mut_slice()) {
Ok(r) => r,
Err(e) => {
plugin.dump_memory();
if let Some(exit) = e.downcast_ref::<wasmtime_wasi::I32Exit>() {
error!("WASI return code: {}", exit.0);
if exit.0 != 0 {
return plugin.error(&e, exit.0);
}
return exit.0;
}
error!("Call: {e:?}");
return plugin.error(e.context("Call failed"), -1);
}
@@ -255,6 +251,12 @@ pub unsafe extern "C" fn extism_plugin_call(
plugin.dump_memory();
// If `results` is empty then the return value is expected to be returned
// in the error block of the func call above using `I32Exit`
if results.is_empty() {
return 0;
}
// Return result to caller
results[0].unwrap_i32()
}

View File

@@ -1,15 +1,16 @@
[package]
name = "extism"
version = "0.0.1-rc.6"
version = "0.1.0"
edition = "2021"
authors = ["The Extism Authors", "oss@extism.org"]
license = "BSD-3-Clause"
links = "extism"
homepage = "https://extism.org"
repository = "https://github.com/extism/extism"
description = "Extism Host SDK for Rust"
[dependencies]
extism-manifest = { version = "0.0.1-rc.6", path = "../manifest" }
extism-manifest = { version = "0.1.0", path = "../manifest" }
extism-runtime = { version = "0.1.0", path = "../runtime"}
serde_json = "1"
log = "0.4"
log = "0.4"
thiserror = "1"

View File

@@ -1,10 +0,0 @@
fn main() {
println!("cargo:rustc-link-search=/usr/local/lib");
if let Ok(home) = std::env::var("HOME") {
let path = std::path::PathBuf::from(home).join(".local").join("lib");
println!("cargo:rustc-link-search={}", path.display());
}
println!("cargo:rustc-link-lib=extism");
}

View File

@@ -1,8 +1,6 @@
use crate::*;
pub struct Context {
pub(crate) pointer: *mut bindings::ExtismContext,
}
pub struct Context(pub(crate) std::sync::Arc<std::sync::Mutex<extism_runtime::Context>>);
impl Default for Context {
fn default() -> Context {
@@ -13,25 +11,20 @@ impl Default for Context {
impl Context {
/// Create a new context
pub fn new() -> Context {
let pointer = unsafe { bindings::extism_context_new() };
Context { pointer }
Context(std::sync::Arc::new(std::sync::Mutex::new(
extism_runtime::Context::new(),
)))
}
/// Remove all registered plugins
pub fn reset(&mut self) {
unsafe { bindings::extism_context_reset(self.pointer) }
unsafe { bindings::extism_context_reset(&mut *self.lock()) }
}
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
impl Drop for Context {
fn drop(&mut self) {
if self.pointer.is_null() {
return;
pub(crate) fn lock(&self) -> std::sync::MutexGuard<extism_runtime::Context> {
match self.0.lock() {
Ok(x) => x,
Err(x) => x.into_inner(),
}
unsafe { bindings::extism_context_free(self.pointer) }
self.pointer = std::ptr::null_mut();
}
}

View File

@@ -1,9 +1,5 @@
use std::collections::BTreeMap;
use extism_manifest::Manifest;
#[allow(non_camel_case_types)]
pub(crate) mod bindings;
pub use extism_manifest::{self as manifest, Manifest};
pub use extism_runtime::sdk as bindings;
mod context;
mod plugin;
@@ -11,17 +7,16 @@ mod plugin;
pub use context::Context;
pub use plugin::Plugin;
#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Unable to load plugin: {0}")]
UnableToLoadPlugin(String),
#[error("{0}")]
Message(String),
Json(serde_json::Error),
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::Json(e)
}
#[error("JSON: {0}")]
Json(#[from] serde_json::Error),
#[error("Runtime: {0}")]
Runtime(#[from] extism_runtime::Error),
}
/// Gets the version of Extism
@@ -54,7 +49,7 @@ mod tests {
let wasm_start = Instant::now();
set_log_file("test.log", Some(log::Level::Info));
let context = Context::new();
let plugin = Plugin::new(&context, WASM, false).unwrap();
let mut plugin = Plugin::new(&context, WASM, false).unwrap();
println!("register loaded plugin: {:?}", wasm_start.elapsed());
let repeat = 1182;
@@ -146,20 +141,20 @@ mod tests {
use std::io::Write;
std::thread::spawn(|| {
let context = Context::new();
let plugin = Plugin::new(&context, WASM, false).unwrap();
let mut plugin = Plugin::new(&context, WASM, false).unwrap();
let output = plugin.call("count_vowels", "this is a test").unwrap();
std::io::stdout().write_all(output).unwrap();
});
std::thread::spawn(|| {
let context = Context::new();
let plugin = Plugin::new(&context, WASM, false).unwrap();
let mut plugin = Plugin::new(&context, WASM, false).unwrap();
let output = plugin.call("count_vowels", "this is a test aaa").unwrap();
std::io::stdout().write_all(output).unwrap();
});
let context = Context::new();
let plugin = Plugin::new(&context, WASM, false).unwrap();
let mut plugin = Plugin::new(&context, WASM, false).unwrap();
let output = plugin.call("count_vowels", "abc123").unwrap();
std::io::stdout().write_all(output).unwrap();
}

View File

@@ -1,16 +1,18 @@
use crate::*;
use std::collections::BTreeMap;
pub struct Plugin<'a> {
id: bindings::ExtismPlugin,
id: extism_runtime::PluginIndex,
context: &'a Context,
}
impl<'a> Plugin<'a> {
/// Create plugin from a known-good ID
///
/// # Safety
/// This function does not check to ensure the provided ID is valid
pub unsafe fn from_id(id: i32, context: &'a Context) -> Plugin<'a> {
Plugin {
id,
context: context,
}
Plugin { id, context }
}
pub fn as_i32(&self) -> i32 {
@@ -19,7 +21,7 @@ impl<'a> Plugin<'a> {
/// Create a new plugin from the given manifest
pub fn new_with_manifest(
ctx: &'a Context,
ctx: &'a mut Context,
manifest: &Manifest,
wasi: bool,
) -> Result<Plugin<'a>, Error> {
@@ -29,17 +31,10 @@ impl<'a> Plugin<'a> {
/// Create a new plugin from a WASM module
pub fn new(ctx: &'a Context, data: impl AsRef<[u8]>, wasi: bool) -> Result<Plugin, Error> {
let plugin = unsafe {
bindings::extism_plugin_new(
ctx.pointer,
data.as_ref().as_ptr(),
data.as_ref().len() as u64,
wasi,
)
};
let plugin = ctx.lock().new_plugin(data, wasi);
if plugin < 0 {
let err = unsafe { bindings::extism_error(ctx.pointer, -1) };
let err = unsafe { bindings::extism_error(&mut *ctx.lock(), -1) };
let buf = unsafe { std::ffi::CStr::from_ptr(err) };
let buf = buf.to_str().unwrap().to_string();
return Err(Error::UnableToLoadPlugin(buf));
@@ -55,7 +50,7 @@ impl<'a> Plugin<'a> {
pub fn update(&mut self, data: impl AsRef<[u8]>, wasi: bool) -> Result<(), Error> {
let b = unsafe {
bindings::extism_plugin_update(
self.context.pointer,
&mut *self.context.lock(),
self.id,
data.as_ref().as_ptr(),
data.as_ref().len() as u64,
@@ -66,7 +61,7 @@ impl<'a> Plugin<'a> {
return Ok(());
}
let err = unsafe { bindings::extism_error(self.context.pointer, -1) };
let err = unsafe { bindings::extism_error(&mut *self.context.lock(), -1) };
if !err.is_null() {
let s = unsafe { std::ffi::CStr::from_ptr(err) };
return Err(Error::Message(s.to_str().unwrap().to_string()));
@@ -82,11 +77,11 @@ impl<'a> Plugin<'a> {
}
/// Set configuration values
pub fn set_config(&self, config: &BTreeMap<String, Option<String>>) -> Result<(), Error> {
pub fn set_config(&mut self, config: &BTreeMap<String, Option<String>>) -> Result<(), Error> {
let encoded = serde_json::to_vec(config)?;
unsafe {
bindings::extism_plugin_config(
self.context.pointer,
&mut *self.context.lock(),
self.id,
encoded.as_ptr() as *const _,
encoded.len() as u64,
@@ -96,7 +91,7 @@ impl<'a> Plugin<'a> {
}
/// Set configuration values, builder-style
pub fn with_config(self, config: &BTreeMap<String, Option<String>>) -> Result<Self, Error> {
pub fn with_config(mut self, config: &BTreeMap<String, Option<String>>) -> Result<Self, Error> {
self.set_config(config)?;
Ok(self)
}
@@ -106,7 +101,7 @@ impl<'a> Plugin<'a> {
let name = std::ffi::CString::new(name.as_ref()).expect("Invalid function name");
unsafe {
bindings::extism_plugin_function_exists(
self.context.pointer,
&mut *self.context.lock(),
self.id,
name.as_ptr() as *const _,
)
@@ -114,11 +109,11 @@ impl<'a> Plugin<'a> {
}
/// Call a function with the given input
pub fn call(&self, name: impl AsRef<str>, input: impl AsRef<[u8]>) -> Result<&[u8], Error> {
pub fn call(&mut self, name: impl AsRef<str>, input: impl AsRef<[u8]>) -> Result<&[u8], Error> {
let name = std::ffi::CString::new(name.as_ref()).expect("Invalid function name");
let rc = unsafe {
bindings::extism_plugin_call(
self.context.pointer,
&mut *self.context.lock(),
self.id,
name.as_ptr() as *const _,
input.as_ref().as_ptr() as *const _,
@@ -127,7 +122,7 @@ impl<'a> Plugin<'a> {
};
if rc != 0 {
let err = unsafe { bindings::extism_error(self.context.pointer, self.id) };
let err = unsafe { bindings::extism_error(&mut *self.context.lock(), self.id) };
if !err.is_null() {
let s = unsafe { std::ffi::CStr::from_ptr(err) };
return Err(Error::Message(s.to_str().unwrap().to_string()));
@@ -137,9 +132,9 @@ impl<'a> Plugin<'a> {
}
let out_len =
unsafe { bindings::extism_plugin_output_length(self.context.pointer, self.id) };
unsafe { bindings::extism_plugin_output_length(&mut *self.context.lock(), self.id) };
unsafe {
let ptr = bindings::extism_plugin_output_data(self.context.pointer, self.id);
let ptr = bindings::extism_plugin_output_data(&mut *self.context.lock(), self.id);
Ok(std::slice::from_raw_parts(ptr, out_len as usize))
}
}
@@ -147,9 +142,6 @@ impl<'a> Plugin<'a> {
impl<'a> Drop for Plugin<'a> {
fn drop(&mut self) {
if self.context.pointer.is_null() {
return;
}
unsafe { bindings::extism_plugin_free(self.context.pointer, self.id) }
unsafe { bindings::extism_plugin_free(&mut *self.context.lock(), self.id) }
}
}