> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainloop.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK for WASM Policies

> Complete guide to writing Chainloop policies in JavaScript/TypeScript

<Warning>
  **Experimental Feature - WASM Policy Engine**

  This SDK is specifically for writing **WASM policies** using Chainloop's experimental WASM policy engine. The WASM policy engine is **NOT the default policy engine** in Chainloop.

  * **Default engine**: Rego-based (recommended for most users)
  * **WASM engine**: Experimental alternative for Go/JavaScript policies
  * **Status**: Experimental preview - APIs may change in future releases

  For the default Rego-based policy engine, see [Writing Custom Policies](/guides/custom-policies).
</Warning>

## Prerequisites

The Chainloop JavaScript SDK for WASM policies is built on top of the [Extism JavaScript PDK](https://github.com/extism/js-pdk), which enables JavaScript code to run inside WebAssembly with QuickJS.

**Required tools:**

<AccordionGroup>
  <Accordion title="Node.js (16 or later)">
    For package management and building.

    ```bash theme={"dark"}
    # Verify installation
    node --version  # Should be v16+
    ```
  </Accordion>

  <Accordion title="extism-js compiler">
    Compiles JavaScript to WebAssembly.

    ```bash theme={"dark"}
    # Install globally
    npm install -g @extism/js-pdk

    # Verify installation
    extism-js --version
    ```
  </Accordion>
</AccordionGroup>

**Dependencies:**

* `@chainloop-dev/policy-sdk` - Chainloop WASM Policy SDK (npm package)
* `@extism/js-pdk` - Extism JavaScript PDK (auto-installed)
* `esbuild` - JavaScript bundler (dev dependency)

The Extism JS PDK handles JavaScript-to-WASM compilation using QuickJS, while Chainloop's WASM Policy SDK provides policy-specific APIs for material validation.

## Project Setup

### Install Dependencies

```bash theme={"dark"}
npm init -y
npm install @chainloop-dev/policy-sdk
npm install --save-dev esbuild
```

### Configure Build

**esbuild.js:**

```javascript theme={"dark"}
const esbuild = require('esbuild');

esbuild.build({
  entryPoints: ['policy.js'],
  outdir: 'dist',
  bundle: true,
  format: 'cjs',
  target: ['es2020'],
  platform: 'node',
}).catch(() => process.exit(1));
```

**package.json:**

```json theme={"dark"}
{
  "name": "my-policy",
  "scripts": {
    "build": "node esbuild.js && extism-js dist/policy.js -i policy.d.ts -o policy.wasm"
  },
  "dependencies": {
    "@chainloop-dev/policy-sdk": "^0.1.0"
  },
  "devDependencies": {
    "esbuild": "^0.19.0"
  }
}
```

**policy.d.ts:**

```typescript theme={"dark"}
declare module "main" {
  export function Execute(): I32;
}
```

**policy.yaml:**

```yaml theme={"dark"}
apiVersion: workflowcontract.chainloop.dev/v1
kind: Policy
metadata:
  name: my-js-policy
  description: My custom JavaScript policy
spec:
  policies:
    - kind: EVIDENCE
      path: policy.wasm
```

## Complete Example

```javascript theme={"dark"}
const {
  getMaterialJSON,
  success,
  skip,
  outputResult,
  logInfo,
  logError,
  run
} = require('@chainloop-dev/policy-sdk');

function Execute() {
  return run(() => {
    // Parse the SBOM material
    const sbom = getMaterialJSON();

    if (!sbom.components) {
      outputResult(skip("Not a valid CycloneDX SBOM"));
      return;
    }

    logInfo(`Validating SBOM with ${sbom.components.length} components`);

    const result = success();
    const approved = ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC"];

    // Validate each component
    sbom.components.forEach((component, index) => {
      // Check version
      if (!component.version || component.version === "") {
        result.addViolation(`component '${component.name}' missing version`);
      }

      // Check licenses
      if (!component.licenses || component.licenses.length === 0) {
        result.addViolation(`component '${component.name}' missing license`);
        return;
      }

      // Validate licenses
      component.licenses.forEach(license => {
        const licenseID = license.id || license.name;
        if (!approved.includes(licenseID)) {
          result.addViolation(
            `component '${component.name}' has unapproved license: ${licenseID}`
          );
        }
      });
    });

    if (result.hasViolations()) {
      logError(`SBOM validation failed with ${result.violations.length} violations`);
    } else {
      logInfo("SBOM validation passed");
    }

    outputResult(result);
  });
}

module.exports = { Execute };
```

## API Quick Reference

The JavaScript SDK provides functions for:

* **Execution**: `run()` - Entry point wrapper
* **Material Extraction**: `getMaterialJSON()`, `getMaterialString()`, `getMaterialBytes()`
* **Arguments**: `getArgs()`, `getArgString()`, `getArgStringDefault()`
* **Results**: `success()`, `fail()`, `skip()`, `outputResult()`, `addViolation()`, `hasViolations()`
* **Logging**: `logInfo()`, `logDebug()`, `logWarn()`, `logError()`
* **HTTP Requests**: `httpGet()`, `httpGetJSON()`, `httpPost()`, `httpPostJSON()`
* **Artifact Discovery**: `discover()`, `discoverByDigest()`

## Building

```bash theme={"dark"}
npm run build
```

This runs:

1. **esbuild** - Bundles policy.js and SDK
2. **extism-js** - Compiles bundle to WASM

**Output:**

* `dist/policy.js` - Bundled JavaScript (\~7KB)
* `policy.wasm` - Compiled WASM (\~2.1MB, includes QuickJS runtime)

## Testing

**test.sh:**

```bash theme={"dark"}
#!/bin/bash

npm run build

echo "Testing valid SBOM..."
chainloop policy devel eval \
  --policy policy.yaml \
  --material test-data/valid-sbom.json \
  --kind SBOM_CYCLONEDX_JSON

echo "Testing invalid SBOM..."
chainloop policy devel eval \
  --policy policy.yaml \
  --material test-data/invalid-sbom.json \
  --kind SBOM_CYCLONEDX_JSON
```

## JavaScript Compatibility

**Supported:**

* ✅ ES2020 JavaScript features
* ✅ JSON parsing and manipulation
* ✅ String operations and regex
* ✅ Arrays, objects, and basic types
* ✅ Synchronous operations

**Not Supported:**

* ❌ async/await (no Promise support)
* ❌ Node.js built-ins (fs, path, http)
* ❌ ES modules (use CommonJS)
* ❌ Browser APIs (fetch, localStorage)
* ❌ setTimeout/setInterval
* ❌ Symbols and WeakMaps

**Recommended:**

```javascript theme={"dark"}
// ✅ Good: Simple objects
const component = {
  name: "lodash",
  version: "4.17.21"
};

// ✅ Good: Synchronous operations
components.forEach(comp => {
  if (!comp.name) {
    result.addViolation("Missing name");
  }
});

// ❌ Avoid: Async operations
// async function validate() {  // Not supported
//   const data = await fetch(...);
// }
```

## Best Practices

1. **Use simple data structures** - Plain objects and arrays
2. **Validate early** - Check material format first
3. **Clear violation messages** - Include specific details
4. **Use skip appropriately** - Don't fail for wrong material types
5. **Test with real data** - Use actual artifacts
6. **Log progress** - Aid debugging
7. **Handle errors** - Use try-catch for parsing

## Next Steps

<CardGroup cols={2}>
  <Card title="Examples" icon="code" href="/guides/wasm-policies/examples">
    Explore complete policy examples
  </Card>

  <Card title="Go SDK" icon="golang" href="/guides/wasm-policies/go-sdk">
    Compare with the Go SDK
  </Card>
</CardGroup>
