refactor(MAKEFILE): migrate to Vite and remove sqlc dependency

This commit is contained in:
2026-01-07 18:33:59 -05:00
parent a46f8bcfec
commit 9dc64814b4
5 changed files with 103 additions and 403 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
example/node_modules
build
example/enclave.wasm
src/dist
src/node_modules
dist
node_modules

View File

@@ -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"

View File

@@ -1,168 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Motr Enclave Test</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
h1 {
color: #333;
}
.card {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card h2 {
margin-top: 0;
color: #555;
font-size: 1.2rem;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #666;
}
input, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: monospace;
font-size: 14px;
margin-bottom: 10px;
}
textarea {
min-height: 100px;
resize: vertical;
}
button {
background: #4a90d9;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
margin-bottom: 10px;
}
button:hover {
background: #357abd;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.output {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
white-space: pre-wrap;
word-break: break-all;
max-height: 300px;
overflow-y: auto;
}
.status {
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
}
.status.success {
background: #d4edda;
color: #155724;
}
.status.error {
background: #f8d7da;
color: #721c24;
}
.status.loading {
background: #fff3cd;
color: #856404;
}
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Motr Enclave</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #0a0a0a; color: #e5e5e5; padding: 2rem; }
h1 { font-size: 1.5rem; margin-bottom: 1rem; color: #fff; }
.container { max-width: 800px; margin: 0 auto; }
.card { background: #171717; border-radius: 8px; padding: 1rem; margin-bottom: 1rem; }
.card h2 { font-size: 0.875rem; color: #a3a3a3; margin-bottom: 0.5rem; font-weight: 500; }
.status { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; }
.status.ok { background: #14532d; color: #4ade80; }
.status.err { background: #7f1d1d; color: #f87171; }
.status.wait { background: #422006; color: #fbbf24; }
button { background: #2563eb; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; font-size: 0.875rem; margin-right: 0.5rem; margin-bottom: 0.5rem; }
button:hover { background: #1d4ed8; }
button:disabled { background: #374151; cursor: not-allowed; }
input { width: 100%; background: #262626; border: 1px solid #404040; color: #fff; padding: 0.5rem; border-radius: 4px; font-family: monospace; font-size: 0.875rem; margin-bottom: 0.5rem; }
.log { background: #0a0a0a; border: 1px solid #262626; border-radius: 4px; padding: 0.75rem; font-family: monospace; font-size: 0.75rem; max-height: 300px; overflow-y: auto; white-space: pre-wrap; }
.log-entry { padding: 0.125rem 0; border-bottom: 1px solid #1a1a1a; }
.log-entry:last-child { border-bottom: none; }
.log-time { color: #525252; }
.log-info { color: #60a5fa; }
.log-ok { color: #4ade80; }
.log-err { color: #f87171; }
.log-data { color: #a78bfa; }
.actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
</style>
</head>
<body>
<h1>Motr Enclave Plugin Test</h1>
<div class="container">
<h1>Motr Enclave</h1>
<div class="card">
<h2>Plugin Status</h2>
<div id="status" class="status loading">Loading plugin...</div>
<button id="loadPluginBtn" onclick="loadPlugin()">Reload Plugin</button>
<h2>Status</h2>
<span id="status" class="status wait">Loading...</span>
<button onclick="runAllTests()" style="margin-left: 1rem;">Run All Tests</button>
</div>
<div class="card">
<h2>generate()</h2>
<p>Initialize database with WebAuthn credential</p>
<label for="credentialInput">Credential (Base64):</label>
<input type="text" id="credentialInput" value="dGVzdC1jcmVkZW50aWFsLWRhdGEtZm9yLXRlc3Rpbmc=" placeholder="Base64-encoded PublicKeyCredential">
<button onclick="testGenerate()">Run generate()</button>
<div id="generateOutput" class="output"></div>
<h2>generate(credential)</h2>
<input type="text" id="credential" value="dGVzdC1jcmVkZW50aWFs" placeholder="Base64 credential">
<button onclick="testGenerate()">Run</button>
</div>
<div class="card">
<h2>load()</h2>
<p>Load database from serialized buffer</p>
<label for="databaseInput">Database Buffer (Base64):</label>
<input type="text" id="databaseInput" placeholder="Base64-encoded database buffer">
<button onclick="testLoad()">Run load()</button>
<button onclick="useGeneratedDb()">Use Generated DB</button>
<div id="loadOutput" class="output"></div>
<h2>load(database)</h2>
<input type="text" id="database" placeholder="Base64 database (auto-filled after generate)">
<button onclick="testLoad()">Run</button>
</div>
<div class="card">
<h2>exec()</h2>
<p>Execute action with GitHub-style filter syntax</p>
<label for="filterInput">Filter:</label>
<input type="text" id="filterInput" value="resource:accounts action:list" placeholder="resource:accounts action:sign subject:did:sonr:abc">
<label for="tokenInput">UCAN Token (optional):</label>
<input type="text" id="tokenInput" placeholder="UCAN token for authorization">
<div class="btn-group">
<button onclick="testExec()">Run exec()</button>
<button onclick="setFilter('resource:accounts action:list')">List Accounts</button>
<button onclick="setFilter('resource:credentials action:list')">List Credentials</button>
<button onclick="setFilter('resource:sessions action:list')">List Sessions</button>
<button onclick="setFilter('resource:accounts action:sign')">Sign</button>
</div>
<div id="execOutput" class="output"></div>
<h2>exec(filter)</h2>
<input type="text" id="filter" value="resource:accounts action:list" placeholder="resource:X action:Y">
<div class="actions">
<button onclick="testExec()">Run</button>
<button onclick="setFilter('resource:accounts action:list')">Accounts</button>
<button onclick="setFilter('resource:credentials action:list')">Credentials</button>
<button onclick="setFilter('resource:sessions action:list')">Sessions</button>
</div>
</div>
<div class="card">
<h2>query()</h2>
<p>Resolve DID to document with resources</p>
<label for="didInput">DID:</label>
<input type="text" id="didInput" placeholder="did:sonr:abc123 (leave empty for current DID)">
<button onclick="testQuery()">Run query()</button>
<div id="queryOutput" class="output"></div>
<h2>query(did)</h2>
<input type="text" id="did" placeholder="did:sonr:... (empty = current)">
<button onclick="testQuery()">Run</button>
</div>
<div class="card">
<h2>Console Log</h2>
<button onclick="clearLog()">Clear</button>
<div id="consoleLog" class="output"></div>
<h2>Log</h2>
<button onclick="clearLog()" style="margin-bottom: 0.5rem;">Clear</button>
<div id="log" class="log"></div>
</div>
</div>
<script type="module" src="test.js"></script>
<script type="module" src="./main.js"></script>
</body>
</html>

View File

@@ -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"

View File

@@ -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();