JCO (JavaScript Component Operations) is the official JavaScript/TypeScript toolchain for WebAssembly components in the WASM Component Model. It transpiles .wasm component binaries to JavaScript modules that run natively in Node.js and browsers, generates TypeScript bindings for type-safe component consumption, and provides a WASI (WebAssembly System Interface) runtime for running WASM in JavaScript environments. When JCO's transpilation output changes subtly across versions and breaks downstream consumers, when WASI shims for filesystem, clock, or socket operations return unexpected values in your Node.js runtime, when module resolution fails because a component's dependencies can't be resolved in the JavaScript module graph, or when your build pipeline silently produces invalid component binaries that JCO can't process, your WASM component architecture breaks in ways that are hard to diagnose. Vigilmon gives you external monitoring for JCO transpilation health, WASI runtime compatibility, module resolution success rates, and build pipeline status so you catch WebAssembly component toolchain regressions before they reach production.
What You'll Set Up
- JCO transpilation pipeline health via cron heartbeats
- WASI runtime compatibility verification
- Module resolution success monitoring
- Build pipeline status tracking
- TypeScript binding generation health
Prerequisites
- JCO 1.0+ installed (
npm install -g @bytecodealliance/jco) - A CI/CD pipeline or build server running JCO transpilation
- A free Vigilmon account
Step 1: Monitor JCO Transpilation Pipeline Health
JCO's core operation is jco transpile — it takes a .wasm component binary and outputs a JavaScript module with TypeScript type definitions. This runs in your CI/CD pipeline or as a build step. When it fails, downstream services that import the transpiled module get import errors that look like missing packages rather than WASM toolchain failures, making the root cause obscure. Monitor your transpilation pipeline with a health heartbeat:
- In Vigilmon, click Add Monitor → Cron Heartbeat.
- Set Expected ping interval to
10 minutes. - Copy the heartbeat URL (e.g.,
https://vigilmon.online/heartbeat/abc123).
Create the transpilation check script:
#!/bin/bash
# /usr/local/bin/jco-transpile-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/abc123"
CANARY_WASM="/opt/jco-canary/health-check.wasm"
OUTPUT_DIR="/tmp/jco-canary-output"
EXPECTED_JS_FILE="$OUTPUT_DIR/health-check.js"
EXPECTED_TS_FILE="$OUTPUT_DIR/health-check.d.ts"
# Clean output directory
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
# Run JCO transpilation on canary component
JCO_OUTPUT=$(jco transpile "$CANARY_WASM" \
--out-dir "$OUTPUT_DIR" \
--no-nodejs-compat 2>&1)
JCO_EXIT=$?
if [ $JCO_EXIT -ne 0 ]; then
echo "JCO transpilation failed: $JCO_OUTPUT"
exit 1
fi
# Verify expected output files were generated
if [ ! -f "$EXPECTED_JS_FILE" ]; then
echo "Transpilation succeeded but JS output missing"
exit 1
fi
if [ ! -f "$EXPECTED_TS_FILE" ]; then
echo "Transpilation succeeded but TypeScript bindings missing"
exit 1
fi
# Verify the JS module is syntactically valid
node --input-type=module --eval "
import { createRequire } from 'module';
import { readFileSync } from 'fs';
const src = readFileSync('$EXPECTED_JS_FILE', 'utf8');
// Basic syntax check — if import fails it throws
console.log('JS module syntax OK, size:', src.length);
" 2>&1
NODE_EXIT=$?
if [ $NODE_EXIT -ne 0 ]; then
echo "Transpiled JS module has syntax errors"
exit 1
fi
echo "JCO transpilation OK"
curl -s "$HEARTBEAT_URL"
Keep a health-check.wasm canary component in your repository that represents a simple but real usage of the component model — ideally a minimal component with one or two exports. Run this check on your build server after every JCO version upgrade.
Step 2: Verify WASI Runtime Compatibility
JCO provides JavaScript shims for WASI interfaces — the system-level APIs that WASM components use for file I/O, clocks, environment variables, and networking. When JCO upgrades its WASI shim implementation or when you upgrade the underlying Node.js version, WASI behavior can change: clock resolution changes, path normalization differences, or socket binding semantics. These regressions are subtle because the component still runs — it just gets different values from WASI calls than it expects.
Build a WASI compatibility verification script:
#!/bin/bash
# /usr/local/bin/jco-wasi-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/def456"
# Run a Node.js script that exercises JCO's WASI shims
node --experimental-wasi-unstable-preview1 << 'EOF'
const { transpile } = await import('@bytecodealliance/jco');
const { WASI } = await import('@bytecodealliance/preview2-shim');
const { readFileSync } = await import('fs');
// Test clock shim — verify monotonic clock returns increasing values
const before = Date.now();
await new Promise(resolve => setTimeout(resolve, 10));
const after = Date.now();
if (after <= before) {
console.error('WASI clock shim: monotonic time not increasing');
process.exit(1);
}
// Test environment shim — verify we can read env vars
const wasi = new WASI({
version: 'preview2',
env: { TEST_VAR: 'jco-health-check' },
});
console.log('WASI shims OK');
console.log('Clock delta:', after - before, 'ms');
process.exit(0);
EOF
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo "WASI compatibility check failed"
exit 1
fi
echo "WASI shims verified"
curl -s "$HEARTBEAT_URL"
Schedule this check every 15 minutes. WASI compatibility issues tend to appear after JCO or Node.js upgrades, so you want frequent checks during the deployment window and steady baseline monitoring afterward.
For environments where you use JCO's jco run command to execute WASM components directly (rather than transpiling), monitor a known-good component execution:
# Run the canary component and verify its exit code
jco run /opt/jco-canary/health-check.wasm --wasi-stdin /dev/null
if [ $? -ne 0 ]; then
echo "jco run failed for canary component"
exit 1
fi
Step 3: Monitor Module Resolution Success
JCO transpiles components to JavaScript modules with static import statements. When the component depends on other WASM components (via the component model's import mechanism), JCO generates corresponding JavaScript imports. If those imports can't be resolved — because the package isn't installed, because a path changed, or because a version mismatch causes exports to be renamed — the transpiled module fails to load with Node.js ERR_MODULE_NOT_FOUND errors.
Monitor module resolution health:
#!/bin/bash
# /usr/local/bin/jco-resolve-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/ghi789"
TRANSPILE_OUTPUT_DIR="/opt/jco-transpiled"
# Try to import each transpiled module and verify it resolves
node --input-type=module << EOF
import { readdir } from 'fs/promises';
import { pathToFileURL } from 'url';
const dir = '${TRANSPILE_OUTPUT_DIR}';
let failed = 0;
let checked = 0;
try {
const files = await readdir(dir);
const jsFiles = files.filter(f => f.endsWith('.js') && !f.endsWith('.d.ts'));
for (const file of jsFiles) {
const filePath = \`\${dir}/\${file}\`;
try {
await import(pathToFileURL(filePath).href);
checked++;
} catch (err) {
if (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
console.error(\`Resolution failed for \${file}: \${err.message}\`);
failed++;
} else {
// Runtime errors during module init are expected for components needing live WASI
checked++;
}
}
}
if (failed > 0) {
console.error(\`\${failed} of \${checked + failed} modules failed to resolve\`);
process.exit(1);
}
console.log(\`All \${checked} modules resolved OK\`);
process.exit(0);
} catch (err) {
console.error('Failed to scan transpile output directory:', err.message);
process.exit(1);
}
EOF
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo "Module resolution check failed"
exit 1
fi
curl -s "$HEARTBEAT_URL"
Run this check every 10 minutes on your staging server where transpiled modules are deployed before going to production. Resolution failures are loud in staging (import errors crash the application) but may be silent in production if error boundaries are too broad.
Step 4: Track Build Pipeline Status
JCO is typically invoked as part of a larger build pipeline: wit-bindgen generates source code, the language toolchain compiles to .wasm, wasm-tools component new packages it into a component binary, and jco transpile converts it to JavaScript. Any step in this chain can fail, and failures often cascade silently: a stale .wasm binary from a previous build gets transpiled instead of the new one, so deployments appear successful but ship old code.
Monitor your build pipeline's output freshness:
#!/bin/bash
# /usr/local/bin/jco-pipeline-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/jkl012"
WASM_OUTPUT_DIR="/opt/build/wasm-components"
JS_OUTPUT_DIR="/opt/jco-transpiled"
MAX_AGE_MINUTES=30 # Alert if build outputs are older than 30 minutes
check_freshness() {
local dir="$1"
local label="$2"
if [ ! -d "$dir" ]; then
echo "$label directory missing: $dir"
return 1
fi
# Find the most recently modified file
NEWEST=$(find "$dir" -name "*.wasm" -o -name "*.js" 2>/dev/null | \
xargs ls -t 2>/dev/null | head -1)
if [ -z "$NEWEST" ]; then
echo "No output files found in $label directory"
return 1
fi
FILE_AGE_MINUTES=$(( ($(date +%s) - $(date +%s -r "$NEWEST")) / 60 ))
if [ "$FILE_AGE_MINUTES" -gt "$MAX_AGE_MINUTES" ]; then
echo "$label outputs stale: newest file is ${FILE_AGE_MINUTES}m old (threshold: ${MAX_AGE_MINUTES}m)"
return 1
fi
echo "$label OK: newest output ${FILE_AGE_MINUTES}m old"
return 0
}
WASM_OK=true
JS_OK=true
check_freshness "$WASM_OUTPUT_DIR" "WASM components" || WASM_OK=false
check_freshness "$JS_OUTPUT_DIR" "JCO transpiled" || JS_OK=false
if [ "$WASM_OK" = false ] || [ "$JS_OK" = false ]; then
exit 1
fi
# Verify JCO binary itself is accessible and working
JCO_VERSION=$(jco --version 2>&1)
if [ $? -ne 0 ]; then
echo "JCO binary not accessible: $JCO_VERSION"
exit 1
fi
echo "Build pipeline OK: $JCO_VERSION"
curl -s "$HEARTBEAT_URL"
Set the heartbeat interval to 15 minutes with an alert after 2 missed pings. If your CI/CD pipeline runs every 10 minutes and this monitor fires, it means the pipeline has been failing for at least two consecutive runs — an urgent signal before stale code causes customer impact.
Step 5: Monitor TypeScript Binding Generation Health
One of JCO's most valuable features is generating TypeScript type definitions from WebAssembly Interface Types (WIT). These .d.ts files provide type-safe imports for transpiled components. When type generation breaks — because of a WIT syntax change, a JCO version regression, or a malformed component binary — TypeScript compilation fails for everything that depends on the component, blocking deploys.
Check TypeScript binding health:
#!/bin/bash
# /usr/local/bin/jco-types-check.sh
HEARTBEAT_URL="https://vigilmon.online/heartbeat/mno345"
TYPES_OUTPUT_DIR="/opt/jco-transpiled/types"
TS_PROJECT_DIR="/opt/your-application"
# Verify type definition files exist
if [ -z "$(ls -A "$TYPES_OUTPUT_DIR"/*.d.ts 2>/dev/null)" ]; then
echo "No TypeScript type definitions found in $TYPES_OUTPUT_DIR"
exit 1
fi
# Count and validate type files
TYPES_COUNT=$(ls "$TYPES_OUTPUT_DIR"/*.d.ts 2>/dev/null | wc -l)
echo "Found $TYPES_COUNT TypeScript type definition files"
# Run tsc type-check against the application using these bindings
cd "$TS_PROJECT_DIR" || exit 1
TSC_OUTPUT=$(npx tsc --noEmit --strict 2>&1)
TSC_EXIT=$?
if [ $TSC_EXIT -ne 0 ]; then
# Check if errors are specifically in JCO-generated type files
JCO_ERRORS=$(echo "$TSC_OUTPUT" | grep -c "jco-transpiled/types" || true)
if [ "$JCO_ERRORS" -gt 0 ]; then
echo "TypeScript errors in JCO-generated types ($JCO_ERRORS errors)"
echo "$TSC_OUTPUT" | grep "jco-transpiled/types" | head -5
exit 1
fi
# Non-JCO TypeScript errors exist but aren't our concern here
echo "TypeScript errors present but not in JCO types — ignoring"
fi
echo "TypeScript bindings OK: $TYPES_COUNT type files, no JCO type errors"
curl -s "$HEARTBEAT_URL"
Run this check every 20 minutes. TypeScript type errors from JCO-generated bindings are particularly insidious because they only surface at compile time — if your CI doesn't run tsc --noEmit, they go undetected until a developer runs a local build or a deploy pipeline runs type checking.
If you use JCO's jco types subcommand separately from jco transpile, add it to the check:
# Regenerate types and verify they match what's deployed
jco types /opt/jco-canary/health-check.wasm \
--out-dir /tmp/jco-types-check
diff -r /tmp/jco-types-check "$TYPES_OUTPUT_DIR"/health-check/ && echo "Types match" || {
echo "Generated types differ from deployed types — stale?"
exit 1
}
Putting It All Together
With these five monitors in place, your Vigilmon dashboard gives you end-to-end JCO pipeline visibility:
| Monitor | Type | Check Interval | Alert On | |---------|------|----------------|----------| | Transpilation pipeline health | Cron heartbeat | 10 min | Transpilation failure or missing outputs | | WASI runtime compatibility | Cron heartbeat | 15 min | Shim behavior changed | | Module resolution success | Cron heartbeat | 10 min | Any import resolution failure | | Build pipeline freshness | Cron heartbeat | 15 min | Outputs older than 30 min | | TypeScript binding health | Cron heartbeat | 20 min | JCO-generated type errors |
Start with transpilation pipeline health — if jco transpile is failing, nothing else matters. Follow it with module resolution, because resolution failures are the most common production failure mode and the most confusing to debug without dedicated monitoring.
JCO's strength is bridging the WebAssembly component model into the JavaScript ecosystem, but that bridge depends on a build pipeline with multiple moving parts. External monitoring with Vigilmon adds a simple feedback loop that catches toolchain regressions, stale build outputs, and WASI compatibility gaps before they cause mysterious failures in your JavaScript applications that consume WebAssembly components.
Sign up for a free Vigilmon account and add your first JCO transpilation heartbeat monitor in under two minutes.