Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
974918b
docs: update README & AGENTS for ns-wasm-core and ns-wasm-kit-runtime
listepo Aug 6, 2026
1c4554f
feat: add @cross-code/ns-endive — Endive Java-based WASM runtime plugin
listepo Aug 6, 2026
fe39392
fix(ci): add new packages to build lists and change filters
listepo Aug 6, 2026
021b6d4
docs(AGENTS): scope Rust/UniFFI architecture to ns-wasm3 and ns-wamr
listepo Aug 6, 2026
7c23cc2
docs(README): fix plugin count — three WASM runtimes, ns-wry separate
listepo Aug 6, 2026
bb0ec60
fix(demo): run Endive only on Android, update success message
listepo Aug 6, 2026
afd69e6
fix(endive): pass log to runFixtureChecks, drop as any cast
listepo Aug 6, 2026
a79caa6
fix(endive): use Double.valueOf(), ArrayList for call args, raw getGl…
listepo Aug 6, 2026
435ddbc
fix(endive): normalizeAndroid globals through java.lang.Long, toJavaW…
listepo Aug 6, 2026
46af590
fix(demo): gate WasmKit to iOS only, Endive to Android only
listepo Aug 6, 2026
b7f1d66
feat: add @cross-code/ns-wasm-edge — WasmEdge runtime plugin
listepo Aug 6, 2026
0f98635
feat: add @cross-code/ns-wasm-chicory — Chicory pure-Java WASM runtime
listepo Aug 6, 2026
5ce773d
chore: sync lockfiles, tsconfig, spec updates across all packages
listepo Aug 7, 2026
00b3fc0
feat: add oxlint and oxfmt Nx inference plugins
listepo Aug 7, 2026
bd3d65f
feat: universal simulator xcframeworks, runtime-support suite gating
listepo Aug 7, 2026
89ffc08
chore: lockfile — add oxlint and oxfmt devDependencies
listepo Aug 7, 2026
e4adca1
chore: update oxlint to 1.77.0 and oxfmt to 0.62.0
listepo Aug 7, 2026
551b6c6
fix(ns-wasm-core): fix fromWire Infinity/NaN rejection and loadModule…
listepo Aug 7, 2026
37bb1f1
fix(ci): add ns-wasm-chicory and ns-wasm-edge to test app build list
listepo Aug 7, 2026
347432d
feat(ns-wasm-chicory): add Android native layer with Chicory Java run…
listepo Aug 7, 2026
301fed3
chore(ns-wasm-chicory,ns-wasm-edge): replace any with unknown in nati…
listepo Aug 7, 2026
89cc194
feat(ns-wasm-kit-runtime): add native Swift layer with WasmKit 0.3.1 …
listepo Aug 7, 2026
205740f
feat(vitest-ns-ui): terminal-style tree UI with monospace font and bo…
listepo Aug 7, 2026
91c40ad
fix(ci): upload coverage to Codecov; green the fix-tests branch
listepo Aug 7, 2026
d246870
fix(ns-wasm-chicory): fix Android byte marshalling; add JVM hosttest …
listepo Aug 7, 2026
f0eaa25
feat(rust): add Clippy and Rustfmt gates for the Rust projects
listepo Aug 8, 2026
527428f
feat(rust): exclude uniffi-bindgen scaffolding bins from lint and format
listepo Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
permissions:
actions: read
contents: read
pull-requests: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -110,6 +111,23 @@ jobs:
rustup component add llvm-tools-preview
pnpm exec nx run-many -t coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: |
packages/ns-wasm-core/test-output/vitest/coverage/lcov.info
packages/ns-wasm3/test-output/vitest/coverage/lcov.info
packages/ns-wamr/test-output/vitest/coverage/lcov.info
packages/ns-wasm-kit-runtime/test-output/vitest/coverage/lcov.info
packages/ns-wasm-chicory/test-output/vitest/coverage/lcov.info
packages/ns-wasm-edge/test-output/vitest/coverage/lcov.info
packages/ns-endive/test-output/vitest/coverage/lcov.info
packages/ns-wry/test-output/vitest/coverage/lcov.info
packages/vitest-ns/test-output/vitest/coverage/lcov.info
packages/vitest-ns-ui/test-output/vitest/coverage/lcov.info
packages/ns-wasm-fixture/target/coverage/lcov.info

# Rust is pre-installed on GitHub's ubuntu-latest runner. When debugging
# locally with `act` the slim image may not have it — install on the fly
# so the same workflow is self-contained.
Expand All @@ -124,6 +142,15 @@ jobs:
- name: Rust unit tests
run: pnpm exec nx run ns-wasm-fixture:test.wasm

# Clippy + rustfmt over the hand-written Rust in all four Rust projects
# (ns-wasm3, ns-wamr, ns-wry, ns-wasm-fixture). Generated bindings
# (bindgen output, UniFFI scaffolding) are excluded: bindgen output is
# `#![rustfmt::skip]` and `clippy::all` is allowed around the includes.
- name: Rust lint (Clippy) + format check
run: |
rustup component add clippy rustfmt
pnpm exec nx run-many -t lint.rust fmt.rust -p ns-wasm3 ns-wamr ns-wry ns-wasm-fixture

# globals.wasm is committed but generated, and the generator is
# deterministic — so a stale copy is a hard error rather than a surprise
# on someone's device. (test_types_bg.wasm needs wasm-pack to rebuild and
Expand Down Expand Up @@ -172,21 +199,21 @@ jobs:
changed="$(git diff --name-only "$base...HEAD")" || run_all

echo "$changed"
if grep -qE '^(packages/ns-wasm3/|\.github/workflows/ci\.yml$)' <<<"$changed"; then
if grep -qE '^(packages/ns-wasm3/|packages/ns-wasm-core/|\.github/workflows/ci\.yml$)' <<<"$changed"; then
echo "wasm3=true" >> "$GITHUB_OUTPUT"
else
echo "wasm3=false" >> "$GITHUB_OUTPUT"
fi

if grep -qE '^(packages/ns-wamr/|\.github/workflows/ci\.yml$)' <<<"$changed"; then
if grep -qE '^(packages/ns-wamr/|packages/ns-wasm-core/|\.github/workflows/ci\.yml$)' <<<"$changed"; then
echo "wamr=true" >> "$GITHUB_OUTPUT"
else
echo "wamr=false" >> "$GITHUB_OUTPUT"
fi

# The app's suite runs the plugin end to end, so a plugin or fixture
# change is as relevant to it as a change to the app itself.
if grep -qE '^(apps/ns-wasm-test/|packages/ns-wasm3/|packages/ns-wamr/|packages/ns-wasm-fixture/|\.github/workflows/ci\.yml$)' <<<"$changed"; then
if grep -qE '^(apps/ns-wasm-test/|packages/ns-wasm3/|packages/ns-wamr/|packages/ns-wasm-kit-runtime/|packages/ns-endive/|packages/ns-wasm-chicory/|packages/ns-wasm-edge/|packages/ns-wasm-core/|packages/ns-wasm-fixture/|\.github/workflows/ci\.yml$)' <<<"$changed"; then
echo "app=true" >> "$GITHUB_OUTPUT"
else
echo "app=false" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -459,7 +486,7 @@ jobs:
# The app snapshots local file: dependencies when pnpm installs them, so
# every package whose entry point lives in dist/ must be built first.
- name: Build the app's local packages
run: pnpm exec nx run-many -t build -p ns-wasm3 ns-wamr vitest-ns vitest-ns-ui
run: pnpm exec nx run-many -t build -p ns-wasm-core ns-wasm3 ns-wamr ns-wasm-kit-runtime ns-endive ns-wasm-chicory ns-wasm-edge vitest-ns vitest-ns-ui

- name: Install the test app's dependencies
working-directory: apps/ns-wasm-test
Expand Down Expand Up @@ -497,7 +524,7 @@ jobs:
# Keep this before the app install: pnpm snapshots local file: packages,
# including their generated dist/ entry points, into app node_modules.
- name: Build the app's local packages
run: pnpm exec nx run-many -t build -p ns-wasm3 ns-wamr vitest-ns vitest-ns-ui
run: pnpm exec nx run-many -t build -p ns-wasm-core ns-wasm3 ns-wamr ns-wasm-kit-runtime ns-endive ns-wasm-chicory ns-wasm-edge vitest-ns vitest-ns-ui

- name: Install the test app's dependencies
working-directory: apps/ns-wasm-test
Expand Down
14 changes: 1 addition & 13 deletions .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,13 @@
node = "24.18.1"
java = "temurin-21.0.2"
"npm:pnpm" = "11.20.0"
buck2 = "latest"

[settings]
# Use Temurin as the default Java distribution
java.default_distribution = "temurin"

# ── Notes ───────────────────────────────────────────────────────────────────
# - Swift 6.3+ and Xcode 16+ are managed by macOS / Xcode.app, not mise.
# - Kotlin 2.4.x and Android NDK 29 are pulled by the Gradle project.
# - Gradle 9.6.1 is managed by the committed wrapper (gradlew), not mise.
# - pnpm is managed by mise via the npm backend (see [tools] above).
# The version matches "packageManager" in root package.json.
#
# Buck2 is NOT available via cargo (the crates.io "buck2" crate v0.1.1 is a
# placeholder). Install it once with the prebuilt binary (~107 MB, no toolchain
# needed):
#
# curl -fsSL https://github.com/facebook/buck2/releases/latest/download/buck2-aarch64-apple-darwin.zst | zstd -d | sudo tee /usr/local/bin/buck2 > /dev/null && sudo chmod +x /usr/local/bin/buck2
#
# Or use the community asdf plugin (mise-compatible, downloads prebuilt):
# mise plugin install buck2 https://github.com/izaakschroeder/asdf-buck2
# buck2 = "latest" # then uncomment the line below
# buck2 = "latest"
12 changes: 12 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"ignorePatterns": [
"**/node_modules/**",
"**/dist/**",
"**/.buck-out/**",
"**/target/**",
"**/.build/**",
"**/platforms/**",
"**/src/vendors/**",
"**/test-output/**"
]
}
31 changes: 31 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/crates/oxc_linter/src/schema.json",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
test -f node_modules/oxlint/configuration_schema.json

Repository: listepo/cross-code

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- tracked configuration and manifests ---'
git ls-files | rg '(^|/)(\.oxlintrc\.json|package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json|bun\.lockb?|oxlint)' || true

printf '%s\n' '--- .oxlintrc.json ---'
cat -n .oxlintrc.json

printf '%s\n' '--- dependency declarations ---'
for f in package.json pnpm-lock.yaml yarn.lock package-lock.json bun.lock bun.lockb; do
  if test -f "$f"; then
    printf '\n--- %s ---\n' "$f"
    rg -n -C 3 'oxlint|configuration_schema|oxc-project/oxc' "$f" || true
  fi
done

printf '%s\n' '--- configured URL headers ---'
curl -L -sS -o /tmp/oxlint-schema.json -w 'HTTP %{http_code}\n' \
  'https://raw.githubusercontent.com/oxc-project/oxc/main/crates/oxc_linter/src/schema.json'
if test -s /tmp/oxlint-schema.json; then
  head -c 300 /tmp/oxlint-schema.json
  printf '\n'
fi

Repository: listepo/cross-code

Length of output: 2156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- locked oxlint entries ---'
rg -n -i -C 5 'oxlint|configuration_schema|oxc_linter' pnpm-lock.yaml package.json

printf '%s\n' '--- npm package metadata ---'
curl -sS -L 'https://registry.npmjs.org/oxlint' |
  node -e '
    let data = "";
    process.stdin.on("data", c => data += c);
    process.stdin.on("end", () => {
      const p = JSON.parse(data);
      for (const version of ["0.15.0", p["dist-tags"]?.latest]) {
        if (p.versions?.[version]) {
          console.log(JSON.stringify({
            version,
            dist: p.versions[version].dist,
            files: p.versions[version].files
          }, null, 2));
        }
      }
    });
  '

printf '%s\n' '--- package tarball file list ---'
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -sS -L 'https://registry.npmjs.org/oxlint/-/oxlint-0.15.0.tgz' -o "$tmpdir/oxlint.tgz"
tar -tzf "$tmpdir/oxlint.tgz" | rg -i 'schema|config|package.json'

Repository: listepo/cross-code

Length of output: 2486


Use the schema shipped with oxlint.

The current URL returns HTTP 404. oxlint@0.15.0 includes configuration_schema.json; set $schema to ./node_modules/oxlint/configuration_schema.json.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.oxlintrc.json at line 2, Update the $schema value in .oxlintrc.json to
reference the local ./node_modules/oxlint/configuration_schema.json file shipped
with oxlint, replacing the invalid remote URL.

"plugins": ["typescript", "import", "unicorn"],
"rules": {
"no-unused-vars": "error",
"no-console": "warn",
"typescript/no-non-null-assertion": "warn",
"import/no-default-export": "off"
},
"env": {
"node": true,
"es2022": true
},
"globals": {
"globalThis": "readonly",
"NSData": "readonly",
"NSMutableArray": "readonly"
},
"settings": {},
"ignorePatterns": [
"**/node_modules/**",
"**/dist/**",
"**/.buck-out/**",
"**/target/**",
"**/.build/**",
"**/platforms/**",
"**/src/vendors/**",
"**/test-output/**",
"**/*.d.ts"
]
}
88 changes: 80 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,23 @@

## NativeScript plugins in this repo

Three sibling plugins live under `packages/`, all sharing the same
architecture: Rust cargo workspace, UniFFI (uniffi-rs) Kotlin/Swift bindings,
and a TypeScript adapter. The two WASM plugins are mirror images of each
other (same class shapes, same wire protocol, same error mapping). Everything
the plugins share is documented in [Shared plugin architecture](#shared-plugin-architecture)
below; each package's AGENTS.md holds only engine-specific detail.

Several sibling plugins live under `packages/`, all sharing the same
TypeScript API (wire protocol, error mapping, and `WasmRuntime` / `WasmModule` /
`WasmFunction` class shapes). The Rust cargo workspace and UniFFI (uniffi-rs)
Kotlin/Swift bindings — together with the mirror-image native architecture
described in [Shared plugin architecture](#shared-plugin-architecture) — apply
to **ns-wasm3 and ns-wamr**. The newer runtimes (`ns-wasm-kit-runtime`, the
Swift-native WasmKit interpreter; `ns-endive`, the Java-native Endive
interpreter) share the same TypeScript adapter pattern, wire protocol, and
`@cross-code/ns-wasm-core` foundation, but have their own per-engine native
layers. Only engine-specific detail lives in each package's AGENTS.md.

- **`ns-wasm-core`** (`@cross-code/ns-wasm-core`) — shared foundation package
providing the wire protocol (`parseSignature`, `toWire`, `fromWire`,
`WasmError`), the native adapter interfaces (`NativeRuntimeAdapter`,
`NativeModuleAdapter`, `NativeFunctionAdapter`), and the generic
`WasmRuntime`/`WasmModule`/`WasmFunction` base classes that every WASM
runtime plugin extends.
- **`ns-wasm3`** (`@cross-code/ns-wasm3`) — mature plugin binding
the wasm3 interpreter (Swift Package on iOS, Kotlin + Rust JNI (cargo-ndk) on Android).
See `packages/ns-wasm3/AGENTS.md`.
Expand All @@ -53,6 +63,11 @@ below; each package's AGENTS.md holds only engine-specific detail.
builds enable only the interpreter (see [Key differences](#key-differences-wasm3-vs-wamr)).
WAMR-2.3.0 sources are vendored at `packages/ns-wamr/src/vendors/wamr/`.
See `packages/ns-wamr/AGENTS.md`.
- **`ns-wasm-kit-runtime`** (`@cross-code/ns-wasm-kit-runtime`) — plugin wrapping
the [WasmKit](https://github.com/swiftwasm/WasmKit) Swift interpreter.
iOS-only at this time (WasmKit is Swift-native and served through SwiftPM);
Android throws a clear unsupported error. Follows the same TypeScript
adapter pattern as the other two plugins.
- **`ns-wry`** (`@cross-code/ns-wry`) — general-purpose NativeScript plugin
scaffold built on Rust + UniFFI (uniffi-rs) with cargo-ndk Android pipeline.
See `packages/ns-wry/AGENTS.md` for the bare-metal architecture; extend the
Expand Down Expand Up @@ -80,7 +95,7 @@ until sources are present.

## Shared plugin architecture

Both plugins follow the same architecture: a platform-agnostic wire protocol
The WASM runtime plugins follow the same architecture: a platform-agnostic wire protocol
(`src/lib/wire.ts`), per-platform TypeScript adapter files, Swift `@objc`
classes on iOS, Kotlin + Rust JNI on Android, and Nx targets declared via
`package.json`. Substitute `<Engine>` = `Wasm3`-family
Expand Down Expand Up @@ -129,6 +144,28 @@ Swapping those groups is a common mistake — verify the regex carefully. The
native layers convert this notation to the engine's own format internally
(WAMR's native format is `"(params)returns"`).

### TypeScript typing guidelines for native adapters

The workspace uses `noImplicitAny: true` and `strict: true`. Follow these
conventions when writing TypeScript adapter code that bridges to native layers:

- **Use `unknown` over `any`** for opaque bridge values (error refs, callback
handles, runtime proxy objects). Cast to a concrete interface only at the
call site.
- **Define proxy interfaces** in `src/lib/native-proxy.d.ts` for each native
class shape (e.g. `NativeChicoryRuntimeProxy`, `IosWasmEdgeFunctionProxy`).
The interfaces model the actual bridge methods; the native layer creates
the objects, TypeScript only calls them.
- **Error ref tuples**: iOS error out-params use `[unknown]` tuple typing
so they can be spread into method signatures. The `withErrorRef` helper
returns the error ref as `[unknown] | null`.
- **globalThis shape**: Define a `NativeScriptOrg` interface in
`native-proxy.d.ts` for `globalThis.org.nativescript.*` access. Cast
`globalThis as unknown as NativeScriptOrg` — never `as any`.
- **Callback narrowing**: When passing a `WireHostCallback` to a native
constructor that expects `(args: unknown[]) => unknown[]`, cast explicitly:
`cb as (args: unknown[]) => unknown[]`.

### Missing imports surface at `findFunction`

Both engines compile functions lazily. A missing imported function is
Expand Down Expand Up @@ -496,6 +533,39 @@ casts are the assertions). Periphery found and removed real dead code in
keep `lint.ios` and `periphery.ios` green; CI runs both in the `wasm3-ios`
and `wamr-ios` jobs.

### Rust linting (Clippy + Rustfmt)

Each Rust workspace — `ns-wasm3` (`src/vendors/wasm3-rust`), `ns-wamr`
(`src/vendors/wamr-rust`), `ns-wry` (`src/vendors/wry-rust`), and the fixture
crate `ns-wasm-fixture` (`src/test-types`) — is linted with **Clippy** and
checked with **Rustfmt**. Generated code never reaches the tools: bindgen
output is written to `OUT_DIR` and `#![allow(clippy::all)]` covers the
`include!`s, the one committed reference copy
(`wamr-sys/src/bindings.rs`) is `#![rustfmt::skip]`, and UniFFI scaffolding
(`include_scaffolding!` / `setup_scaffolding!`) expands in `OUT_DIR`. The JNI
crates (`wasm3-jni`, `wamr-jni`) allow `clippy::not_unsafe_ptr_arg_deref`
crate-wide — `#[no_mangle] extern "system"` entry points deref raw JVM-owned
pointer args by design.

The `uniffi-bindgen` scaffolding bins (`*/ffi/src/bin/uniffi-bindgen.rs`) are
excluded by config, not by editing them: each `[[bin]]` carries `test = false`
in its crate's `Cargo.toml` so `cargo clippy --lib --tests` never compiles it,
and the `fmt.rust`/`format.rust` scripts format the `find`-listed `.rs` files
minus any named `uniffi-bindgen.rs` (rustfmt's `ignore` config is nightly-only,
so the file filter lives in the script).

```bash
pnpm exec nx run <pkg>:lint.rust # cargo clippy --all-features --lib --tests -- -D warnings
pnpm exec nx run <pkg>:fmt.rust # find + rustfmt --check --edition 2021 (excludes uniffi-bindgen.rs)
pnpm exec nx run <pkg>:format.rust # find + rustfmt --edition 2021 (auto-fix)
pnpm exec nx run-many -t lint.rust fmt.rust -p ns-wasm3 ns-wamr ns-wry ns-wasm-fixture
```

CI runs the `run-many` form in the `unit-tests` job (Rust is preinstalled on
the GitHub runner). Requires the `clippy` and `rustfmt` rustup components
(`rustup component add clippy rustfmt`). The `fmt.rust`/`format.rust` scripts
honour `#![rustfmt::skip]` on generated files — don't reformat them by hand.

Code coverage, per project and per language:

- **TypeScript** — every package's vitest config has a `coverage` block
Expand Down Expand Up @@ -542,8 +612,10 @@ wasm-pack-generated `.d.ts`. See `packages/ns-wasm-fixture/README.md`.

| Path | Contents |
| --------------------------------------- | ----------------------------------------------------------------------- |
| `packages/ns-wasm-core/AGENTS.md` | (none — shared foundation; see Shared plugin architecture above) |
| `packages/ns-wasm3/AGENTS.md` | wasm3-specific: stack ABI, globals, fixtures, build/test |
| `packages/ns-wamr/AGENTS.md` | WAMR-specific: two-phase load, exec env, WASI, tiers, trampolines, shim |
| `packages/ns-wasm-kit-runtime/AGENTS.md` | WasmKit-specific: iOS-only Swift interpreter, Android unsupported stub |
| `packages/ns-wry/AGENTS.md` | wry scaffold: Rust + UniFFI architecture, platform stubs, extension guide |
| `apps/ns-wasm-test/AGENTS.md` | test app: layout, design decisions, running the suites, adding specs |
| `apps/ns-wry-app` | test app: WebView demo, build-plugin-and-run workflow |
Expand Down
Loading
Loading