Skip to content

Commit 42bce60

Browse files
aepfliclaude
andauthored
feat(js): add JavaScript/TypeScript WASM evaluator package (#110)
## Summary Adds a TypeScript wrapper package (`@openfeature/flagd-wasm-evaluator`) for the flagd-evaluator WASM module, following the same architecture as the Java, Go, and .NET packages. - **`FlagEvaluator` class** with async `create()` factory that loads and instantiates the WASM module - **Three-tier evaluation**: pre-evaluated cache → indexed path with context filtering → full fallback - **Pre-allocated WASM buffers** (256KB flag key + 1MB context) to minimize allocation overhead - **Context key filtering**: only serialize fields referenced by targeting rules - **Host function wiring**: timestamp, random values, wbindgen stubs with prefix matching - **18 Vitest tests** covering static flags, targeting, caching, error handling, typed variants, fractional operator - **Comparison benchmarks** vs `json-logic-engine` v4.0.2 AOT across 4 scenarios (simple, targeting, context sweep, mixed workload) ### Package structure ``` js/ ├── src/ │ ├── evaluator.ts # FlagEvaluator class (206 lines) │ ├── wasm-runtime.ts # WASM loading, memory helpers (156 lines) │ ├── context.ts # Context serialization + filtering (49 lines) │ └── types.ts # EvaluationResult, UpdateStateResult types (27 lines) ├── tests/ │ └── evaluator.test.ts # 18 test cases (228 lines) ├── benchmarks/ │ ├── comparison.bench.ts # 4-scenario benchmark suite │ └── old-resolver.ts # json-logic-engine baseline ├── Makefile # wasm, build, test, bench targets ├── package.json # @openfeature/flagd-wasm-evaluator └── tsconfig.json # ES2022, NodeNext, strict ``` ## Test plan - [ ] `cd js && make test` — 18 Vitest tests pass - [ ] `cd js && make bench` — comparison benchmarks run - [ ] `cd js && npx tsc --noEmit` — TypeScript compiles cleanly 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 46e25ef commit 42bce60

13 files changed

Lines changed: 2817 additions & 31 deletions

File tree

.github/workflows/ci.yml

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ jobs:
3333
${{ runner.os }}-cargo-test-
3434
3535
- name: Run tests
36-
run: cargo test --verbose
36+
run: |
37+
cargo test --lib --verbose -- --test-threads=1
38+
cargo test --test integration_tests --verbose -- --test-threads=1
39+
cargo test --test gherkin_tests --verbose
3740
3841
clippy:
3942
name: Clippy
@@ -114,7 +117,6 @@ jobs:
114117
name: Test Go Package
115118
needs: build-wasm
116119
runs-on: ubuntu-latest
117-
if: hashFiles('go/go.mod') != ''
118120
steps:
119121
- uses: actions/checkout@v4
120122

@@ -133,16 +135,62 @@ jobs:
133135
- name: Run Go tests
134136
run: cd go && go test -v -count=1 ./...
135137

136-
test-python:
137-
name: Test Python Bindings
138+
test-js:
139+
name: Test JS Package
140+
needs: build-wasm
141+
runs-on: ubuntu-latest
142+
steps:
143+
- uses: actions/checkout@v4
144+
145+
- name: Set up Node.js
146+
uses: actions/setup-node@v4
147+
with:
148+
node-version: '20'
149+
150+
- name: Download WASM artifact
151+
uses: actions/download-artifact@v4
152+
with:
153+
name: flagd_evaluator.wasm
154+
path: js/
155+
156+
- name: Install dependencies
157+
run: cd js && npm ci
158+
159+
- name: Type check
160+
run: cd js && npx tsc --noEmit
161+
162+
- name: Run tests
163+
run: cd js && npx vitest run
164+
165+
test-dotnet:
166+
name: Test .NET Package
167+
needs: build-wasm
168+
runs-on: ubuntu-latest
169+
steps:
170+
- uses: actions/checkout@v4
171+
172+
- name: Set up .NET
173+
uses: actions/setup-dotnet@v4
174+
with:
175+
dotnet-version: '8.0'
176+
177+
- name: Download WASM artifact
178+
uses: actions/download-artifact@v4
179+
with:
180+
name: flagd_evaluator.wasm
181+
path: dotnet/src/FlagdEvaluator/
182+
183+
- name: Run tests
184+
run: cd dotnet && dotnet test dotnet.sln -c Release --logger "console;verbosity=detailed"
185+
186+
test-python-native:
187+
name: Test Python Native Bindings (PyO3)
138188
runs-on: ubuntu-latest
139189
steps:
140190
- uses: actions/checkout@v4
141191

142192
- name: Install Rust
143193
uses: dtolnay/rust-toolchain@stable
144-
with:
145-
targets: wasm32-unknown-unknown
146194

147195
- uses: actions/setup-python@v5
148196
with:
@@ -153,26 +201,56 @@ jobs:
153201
with:
154202
enable-cache: true
155203

156-
- name: Build WASM for Python WASM evaluator
157-
run: |
158-
cargo build --target wasm32-unknown-unknown --no-default-features --release --lib
159-
cp target/wasm32-unknown-unknown/release/flagd_evaluator.wasm python/flagd_evaluator_wasm/flagd_evaluator.wasm
160-
161204
- name: Install dependencies and build package
162205
run: |
163206
cd python
164207
uv sync --group dev
165208
source .venv/bin/activate
166209
maturin develop
167210
168-
- name: Run Python tests
211+
- name: Run native tests
169212
run: |
170213
cd python
171214
source .venv/bin/activate
172-
pytest tests/ -v
215+
pytest tests/ -v --ignore=tests/test_wasm_evaluator.py
216+
217+
test-python-wasm:
218+
name: Test Python WASM Evaluator
219+
needs: build-wasm
220+
runs-on: ubuntu-latest
221+
steps:
222+
- uses: actions/checkout@v4
223+
224+
- uses: actions/setup-python@v5
225+
with:
226+
python-version: '3.9'
227+
228+
- name: Install uv
229+
uses: astral-sh/setup-uv@v4
230+
with:
231+
enable-cache: true
232+
233+
- name: Download WASM artifact
234+
uses: actions/download-artifact@v4
235+
with:
236+
name: flagd_evaluator.wasm
237+
path: python/flagd_evaluator_wasm/
238+
239+
- name: Install dependencies
240+
run: |
241+
cd python
242+
uv sync --group dev
243+
source .venv/bin/activate
244+
245+
- name: Run WASM tests
246+
run: |
247+
cd python
248+
source .venv/bin/activate
249+
pytest tests/test_wasm_evaluator.py -v
173250
174251
test-java:
175252
name: Test Java Library
253+
needs: build-wasm
176254
runs-on: ubuntu-latest
177255
steps:
178256
- uses: actions/checkout@v4
@@ -184,29 +262,17 @@ jobs:
184262
distribution: 'temurin'
185263
cache: 'maven'
186264

187-
- name: Install Rust
188-
uses: dtolnay/rust-toolchain@stable
189-
with:
190-
targets: wasm32-unknown-unknown
191-
192-
- name: Cache cargo
193-
uses: actions/cache@v4
265+
- name: Download WASM artifact
266+
uses: actions/download-artifact@v4
194267
with:
195-
path: |
196-
~/.cargo/bin/
197-
~/.cargo/registry/index/
198-
~/.cargo/registry/cache/
199-
~/.cargo/git/db/
200-
target/
201-
key: ${{ runner.os }}-cargo-java-${{ hashFiles('**/Cargo.lock') }}
202-
restore-keys: |
203-
${{ runner.os }}-cargo-java-
268+
name: flagd_evaluator.wasm
269+
path: target/wasm32-unknown-unknown/release/
204270

205271
- name: Make Maven wrapper executable
206272
run: chmod +x java/mvnw
207273

208-
- name: Build with Maven
209-
run: cd java && ./mvnw clean verify
274+
- name: Build with Maven (skip WASM build)
275+
run: cd java && ./mvnw clean verify -Dexec.skip=true
210276

211277
- name: Upload test results
212278
if: always()

js/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
dist/
3+
*.wasm

js/Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
WASM_SRC = ..
2+
WASM_TARGET = flagd_evaluator.wasm
3+
4+
.PHONY: wasm build test bench clean
5+
6+
wasm:
7+
cargo build --manifest-path $(WASM_SRC)/Cargo.toml \
8+
--target wasm32-unknown-unknown --no-default-features --release --lib
9+
cp $(WASM_SRC)/target/wasm32-unknown-unknown/release/flagd_evaluator.wasm $(WASM_TARGET)
10+
11+
build: wasm
12+
npx tsc --noEmit
13+
14+
test: wasm
15+
npx vitest run
16+
17+
bench: wasm
18+
npx tsx benchmarks/comparison.bench.ts
19+
20+
clean:
21+
rm -rf node_modules dist $(WASM_TARGET)

0 commit comments

Comments
 (0)