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

# Base64 Encoder

> Reference implementation of a bidirectional CTP tool

# Base64 Encoder/Decoder

A bidirectional CTP tool demonstrating mode selection and conditional parameters.

## Overview

| Property           | Value            |
| ------------------ | ---------------- |
| **ID**             | `base64-encoder` |
| **Category**       | `encoders`       |
| **Execution Mode** | `client`         |
| **Method**         | `POST`           |

## Parameters

| Name      | Type       | Required | Default  | Description                     |
| --------- | ---------- | -------- | -------- | ------------------------------- |
| `input`   | `textarea` | Yes      | -        | Text or Base64 string           |
| `mode`    | `select`   | No       | `encode` | Operation mode                  |
| `urlSafe` | `boolean`  | No       | `false`  | URL-safe encoding (encode only) |

### Mode Options

| Value    | Description           |
| -------- | --------------------- |
| `encode` | Convert text → Base64 |
| `decode` | Convert Base64 → text |

### URL-Safe Mode

When enabled (encode only):

* `+` becomes `-`
* `/` becomes `_`
* Padding `=` is removed

## Example

**Encode:**

```json theme={null}
{
  "input": "Hello, World!",
  "mode": "encode"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "data": {
    "output": "SGVsbG8sIFdvcmxkIQ==",
    "mode": "encode",
    "inputLength": 13,
    "outputLength": 20
  }
}
```

**Decode:**

```json theme={null}
{
  "input": "SGVsbG8sIFdvcmxkIQ==",
  "mode": "decode"
}
```

**Output:**

```json theme={null}
{
  "success": true,
  "data": {
    "output": "Hello, World!",
    "mode": "decode",
    "inputLength": 20,
    "outputLength": 13
  }
}
```

## Implementation

```typescript theme={null}
import type { ToolDefinition, ToolFunction } from '@conveniencepro/ctp-core';

interface Base64Result {
  output: string;
  mode: 'encode' | 'decode';
  inputLength: number;
  outputLength: number;
}

export const base64EncoderDefinition: ToolDefinition = {
  id: 'base64-encoder',
  name: 'Base64 Encoder/Decoder',
  description: 'Encode text to Base64 or decode Base64 back to text.',
  category: 'encoders',
  tags: ['base64', 'encode', 'decode', 'text'],
  method: 'POST',
  parameters: [
    {
      name: 'input',
      type: 'textarea',
      label: 'Input',
      description: 'Text to encode or Base64 to decode',
      required: true,
    },
    {
      name: 'mode',
      type: 'select',
      label: 'Mode',
      description: 'Operation mode',
      required: false,
      defaultValue: 'encode',
      options: [
        { value: 'encode', label: 'Encode' },
        { value: 'decode', label: 'Decode' },
      ],
    },
    {
      name: 'urlSafe',
      type: 'boolean',
      label: 'URL Safe',
      description: 'Use URL-safe Base64 variant',
      required: false,
      defaultValue: false,
      // Only show when mode is 'encode'
      dependsOn: [{ field: 'mode', condition: 'equals', value: 'encode' }],
    },
  ],
  outputDescription: 'Encoded or decoded string',
  example: {
    input: { input: 'Hello', mode: 'encode' },
    output: { output: 'SGVsbG8=', mode: 'encode', inputLength: 5, outputLength: 8 },
  },
  executionMode: 'client',
};

function encodeBase64(str: string, urlSafe: boolean): string {
  // Convert string to UTF-8 bytes
  const bytes = new TextEncoder().encode(str);
  let binary = '';
  bytes.forEach(byte => {
    binary += String.fromCharCode(byte);
  });

  // Encode to Base64
  let result = btoa(binary);

  // Apply URL-safe transformation
  if (urlSafe) {
    result = result
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');
  }

  return result;
}

function decodeBase64(str: string): string {
  // Handle URL-safe variant
  let input = str.replace(/-/g, '+').replace(/_/g, '/');

  // Add padding if needed
  while (input.length % 4) {
    input += '=';
  }

  // Decode from Base64
  const binary = atob(input);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }

  // Convert UTF-8 bytes to string
  return new TextDecoder().decode(bytes);
}

export const base64EncoderFn: ToolFunction<Base64Result> = (params) => {
  const input = params.input as string;
  const mode = (params.mode as 'encode' | 'decode') || 'encode';
  const urlSafe = params.urlSafe === true || params.urlSafe === 'true';

  // Validation
  if (!input) {
    return {
      success: false,
      error: 'Input is required',
      errorCode: 'MISSING_REQUIRED',
    };
  }

  try {
    const output = mode === 'encode'
      ? encodeBase64(input, urlSafe)
      : decodeBase64(input);

    return {
      success: true,
      data: {
        output,
        mode,
        inputLength: input.length,
        outputLength: output.length,
      },
    };
  } catch (e) {
    return {
      success: false,
      error: mode === 'decode'
        ? `Invalid Base64: ${(e as Error).message}`
        : `Encoding failed: ${(e as Error).message}`,
      errorCode: 'INVALID_INPUT',
    };
  }
};

export default { definition: base64EncoderDefinition, fn: base64EncoderFn };
```

## Key Patterns

### Bidirectional Operations

Handle two opposite operations in one tool:

```typescript theme={null}
const output = mode === 'encode'
  ? encodeBase64(input, urlSafe)
  : decodeBase64(input);
```

### Conditional Parameters

Show `urlSafe` only when encoding:

```typescript theme={null}
{
  name: 'urlSafe',
  type: 'boolean',
  dependsOn: [{ field: 'mode', condition: 'equals', value: 'encode' }],
}
```

### UTF-8 Support

Properly handle international characters:

```typescript theme={null}
// Encode: Convert string to UTF-8 bytes first
const bytes = new TextEncoder().encode(str);

// Decode: Convert bytes back to string
return new TextDecoder().decode(bytes);
```

### URL-Safe Variant

Transform standard Base64 for URL safety:

```typescript theme={null}
if (urlSafe) {
  result = result
    .replace(/\+/g, '-')   // + → -
    .replace(/\//g, '_')   // / → _
    .replace(/=/g, '');    // Remove padding
}
```

## Error Handling

| Error Code         | Cause                               |
| ------------------ | ----------------------------------- |
| `MISSING_REQUIRED` | `input` not provided                |
| `INVALID_INPUT`    | Invalid Base64 string (decode mode) |
