npm package: @spruceid/opencred-dc-api
Description: TypeScript/Node.js wrapper for OpenCred DC API WASM binary
Author: SpruceID
Latest version: 0.2.0

# DC API WASM - Node.js/TypeScript Package

A WebAssembly-based Node.js/TypeScript package for the DC API, providing digital credential operations with OpenID4VP support and session management through wasm-bindgen generated bindings.

## Installation

```bash
npm install @spruceid/opencred-dc-api
```

## Quick Start

```typescript
import { DcApi, JsOid4VpSessionStore } from '@spruceid/opencred-dc-api';

// Prepare your PKCS8 PEM encoded private key for signing
const privateKeyPem = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----`;

// Create session stores
const oid4vpStore = JsOid4VpSessionStore.createMemoryStore();
const dcApiStore = {
  async newSession(sessionId: string, session: any) {
    // Implementation
    return { id: sessionId, secret: 'generated-secret' };
  },
  async getSession(id: string, clientSecret: string) {
    // Implementation
    return null;
  },
  async getSessionUnauthenticated(id: string) {
    // Implementation
    return null;
  },
  async updateSession(sessionId: string, session: any) {
    // Implementation
  },
  async removeSession(sessionId: string) {
    // Implementation
  }
};

// Create and initialize DC API instance
const dcApi = await DcApi.new(
  privateKeyPem,  // PKCS8 PEM encoded private key for signing
  'https://api.example.com',
  'https://api.example.com/submit',
  'https://api.example.com/reference',
  new Uint8Array([/* your cert chain */]),
  oid4vpStore,
  dcApiStore
);

// Create a session
const session = await dcApi.create_new_session();

// Make a request with the session
const request = {
  // Your DC API request data
};

const result = await dcApi.initiate_request(
  session.id,
  session.secret,
  request,
  'my-app/1.0.0'
);
console.log(result);

// Submit a response
const response = {
  // Your response data
};

const submitResult = await dcApi.submit_response(
  session.id,
  session.secret,
  response
);

// Clean up when done
dcApi.free();
```

## Building from Source

This package wraps a Rust WASM binary. To build the complete package:

1. **Build the WASM binary:**
   ```bash
   npm run build:wasm
   ```

   This runs the build script that uses wasm-bindgen to generate the JavaScript bindings.

2. **Build the TypeScript wrapper:**
   ```bash
   npm run build:ts
   ```

3. **Build everything:**
   ```bash
   npm run build
   ```

## API Reference

### DcApi Class

The main WebAssembly class generated by wasm-bindgen that provides DC API functionality.

```typescript
import { DcApi } from '@spruceid/opencred-dc-api';
```

#### Constructor

##### `static new(key: string, base_url: string, submission_endpoint: string, reference_endpoint: string, cert_chain_pem: Uint8Array, oid4vp_session_store: JsOid4VpSessionStore, js_dc_api_session_store: DcApiSessionStore): Promise<DcApi>`

Create and initialize a new DC API instance.

**Parameters:**
- `key`: PKCS8 PEM encoded private key used for signing operations
- `base_url`: Base URL for the DC API
- `submission_endpoint`: Endpoint for submitting responses
- `reference_endpoint`: Endpoint for references
- `cert_chain_pem`: Certificate chain in PEM format as Uint8Array
- `oid4vp_session_store`: OID4VP session storage implementation
- `js_dc_api_session_store`: DC API session storage implementation

```typescript
const privateKeyPem = `-----BEGIN PRIVATE KEY-----
MIGH...
-----END PRIVATE KEY-----`;

const dcApi = await DcApi.new(
  privateKeyPem,
  'https://api.example.com',
  'https://api.example.com/submit',
  'https://api.example.com/reference',
  certChainPem,
  oid4vpStore,
  dcApiStore
);
```

#### Methods

##### `create_new_session(): Promise<any>`

Create a new DC API session.

```typescript
const session = await dcApi.create_new_session();
console.log('Session:', session);
```

##### `initiate_request(session_id: string, session_secret: string, request: any, user_agent?: string | null): Promise<any>`

Initiate a DC API request with session credentials.

```typescript
const result = await dcApi.initiate_request(
  sessionId,
  sessionSecret,
  request,
  'my-app/1.0.0'
);
```

##### `submit_response(session_id: string, session_secret: string, response: any): Promise<any>`

Submit a response for a DC API session.

```typescript
const result = await dcApi.submit_response(
  sessionId,
  sessionSecret,
  response
);
```

##### `free(): void`

Free the WebAssembly memory used by this instance.

```typescript
dcApi.free();
```

### JsOid4VpSessionStore Class

WebAssembly-compatible session store that delegates to JavaScript storage implementations.

#### Constructor

```typescript
constructor(store: Oid4VpSessionStore)
```

Creates a new WebAssembly session store with JavaScript storage implementation.

```typescript
class MySessionStore {
  async initiate(session) {
    // Store session in your preferred storage
    localStorage.setItem(`session_${session.uuid}`, JSON.stringify(session));
  }

  async updateStatus(uuid, status) {
    // Update session status
    const session = JSON.parse(localStorage.getItem(`session_${uuid}`));
    session.status = status;
    localStorage.setItem(`session_${uuid}`, JSON.stringify(session));
  }

  async getSession(uuid) {
    // Get session from storage
    const sessionData = localStorage.getItem(`session_${uuid}`);
    if (!sessionData) throw new Error('Session not found');
    return JSON.parse(sessionData);
  }

  async removeSession(uuid) {
    // Remove session from storage
    localStorage.removeItem(`session_${uuid}`);
  }
}

const sessionStore = new JsOid4VpSessionStore(new MySessionStore());
```

#### Static Methods

##### `createMemoryStore(): JsOid4VpSessionStore`

Create a simple in-memory session store for testing purposes.

```typescript
const sessionStore = JsOid4VpSessionStore.createMemoryStore();
```

##### `generateSessionUuid(): string`

Generate a new UUID for session identification.

```typescript
const uuid = JsOid4VpSessionStore.generateSessionUuid();
```

##### `parseUuid(uuid_str: string): string`

Parse and validate a UUID string.

```typescript
const parsed = JsOid4VpSessionStore.parseUuid('550e8400-e29b-41d4-a716-446655440000');
```

##### Status Creation Helpers

```typescript
// Create different status types
const sentRequestByRef = JsOid4VpSessionStore.createStatusSentRequestByReference();
const sentRequest = JsOid4VpSessionStore.createStatusSentRequest();
const receivedResponse = JsOid4VpSessionStore.createStatusReceivedResponse();
const success = JsOid4VpSessionStore.createStatusCompleteSuccess({ data: 'result' });
const failure = JsOid4VpSessionStore.createStatusCompleteFailure('reason');
const error = JsOid4VpSessionStore.createStatusCompleteError('cause');
```

### Session Storage Interfaces

#### OID4VP Session Store Interface

```typescript
interface Oid4VpSessionStore {
  initiate(session: any): Promise<void>;
  updateStatus(uuid: string, status: any): Promise<void>;
  getSession(uuid: string): Promise<any>;
  removeSession(uuid: string): Promise<void>;
}
```

#### DC API Session Store Interface

```typescript
interface DcApiSessionStore {
  newSession(sessionId: string, session: any): Promise<SessionCreationResponse>;
  getSession(id: string, clientSecret: string): Promise<any | null>;
  getSessionUnauthenticated(id: string): Promise<any | null>;
  updateSession(sessionId: string, session: any): Promise<void>;
  removeSession(sessionId: string): Promise<void>;
}
```

### JsDcApiSessionDriver Class

A supporting class for DC API session management (generated by wasm-bindgen).

```typescript
class JsDcApiSessionDriver {
  free(): void;
}
```

This class is primarily used internally by the DC API implementation.

## Error Handling

The WASM functions may throw errors that are propagated as JavaScript exceptions:

```typescript
try {
  const result = await dcApi.initiate_request(
    sessionId,
    sessionSecret,
    request
  );
} catch (error) {
  console.error('DC API Error:', error);
}
```



## CI/CD & Releases

This package uses automated CI/CD workflows for testing, validation, and publishing.

### Automated Testing

Every push and pull request triggers comprehensive testing:

- **WASM Build Validation**: Ensures the Rust/WASM build succeeds
- **Package Structure**: Validates correct file locations and package.json structure
- **Integration Tests**: Tests that generated bindings load correctly
- **Security Audits**: npm and Cargo dependency vulnerability scanning
- **Code Quality**: Rust formatting and Clippy linting

### Automated Publishing

Releases are triggered by pushing git tags in the format `npm/v[version]`:

```bash
# Create and push a release
npm run release:patch  # Creates tag npm/v1.0.1
git push origin npm/v1.0.1
```

The release workflow will:
1. Validate the package version matches the tag
2. Set up Rust toolchain and wasm-bindgen
3. Build the WASM binary and JS bindings
4. Publish to npm with public access

### Release Process

For detailed release instructions, see [RELEASE.md](./RELEASE.md).

Quick release workflow:
```bash
# Increment version and create tag
npm run release:minor  # or release:patch, release:major

# Push the tag to trigger publishing
git push origin npm/v$(node -p "require('./package.json').version")
```

### Manual Development

For local development without CI:

#### Prerequisites

- Node.js 16+
- Rust toolchain with `wasm32-unknown-unknown` target
- wasm-bindgen-cli

#### Setup

1. Clone the repository
2. Install dependencies:
   ```bash
   npm install
   ```

3. Build the project:
   ```bash
   npm run build
   ```

#### Scripts

- `npm run build` - Build only WASM (for publishing)
- `npm run build:dev` - Build WASM + TypeScript (for development)
- `npm run build:wasm` - Build only the WASM binary
- `npm run build:ts` - Build only TypeScript wrapper
- `npm run clean` - Clean build artifacts
- `npm run prerelease` - Validate package before release

#### Testing

Run the test suite:

```bash
npm test
```

Tests include:
- Unit tests for all API classes
- Session management tests
- WASM loader tests
- Error handling tests
- Storage implementation tests

## Package Structure

```
npm-package/
├── dist/                       # Built output (published to npm)
│   ├── dc_api_wasm.js         # wasm-bindgen generated JavaScript
│   ├── dc_api_wasm.d.ts       # wasm-bindgen generated TypeScript definitions
│   ├── dc_api_wasm_bg.wasm    # WebAssembly binary
│   └── dc_api_wasm_bg.wasm.d.ts  # WebAssembly TypeScript definitions
├── src/                        # TypeScript source files (if any)
├── tests/                      # Test files (excluded from npm)
├── build-wasm.sh              # WASM build script (excluded from npm)
├── package.json               # Package configuration
├── tsconfig.json              # TypeScript configuration (excluded from npm)
├── README.md                  # Package documentation
└── RELEASE.md                 # Release process documentation (excluded from npm)
```

## Example: Complete Flow

```typescript
import { DcApi, JsOid4VpSessionStore } from '@spruceid/opencred-dc-api';

async function main() {
  // Create session stores
  const oid4vpStore = JsOid4VpSessionStore.createMemoryStore();

  const dcApiStore = {
    async newSession(sessionId: string, session: any) {
      console.log('Creating new session:', sessionId);
      return { id: sessionId, secret: 'generated-secret' };
    },
    async getSession(id: string, clientSecret: string) {
      console.log('Getting session:', id);
      return null;
    },
    async getSessionUnauthenticated(id: string) {
      return null;
    },
    async updateSession(sessionId: string, session: any) {
      console.log('Updating session:', sessionId);
    },
    async removeSession(sessionId: string) {
      console.log('Removing session:', sessionId);
    }
  };

  // Initialize the API
  const privateKeyPem = process.env.PRIVATE_KEY_PEM!; // PKCS8 PEM private key
  const dcApi = await DcApi.new(
    privateKeyPem,
    process.env.DC_API_URL!,
    process.env.DC_API_SUBMIT_URL!,
    process.env.DC_API_REFERENCE_URL!,
    new Uint8Array(Buffer.from(process.env.CERT_CHAIN!, 'base64')),
    oid4vpStore,
    dcApiStore
  );

  try {
    // Create a session
    const session = await dcApi.create_new_session();
    console.log('Created session:', session);

    // Prepare a request
    const request = {
      type: 'credential_request',
      data: {
        // Request data
      }
    };

    // Make the request
    const result = await dcApi.initiate_request(
      session.id,
      session.secret,
      request,
      'my-app/1.0.0'
    );

    console.log('Request result:', result);

    // Submit a response if needed
    const response = {
      // Response data
    };

    const submitResult = await dcApi.submit_response(
      session.id,
      session.secret,
      response
    );

    console.log('Submit result:', submitResult);
  } catch (error) {
    console.error('Error:', error);
  } finally {
    // Clean up
    dcApi.free();
  }
}

main().catch(console.error);
```

## Requirements

- **Node.js**: >= 16.0.0
- **WASM Support**: Node.js has built-in WebAssembly support

## License

MIT

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Run `npm test`
6. Submit a pull request

## Troubleshooting

### WASM Module Not Found

If you get errors about missing WASM files:

```bash
npm run build:wasm
```

Make sure wasm-bindgen-cli is installed:

```bash
cargo install wasm-bindgen-cli
```

### Build Failures

If the WASM build fails, ensure you have the correct environment:

```bash
# Ensure the correct target is available
rustup target add wasm32-unknown-unknown

# Install wasm-bindgen-cli if not already installed
cargo install wasm-bindgen-cli
```

### Runtime Errors

If you encounter runtime errors:

1. Ensure `dcApi.init(config)` was called with valid configuration before using other methods
2. Check that the WASM binary is properly included in your bundle
3. Verify Node.js version compatibility (>= 16.0.0)
4. Make sure session credentials are valid when making requests

### Session Management Issues

- Sessions have limited lifetimes; create new sessions as needed
- Store session credentials securely
- Don't reuse sessions across different security contexts

## Support

For issues and questions:
- GitHub Issues: [Create an issue](https://github.com/spruceid/dc-api/issues)
- Documentation: Check the inline TypeScript documentation
- Examples: See the `tests/` directory for usage examples