diff --git a/.gitignore b/.gitignore
index 5c5acf0..e070b11 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
example/node_modules
build
example/enclave.wasm
+src/dist
+src/node_modules
+dist
+node_modules
diff --git a/Makefile b/Makefile
index badaa45..6836929 100644
--- a/Makefile
+++ b/Makefile
@@ -1,84 +1,72 @@
-.PHONY: all generate build build-debug build-opt test test-cover test-plugin lint fmt vet clean tidy deps verify serve help
+.PHONY: start deps build sdk dev test test-plugin lint fmt clean help
-MODULE := enclave
BINARY := enclave.wasm
BUILD_DIR := example
-all: generate build
+# === Primary Commands ===
-generate:
- @sqlc generate
+start: deps build sdk dev
+
+deps:
+ @command -v sqlc >/dev/null || go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
+ @command -v golangci-lint >/dev/null || go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+ @bun install
+ @cd example && bun install
build:
+ @echo "Building WASM plugin..."
@GOOS=wasip1 GOARCH=wasm go build -o $(BUILD_DIR)/$(BINARY) .
+ @echo "Built $(BUILD_DIR)/$(BINARY)"
-build-debug:
- @GOOS=wasip1 GOARCH=wasm go build -gcflags="all=-N -l" -o $(BUILD_DIR)/$(BINARY) .
+sdk:
+ @echo "Building TypeScript SDK..."
+ @bun run build
+ @echo "Built dist/enclave.js"
-build-opt:
- @GOOS=wasip1 GOARCH=wasm go build -ldflags="-s -w" -o $(BUILD_DIR)/$(BINARY) .
- @wasm-opt -Os $(BUILD_DIR)/$(BINARY) -o $(BUILD_DIR)/$(BINARY)
+dev:
+ @echo "Starting dev server at http://localhost:8080"
+ @cd example && bun run dev
+
+# === Testing ===
test:
@go test -v ./...
-test-cover:
- @go test -coverprofile=coverage.out ./...
- @go tool cover -html=coverage.out -o coverage.html
+test-plugin: build
+ @echo "Testing generate()..."
+ @extism call $(BUILD_DIR)/$(BINARY) generate --input '{"credential":"dGVzdC1jcmVkZW50aWFs"}' --wasi
+ @echo "\nTesting query()..."
+ @extism call $(BUILD_DIR)/$(BINARY) query --input '{"did":""}' --wasi
-test-plugin:
- @extism call $(BUILD_DIR)/$(BINARY) generate --input '{"credential":"dGVzdA=="}' --wasi
+test-sdk: sdk
+ @cd example && bun run test
-serve: build
- @echo "Starting Vite dev server at http://localhost:8080"
- @cd example && npm run dev
+# === Code Quality ===
lint:
@golangci-lint run ./...
fmt:
@go fmt ./...
- @gofumpt -w .
+ @bun run --filter '*' format 2>/dev/null || true
-vet:
- @go vet ./...
+generate:
+ @sqlc generate
+
+# === Utilities ===
clean:
@rm -f $(BUILD_DIR)/$(BINARY)
- @rm -f coverage.out coverage.html
-
-tidy:
- @go mod tidy
-
-deps:
- @go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
- @go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
- @go install mvdan.cc/gofumpt@latest
- @cd example && npm install
- @echo "Install Extism CLI: https://extism.org/docs/install"
-
-verify: fmt vet lint test
+ @rm -rf dist
help:
- @echo "Motr Enclave - Extism Plugin (Go 1.25/wasip1)"
+ @echo "Motr Enclave"
@echo ""
- @echo "Build targets:"
- @echo " build - Build WASM plugin for wasip1"
- @echo " build-debug - Build with debug symbols"
- @echo " build-opt - Build optimized (requires wasm-opt)"
- @echo ""
- @echo "Development targets:"
- @echo " generate - Run sqlc to generate Go code"
- @echo " test - Run tests"
- @echo " test-cover - Run tests with coverage"
- @echo " test-plugin - Test plugin with Extism CLI"
- @echo " serve - Build and serve example/ for browser testing"
- @echo " lint - Run golangci-lint"
- @echo " fmt - Format code"
- @echo " vet - Run go vet"
- @echo " verify - Run fmt, vet, lint, and test"
- @echo ""
- @echo "Utility targets:"
- @echo " clean - Remove build artifacts"
- @echo " tidy - Run go mod tidy"
- @echo " deps - Install development dependencies"
+ @echo " make start - Full setup + dev server (recommended)"
+ @echo " make build - Build WASM plugin"
+ @echo " make sdk - Build TypeScript SDK"
+ @echo " make dev - Start dev server"
+ @echo " make test - Run Go tests"
+ @echo " make test-plugin - Test plugin with Extism CLI"
+ @echo " make test-sdk - Run SDK tests in browser"
+ @echo " make clean - Remove build artifacts"
diff --git a/example/index.html b/example/index.html
index 70b9eb3..446067a 100644
--- a/example/index.html
+++ b/example/index.html
@@ -1,168 +1,81 @@
-
-
- Motr Enclave Test
-
+
+
+ Motr Enclave
+
- Motr Enclave Plugin Test
+
+
Motr Enclave
-
Plugin Status
-
Loading plugin...
-
+
Status
+
Loading...
+
-
generate()
-
Initialize database with WebAuthn credential
-
-
-
-
+
generate(credential)
+
+
-
load()
-
Load database from serialized buffer
-
-
-
-
-
+
load(database)
+
+
-
exec()
-
Execute action with GitHub-style filter syntax
-
-
-
-
-
-
-
-
-
-
-
-
+
exec(filter)
+
+
+
+
+
+
+
-
query()
-
Resolve DID to document with resources
-
-
-
-
+
query(did)
+
+
-
Console Log
-
-
+
Log
+
+
+
-
+
diff --git a/example/package.json b/example/package.json
index c604c65..6a54102 100644
--- a/example/package.json
+++ b/example/package.json
@@ -5,7 +5,7 @@
"scripts": {
"dev": "vite",
"build": "vite build",
- "preview": "vite preview"
+ "test": "vite --port 8081 & sleep 2 && node test.runner.js; kill %1"
},
"dependencies": {
"@extism/extism": "^2.0.0-rc13"
diff --git a/example/test.js b/example/test.js
deleted file mode 100644
index 9537320..0000000
--- a/example/test.js
+++ /dev/null
@@ -1,205 +0,0 @@
-import createPlugin from '@extism/extism';
-
-let plugin = null;
-let generatedDatabase = null;
-
-function log(message, type = 'info') {
- const consoleLog = document.getElementById('consoleLog');
- const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
- const prefix = type === 'error' ? '[ERROR]' : type === 'success' ? '[OK]' : '[INFO]';
- consoleLog.textContent += `${timestamp} ${prefix} ${message}\n`;
- consoleLog.scrollTop = consoleLog.scrollHeight;
- console[type === 'error' ? 'error' : 'log'](message);
-}
-
-function setStatus(message, type) {
- const status = document.getElementById('status');
- status.textContent = message;
- status.className = `status ${type}`;
-}
-
-function formatOutput(data) {
- try {
- if (typeof data === 'string') {
- const parsed = JSON.parse(data);
- return JSON.stringify(parsed, null, 2);
- }
- return JSON.stringify(data, null, 2);
- } catch {
- return String(data);
- }
-}
-
-async function loadPlugin() {
- setStatus('Loading plugin...', 'loading');
- log('Loading enclave.wasm...');
-
- try {
- const wasmUrl = new URL('./enclave.wasm', window.location.href).href;
- log(`WASM URL: ${wasmUrl}`);
-
- const manifest = {
- wasm: [{ url: wasmUrl }]
- };
-
- plugin = await createPlugin(manifest, {
- useWasi: true,
- logger: console
- });
-
- setStatus('Plugin loaded successfully', 'success');
- log('Plugin loaded successfully', 'success');
- } catch (error) {
- setStatus(`Failed to load plugin: ${error.message}`, 'error');
- log(`Failed to load plugin: ${error.message}`, 'error');
- }
-}
-
-async function testGenerate() {
- if (!plugin) {
- log('Plugin not loaded', 'error');
- return;
- }
-
- const output = document.getElementById('generateOutput');
- const credential = document.getElementById('credentialInput').value;
-
- log(`Calling generate() with credential: ${credential.substring(0, 20)}...`);
- output.textContent = 'Running...';
-
- try {
- const input = JSON.stringify({ credential });
- const result = await plugin.call('generate', input);
- const data = result.json();
-
- output.textContent = formatOutput(data);
- log(`generate() completed. DID: ${data.did}`, 'success');
-
- if (data.database) {
- generatedDatabase = data.database;
- log('Database buffer stored for load() test');
- }
- } catch (error) {
- output.textContent = `Error: ${error.message}`;
- log(`generate() failed: ${error.message}`, 'error');
- }
-}
-
-async function testLoad() {
- if (!plugin) {
- log('Plugin not loaded', 'error');
- return;
- }
-
- const output = document.getElementById('loadOutput');
- const databaseInput = document.getElementById('databaseInput').value;
-
- if (!databaseInput) {
- output.textContent = 'Error: Database buffer is required';
- log('load() requires database buffer', 'error');
- return;
- }
-
- log('Calling load()...');
- output.textContent = 'Running...';
-
- try {
- const input = JSON.stringify({
- database: Array.from(atob(databaseInput), c => c.charCodeAt(0))
- });
- const result = await plugin.call('load', input);
- const data = result.json();
-
- output.textContent = formatOutput(data);
- log(`load() completed. Success: ${data.success}`, data.success ? 'success' : 'error');
- } catch (error) {
- output.textContent = `Error: ${error.message}`;
- log(`load() failed: ${error.message}`, 'error');
- }
-}
-
-function useGeneratedDb() {
- if (generatedDatabase) {
- const base64 = btoa(String.fromCharCode(...generatedDatabase));
- document.getElementById('databaseInput').value = base64;
- log('Populated database input with generated database');
- } else {
- log('No generated database available. Run generate() first.', 'error');
- }
-}
-
-async function testExec() {
- if (!plugin) {
- log('Plugin not loaded', 'error');
- return;
- }
-
- const output = document.getElementById('execOutput');
- const filter = document.getElementById('filterInput').value;
- const token = document.getElementById('tokenInput').value;
-
- if (!filter) {
- output.textContent = 'Error: Filter is required';
- log('exec() requires filter', 'error');
- return;
- }
-
- log(`Calling exec() with filter: ${filter}`);
- output.textContent = 'Running...';
-
- try {
- const input = JSON.stringify({ filter, token: token || undefined });
- const result = await plugin.call('exec', input);
- const data = result.json();
-
- output.textContent = formatOutput(data);
- log(`exec() completed. Success: ${data.success}`, data.success ? 'success' : 'error');
- } catch (error) {
- output.textContent = `Error: ${error.message}`;
- log(`exec() failed: ${error.message}`, 'error');
- }
-}
-
-function setFilter(filter) {
- document.getElementById('filterInput').value = filter;
-}
-
-async function testQuery() {
- if (!plugin) {
- log('Plugin not loaded', 'error');
- return;
- }
-
- const output = document.getElementById('queryOutput');
- const did = document.getElementById('didInput').value;
-
- log(`Calling query() with DID: ${did || '(current)'}`);
- output.textContent = 'Running...';
-
- try {
- const input = JSON.stringify({ did: did || '' });
- const result = await plugin.call('query', input);
- const data = result.json();
-
- output.textContent = formatOutput(data);
- log(`query() completed. DID: ${data.did}`, 'success');
- } catch (error) {
- output.textContent = `Error: ${error.message}`;
- log(`query() failed: ${error.message}`, 'error');
- }
-}
-
-function clearLog() {
- document.getElementById('consoleLog').textContent = '';
-}
-
-window.loadPlugin = loadPlugin;
-window.testGenerate = testGenerate;
-window.testLoad = testLoad;
-window.useGeneratedDb = useGeneratedDb;
-window.testExec = testExec;
-window.setFilter = setFilter;
-window.testQuery = testQuery;
-window.clearLog = clearLog;
-
-loadPlugin();