From 730205f4071aef54f9273d3555ed95c9e72caef7 Mon Sep 17 00:00:00 2001 From: jonaslagoni Date: Sun, 18 Jan 2026 14:24:51 +0100 Subject: [PATCH] add mcp --- mcp-server/.gitignore | 32 + mcp-server/README.md | 142 + mcp-server/app/api/mcp/route.ts | 509 ++ mcp-server/app/layout.tsx | 20 + mcp-server/app/not-found.tsx | 11 + mcp-server/app/page.tsx | 162 + mcp-server/eslint.config.mjs | 21 + mcp-server/lib/data/examples.ts | 500 ++ mcp-server/lib/data/generators.ts | 291 + mcp-server/lib/resources/bundled-docs.ts | 181 + mcp-server/lib/tools/config-tools.ts | 439 ++ mcp-server/lib/tools/integration-tools.ts | 231 + mcp-server/next.config.js | 13 + mcp-server/package-lock.json | 6810 +++++++++++++++++++++ mcp-server/package.json | 31 + mcp-server/scripts/bundle-docs.ts | 174 + mcp-server/tsconfig.json | 27 + mcp-server/vercel.json | 7 + 18 files changed, 9601 insertions(+) create mode 100644 mcp-server/.gitignore create mode 100644 mcp-server/README.md create mode 100644 mcp-server/app/api/mcp/route.ts create mode 100644 mcp-server/app/layout.tsx create mode 100644 mcp-server/app/not-found.tsx create mode 100644 mcp-server/app/page.tsx create mode 100644 mcp-server/eslint.config.mjs create mode 100644 mcp-server/lib/data/examples.ts create mode 100644 mcp-server/lib/data/generators.ts create mode 100644 mcp-server/lib/resources/bundled-docs.ts create mode 100644 mcp-server/lib/tools/config-tools.ts create mode 100644 mcp-server/lib/tools/integration-tools.ts create mode 100644 mcp-server/next.config.js create mode 100644 mcp-server/package-lock.json create mode 100644 mcp-server/package.json create mode 100644 mcp-server/scripts/bundle-docs.ts create mode 100644 mcp-server/tsconfig.json create mode 100644 mcp-server/vercel.json diff --git a/mcp-server/.gitignore b/mcp-server/.gitignore new file mode 100644 index 0000000..2c30316 --- /dev/null +++ b/mcp-server/.gitignore @@ -0,0 +1,32 @@ +# Dependencies +node_modules/ + +# Next.js build output +.next/ +out/ + +# Production build +dist/ + +# Local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Debug logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Vercel +.vercel + +# TypeScript +*.tsbuildinfo +next-env.d.ts + +# OS files +.DS_Store +Thumbs.db diff --git a/mcp-server/README.md b/mcp-server/README.md new file mode 100644 index 0000000..8e8b68d --- /dev/null +++ b/mcp-server/README.md @@ -0,0 +1,142 @@ +# The Codegen Project - MCP Server + +A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for [The Codegen Project](https://github.com/the-codegen-project/cli). This server helps AI assistants create and manage codegen configurations and integrate generated code into projects. + +## Features + +### Tools + +**Configuration Tools:** +- `create_config` - Create a new codegen configuration file +- `add_generator` - Generate configuration for adding a single generator +- `list_generators` - List available generators and their options +- `validate_config` - Validate a configuration object + +**Integration Tools:** +- `get_usage_example` - Get code examples for using generated code +- `get_imports` - Generate TypeScript import statements + +### Resources + +Documentation from The Codegen Project is exposed as MCP resources: +- `codegen://docs` - List all available documentation +- `codegen://docs/configurations` - Configuration guide +- `codegen://docs/generators/{name}` - Generator-specific documentation +- `codegen://docs/protocols/{name}` - Protocol documentation +- `codegen://docs/inputs/{name}` - Input type documentation + +### Prompts + +- `codegen-guidelines` - Guidelines for AI assistants working with generated code (e.g., never edit generated files, how to modify output, regeneration workflow) + +## Development + +### Prerequisites + +- Node.js 18+ +- npm or yarn + +### Setup + +```bash +# Install dependencies +npm install + +# Bundle documentation from ../docs/ +npm run bundle-docs + +# Start development server +npm run dev +``` + +The server will be available at `http://localhost:3000`. + +### Testing with MCP Inspector + +1. Install the MCP inspector: + ```bash + npx @anthropic-ai/mcp-inspector + ``` + +2. Open `http://127.0.0.1:6274` + +3. Connect to your local server: + - Select "Streamable HTTP" + - URL: `http://localhost:3000/api/mcp` + - Click "Connect" + +4. Test tools and resources in the inspector interface. + +## Deployment + +### Deploy to Vercel + +1. Push this directory to a Git repository + +2. Import to Vercel: + ```bash + vercel + ``` + +3. The MCP server will be available at your Vercel URL + `/api/mcp` + +### Connecting from AI Assistants + +**Cursor:** + +Add to `.cursor/mcp.json`: +```json +{ + "mcpServers": { + "codegen": { + "url": "https://your-vercel-url.vercel.app/api/mcp" + } + } +} +``` + +**Claude Code:** + +Add to `~/.claude/mcp.json`: +```json +{ + "mcpServers": { + "codegen": { + "url": "https://your-vercel-url.vercel.app/api/mcp" + } + } +} +``` + +## Scripts + +- `npm run dev` - Start development server +- `npm run build` - Build for production (includes doc bundling) +- `npm run start` - Start production server +- `npm run bundle-docs` - Bundle documentation from ../docs/ + +## Project Structure + +``` +mcp-server/ +├── app/ +│ ├── api/mcp/route.ts # MCP handler endpoint +│ ├── layout.tsx # Next.js layout +│ └── page.tsx # Info landing page +├── lib/ +│ ├── data/ +│ │ ├── generators.ts # Generator schemas and options +│ │ └── examples.ts # Usage examples +│ ├── resources/ +│ │ └── bundled-docs.ts # Bundled documentation (generated) +│ └── tools/ +│ ├── config-tools.ts # Configuration tools +│ └── integration-tools.ts # Integration tools +├── scripts/ +│ └── bundle-docs.ts # Documentation bundler +└── package.json +``` + +## License + +Same as The Codegen Project - see the main repository for details. diff --git a/mcp-server/app/api/mcp/route.ts b/mcp-server/app/api/mcp/route.ts new file mode 100644 index 0000000..d4c451c --- /dev/null +++ b/mcp-server/app/api/mcp/route.ts @@ -0,0 +1,509 @@ +/** + * MCP Server route handler for The Codegen Project. + * Exposes tools and resources to help AI assistants work with codegen. + */ + +import { createMcpHandler } from 'mcp-handler'; +import { z } from 'zod'; +import { + createConfig, + addGenerator, + listGenerators, + validateConfig, +} from '@/lib/tools/config-tools'; +import { + getUsageExample, + getImports, +} from '@/lib/tools/integration-tools'; +import { getDocKeys, getDoc } from '@/lib/resources/bundled-docs'; + +const handler = createMcpHandler( + (server) => { + // ========================================== + // CONFIGURATION TOOLS + // ========================================== + + server.tool( + 'create_config', + 'Create a new codegen configuration file. Generates a complete configuration based on input type, generators, and optional protocols.', + { + inputType: z + .enum(['asyncapi', 'openapi', 'jsonschema']) + .describe('The type of input specification'), + inputPath: z.string().describe('Path to the input specification file'), + generators: z + .array( + z.enum([ + 'payloads', + 'parameters', + 'headers', + 'types', + 'channels', + 'client', + 'models', + 'custom', + ]) + ) + .describe('Generator presets to include'), + configFormat: z + .enum(['mjs', 'ts', 'json', 'yaml']) + .optional() + .describe('Output format for the configuration file (default: mjs)'), + protocols: z + .array( + z.enum([ + 'nats', + 'kafka', + 'mqtt', + 'amqp', + 'websocket', + 'http_client', + 'event_source', + ]) + ) + .optional() + .describe('Protocols to generate for channels/client generators'), + }, + async (input) => { + const result = createConfig(input as Parameters[0]); + const lang = result.filename.endsWith('.json') ? 'json' : result.filename.endsWith('.yaml') ? 'yaml' : result.filename.endsWith('.ts') ? 'typescript' : 'javascript'; + + const warnings = result.warnings.length > 0 + ? `**Warnings:** +${result.warnings.map((w) => `- ${w}`).join('\n')}` + : ''; + + const text = `# Generated Configuration + +**Filename:** \`${result.filename}\` + +\`\`\`${lang} +${result.content} +\`\`\` +${warnings}`; + + return { content: [{ type: 'text', text }] }; + } + ); + + server.tool( + 'add_generator', + 'Generate a configuration object for a single generator to add to an existing config.', + { + preset: z + .enum([ + 'payloads', + 'parameters', + 'headers', + 'types', + 'channels', + 'client', + 'models', + 'custom', + ]) + .describe('Generator preset to add'), + outputPath: z.string().optional().describe('Output path for generated code'), + protocols: z + .array( + z.enum([ + 'nats', + 'kafka', + 'mqtt', + 'amqp', + 'websocket', + 'http_client', + 'event_source', + ]) + ) + .optional() + .describe('Protocols for channels/client generators'), + serializationType: z + .enum(['json']) + .optional() + .describe('Serialization format'), + includeValidation: z + .boolean() + .optional() + .describe('Include AJV validation'), + }, + async (input) => { + const result = addGenerator(input as Parameters[0]); + + const notes = result.notes.length > 0 + ? `**Notes:** +${result.notes.map((n) => `- ${n}`).join('\n')}` + : ''; + + const text = `# Generator Configuration + +Add this to your \`generators\` array: + +\`\`\`javascript +${JSON.stringify(result.config, null, 2)} +\`\`\` +${notes}`; + + return { content: [{ type: 'text', text }] }; + } + ); + + server.tool( + 'list_generators', + 'List all available generators and their configuration options, optionally filtered by input type.', + { + inputType: z + .enum(['asyncapi', 'openapi', 'jsonschema']) + .optional() + .describe('Filter generators by input type'), + }, + async (input) => { + const result = listGenerators(input as Parameters[0]); + + const filterLine = input.inputType + ? `Filtered by input type: **${input.inputType}** + +` + : ''; + + const generatorSections = result.generators.map((gen) => { + const dependencies = gen.dependencies?.length + ? `- **Dependencies:** ${gen.dependencies.join(', ')}` + : ''; + + const options = gen.options.length > 0 + ? `**Options:** +${gen.options.map((opt) => { + let line = `- \`${opt.name}\` (${opt.type}): ${opt.description}`; + if (opt.default !== undefined) { + line += ` [default: ${JSON.stringify(opt.default)}]`; + } + if (opt.required) { + line += ' *required*'; + } + return line; +}).join('\n')}` + : ''; + + return `## ${gen.preset} + +${gen.description} + +- **Supported inputs:** ${gen.supportedInputs.join(', ')} +- **Default output:** \`${gen.defaultOutputPath}\` +${dependencies}${options}`; + }).join('\n\n'); + + const protocols = result.protocols + .map((proto) => `- **${proto.name}:** ${proto.description}`) + .join('\n'); + + const text = `# Available Generators + +${filterLine}${generatorSections} + +## Supported Protocols + +${protocols}`; + + return { content: [{ type: 'text', text }] }; + } + ); + + server.tool( + 'validate_config', + 'Validate a codegen configuration object and report any errors or warnings.', + { + config: z.record(z.unknown()).describe('Configuration object to validate'), + }, + async (input) => { + const result = validateConfig(input as Parameters[0]); + + const errors = result.errors.length > 0 + ? `**Errors:** +${result.errors.map((e) => `- ${e}`).join('\n')} + +` + : ''; + + const warnings = result.warnings.length > 0 + ? `**Warnings:** +${result.warnings.map((w) => `- ${w}`).join('\n')}` + : ''; + + const success = result.valid && result.warnings.length === 0 + ? 'No issues found. Configuration looks good!' + : ''; + + const text = `# Configuration Validation + +**Status:** ${result.valid ? 'Valid' : 'Invalid'} + +${errors}${warnings}${success}`; + + return { content: [{ type: 'text', text }] }; + } + ); + + // ========================================== + // INTEGRATION TOOLS + // ========================================== + + server.tool( + 'get_usage_example', + 'Get code examples showing how to use generated code for a specific generator type and optional protocol.', + { + generatorType: z + .enum([ + 'payloads', + 'parameters', + 'headers', + 'types', + 'channels', + 'client', + 'models', + 'custom', + ]) + .describe('Type of generator to get examples for'), + protocol: z + .enum([ + 'nats', + 'kafka', + 'mqtt', + 'amqp', + 'websocket', + 'http_client', + 'event_source', + ]) + .optional() + .describe('Protocol for protocol-specific examples'), + }, + async (input) => { + const result = getUsageExample(input as Parameters[0]); + + const examplesContent = result.examples.length === 0 + ? 'No examples available for this generator type.' + : result.examples.map((example) => { + const deps = example.dependencies?.length + ? `**Required packages:** ${example.dependencies.map((d) => `\`${d}\``).join(', ')}` + : ''; + + return `## ${example.title} + +${example.description} + +\`\`\`typescript +${example.code} +\`\`\` + +${deps}`; + }).join(''); + + const notes = result.notes.length > 0 + ? `**Notes:** +${result.notes.map((n) => `- ${n}`).join('\n')}` + : ''; + + const text = `# Usage Examples: ${input.generatorType} + +${examplesContent}${notes}`; + + return { content: [{ type: 'text', text }] }; + } + ); + + server.tool( + 'get_imports', + 'Generate TypeScript import statements for generated code.', + { + generatorType: z + .enum([ + 'payloads', + 'parameters', + 'headers', + 'types', + 'channels', + 'client', + 'models', + ]) + .describe('Type of generator'), + outputPath: z.string().describe('Path where the generated code is located'), + items: z + .array(z.string()) + .optional() + .describe('Specific items to import (class names, function names)'), + protocol: z + .enum([ + 'nats', + 'kafka', + 'mqtt', + 'amqp', + 'websocket', + 'http_client', + 'event_source', + ]) + .optional() + .describe('Protocol for channels imports'), + }, + async (input) => { + const result = getImports(input as Parameters[0]); + + const notes = result.notes.length > 0 + ? `**Notes:** +${result.notes.map((n) => `- ${n}`).join('\n')}` + : ''; + + const text = `# Import Statements + +\`\`\`typescript +${result.imports.join('\n')} +\`\`\` +${notes}`; + + return { content: [{ type: 'text', text }] }; + } + ); + + // ========================================== + // DOCUMENTATION RESOURCES + // ========================================== + + // List all available documentation + server.resource( + 'codegen://docs', + 'List of all available documentation topics', + async () => { + const keys = getDocKeys(); + + // Group by category + const categories: Record = { + 'Getting Started': [], + Generators: [], + Protocols: [], + Inputs: [], + Other: [], + }; + + for (const key of keys) { + if (key.startsWith('getting-started')) { + categories['Getting Started'].push(key); + } else if (key.startsWith('generators')) { + categories['Generators'].push(key); + } else if (key.startsWith('protocols')) { + categories['Protocols'].push(key); + } else if (key.startsWith('inputs')) { + categories['Inputs'].push(key); + } else { + categories['Other'].push(key); + } + } + + const categorySections = Object.entries(categories) + .filter(([, docKeys]) => docKeys.length > 0) + .map(([category, docKeys]) => { + const items = docKeys + .sort() + .map((key) => { + const doc = getDoc(key); + return `- \`codegen://docs/${key}\` - ${doc?.title || key}`; + }) + .join('\n'); + + return `## ${category} + +${items}`; + }) + .join('\n\n'); + + const text = `# The Codegen Project Documentation + +Available documentation topics: + +${categorySections}`; + + return { + contents: [{ uri: 'codegen://docs', text, mimeType: 'text/markdown' }], + }; + } + ); + + // Register individual documentation resources + for (const key of getDocKeys()) { + const doc = getDoc(key); + if (doc) { + server.resource( + `codegen://docs/${key}`, + doc.title, + async () => ({ + contents: [ + { + uri: `codegen://docs/${key}`, + text: doc.content, + mimeType: 'text/markdown', + }, + ], + }) + ); + } + } + + // ========================================== + // PROMPTS + // ========================================== + + server.prompt( + 'codegen-guidelines', + 'Guidelines for working with The Codegen Project and generated code', + () => ({ + messages: [ + { + role: 'user', + content: { + type: 'text', + text: `# The Codegen Project - Guidelines for AI Assistants + +When working with projects that use The Codegen Project, follow these guidelines: + +## Never Edit Generated Files + +Generated code should NEVER be manually edited. Any manual changes will be overwritten the next time code generation runs. Generated files are typically located in output directories specified in the codegen configuration file (e.g., \`codegen.config.mjs\`). + +## How to Modify Generated Output + +To change what code is generated, you have two options: + +1. **Modify the input specification** - Update the AsyncAPI, OpenAPI, or JSON Schema document that defines the API/schema. This changes the source of truth. + +2. **Modify the codegen configuration** - Update the \`codegen.config.mjs\` (or equivalent) file to change generator options, add/remove generators, change output paths, or adjust protocols. + +## Regeneration Workflow + +After making changes to either the input specification or configuration: + +1. Run \`npx @the-codegen-project/cli generate\` to regenerate code +2. Or use watch mode during development: \`npx @the-codegen-project/cli generate --watch\` + +## Identifying Generated Files + +Check the project's codegen configuration file to find: +- \`inputPath\` - The source specification file +- \`generators[].outputPath\` - Where each generator writes its output + +## Best Practices + +- Keep the input specification as the single source of truth +- Commit both the specification and generated code to version control +- Use the \`custom\` generator preset if you need project-specific code generation +- Run generation as part of CI/CD to catch spec/code drift`, + }, + }, + ], + }) + ); + }, + {}, + { + basePath: '/api', + } +); + +export { handler as GET, handler as POST, handler as DELETE }; diff --git a/mcp-server/app/layout.tsx b/mcp-server/app/layout.tsx new file mode 100644 index 0000000..3713f03 --- /dev/null +++ b/mcp-server/app/layout.tsx @@ -0,0 +1,20 @@ +import type { Metadata } from 'next'; +import type { ReactNode } from 'react'; + +export const metadata: Metadata = { + title: 'The Codegen Project - MCP Server', + description: + 'Model Context Protocol server for The Codegen Project. Helps AI assistants create configurations and integrate generated code.', +}; + +export default function RootLayout({ + children, +}: { + children: ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/mcp-server/app/not-found.tsx b/mcp-server/app/not-found.tsx new file mode 100644 index 0000000..a7d9081 --- /dev/null +++ b/mcp-server/app/not-found.tsx @@ -0,0 +1,11 @@ +export default function NotFound() { + return ( +
+

404 - Not Found

+

The page you are looking for does not exist.

+

+ Return to Home +

+
+ ); +} diff --git a/mcp-server/app/page.tsx b/mcp-server/app/page.tsx new file mode 100644 index 0000000..69c1540 --- /dev/null +++ b/mcp-server/app/page.tsx @@ -0,0 +1,162 @@ +export default function Home() { + return ( +
+

The Codegen Project - MCP Server

+ +

+ This is a{' '} + + Model Context Protocol (MCP) + {' '} + server for{' '} + + The Codegen Project + + . +

+ +

Available Tools

+ +

Configuration Tools

+
    +
  • + create_config - Create a new codegen configuration + file +
  • +
  • + add_generator - Generate configuration for a single + generator +
  • +
  • + list_generators - List available generators and their + options +
  • +
  • + validate_config - Validate a configuration object +
  • +
+ +

Integration Tools

+
    +
  • + get_usage_example - Get code examples for generated + code +
  • +
  • + get_imports - Generate import statements +
  • +
+ +

Resources

+

+ Documentation from The Codegen Project is available as MCP resources: +

+
    +
  • + codegen://docs - List all documentation +
  • +
  • + codegen://docs/configurations - Configuration guide +
  • +
  • + codegen://docs/generators/* - Generator documentation +
  • +
  • + codegen://docs/protocols/* - Protocol documentation +
  • +
  • + codegen://docs/inputs/* - Input type documentation +
  • +
+ +

Connecting

+ +

Cursor

+

+ Add to your .cursor/mcp.json: +

+
+        {JSON.stringify(
+          {
+            mcpServers: {
+              codegen: {
+                url: 'https://YOUR_VERCEL_URL/api/mcp',
+              },
+            },
+          },
+          null,
+          2
+        )}
+      
+ +

Claude Code

+

+ Add to your ~/.claude/mcp.json: +

+
+        {JSON.stringify(
+          {
+            mcpServers: {
+              codegen: {
+                url: 'https://YOUR_VERCEL_URL/api/mcp',
+              },
+            },
+          },
+          null,
+          2
+        )}
+      
+ +

Links

+ +
+ ); +} diff --git a/mcp-server/eslint.config.mjs b/mcp-server/eslint.config.mjs new file mode 100644 index 0000000..dec72e8 --- /dev/null +++ b/mcp-server/eslint.config.mjs @@ -0,0 +1,21 @@ +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { FlatCompat } from '@eslint/eslintrc'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends('next/core-web-vitals'), + { + rules: { + 'quote-props': 'off', + }, + }, +]; + +export default eslintConfig; diff --git a/mcp-server/lib/data/examples.ts b/mcp-server/lib/data/examples.ts new file mode 100644 index 0000000..f5a6edd --- /dev/null +++ b/mcp-server/lib/data/examples.ts @@ -0,0 +1,500 @@ +/** + * Usage examples for generated code from The Codegen Project. + * These examples help users understand how to integrate generated code into their projects. + */ + +import type { GeneratorPreset, Protocol } from './generators'; + +export interface UsageExample { + title: string; + description: string; + code: string; + dependencies?: string[]; +} + +/** + * Examples for each generator type + */ +export const generatorExamples: Record = { + payloads: [ + { + title: 'Creating and validating a payload', + description: 'Create a message payload instance and validate it before sending', + code: `import { UserSignedUp } from './payloads/UserSignedUp'; + +// Create a new payload instance +const message = new UserSignedUp({ + displayName: 'John Doe', + email: 'john@example.com', + createdAt: new Date().toISOString(), +}); + +// Validate the payload (throws if invalid) +message.validate(); + +// Serialize to JSON string for sending +const json = message.marshal(); +console.log(json);`, + dependencies: ['ajv', 'ajv-formats'], + }, + { + title: 'Deserializing a received payload', + description: 'Parse and validate an incoming message', + code: `import { UserSignedUp } from './payloads/UserSignedUp'; + +// Received JSON string from message broker +const receivedJson = '{"display_name":"John Doe","email":"john@example.com"}'; + +// Unmarshal and validate +const message = UserSignedUp.unmarshal(receivedJson); +message.validate(); + +// Access typed properties +console.log(message.displayName); // "John Doe" +console.log(message.email); // "john@example.com"`, + dependencies: ['ajv', 'ajv-formats'], + }, + ], + + parameters: [ + { + title: 'Working with channel parameters', + description: 'Create parameters and substitute them into channel topics', + code: `import { UserEventsParameters } from './parameters/UserEventsParameters'; + +// Create parameters instance +const params = new UserEventsParameters({ + userId: '12345', + eventType: 'signup', +}); + +// Get the channel with parameters substituted +const channel = params.getChannelWithParameters('users/{userId}/events/{eventType}'); +console.log(channel); // "users/12345/events/signup"`, + }, + { + title: 'Extracting parameters from a topic', + description: 'Parse parameters from an incoming message topic', + code: `import { UserEventsParameters } from './parameters/UserEventsParameters'; + +// Extract parameters from an actual topic +const topic = 'users/12345/events/signup'; +const pattern = 'users/{userId}/events/{eventType}'; +const params = UserEventsParameters.createFromChannel(topic, pattern); + +console.log(params.userId); // "12345" +console.log(params.eventType); // "signup"`, + }, + ], + + headers: [ + { + title: 'Creating message headers', + description: 'Create and validate message headers', + code: `import { MessageHeaders } from './headers/MessageHeaders'; + +// Create headers instance +const headers = new MessageHeaders({ + correlationId: 'abc-123', + contentType: 'application/json', + timestamp: Date.now(), +}); + +// Validate headers +headers.validate(); + +// Marshal for sending +const headerData = headers.marshal();`, + dependencies: ['ajv', 'ajv-formats'], + }, + ], + + types: [ + { + title: 'Using topic type definitions', + description: 'Work with strongly-typed topic identifiers', + code: `import { Topics, TopicIds, ToTopicIds, ToTopics } from './Types'; + +// Get all available topics +const allTopics: Topics[] = ['users/signup', 'users/login', 'orders/created']; + +// Convert between topic string and ID +const topicId: TopicIds = ToTopicIds('users/signup'); // 'usersSignup' +const topic: Topics = ToTopics('usersSignup'); // 'users/signup' + +// Type-safe topic handling +function handleTopic(topic: Topics) { + switch (ToTopicIds(topic)) { + case 'usersSignup': + console.log('Handling user signup'); + break; + case 'usersLogin': + console.log('Handling user login'); + break; + } +}`, + }, + ], + + channels: [ + { + title: 'Publishing a message (NATS)', + description: 'Publish a typed message to a NATS subject', + code: `import { connect } from 'nats'; +import { publishToUserSignup } from './channels/nats'; +import { UserSignedUp } from './payloads/UserSignedUp'; + +const nc = await connect({ servers: 'nats://localhost:4222' }); + +const message = new UserSignedUp({ + displayName: 'John Doe', + email: 'john@example.com', +}); + +await publishToUserSignup(nc, message); +console.log('Message published'); + +await nc.close();`, + dependencies: ['nats'], + }, + { + title: 'Subscribing to messages (NATS)', + description: 'Subscribe to a channel and receive typed messages', + code: `import { connect } from 'nats'; +import { subscribeToUserSignup } from './channels/nats'; + +const nc = await connect({ servers: 'nats://localhost:4222' }); + +await subscribeToUserSignup(nc, { + callback: (err, msg, parameters, headers, natsMsg) => { + if (err) { + console.error('Error:', err); + return; + } + console.log('Received:', msg?.displayName, msg?.email); + }, +}); + +console.log('Subscribed, waiting for messages...');`, + dependencies: ['nats'], + }, + ], + + client: [ + { + title: 'Using the NATS client', + description: 'High-level client for NATS messaging', + code: `import { NatsClient } from './client/NatsClient'; +import { UserSignedUp } from './payloads/UserSignedUp'; + +// Create and connect the client +const client = new NatsClient(); +await client.connectToHost('nats://localhost:4222'); + +// Publish a message +const message = new UserSignedUp({ + displayName: 'John Doe', + email: 'john@example.com', +}); +await client.publishToUserSignup({ message }); + +// Subscribe to messages +await client.subscribeToUserSignup({ + onDataCallback: (err, msg) => { + if (!err && msg) { + console.log('Received:', msg.displayName); + } + }, +}); + +// Disconnect when done +await client.disconnect();`, + dependencies: ['nats'], + }, + ], + + models: [ + { + title: 'Using generated models', + description: 'Work with data models generated from JSON Schema', + code: `import { User } from './models/User'; + +// Create a new model instance +const user = new User({ + id: '123', + name: 'John Doe', + email: 'john@example.com', + roles: ['admin', 'user'], +}); + +// Access properties +console.log(user.name); // "John Doe" +console.log(user.roles); // ["admin", "user"] + +// Models are plain classes - use as needed +const users: User[] = [user];`, + }, + ], + + custom: [ + { + title: 'Custom generator configuration', + description: 'Define a custom generator with a render function', + code: `// codegen.config.mjs +export default { + inputType: 'asyncapi', + inputPath: './asyncapi.json', + generators: [ + { + preset: 'custom', + outputPath: './src/custom', + renderFunction: (context) => { + const { asyncapiDocument } = context; + const channels = asyncapiDocument.channels(); + + // Generate custom code based on the spec + let code = '// Custom generated code\\n'; + for (const [name, channel] of Object.entries(channels)) { + code += \`export const \${name}Channel = '\${channel.address()}';\n\`; + } + + return { + files: [ + { + fileName: 'channels.ts', + content: code, + }, + ], + }; + }, + }, + ], +};`, + }, + ], +}; + +/** + * Protocol-specific examples + */ +export const protocolExamples: Record = { + nats: [ + { + title: 'NATS Core Publish/Subscribe', + description: 'Basic publish/subscribe with NATS core', + code: `import { connect } from 'nats'; +import { publishToOrders, subscribeToOrders } from './channels/nats'; + +const nc = await connect({ servers: 'nats://localhost:4222' }); + +// Subscribe +await subscribeToOrders(nc, { + callback: (err, msg) => { + console.log('Order received:', msg); + }, +}); + +// Publish +await publishToOrders(nc, orderPayload);`, + dependencies: ['nats'], + }, + { + title: 'NATS JetStream', + description: 'Persistent messaging with JetStream', + code: `import { connect } from 'nats'; +import { jetStreamPublishToOrders, jetStreamPullSubscribeToOrders } from './channels/nats'; + +const nc = await connect({ servers: 'nats://localhost:4222' }); +const js = nc.jetstream(); + +// Publish to JetStream +await jetStreamPublishToOrders(js, orderPayload); + +// Pull subscribe from JetStream +const subscription = await jetStreamPullSubscribeToOrders(js, { + stream: 'ORDERS', + consumer: 'order-processor', + callback: (err, msg) => { + console.log('Processing order:', msg); + }, +});`, + dependencies: ['nats'], + }, + ], + + kafka: [ + { + title: 'Kafka Publish/Subscribe', + description: 'Produce and consume Kafka messages', + code: `import { Kafka } from 'kafkajs'; +import { publishToOrders, subscribeToOrders } from './channels/kafka'; + +const kafka = new Kafka({ brokers: ['localhost:9092'] }); +const producer = kafka.producer(); +const consumer = kafka.consumer({ groupId: 'my-group' }); + +await producer.connect(); +await consumer.connect(); + +// Publish +await publishToOrders(producer, orderPayload); + +// Subscribe +await subscribeToOrders(consumer, { + callback: (err, msg) => { + console.log('Order received:', msg); + }, +});`, + dependencies: ['kafkajs'], + }, + ], + + mqtt: [ + { + title: 'MQTT Publish/Subscribe', + description: 'IoT-style messaging with MQTT', + code: `import mqtt from 'mqtt'; +import { publishToSensors, subscribeToSensors } from './channels/mqtt'; + +const client = mqtt.connect('mqtt://localhost:1883'); + +client.on('connect', async () => { + // Subscribe + await subscribeToSensors(client, { + callback: (err, msg) => { + console.log('Sensor data:', msg); + }, + }); + + // Publish + await publishToSensors(client, sensorPayload, { qos: 1 }); +});`, + dependencies: ['mqtt'], + }, + ], + + amqp: [ + { + title: 'AMQP Queue Operations', + description: 'RabbitMQ queue publish/subscribe', + code: `import amqp from 'amqplib'; +import { publishToOrderQueue, subscribeToOrderQueue } from './channels/amqp'; + +const connection = await amqp.connect('amqp://localhost'); +const channel = await connection.createChannel(); + +// Subscribe to queue +await subscribeToOrderQueue(channel, { + callback: (err, msg) => { + console.log('Order from queue:', msg); + }, +}); + +// Publish to queue +await publishToOrderQueue(channel, orderPayload);`, + dependencies: ['amqplib'], + }, + ], + + websocket: [ + { + title: 'WebSocket Messaging', + description: 'Bidirectional WebSocket communication', + code: `import WebSocket from 'ws'; +import { publishToChat, subscribeToChat } from './channels/websocket'; + +const ws = new WebSocket('ws://localhost:8080'); + +ws.on('open', () => { + // Subscribe to incoming messages + subscribeToChat(ws, { + callback: (err, msg) => { + console.log('Chat message:', msg); + }, + }); + + // Send a message + publishToChat(ws, chatPayload); +});`, + dependencies: ['ws'], + }, + ], + + http_client: [ + { + title: 'HTTP Client Requests', + description: 'Make typed HTTP API calls', + code: `import { createOrder, getOrder } from './channels/http_client'; + +// POST request +const newOrder = await createOrder({ + body: { items: ['item1', 'item2'], total: 99.99 }, + headers: { Authorization: 'Bearer token' }, +}); + +// GET request with path parameters +const order = await getOrder({ + parameters: { orderId: '123' }, + headers: { Authorization: 'Bearer token' }, +}); + +console.log('Order:', order);`, + }, + ], + + event_source: [ + { + title: 'Server-Sent Events', + description: 'Subscribe to SSE streams', + code: `import { subscribeToNotifications } from './channels/event_source'; + +// Subscribe to SSE stream +const unsubscribe = await subscribeToNotifications({ + url: 'https://api.example.com/notifications', + callback: (err, msg) => { + if (!err) { + console.log('Notification:', msg); + } + }, + headers: { + Authorization: 'Bearer token', + }, +}); + +// Later: stop listening +unsubscribe();`, + dependencies: ['@microsoft/fetch-event-source'], + }, + ], +}; + +/** + * Get examples for a generator type + */ +export function getExamplesForGenerator(preset: GeneratorPreset): UsageExample[] { + return generatorExamples[preset] || []; +} + +/** + * Get examples for a protocol + */ +export function getExamplesForProtocol(protocol: Protocol): UsageExample[] { + return protocolExamples[protocol] || []; +} + +/** + * Get all examples for a generator, including protocol-specific if applicable. + * For channels/client generators, returns only protocol-specific examples when a protocol is specified. + */ +export function getAllExamples( + preset: GeneratorPreset, + protocol?: Protocol +): UsageExample[] { + // For channels/client, return protocol-specific examples when a protocol is specified + if ((preset === 'channels' || preset === 'client') && protocol) { + return getExamplesForProtocol(protocol); + } + + // For other generators or when no protocol specified, return generic examples + return getExamplesForGenerator(preset); +} diff --git a/mcp-server/lib/data/generators.ts b/mcp-server/lib/data/generators.ts new file mode 100644 index 0000000..3f87967 --- /dev/null +++ b/mcp-server/lib/data/generators.ts @@ -0,0 +1,291 @@ +/** + * Generator definitions and schemas for The Codegen Project. + * This data is used by MCP tools to help users configure generators. + */ + +export type InputType = 'asyncapi' | 'openapi' | 'jsonschema'; +export type GeneratorPreset = + | 'payloads' + | 'parameters' + | 'headers' + | 'types' + | 'channels' + | 'client' + | 'models' + | 'custom'; +export type Protocol = + | 'nats' + | 'kafka' + | 'mqtt' + | 'amqp' + | 'websocket' + | 'http_client' + | 'event_source'; + +export interface GeneratorOption { + name: string; + type: 'string' | 'boolean' | 'array' | 'enum' | 'object'; + description: string; + default?: unknown; + required?: boolean; + enumValues?: string[]; +} + +export interface GeneratorDefinition { + preset: GeneratorPreset; + description: string; + supportedInputs: InputType[]; + defaultOutputPath: string; + options: GeneratorOption[]; + dependencies?: GeneratorPreset[]; +} + +/** + * All available generators and their configurations + */ +export const generators: Record = { + payloads: { + preset: 'payloads', + description: + 'Generates TypeScript classes/types for message payloads with JSON serialization and AJV validation.', + supportedInputs: ['asyncapi', 'openapi'], + defaultOutputPath: 'src/__gen__/payloads', + options: [ + { + name: 'serializationType', + type: 'enum', + description: 'Serialization format for messages', + default: 'json', + enumValues: ['json'], + }, + { + name: 'enum', + type: 'enum', + description: 'How to generate enum types', + default: 'enum', + enumValues: ['enum', 'union'], + }, + { + name: 'map', + type: 'enum', + description: 'How to generate map/dictionary types', + default: 'record', + enumValues: ['indexedObject', 'map', 'record'], + }, + { + name: 'includeValidation', + type: 'boolean', + description: 'Include AJV validation in generated payloads', + default: true, + }, + { + name: 'useForJavaScript', + type: 'boolean', + description: 'Generate code compatible with plain JavaScript', + default: true, + }, + { + name: 'rawPropertyNames', + type: 'boolean', + description: 'Use raw property names without transformation', + default: false, + }, + ], + }, + + parameters: { + preset: 'parameters', + description: + 'Generates TypeScript classes for channel/path parameters with extraction and substitution methods.', + supportedInputs: ['asyncapi', 'openapi'], + defaultOutputPath: 'src/__gen__/parameters', + options: [ + { + name: 'serializationType', + type: 'enum', + description: 'Serialization format', + default: 'json', + enumValues: ['json'], + }, + ], + }, + + headers: { + preset: 'headers', + description: + 'Generates TypeScript classes for message headers with validation.', + supportedInputs: ['asyncapi', 'openapi'], + defaultOutputPath: 'src/__gen__/headers', + options: [ + { + name: 'serializationType', + type: 'enum', + description: 'Serialization format', + default: 'json', + enumValues: ['json'], + }, + { + name: 'includeValidation', + type: 'boolean', + description: 'Include AJV validation in generated headers', + default: true, + }, + ], + }, + + types: { + preset: 'types', + description: + 'Generates TypeScript type definitions for topics/channels with mapping utilities.', + supportedInputs: ['asyncapi', 'openapi'], + defaultOutputPath: 'src/__gen__', + options: [], + }, + + channels: { + preset: 'channels', + description: + 'Generates protocol-specific messaging functions (publish, subscribe, request, reply) for each channel.', + supportedInputs: ['asyncapi'], + defaultOutputPath: 'src/__gen__/channels', + dependencies: ['payloads', 'parameters', 'headers'], + options: [ + { + name: 'protocols', + type: 'array', + description: 'Protocols to generate functions for', + required: true, + enumValues: [ + 'nats', + 'kafka', + 'mqtt', + 'amqp', + 'websocket', + 'http_client', + 'event_source', + ], + }, + { + name: 'asyncapiReverseOperations', + type: 'boolean', + description: 'Reverse the direction of operations (send becomes receive)', + default: false, + }, + { + name: 'asyncapiGenerateForOperations', + type: 'boolean', + description: 'Generate functions based on operations', + default: true, + }, + { + name: 'kafkaTopicSeparator', + type: 'string', + description: 'Topic separator for Kafka channels', + default: '.', + }, + { + name: 'eventSourceDependency', + type: 'string', + description: 'NPM package for EventSource implementation', + default: '@microsoft/fetch-event-source', + }, + ], + }, + + client: { + preset: 'client', + description: + 'Generates a high-level client class that wraps channel functions with connection management.', + supportedInputs: ['asyncapi'], + defaultOutputPath: 'src/__gen__/client', + dependencies: ['channels'], + options: [ + { + name: 'protocols', + type: 'array', + description: 'Protocols to generate clients for (currently only NATS supported)', + required: true, + enumValues: ['nats'], + }, + ], + }, + + models: { + preset: 'models', + description: + 'Generates TypeScript data models using Modelina with customizable presets.', + supportedInputs: ['asyncapi', 'openapi', 'jsonschema'], + defaultOutputPath: 'src/__gen__/models', + options: [ + { + name: 'options', + type: 'object', + description: 'Modelina TypeScript options (modelType, enumType, indentation, etc.)', + }, + { + name: 'renderers', + type: 'array', + description: 'Custom Modelina presets for code customization', + }, + ], + }, + + custom: { + preset: 'custom', + description: + 'Run a custom generator function for specialized code generation needs.', + supportedInputs: ['asyncapi', 'openapi', 'jsonschema'], + defaultOutputPath: 'custom', + options: [ + { + name: 'renderFunction', + type: 'object', + description: 'Custom function that receives context and returns generated code', + required: true, + }, + { + name: 'options', + type: 'object', + description: 'Custom options passed to the render function', + }, + ], + }, +}; + +/** + * Get generators available for a specific input type + */ +export function getGeneratorsForInput(inputType: InputType): GeneratorDefinition[] { + return Object.values(generators).filter((g) => + g.supportedInputs.includes(inputType) + ); +} + +/** + * Get generator by preset name + */ +export function getGenerator(preset: GeneratorPreset): GeneratorDefinition | undefined { + return generators[preset]; +} + +/** + * Input type compatibility matrix + */ +export const inputTypeGenerators: Record = { + asyncapi: ['payloads', 'parameters', 'headers', 'types', 'channels', 'client', 'models', 'custom'], + openapi: ['payloads', 'parameters', 'headers', 'types', 'models', 'custom'], + jsonschema: ['models', 'custom'], +}; + +/** + * Protocol descriptions + */ +export const protocolDescriptions: Record = { + nats: 'NATS messaging with support for core pub/sub, request/reply, and JetStream', + kafka: 'Apache Kafka with consumer groups and topic management', + mqtt: 'MQTT protocol with QoS levels and topic wildcards', + amqp: 'AMQP 0-9-1 (RabbitMQ) with queues and exchanges', + websocket: 'WebSocket bidirectional messaging for real-time communication', + http_client: 'HTTP/REST client with various authentication methods', + event_source: 'Server-Sent Events (SSE) for server-to-client streaming', +}; diff --git a/mcp-server/lib/resources/bundled-docs.ts b/mcp-server/lib/resources/bundled-docs.ts new file mode 100644 index 0000000..13d960f --- /dev/null +++ b/mcp-server/lib/resources/bundled-docs.ts @@ -0,0 +1,181 @@ +/** + * Auto-generated documentation bundle. + * DO NOT EDIT - regenerate with: npm run bundle-docs + * Generated at: 2026-01-18T10:50:15.868Z + */ + +export interface DocEntry { + content: string; + title: string; +} + +export const docs: Record = { + "api/API": { + title: "API", + content: "# API\r\n\r\nThese are all the exposed types and functions when installing the CLI as a dependency.", + }, + "architectural-decisions/typescript": { + title: "TypeScript", + content: "# TypeScript\nAll decision worth mentioning about TypeScript can be found here, simply to keep track.\n\n### 21.08.2024\n\n1. All functions in channel protocols are to be designed arrow function as it's more versatile when combining it together with other code sections.\n\n### 28.04.2025\n\n1. All function parameters MUST be object parameters to better support many optional parameters without having to write `functionCall(undefined, undefined, 'value')`", + }, + "configurations": { + title: "Configurations", + content: "# Configurations\n\nThere are 5 possible configuration file types, `json`, `yaml`, `esm` (ECMAScript modules JavaScript), `cjs` (CommonJS modules JavaScript) and `ts` (TypeScript).\n\nThe only difference between them is what they enable you to do. The difference is `callbacks`, in a few places, you might want to provide a callback to control certain behavior in the generation library.\n\nFor example, with the [`custom`](./generators/custom.md) generator, you provide a callback to render something, this is not possible if your configuration file is either `json` or `yaml` format.\n\nReason those two exist, is because adding a `.js` configuration file to a Java project, might confuse developers, and if you dont need to take advantage of the customization features that require callback, it will probably be better to use one of the other two.\n\n## Creating Configurations with the CLI\n\nThe easiest way to create a configuration file is by using the `codegen init` command. This interactive command will guide you through setting up a configuration file for your project, allowing you to specify:\n\n- Input file (AsyncAPI or OpenAPI document)\n- Configuration file type (`esm`, `json`, `yaml`, `ts`)\n- Output directory\n- Language and generation options\n\n```sh\ncodegen init --input-file ./ecommerce.yml --input-type asyncapi --config-type ts --languages typescript\n```\n\nFor detailed usage instructions and all available options, see the [CLI usage documentation](./usage.md#codegen-init).\n\n## Configuration File Lookup\n\nIf no explicit configuration file is sat, it will be looked for in the following order:\n- package.json\n- .codegenrc\n- .codegenrc.json\n- .codegenrc.yaml\n- .codegenrc.yml\n- .codegenrc.js\n- .codegenrc.ts\n- .codegenrc.cjs\n- .config/codegenrc\n- .config/codegenrc.json\n- .config/codegenrc.yaml\n- .config/codegenrc.yml\n- .config/codegenrc.js\n- .config/codegenrc.ts\n- .config/codegenrc.mjs\n- .config/codegenrc.cjs\n- codegen.config.js\n- codegen.config.ts\n- codegen.config.mjs\n- codegen.config.cjs\n- codegen.json\n- codegen.yaml\n- codegen.yml\n- codegen.js\n- codegen.ts\n- codegen.mjs\n- codegen.cjs", + }, + "contributing": { + title: "Contributing to The Codegen Project", + content: "# Contributing to The Codegen Project\n\n\n\n\n\n- [Acceptance criteria and process](#acceptance-criteria-and-process)\n * [Fixing bugs](#fixing-bugs)\n * [New features](#new-features)\n- [Repository Architecture](#repository-architecture)\n- [Getting started](#getting-started)\n- [Contribution recogniton](#contribution-recogniton)\n- [Summary of the contribution flow](#summary-of-the-contribution-flow)\n- [Code of Conduct](#code-of-conduct)\n- [Our Development Process](#our-development-process)\n- [Pull Requests](#pull-requests)\n- [Conventional commits](#conventional-commits)\n\n\n\nFirst of all, thank you 🙇🏾‍♀️ for considering contributing to The Codegen Project\n\nIf you have any questions, are unsure how your use-case fits in, or want something clarified, don't hesitate to reach out, we are always happy to help out!\n\n## Acceptance criteria and process\n\nEven though we love contributions, we need to maintain a certain standard of what can be merged into the codebase. \n\nThe below sections provide information about our acceptance criteria, based on the type of contribution you make.\n\n### Fixing bugs \n\nThe Acceptance Criteria for _fixing any bug_ means that you should be able to reproduce the error using tests that will fail, unless a fix is implemented.\n\n### New features\n\nThe Acceptance Criteria for _adding new features_ requires a few things in order to be accepted. This ensures all features are well described and implemented before being released.\n\n1. **Not all feature requests from the community (or maintainers!) are accepted:** Even though you are welcome to create a new feature without an issue, it might be rejected and turn out to be a waste of your time. We don't want that to happen, so make sure to create an issue first and wait to see if it's accepted after community discussion of the proposal.\n1. **When creating tests for your new feature, aim for as high coverage numbers as possible:** When you run the tests (`npm run test`), you should see a `./coverage/lcov-report/index.html` file being generated. Use this to see in depth where your tests are not covering your implementation.\n1. **No documentation, no feature:** If a user cannot understand a new feature, that feature basically doesn't exist! Remember to make sure that any and all relevant [documentation](./) is consistently updated.\n - New features such as new generators or inputs, etc, need associated use case documentation along side [examples](../examples).\n\n## Repository Architecture\n\nThe repository is setup with multiple functions to keep it simple.\n- `src`; includes the CLI and library code both exposed through the same package.\n- `website`; is... Well the website.\n- `scripts`; includes helpfull scripts for the release flow such as for building JSON Schema files from the code for configuration validation and CLI release configurations\n- `test`; include all the testing done for the repository\n - `blackbox`; is a quick and dirt syntax testing of different configurations against different inputs to quickly detect problems\n - `runtime`; is sematic testing of the generated code in the corresponding languages they generate for, ensuring correct behaviour at runtime\n - The rest normal unit and integration testing of the actual CLI and library\n- `examples`; is the showcase of actual projects using the CLI to simplify the implementation phase of software development.\n- `schemas`; is the autogenerated JSON schemas for validating configurations.\n\n## Getting started\n\nHere is a quick litle get started quick tutorial;\n\n1. Fork the repository\n2. Create a branch from the upstream repository\n3. Install the dependencies `npm install`\n4. Make sure test pass `npm run test`\n5. Adapt the source code as well as test and documentation\n6. Push the changes\n7. Create a PR with the proposed change\n8. Get the change reviewed and merged :tada:\n\nHappy contributing :heart:\n\n## Contribution recogniton\n\nWe use [All Contributors](https://allcontributors.org/docs/en/specification) specification to handle recognitions.\n\n## Summary of the contribution flow\n\nThe following is a summary of the ideal contribution flow. Please, note that Pull Requests can also be rejected by the maintainers when appropriate.\n\n```\n ┌───────────────────────┐\n │ │\n │ Open an issue │\n │ (a bug report or a │\n │ feature request) │\n │ │\n └───────────────────────┘\n ⇩\n ┌───────────────────────┐\n │ │\n │ Open a Pull Request │\n │ (only after issue │\n │ is approved) │\n │ │\n └───────────────────────┘\n ⇩\n ┌───────────────────────┐\n │ │\n │ Your changes will │\n │ be merged and │\n │ published on the next │\n │ release │\n │ │\n └───────────────────────┘\n```\n\n## Code of Conduct\nWe have adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://github.com/the-codegen-project/cli/blob/main/CODE_OF_CONDUCT.md) so that you can understand what sort of behaviour is expected.\n\n## Our Development Process\nWe use Github to host code, to track issues and feature requests, as well as accept pull requests.\n\n## Pull Requests\n\n**Please, make sure you open an issue before starting with a Pull Request, unless it's a typo or a really obvious error.** Pull requests are the best way to propose changes to the specification. \n\n## Conventional commits\n\nOur repositories follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) specification. Releasing to GitHub and NPM is done with the support of [semantic-release](https://semantic-release.gitbook.io/semantic-release/).\n\nPull requests should have a title that follows the specification, otherwise, merging is blocked. If you are not familiar with the specification simply ask maintainers to modify. You can also use this cheatsheet if you want:\n\n- `fix: ` prefix in the title indicates that PR is a bug fix and PATCH release must be triggered.\n- `feat: ` prefix in the title indicates that PR is a feature and MINOR release must be triggered.\n- `docs: ` prefix in the title indicates that PR is only related to the documentation and there is no need to trigger release.\n- `chore: ` prefix in the title indicates that PR is only related to cleanup in the project and there is no need to trigger release.\n- `test: ` prefix in the title indicates that PR is only related to tests and there is no need to trigger release.\n- `refactor: ` prefix in the title indicates that PR is only related to refactoring and there is no need to trigger release.\n\nWhat about MAJOR release? just add `!` to the prefix, like `fix!: ` or `refactor!: `\n\nPrefix that follows specification is not enough though. Remember that the title must be clear and descriptive with usage of [imperative mood](https://chris.beams.io/posts/git-commit/#imperative).", + }, + "generators/channels": { + title: "Channels", + content: "# Channels\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'channels',\n outputPath: './src/__gen__/', \n language: 'typescript',\n protocols: ['nats']\n }\n ]\n};\n```\n\n`channels` preset with `asyncapi` input generates support functions for each operation based on the selected protocol.\n\nThis generator uses [`payloads`](./payloads.md), [`headers`](./headers.md) and [`parameters`](./parameters.md) generators, in case you dont have any defined, it will automatically include them with default values.\n\nThis is supported through the following inputs: [`asyncapi`](../inputs/asyncapi.md)\n\nIt supports the following languages; [`typescript`](#typescript)\n\nIt supports the following protocols; [`nats`](../protocols/nats.md), [`kafka`](../protocols/kafka.md), [`mqtt`](../protocols/mqtt.md), [`amqp`](../protocols/amqp.md), [`event_source`](../protocols/eventsource.md), [`http_client`](../protocols/http_client.md), [`websocket`](../protocols/websocket.md)\n\n## Options\nThese are the available options for the `channels` generator; \n\n| **Option** | Default | Type | Description |\n|---|---|---|---|\n| asyncapiReverseOperations | `false` | Boolean | Used in conjunction with AsyncAPI input, and reverses the operation actions i.e. send becomes receive and receive becomes send. Often used in testing scenarios to act as the reverse API. |\n| asyncapiGenerateForOperations | `true` | Boolean | Used in conjunction with AsyncAPI input, which if `true` generate the functions upholding how operations are defined. If `false` the functions are generated regardless of what operations define. I.e. `send` and `receive` does not matter. |\n| functionTypeMapping | `{}` | Record\\ | Used in conjunction with AsyncAPI input, can define channel ID along side the type of functions that should be rendered. |\n| kafkaTopicSeparator | `'.'` | String | Used with AsyncAPI to ensure the right character separate topics, example if address is my/resource/path it will be converted to my.resource.path |\n| eventSourceDependency | `'@microsoft/fetch-event-source'` | String | Because @microsoft/fetch-event-source is out-dated in some areas we allow you to change the fork/variant that can be used instead |\n\n## TypeScript\nRegardless of protocol, these are the dependencies: \n- If validation enabled, [ajv](https://ajv.js.org/guide/getting-started.html): ^8.17.1\n \nDepending on which protocol, these are the dependencies:\n- `NATS`: https://github.com/nats-io/nats.js v2\n- `Kafka`: https://github.com/tulios/kafkajs v2\n- `MQTT`: https://github.com/mqttjs/MQTT.js v5\n- `AMQP`: https://github.com/amqp-node/amqplib v0\n- `EventSource`: `event_source_fetch`: https://github.com/Azure/fetch-event-source v2, `event_source_express`: https://github.com/expressjs/express v4\n- `HTTP`: https://github.com/node-fetch/node-fetch v2\n- `WebSocket`: https://github.com/websockets/ws v8\n\nFor TypeScript, the generator creates one file per protocol plus an index file that re-exports all protocols as namespaces. For example;\n\n```ts\n// Import specific functions from a protocol file\nimport {\n jetStreamPublishToSendUserSignedup,\n subscribeToReceiveUserSignedup,\n publishToSendUserSignedup\n} from 'src/__gen__/nats';\n\n// Or import the entire protocol namespace\nimport * as nats from 'src/__gen__/nats';\n\n// Or import all protocols from the index\nimport { nats, kafka, mqtt, amqp, event_source } from 'src/__gen__/index';\n```\n\nThe generated file structure is:\n```\noutputPath/\n├── index.ts # Re-exports all protocol namespaces\n├── nats.ts # NATS-specific functions\n├── kafka.ts # Kafka-specific functions\n├── mqtt.ts # MQTT-specific functions\n├── amqp.ts # AMQP-specific functions\n├── event_source.ts # EventSource-specific functions\n├── http_client.ts # HTTP client-specific functions\n└── websocket.ts # WebSocket-specific functions\n```\n\nEach protocol file contains standalone exported functions for interacting with channels defined in your AsyncAPI document.", + }, + "generators/client": { + title: "Client", + content: "# Client\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'client',\n outputPath: './src/__gen__/', \n language: 'typescript',\n protocols: ['nats']\n }\n ]\n};\n```\n\n`client` preset with `asyncapi` input generates for each protocol a class (`{Protocol}Client`) that makes it easier to interact with the protocol.\n\nIt will generate;\n- Support function for connecting to the protocol\n- Simpler functions then those generated by [`channels`](./channels.md) to interact with the given protocols\n- Exporting all generated [`parameters`](./parameters.md)\n- Exporting all generated [`payloads`](./payloads.md)\n\nThis generator uses `channels` generators, in case you dont have any defined, it will automatically include them with default values and dependencies.\n\nThis is supported through the following inputs: [`asyncapi`](../inputs/asyncapi.md)\n\nIt supports the following languages; [`typescript`](#typescript)\n\nIt supports the following protocols; [`nats`](../protocols/nats.md)\n\n## TypeScript\n\n### Nats\n\nDependencies;\n- `NATS`: https://github.com/nats-io/nats.js v2\n\nFor Nats the `NatsClient` is generated that setups the correct [Nats.js](https://github.com/nats-io/nats.js) clients, marshalling codex, and provide simplier functions to improve DX.\n\nExample;\n```ts\n//Import and export payload models\nimport {Payload} from './payload/Payload';\nexport {Payload};\n\n//Import and export parameter models\nimport {Parameters} from './parameters/Parameters';\n\n//Import channel functions\nimport { Protocols } from './channels/index';\nconst { nats } = Protocols;\n\nimport * as Nats from 'nats';\n\n/**\n * @class NatsClient\n */\nexport class NatsClient {\n /**\n * Disconnect all clients from the server\n */\n async disconnect() {\n ...\n }\n /**\n * Returns whether or not any of the clients are closed\n */\n isClosed() {\n ...\n }\n /**\n * Try to connect to the NATS server with user credentials\n */\n async connectWithUserCreds(userCreds: string, options ? : Nats.ConnectionOptions, codec ? : Nats.Codec < any > ) {\n ...\n }\n /**\n * Try to connect to the NATS server with user and password\n */\n async connectWithUserPass(user: string, pass: string, options ? : Nats.ConnectionOptions, codec ? : Nats.Codec < any > ) {\n ...\n }\n /**\n * Try to connect to the NATS server which has no authentication\n */\n async connectToHost(host: string, options ? : Nats.ConnectionOptions, codec ? : Nats.Codec < any > ) {\n ...\n }\n\n /**\n * Try to connect to the NATS server with the different payloads.\n */\n connect(options: Nats.ConnectionOptions, codec?: Nats.Codec): Promise {\n ...\n }\n \n public async jetStreamPublishToChannel(\n message: Payload, \n parameters: Parameters, \n options: Partial = {}\n ): Promise {\n ...\n }\n jetStreamPullSubscribeToChannel\n jetStreamPushSubscriptionFromChannel\n publishToChannel\n subscribeToChannel\n}\n```", + }, + "generators/custom": { + title: "Custom generator", + content: "# Custom generator\n\nThis generator is simple, it's a callback that enable you to write any file or do any operation in the code generation process. This preset is available for all languages.\n\n## Imports\n\nThe dependencies you have access to is any native `node` dependency and all dependencies listed in [The Codegen Project](https://github.com/the-codegen-project/cli/blob/8b8fa6f0c5b0c0c63515a8ca439f72872815f491/package.json#L9). Here is an example:\n\n```ts\nimport { JavaFileGenerator } from \"@asyncapi/modelina\";\nexport default {\n ...\n generators: [\n {\n preset: 'custom',\n ...\n renderFunction: ({generator, inputType, asyncapiDocument, openapiDocument, dependencyOutputs}) \n {\n const modelinaGenerator = new JavaFileGenerator({});\n modelinaGenerator.generateCompleteModels(...)\n }\n }\n ]\n};\n```\n\n# Dependencies\n\nIn each generator (don't manually use it unless you use `preset: custom`), you can add `dependencies` property, which takes an array of `id`'s that the rendering engine ensures are rendered before the dependant one. \n\nEach generator has a specific output (except `custom` which is dynamic and under your control), they are documented under each [./generators](./README.md). These outputs can be accessed under `dependencyOutputs`.\n\nThere are two rules though;\n\n1. You are not allowed to have circular dependencies, i.e. two generators both depending on each other.\n2. You are not allowed to have self-dependant generators\n\n## How does it work?\n\nFor example, take two generators, you can chain them together and use one's output in the other, as for example below, to have the console print out `Hello World!`.\n```js\nexport default {\n ...\n generators: [\n {\n preset: 'custom',\n renderFunction: ({dependencyOutputs}) => {\n console.log(dependencyOutputs['bar'])\n },\n dependencies: ['bar']\n },\n {\n preset: 'custom',\n id: 'bar',\n renderFunction: () => {\n return 'Hello World!'\n }\n }\n ]\n};\n```\n\n## Arguments\nIn the `renderFunction` you have access to a bunch of arguments to help you create the callback;\n\n- `generator` - is the generator configuration, where you have access to the `options` and all other information.\n- `inputType` - is the root `inputType` for the input document\n- `asyncapiDocument` - is the parsed AsyncAPI document input (according to the [AsyncAPI parser](https://github.com/asyncapi/parser-js/)), undefined if the `inputType` is not `asyncapi`\n- `openapiDocument` - is the parsed OpenAPI document input (according to the [readme/openapi-parser](https://github.com/readmeio/oas)), undefined if the `inputType` is not `openapi`\n- `dependencyOutputs` - if you have defined any `dependencies`, this is where you can access the output. Checkout the [dependency documentation](#dependencies) for more information.", + }, + "generators/headers": { + title: "Headers", + content: "# Headers\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'headers',\n outputPath: './src/headers',\n serializationType: 'json',\n includeValidation: true,\n language: 'typescript',\n }\n ]\n};\n```\n\n`headers` preset is for generating models that represent typed models representing headers.\n\nThis is supported through the following inputs: [`asyncapi`](#inputs), [`openapi`](#inputs)\n\nIt supports the following languages; `typescript`\n\n## Inputs\n\n### `asyncapi`\nThe `headers` preset with `asyncapi` input generates all the message headers for each channel in the AsyncAPI document.\n\nThe return type is a map of channels and the model that represent the headers. \n\n### `openapi`\nThe `headers` preset with `openapi` input generates all the headers for each path in the OpenAPI document.\n\nThe return type is a map of paths and the model that represent the headers. \n\n## Typescript\nDependencies: \n- If validation enabled, [ajv](https://ajv.js.org/guide/getting-started.html): ^8.17.1\n\n### Validation\nEach generated class includes built-in JSON Schema validation capabilities through two static methods:\n\n- `validate`: Validates headers against the schema. Use this method when you want to validate data.\n\n```typescript\n// Example\nconst result = UserSignedUpHeaders.validate({ data: headers });\nif (!result.valid) {\n console.error('Validation errors:', result.errors);\n}\n```\n\n- `createValidator`: Creates a reusable validator function. Use this when you need to validate multiple instances of the same type and want to avoid recreating the validator each time.\n\n```typescript\n// Example\nconst validator = UserSignedUpHeaders.createValidator();\nconst result = UserSignedUpHeaders.validate({ data: headers, ajvValidatorFunction: validator });\nif (!result.valid) {\n console.error('Validation errors:', result.errors);\n}\n```\n\nBoth methods support custom Ajv instances and options for advanced validation scenarios.", + }, + "generators/models": { + title: "🏗️ Models", + content: "# 🏗️ Models\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'models',\n outputPath: './src/models',\n language: 'typescript',\n renderers: [...],\n options: {...}\n }\n ]\n};\n```\n\nThe `models` preset provides native integration with [AsyncAPI Modelina](https://modelina.org) for generating TypeScript models directly from AsyncAPI, OpenAPI, and JSON Schema documents. This generator exposes Modelina's full capabilities, giving you complete control over model generation.\n\nThis is supported through the following inputs: `asyncapi`, `openapi`, `jsonschema`\n\nIt supports the following languages; [`typescript`](#typescript)\n\n## Core Features\n\n- **Native Modelina Integration**: Direct access to Modelina's TypeScript generator\n- **Custom Presets**: Full control over generated code through Modelina's preset system\n- **Flexible Options**: Configure all TypeScript generation options\n- **Production Ready**: Generate models that are immediately usable in your applications\n\n## Configuration\n\n### `renderers`\n\nThe `renderers` property exposes Modelina's [preset system](https://raw.githubusercontent.com/asyncapi/modelina/refs/heads/master/docs/presets.md), allowing you to customize every aspect of the generated models.\n\nPresets can:\n- Add custom content to classes, interfaces, enums, and types\n- Override default rendering behavior\n- Inject validation logic, serialization methods, or custom properties\n- Apply consistent formatting and documentation\n\n### `options`\n\nThe `options` property provides access to all [Modelina TypeScript options](https://github.com/asyncapi/modelina/blob/master/docs/languages/TypeScript.md), including:\n\n- Model types (class, interface, type alias)\n- Enum generation styles\n- Property naming conventions\n- Module system preferences\n- Type mappings and constraints\n\n## Examples\n\n### Basic Usage\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: 'asyncapi.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n outputPath: './src/models'\n }\n ]\n};\n```\n\n### Using Built-in Presets\n\n```js\nimport { modelina } from '@the-codegen-project/cli';\nconst { TS_COMMON_PRESET } = modelina;\n\nexport default {\n inputType: 'asyncapi',\n inputPath: 'asyncapi.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n renderers: [\n {\n preset: TS_COMMON_PRESET,\n options: {\n marshalling: true\n }\n }\n ],\n outputPath: './src/models'\n }\n ]\n};\n```\n\n### Custom Presets\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: 'asyncapi.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n renderers: [\n {\n class: {\n self: ({model}) => `class ${model.name} {}`\n },\n interface: {\n self: ({model}) => `interface ${model.name} {}`\n },\n type: {\n self: ({model}) => `type ${model.name} = string;`\n }\n }\n ],\n outputPath: './src/models'\n }\n ]\n};\n```\n\n### Advanced Configuration with Options\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: 'asyncapi.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n options: {\n modelType: 'interface',\n enumType: 'union',\n mapType: 'indexedObject',\n moduleSystem: 'ESM',\n rawPropertyNames: false,\n useJavascriptReservedKeywords: false\n },\n renderers: [\n {\n interface: {\n property: ({ content, property }) => {\n return `/** ${property.property.description || 'Auto-generated property'} */\\n${content}`;\n }\n }\n }\n ],\n outputPath: './src/models'\n }\n ]\n};\n```\n\n### JSON Schema Input\n\n```js\nexport default {\n inputType: 'jsonschema',\n inputPath: 'user-schema.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n options: {\n modelType: 'class',\n enumType: 'enum'\n },\n renderers: [\n {\n class: {\n additionalContent: ({ content, model }) => {\n return `${content}\\n\\n // Custom validation method\\n public validate(): boolean {\\n return true;\\n }`;\n }\n }\n }\n ],\n outputPath: './src/models'\n }\n ]\n};\n```\n\n## Languages\n\n### TypeScript\n\nThe TypeScript implementation provides full access to Modelina's TypeScript generator capabilities.\n\n**Dependencies**: None (generates plain TypeScript)\n\n**Supported Features**:\n- Classes, interfaces, type aliases, and enums\n- Complex nested types and circular references\n- Union types and discriminated unions\n- Optional and required properties\n- Custom property naming and constraints\n- Marshalling and unmarshalling methods (with TS_COMMON_PRESET)\n- JSON Schema validation (with custom presets)\n\n**Common Options**:\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `modelType` | `'class' \\| 'interface'` | `'class'` | Type of models to generate |\n| `enumType` | `'enum' \\| 'union'` | `'enum'` | How to render enum types |\n| `mapType` | `'indexedObject' \\| 'record'` | `'record'` | How to render map/dictionary types |\n| `moduleSystem` | `'CJS' \\| 'ESM'` | `'ESM'` | Module system to use |\n| `rawPropertyNames` | `boolean` | `false` | Use raw property names without transformation |\n| `useJavascriptReservedKeywords` | `boolean` | `true` | Allow JavaScript reserved keywords |\n\n**Common Presets**:\n\n| Preset | Description |\n|--------|-------------|\n| `TS_COMMON_PRESET` | Adds marshalling/unmarshalling methods |\n| `TS_DESCRIPTION_PRESET` | Adds JSDoc descriptions from schemas |\n| Custom presets | Define your own rendering behavior |\n\n**Generated Code Structure**:\n\n```typescript\n// Example generated class with TS_COMMON_PRESET\nexport class UserProfile {\n private _id?: string;\n private _email?: string;\n private _name?: string;\n\n constructor(input: {\n id?: string;\n email?: string;\n name?: string;\n }) {\n this._id = input.id;\n this._email = input.email;\n this._name = input.name;\n }\n\n get id(): string | undefined { return this._id; }\n set id(id: string | undefined) { this._id = id; }\n\n get email(): string | undefined { return this._email; }\n set email(email: string | undefined) { this._email = email; }\n\n get name(): string | undefined { return this._name; }\n set name(name: string | undefined) { this._name = name; }\n\n public marshal(): string {\n return JSON.stringify({\n id: this.id,\n email: this.email,\n name: this.name\n });\n }\n\n public static unmarshal(data: string): UserProfile {\n const obj = JSON.parse(data);\n return new UserProfile(obj);\n }\n}\n```\n\n## Integration Examples\n\n### With Channels Generator\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: 'asyncapi.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n renderers: [\n {\n preset: TS_COMMON_PRESET,\n options: { marshalling: true }\n }\n ],\n outputPath: './src/models'\n },\n {\n preset: 'channels',\n outputPath: './src/channels',\n protocols: ['nats', 'kafka']\n }\n ]\n};\n```\n\n### With Custom Validation\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: 'asyncapi.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n renderers: [\n {\n class: {\n additionalContent: ({ content, model }) => {\n return `${content}\n \n public validate(): boolean {\n // Custom validation logic\n return true;\n }`;\n }\n }\n }\n ],\n outputPath: './src/models'\n }\n ]\n};\n```\n\n## Resources\n\n- [Modelina Presets Documentation](https://github.com/asyncapi/modelina/blob/master/refs/heads/master/docs/presets.md)\n- [Modelina TypeScript Options](https://github.com/asyncapi/modelina/blob/master/docs/languages/TypeScript.md)\n- [Modelina Examples](https://github.com/asyncapi/modelina/tree/master/examples)", + }, + "generators/parameters": { + title: "Parameters", + content: "# Parameters\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'parameters',\n outputPath: './src/parameters',\n serializationType: 'json',\n language: 'typescript',\n }\n ]\n};\n```\n\n`parameters` preset is for generating models that represent typed models for parameters used in API operations.\n\nThis is supported through the following inputs: [`asyncapi`](#inputs), [`openapi`](#inputs)\n\nIt supports the following languages; `typescript`\n\n## Inputs\n\n### `asyncapi`\nThe `parameters` preset with `asyncapi` input generates all the parameters for each channel in the AsyncAPI document.\n\nThe return type is a map of channels and the model that represent the parameters.\n\n### `openapi`\nThe `parameters` preset with `openapi` input generates all the parameters for each operation in the OpenAPI document, including both path and query parameters.\n\nThe return type is a map of operations and the model that represent the parameters.\n\n## Typescript\n\n### AsyncAPI Functions\n\nEach generated AsyncAPI parameter class includes the following methods:\n\n#### Channel Parameter Substitution\n- `getChannelWithParameters(channel: string): string`: Replaces parameter placeholders in the channel/topic string with actual parameter values.\n\n```typescript\n// Example\nconst params = new UserSignedupParameters({\n myParameter: 'test',\n enumParameter: 'openapi'\n});\nconst channel = params.getChannelWithParameters('user/{my_parameter}/signup/{enum_parameter}');\n// Result: 'user/test/signup/openapi'\n```\n\n#### Static Factory Method\n- `static createFromChannel(msgSubject: string, channel: string, regex: RegExp): ParameterClass`: Creates a parameter instance by extracting values from a message subject using the provided channel template and regex.\n\n```typescript\n// Example\nconst params = UserSignedupParameters.createFromChannel(\n 'user.test.signup.openapi',\n 'user/{my_parameter}/signup/{enum_parameter}',\n /user\\.(.+)\\.signup\\.(.+)/\n);\n```\n\n### OpenAPI Functions\n\nEach generated OpenAPI parameter class includes comprehensive serialization and deserialization capabilities:\n\n#### Path Parameter Serialization\n- `serializePathParameters(): Record`: Serializes path parameters according to OpenAPI 2.0/3.x specification for URL path substitution.\n\n```typescript\n// Example\nconst params = new FindPetsByStatusParameters({\n status: 'available',\n categoryId: 123\n});\nconst pathParams = params.serializePathParameters();\n// Result: { status: 'available', categoryId: '123' }\n```\n\n#### Query Parameter Serialization\n- `serializeQueryParameters(): URLSearchParams`: Serializes query parameters according to OpenAPI specification with proper encoding and style handling.\n\n```typescript\n// Example\nconst queryParams = params.serializeQueryParameters();\nconst queryString = queryParams.toString();\n// Result: 'limit=10&offset=0&tags=dog,cat'\n```\n\n#### Complete URL Serialization\n- `serializeUrl(basePath: string): string`: Generates the complete URL with both path and query parameters properly serialized.\n\n```typescript\n// Example\nconst url = params.serializeUrl('/pet/findByStatus/{status}/{categoryId}');\n// Result: '/pet/findByStatus/available/123?limit=10&offset=0&tags=dog,cat'\n```\n\n#### URL Deserialization\n- `deserializeUrl(url: string): void`: Parses a URL and populates the instance properties from query parameters.\n\n```typescript\n// Example\nconst params = new FindPetsByStatusParameters({ status: 'available', categoryId: 123 });\nparams.deserializeUrl('/pet/findByStatus/available/123?limit=5&tags=dog,cat');\n// params.limit is now 5, params.tags is now ['dog', 'cat']\n```\n\n#### Static Factory Methods\n- `static fromUrl(url: string, basePath: string, ...requiredDefaults): ParameterClass`: Creates a new parameter instance from a complete URL by extracting both path and query parameters.\n\n```typescript\n// Example\nconst params = FindPetsByStatusParameters.fromUrl(\n '/pet/findByStatus/available/123?limit=5&tags=dog',\n '/pet/findByStatus/{status}/{categoryId}'\n);\n// params.status is 'available', params.categoryId is 123, params.limit is 5\n```\n\n### Parameter Style Support\n\nThe OpenAPI generator supports all OpenAPI parameter styles and serialization formats:\n\n#### Path Parameters\n- **simple** (default): `value1,value2` or `key1,value1,key2,value2`\n- **label**: `.value1.value2` or `.key1.value1.key2.value2`\n- **matrix**: `;param=value1,value2` or `;key1=value1;key2=value2`\n\n#### Query Parameters\n- **form** (default): `param=value1¶m=value2` (exploded) or `param=value1,value2`\n- **spaceDelimited**: `param=value1 value2`\n- **pipeDelimited**: `param=value1|value2`\n- **deepObject**: `param[key1]=value1¶m[key2]=value2`\n\n### Type Safety\n\nAll parameter classes are fully typed with:\n- Enum parameter types for restricted values\n- Required vs optional parameter distinction\n- Proper TypeScript casting for different parameter types (string, number, boolean, arrays)\n- Support for complex parameter schemas including nested objects and arrays\n\n### OpenAPI 2.0 Compatibility\n\nThe generator supports OpenAPI 2.0 `collectionFormat` parameter serialization:\n- `csv`: Comma-separated values\n- `ssv`: Space-separated values \n- `tsv`: Tab-separated values (treated as CSV)\n- `pipes`: Pipe-separated values\n- `multi`: Multiple parameter instances\n\nThese are automatically converted to equivalent OpenAPI 3.0 style/explode combinations for consistent handling.", + }, + "generators/payloads": { + title: "🐔 Payloads", + content: "# 🐔 Payloads\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'payloads',\n outputPath: './src/payloads',\n serializationType: 'json', \n language: 'typescript'\n }\n ]\n};\n```\n\n`payloads` preset is for generating models that represent typed models that can be serialized into message payloads for communication use-cases.\n\nThis is supported through the following inputs: `asyncapi`, `openapi`\n\nIt supports the following languages; [`typescript`](#typescript)\n\n## Languages\nEach language has a set of constraints which means that some typed model types are either supported or not, or it might just be the code generation library that does not yet support it.\n\n| | Circular models | Enums | Tuples | Arrays | Nested Arrays | Dictionaries | Json Serialization | Validation |\n|---|---|---|---|---|---|---|---|---|\n| **TypeScript** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n\n### TypeScript\n\nDependencies: \n- If validation enabled, [ajv](https://ajv.js.org/guide/getting-started.html): ^8.17.1\n\n#### Validation\nEach generated class includes built-in JSON Schema validation capabilities through two static methods:\n\n- `validate`: Validates data against the schema. Use this method when you want to validate data.\n\n```typescript\n// Example\nconst result = UserSignedUp.validate({ data: userData });\nif (!result.valid) {\n console.error('Validation errors:', result.errors);\n}\n```\n\n- `createValidator`: Creates a reusable validator function. Use this when you need to validate multiple instances of the same type and want to avoid recreating the validator each time.\n\n```typescript\n// Example\nconst validator = UserSignedUp.createValidator();\nconst result = UserSignedUp.validate({ data: userData, ajvValidatorFunction: validator });\nif (!result.valid) {\n console.error('Validation errors:', result.errors);\n}\n```\n\nBoth methods support custom Ajv instances and options for advanced validation scenarios.", + }, + "generators": { + title: "Generators", + content: "# Generators\nGenerators, or preset's are the core of **The Codegen Project**, that determines what is generated for your project.\n\nEach language and inputs have specific generators;\n\nAll available generators, across languages and inputs:\n- [`payloads`](./payloads.md)\n- [`parameters`](./parameters.md)\n- [`headers`](./headers.md)\n- [`types`](./types.md)\n- [`channels`](./channels.md)\n- [`client`](./client.md)\n- [`models`](./models.md)\n- [`custom`](./custom.md)\n\n| **Inputs** | [`payloads`](./payloads.md) | [`parameters`](./parameters.md) | [`headers`](./headers.md) | [`types`](./types.md) | [`channels`](./channels.md) | [`client`](./client.md) | [`models`](./models.md) | [`custom`](./custom.md) |\n|---|---|---|---|---|---|---|---|---|\n| AsyncAPI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n| OpenAPI | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |\n| JSON Schema | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |\n\n| **Languages** | [`payloads`](./payloads.md) | [`parameters`](./parameters.md) | [`headers`](./headers.md) | [`types`](./types.md) | [`channels`](./channels.md) | [`client`](./client.md) | [`models`](./models.md) | [`custom`](./custom.md) |\n|---|---|---|---|---|---|---|---|---|\n| TypeScript | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |", + }, + "generators/types": { + title: "Types", + content: "# Types\n\n```js\nexport default {\n ...,\n generators: [\n {\n preset: 'types',\n outputPath: './src/types',\n language: 'typescript',\n }\n ]\n};\n```\n\n`types` preset is for generating simple types and utility functions that change based on the AsyncAPI document.\n\nThis is supported through the following inputs: `asyncapi` and `openapi`\n\nIt supports the following languages; `typescript`\n\n## What it generates\nHere is what each language generate with this generator.\n\n### AsyncAPI\n\n- A type that represents all the channel addresses in the document (exported through `Topics`)\n- A type that represents all the channel IDs in the document (exported through `TopicIds`)\n- A function that converts channel addresses to channel IDs (exported through `ToTopicIds`)\n- A function that converts channel IDs to channel addresses (exported through `ToTopics`)\n- A constant record mapping channel IDs to channel addresses (exported through `TopicsMap`)\n\n**Example usage:**\n```typescript\nimport { Topics, TopicIds, ToTopics, ToTopicIds, TopicsMap } from './src/types/Types';\n\n// Use the Topics type for type safety\nconst myTopic: Topics = 'user/signedup/{userId}';\n\n// Convert channel ID to address\nconst address = ToTopics('userSignedup'); // Returns 'user/signedup/{userId}'\n\n// Convert address to channel ID\nconst channelId = ToTopicIds('user/signedup/{userId}'); // Returns 'userSignedup'\n\n// Use the map for direct lookup\nconst addressFromMap = TopicsMap['userSignedup']; // Returns 'user/signedup/{userId}'\n```\n\n### OpenAPI\n\n- A type that represents all the operation paths in the document (exported through `Paths`)\n- A type that represents all the operation IDs in the document (exported through `OperationIds`)\n- A function that converts operation IDs to paths (exported through `ToPath`)\n- A function that converts paths to operation IDs (exported through `ToOperationIds`)\n- A constant record mapping operation IDs to paths (exported through `PathsMap`)\n\n**Example usage:**\n```typescript\nimport { Paths, OperationIds, ToPath, ToOperationIds, PathsMap } from './src/types/Types';\n\n// Use the Paths type for type safety\nconst myPath: Paths = '/users/{userId}';\n\n// Convert operation ID to path\nconst path = ToPath('getUserById'); // Returns '/users/{userId}'\n\n// Convert path to operation IDs\nconst operationIds = ToOperationIds('/users/{userId}'); // Returns ['getUserById', 'updateUser', ...]\n\n// Use the map for direct lookup\nconst pathFromMap = PathsMap['getUserById']; // Returns '/users/{userId}'\n```", + }, + "getting-started/generators": { + title: "Understanding Generators", + content: "# Understanding Generators\n\nGenerators (also called \"presets\") are the core of **The Codegen Project**. They determine what code gets generated from your inputs. Think of generators as specialized code factories - each one produces a specific type of code that helps you build your application faster.\n\n## What Generators do you have?\n\nEach generator focuses on a specific aspect of your application:\n\n### Model Generators\nThese generators create data models and type definitions:\n\n- [`payloads` preset](../generators/payloads.md) - Type-safe message/payload classes with serialization and validation support\n- [`parameters` preset](../generators/parameters.md) - Type-safe parameter classes for API endpoints that make it easier to work with topics/paths/channels\n- [`headers` preset](../generators/headers.md) - Type-safe header classes for message protocols, with serialization and validation support\n- [`types` preset](../generators/types.md) - Shared type definitions and interfaces, which simplify your code in various ways\n- [`models` preset](../generators/models.md) - General-purpose models from JSON Schema\n\n### Communication Generators\nThese generators create code for interacting with APIs and message brokers:\n\n- [`channels` preset](../generators/channels.md) - Communication functions for message brokers, ensure the right message, headers, and topics/paths/channels are used\n- [`client` preset](../generators/client.md) - Wraps channels into a reusable wrappers, cant get more code then this.\n\n### Custom Generators\n- [`custom` preset](../generators/custom.md) - Your own custom code generation logic\n\n## How Generators Work\n\n### 1. Input Processing\nGenerators take your specifications (AsyncAPI, OpenAPI, or JSON Schema) and extract the relevant information:\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: './my-api.yaml'\n};\n```\n\n### 2. Code Generation\nBased on the generator configuration, The Codegen Project:\n- Parses your API specification\n- Extracts schemas, operations, channels, and other relevant data\n- Use whatever generators you added and outputs files to your specified directory\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: './my-api.yaml',\n generators: [\n {\n preset: 'payloads',\n outputPath: './src/payloads',\n language: 'typescript'\n }\n ]\n};\n```\n\n### 3. Generated Output\nEach generator produces different code, so have a look at each generator to get a full picture, but here is a few examples:\n\n**Payload Generator** produces:\n```typescript\nexport class UserSignup {\n constructor(data: UserSignupData) { /* ... */ }\n marshal(): string { /* ... */ }\n static unmarshal(json: string): UserSignup { /* ... */ }\n}\n```\n\n**Channels Generator** produces:\n```typescript\nexport const Protocols = {\n nats: {\n publishToUserSignup: ...,\n subscribeToUserSignup: ...,\n jetStreamPublishToUserSignup: ...\n },\n kafka: {\n publishToUserSignup: ...,\n subscribeToUserSignup: ...\n },\n // ... other protocols\n};\n```\n\n## Input Type Support\n\nDifferent generators work with different input types:\n\n| Generator | AsyncAPI | OpenAPI | JSON Schema |\n|-----------|----------|---------|-------------|\n| `payloads` | ✅ | ✅ | ❌ |\n| `parameters` | ✅ | ✅ | ❌ |\n| `headers` | ✅ | ✅ | ❌ |\n| `types` | ✅ | ✅ | ❌ |\n| `channels` | ✅ | ❌ | ❌ |\n| `client` | ✅ | ❌ | ❌ |\n| `models` | ✅ | ✅ | ✅ |\n| `custom` | ✅ | ✅ | ✅ |\n\n## Language Support\n\nCurrently, The Codegen Project supports:\n\n- **TypeScript** - Full support for all generators\n\nEach language has specific capabilities and constraints. Check the [generator documentation](../generators/README.md) for details.\n\n## Generator Dependencies\n\nSome generators automatically include dependencies on others:\n\n- **`channels`** generator automatically uses `payloads`, `headers`, and `parameters` generators if they're not already configured\n- This ensures you have all the necessary models and types for your channel functions\n\n## Configuration Options\n\nEach generator has its own set of configuration options, for example here is payloads:\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: './my-api.yaml',\n generators: [\n {\n preset: 'payloads',\n outputPath: './src/payloads',\n language: 'typescript',\n includeValidation: true,\n serializationType: 'json'\n }\n ]\n};\n```\n\n## Next Steps\n\n- **[Explore Generator Documentation](../generators/README.md)** - Detailed docs for each generator\n- **[Learn about Protocol Support](./protocols.md)** - How generators work with messaging protocols\n- **[Check Out Examples](../../examples/)** - See generators in action", + }, + "getting-started/protocols": { + title: "Understanding Protocols", + content: "# Understanding Protocols\n\nThe Codegen Project supports multiple messaging protocols, allowing you to generate protocol-specific code for your message-driven applications. This enables type-safe, production-ready communication code for various messaging systems.\n\n## What Are Protocols?\n\nProtocols define how messages are sent and received in distributed systems. The Codegen Project generates protocol-specific functions that handle the low-level details of message communication, so you can focus on your business logic.\n\n## Supported Protocols\n\nThe Codegen Project currently supports these messaging protocols:\n\n| Protocol | Description | Use Cases |\n|----------|-------------|-----------|\n| **[NATS](../protocols/nats.md)** | High-performance, cloud-native messaging system | Microservices, real-time systems, IoT |\n| **[Kafka](../protocols/kafka.md)** | Distributed event streaming platform | Event streaming, log aggregation, real-time analytics |\n| **[MQTT](../protocols/mqtt.md)** | Lightweight messaging protocol for IoT | IoT devices, mobile apps, low-bandwidth scenarios |\n| **[AMQP](../protocols/amqp.md)** | Advanced Message Queuing Protocol | Enterprise messaging, reliable message delivery |\n| **[EventSource](../protocols/eventsource.md)** | Server-Sent Events (SSE) protocol | Real-time web updates, streaming data to browsers |\n| **[HTTP Client](../protocols/http_client.md)** | RESTful API communication | HTTP APIs, REST services |\n| **[WebSocket](../protocols/websocket.md)** | Full-duplex communication protocol | Real-time web applications, bidirectional communication |\n\n## How Protocol Support Works\nEach protocol usually requires specific dependencies and should be installed in your project (if its noy already are). Make sure to check the documentation for each protocol to figure out which one you need!\n\n### 1. Protocol Configuration\n\nProtocols are configured through the [`channels`](../generators/channels.md) generator:\n\n```js\nexport default {\n inputType: 'asyncapi',\n inputPath: './my-api.yaml',\n generators: [\n {\n preset: 'channels',\n outputPath: './src/__gen__/',\n language: 'typescript',\n protocols: ['nats', 'kafka'] // Specify which protocols to generate\n }\n ]\n};\n```\n\n### 2. Generated Protocol Functions\n\nThe `channels` generator creates protocol-specific functions for each channel in your AsyncAPI specification:\n\n```typescript\nimport { Protocols } from './src/__gen__/index';\n\nconst { nats, kafka } = Protocols;\n\n// NATS functions\nawait nats.publishToUserSignup(connection, message);\nawait nats.subscribeToUserSignup(connection, callback);\n\n// Kafka functions\nawait kafka.publishToUserSignup(producer, message);\nawait kafka.subscribeToUserSignup(consumer, callback);\n```\n\nEach protocol has unique features that are reflected in the generated code:\n\n**NATS** supports:\n- Core publish/subscribe\n- JetStream (persistent messaging)\n- Request/reply patterns\n\n**Kafka** supports:\n- Producer/consumer patterns\n- Consumer groups\n- Topic partitioning\n\n**MQTT** supports:\n- QoS levels (0, 1, 2)\n- Retained messages\n- User properties (headers)\n\n**AMQP** supports:\n- Exchanges and queues\n- Routing patterns\n- Message acknowledgments\n\n## Protocol Selection\n\n### Single Protocol\nGenerate code for one protocol:\n\n```js\n{\n preset: 'channels',\n protocols: ['nats']\n}\n```\nor generate for multiple protocols at once:\n\n```js\n{\n preset: 'channels',\n protocols: ['nats', 'kafka', 'mqtt']\n}\n```\n\n\n## Protocol-Specific Options\n\nSome generators support protocol-specific configuration options:\n\n```js\n{\n preset: 'channels',\n protocols: ['kafka'],\n kafkaTopicSeparator: '.', // Customize topic separator\n eventSourceDependency: '@microsoft/fetch-event-source' // Custom EventSource dependency\n}\n```\n\n## Generated Code Structure\n\nProtocol functions are organized by protocol:\n\n```typescript\nexport const Protocols = {\n nats: {\n publishToUserSignup: ...,\n subscribeToUserSignup: ...,\n jetStreamPublishToUserSignup: ...\n },\n kafka: {\n publishToUserSignup: ...,\n subscribeToUserSignup: ...\n },\n // ... other protocols\n};\n```\n\n## Next Steps\n\n- **[Explore Protocol Documentation](../protocols/)** - Detailed docs for each protocol\n- **[Learn about Channels Generator](../generators/channels.md)** - How to configure protocol generation\n- **[Check Out Examples](../../examples/)** - See the code generation in action\n- **[Understanding Generators](./generators.md)** - Learn how generators work", + }, + "getting-started": { + title: "Getting Started", + content: "# Getting Started\n\nIts simple, [install the CLI](#install) into your project or machine, [setup the Codegen configuration file](#initialize) to include all the code your heart desire, customize it, and generate it at build time or whenever you feel like it.\n\n## Install\nInstalling the CLI can be done inside a project or within your system.\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Package managerMacOS x64MacOS arm64Windows x64Windows x32Linux (Debian)Linux (Others)
\n
\n\n#### NPM\n\n```sh\nnpm install --save-dev @the-codegen-project/cli\n\nnpm install -g @the-codegen-project/cli\n```\n\n#### Yarn\n\n```sh\nyarn add @the-codegen-project/cli\n```\n\n#### Pnpm\n\n```sh\npnpm add @the-codegen-project/cli\n```\n\n#### Bun\n\n```sh\nbun add @the-codegen-project/cli\n```\n\n
\n
\n
\n\n#### Download\n```sh\ncurl -OL https://github.com/the-codegen-project/cli/releases/latest/download/codegen.x64.pkg\n```\n\n#### Install\n```sh\nsudo installer -pkg codegen.x64.pkg -target /\n```\n\n
\n
\n
\n\n#### Download\n```sh\ncurl -OL https://github.com/the-codegen-project/cli/releases/latest/download/codegen.arm64.pkg\n```\n#### Install\n\n```sh\nsudo installer -pkg codegen.arm64.pkg -target /\n```\n
\n
\n \n \n \n \n
\n\n#### Download\n```sh\ncurl -OL https://github.com/the-codegen-project/cli/releases/latest/download/codegen.deb\n```\n\n#### Install\n```sh\nsudo apt install ./codegen.deb\n```\n
\n
\n
\n\n#### Download\n```sh\ncurl -OL https://github.com/the-codegen-project/cli/releases/latest/download/codegen.tar.gz\n```\n\n#### Install\n\n```sh\ntar -xzf codegen.tar.gz\n```\n\n#### Symlink\n```sh\nln -s /bin/codegen /usr/local/bin/codegen\n```\n\n
\n
\n\nYou can find all the possible commands in [the usage documentation](../usage.md).\n\n## Initialize\nAdd a configuration file, either manually or through the CLI;\n```sh\ncodegen init\n```\n\n
\n\n\n![Initialize The Codegen Project](../../static/assets/videos/initialize.gif)\n\nCustomize it to your heart's desire! [Each generator has unique set of options](../generators/README.md)\n\n
\n\n## Integrate\nWith your configuration file in hand, time to integrate it into your project and generate some code! Checkout [all the integrations](../../examples/) for inspiration how to do it.\n\n### Generate Code\n\n#### One-time Generation\n```sh\n# Generate code once\ncodegen generate\n\n# Generate with specific config file\ncodegen generate ./my-config.js\n```\n\n#### Development with Watch Mode\nFor active development, use watch mode to automatically regenerate code when your input files change:\n\n```sh\n# Watch for changes in the input file specified in your config\ncodegen generate --watch\n\n# Watch for changes in a specific file or directory\ncodegen generate --watch --watchPath ./my-asyncapi.yaml\n\n# Short form\ncodegen generate -w -p ./schemas/\n```\n\n**Pro tip:** Use watch mode during development to keep your generated code in sync with your API specifications. Press `Ctrl+C` to stop watching.\n\n## What's Next?\n\nNow that you've installed the CLI and generated your first code, here's where to go next:\n\n### Understanding Generators\nLearn how generators work and what they can do for your project. Generators are the core of The Codegen Project - they determine what code gets generated from your API specifications.\n\n👉 **[Learn about Generators →](./generators.md)**\n\n### Protocol Support\nDiscover how The Codegen Project supports various messaging protocols like NATS, Kafka, MQTT, and more. Understand how protocol-specific code generation works and which protocols are available.\n\n👉 **[Learn about Protocol Support →](./protocols.md)**\n\n### Explore Further\n- **[Generator Documentation](../generators/README.md)** - Detailed documentation for each generator type\n- **[Protocol Documentation](../protocols/)** - Complete protocol reference and implementation details\n- **[Input Types](../inputs/)** - Learn about AsyncAPI, OpenAPI, and JSON Schema support\n- **[Examples](../../examples/)** - Real-world examples and integration patterns", + }, + "inputs/asyncapi": { + title: "AsyncAPI", + content: "# AsyncAPI\nSupported versions: 2.0 -> 3.0\n\nIf you arrive from the AsyncAPI community, you might be wondering what this project is and how does it relate?\n\nThe Codegen Project was started because of a need for a code generator that;\n1. could easily be integrated into development workflows\n2. can easily be extended or customized to specific use-cases\n3. forms a community across communities in languages and standards\n4. are financially sustainable long term through open source at it's core.\n\nThere is a lot of overlap with existing tooling, however the idea is to form the same level of quality that the OpenAPI Generator provides to OpenAPI community for HTTP, for AsyncAPI and **any** protocol (including HTTP), and the usability of the Apollo GraphQL generator. How are we gonna achieve it? Together.\n\n| **Presets** | AsyncAPI | \n|---|---|\n| [`payloads`](../generators/payloads.md) | ✅ |\n| [`parameters`](../generators/parameters.md) | ✅ |\n| [`headers`](../generators/headers.md) | ✅ |\n| [`types`](../generators/types.md) | ✅ |\n| [`channels`](../generators/channels.md) | ✅ |\n| [`client`](../generators/client.md) | ✅ |\n| [`custom`](../generators/custom.md) | ✅ |\n| [`models`](../generators/custom.md) | ✅ |\n\n## Basic AsyncAPI Document Structure\n\nHere's a complete basic AsyncAPI document example to get you started:\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"User Service API\",\n \"version\": \"1.0.0\",\n \"description\": \"API for user management events\"\n },\n \"channels\": {\n \"userSignedup\": {\n \"address\": \"user/signedup/{userId}/{region}\",\n \"parameters\": {\n \"userId\": {\n \"description\": \"The unique identifier for the user\"\n },\n \"region\": {\n \"description\": \"The geographic region\",\n \"enum\": [\"us-east\", \"us-west\", \"eu-central\"]\n }\n },\n \"messages\": {\n \"UserSignedUp\": {\n \"$ref\": \"#/components/messages/UserSignedUp\"\n }\n }\n }\n },\n \"operations\": {\n \"sendUserSignedup\": {\n \"action\": \"send\",\n \"channel\": {\n \"$ref\": \"#/channels/userSignedup\"\n },\n \"messages\": [\n {\n \"$ref\": \"#/channels/userSignedup/messages/UserSignedUp\"\n }\n ]\n },\n \"receiveUserSignedup\": {\n \"action\": \"receive\",\n \"channel\": {\n \"$ref\": \"#/channels/userSignedup\"\n },\n \"messages\": [\n {\n \"$ref\": \"#/channels/userSignedup/messages/UserSignedUp\"\n }\n ]\n }\n },\n \"components\": {\n \"messages\": {\n \"UserSignedUp\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/UserSignedUpPayload\"\n },\n \"headers\": {\n \"$ref\": \"#/components/schemas/UserHeaders\"\n }\n }\n },\n \"schemas\": {\n \"UserSignedUpPayload\": {\n \"type\": \"object\",\n \"properties\": {\n \"display_name\": {\n \"type\": \"string\",\n \"description\": \"Name of the user\"\n },\n \"email\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"description\": \"Email of the user\"\n },\n \"created_at\": {\n \"type\": \"string\",\n \"format\": \"date-time\",\n \"description\": \"When the user was created\"\n }\n },\n \"required\": [\"display_name\", \"email\"]\n },\n \"UserHeaders\": {\n \"type\": \"object\",\n \"properties\": {\n \"correlation_id\": {\n \"type\": \"string\",\n \"description\": \"Correlation ID for tracking\"\n },\n \"source\": {\n \"type\": \"string\",\n \"description\": \"Source system\"\n }\n }\n }\n }\n }\n}\n```\n\n## Extensions\n\nTo customize the code generation through the AsyncAPI document, use the `x-the-codegen-project` [extension object](https://www.asyncapi.com/docs/reference/specification/v3.0.0#specificationExtensions) with the following properties:\n\n### Channel Extensions\n\n`channelName`, string, customize the name of the functions generated for the channel, use this to overwrite the automatically determined name for models and functions. This will be used by the following generators; [payloads](../generators/payloads.md), [parameters](../generators/parameters.md) and [channels](../generators/channels.md). \n\n`functionTypeMapping`, [ChannelFunctionTypes](https://the-codegen-project.org/docs/api/enumerations/ChannelFunctionTypes), customize which generators to generate for the given channel, use this to specify further which functions we render. This will be used by the following generators; [channels](../generators/channels.md). \n\n#### Example: Custom Channel Configuration\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Custom Channel Example\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"user-events\": {\n \"address\": \"events/user/{action}\",\n \"parameters\": {\n \"action\": {\n \"enum\": [\"created\", \"updated\", \"deleted\"]\n }\n },\n \"messages\": {\n \"UserEvent\": {\n \"payload\": {\n \"type\": \"object\",\n \"properties\": {\n \"userId\": {\"type\": \"string\"},\n \"action\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"}\n }\n }\n }\n },\n \"x-the-codegen-project\": {\n \"channelName\": \"UserEventChannel\",\n \"functionTypeMapping\": [\"event_source_express\", \"kafka_publish\"]\n }\n }\n }\n}\n```\n\n### Operation Extensions\n\n`functionTypeMapping`, [ChannelFunctionTypes](https://the-codegen-project.org/docs/api/enumerations/ChannelFunctionTypes), customize which generators to generate for the given operation, use this to specify further which functions we render. This will be used by the following generators; [channels](../generators/channels.md). \n\n#### Example: Custom Operation Configuration\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Custom Operation Example\",\n \"version\": \"1.0.0\"\n },\n \"operations\": {\n \"publishUserEvent\": {\n \"action\": \"send\",\n \"channel\": {\n \"$ref\": \"#/channels/user-events\"\n },\n \"messages\": [\n {\"$ref\": \"#/channels/user-events/messages/UserEvent\"}\n ],\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"kafka_publish\"]\n }\n },\n \"subscribeToUserEvents\": {\n \"action\": \"receive\",\n \"channel\": {\n \"$ref\": \"#/channels/user-events\"\n },\n \"messages\": [\n {\"$ref\": \"#/channels/user-events/messages/UserEvent\"}\n ],\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"kafka_subscribe\"]\n }\n }\n }\n}\n```\n\n## Protocol Support\n\n### HTTP Client\n\nUse HTTP bindings to generate HTTP client code. Supports all standard HTTP methods and status codes.\n\n#### Example: REST API with Multiple HTTP Methods\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"User Management API\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"users\": {\n \"address\": \"/users/{userId}\",\n \"parameters\": {\n \"userId\": {\n \"description\": \"User identifier\"\n }\n },\n \"messages\": {\n \"UserRequest\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/User\"\n }\n },\n \"UserResponse\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/User\"\n },\n \"bindings\": {\n \"http\": {\n \"statusCode\": 200\n }\n }\n },\n \"NotFound\": {\n \"payload\": {\n \"type\": \"object\",\n \"properties\": {\n \"error\": {\"type\": \"string\"},\n \"code\": {\"type\": \"string\"}\n }\n },\n \"bindings\": {\n \"http\": {\n \"statusCode\": 404\n }\n }\n }\n }\n }\n },\n \"operations\": {\n \"createUser\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [{\"$ref\": \"#/channels/users/messages/UserRequest\"}],\n \"bindings\": {\n \"http\": {\"method\": \"POST\"}\n },\n \"reply\": {\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [{\"$ref\": \"#/channels/users/messages/UserResponse\"}]\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"http_client\"]\n }\n },\n \"getUser\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [],\n \"bindings\": {\n \"http\": {\"method\": \"GET\"}\n },\n \"reply\": {\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [\n {\"$ref\": \"#/channels/users/messages/UserResponse\"},\n {\"$ref\": \"#/channels/users/messages/NotFound\"}\n ]\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"http_client\"]\n }\n },\n \"updateUser\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [{\"$ref\": \"#/channels/users/messages/UserRequest\"}],\n \"bindings\": {\n \"http\": {\"method\": \"PUT\"}\n },\n \"reply\": {\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [{\"$ref\": \"#/channels/users/messages/UserResponse\"}]\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"http_client\"]\n }\n },\n \"deleteUser\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [],\n \"bindings\": {\n \"http\": {\"method\": \"DELETE\"}\n },\n \"reply\": {\n \"channel\": {\"$ref\": \"#/channels/users\"},\n \"messages\": [{\"$ref\": \"#/channels/users/messages/UserResponse\"}]\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"http_client\"]\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"User\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\"type\": \"string\"},\n \"name\": {\"type\": \"string\"},\n \"email\": {\"type\": \"string\", \"format\": \"email\"},\n \"created_at\": {\"type\": \"string\", \"format\": \"date-time\"}\n },\n \"required\": [\"name\", \"email\"]\n }\n }\n }\n}\n```\n\n### Kafka\n\nGenerate Kafka producers and consumers with proper serialization.\n\n#### Example: Kafka Event Streaming\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Order Processing Events\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"order-events\": {\n \"address\": \"orders.{eventType}.{region}\",\n \"parameters\": {\n \"eventType\": {\n \"enum\": [\"created\", \"updated\", \"cancelled\", \"completed\"]\n },\n \"region\": {\n \"enum\": [\"us\", \"eu\", \"asia\"]\n }\n },\n \"messages\": {\n \"OrderEvent\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/OrderEvent\"\n },\n \"headers\": {\n \"$ref\": \"#/components/schemas/EventHeaders\"\n }\n }\n }\n }\n },\n \"operations\": {\n \"publishOrderEvent\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/order-events\"},\n \"messages\": [{\"$ref\": \"#/channels/order-events/messages/OrderEvent\"}],\n \"bindings\": {\n \"kafka\": {\n \"clientId\": \"order-service\",\n \"groupId\": \"order-processors\"\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"kafka_publish\"]\n }\n },\n \"subscribeToOrderEvents\": {\n \"action\": \"receive\",\n \"channel\": {\"$ref\": \"#/channels/order-events\"},\n \"messages\": [{\"$ref\": \"#/channels/order-events/messages/OrderEvent\"}],\n \"bindings\": {\n \"kafka\": {\n \"groupId\": \"order-processors\",\n \"clientId\": \"order-consumer\"\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"kafka_subscribe\"]\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"OrderEvent\": {\n \"type\": \"object\",\n \"properties\": {\n \"orderId\": {\"type\": \"string\"},\n \"customerId\": {\"type\": \"string\"},\n \"amount\": {\"type\": \"number\"},\n \"currency\": {\"type\": \"string\"},\n \"status\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"}\n },\n \"required\": [\"orderId\", \"customerId\", \"amount\", \"status\"]\n },\n \"EventHeaders\": {\n \"type\": \"object\",\n \"properties\": {\n \"correlationId\": {\"type\": \"string\"},\n \"source\": {\"type\": \"string\"},\n \"version\": {\"type\": \"string\"}\n }\n }\n }\n }\n}\n```\n\n### NATS\n\nGenerate NATS request/reply patterns and pub/sub functionality.\n\n#### Example: NATS Request-Reply Pattern\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"User Service NATS API\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"user-service\": {\n \"address\": \"user.service.{operation}\",\n \"parameters\": {\n \"operation\": {\n \"enum\": [\"get\", \"create\", \"update\", \"delete\"]\n }\n },\n \"messages\": {\n \"UserRequest\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/UserRequest\"\n }\n },\n \"UserResponse\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/UserResponse\"\n }\n }\n }\n }\n },\n \"operations\": {\n \"requestUserOperation\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/user-service\"},\n \"messages\": [{\"$ref\": \"#/channels/user-service/messages/UserRequest\"}],\n \"reply\": {\n \"channel\": {\"$ref\": \"#/channels/user-service\"},\n \"messages\": [{\"$ref\": \"#/channels/user-service/messages/UserResponse\"}]\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"nats_request\"]\n }\n },\n \"replyToUserOperation\": {\n \"action\": \"receive\",\n \"channel\": {\"$ref\": \"#/channels/user-service\"},\n \"messages\": [{\"$ref\": \"#/channels/user-service/messages/UserRequest\"}],\n \"reply\": {\n \"channel\": {\"$ref\": \"#/channels/user-service\"},\n \"messages\": [{\"$ref\": \"#/channels/user-service/messages/UserResponse\"}]\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"nats_reply\"]\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"UserRequest\": {\n \"type\": \"object\",\n \"properties\": {\n \"operation\": {\"type\": \"string\"},\n \"userId\": {\"type\": \"string\"},\n \"data\": {\"type\": \"object\"}\n },\n \"required\": [\"operation\"]\n },\n \"UserResponse\": {\n \"type\": \"object\",\n \"properties\": {\n \"success\": {\"type\": \"boolean\"},\n \"data\": {\"type\": \"object\"},\n \"error\": {\"type\": \"string\"}\n },\n \"required\": [\"success\"]\n }\n }\n }\n}\n```\n\n### MQTT\n\nGenerate MQTT publish/subscribe clients with QoS levels.\n\n#### Example: IoT Device Communications\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"IoT Device Management\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"device-telemetry\": {\n \"address\": \"devices/{deviceId}/telemetry/{sensorType}\",\n \"parameters\": {\n \"deviceId\": {\n \"description\": \"Unique device identifier\"\n },\n \"sensorType\": {\n \"enum\": [\"temperature\", \"humidity\", \"pressure\", \"motion\"]\n }\n },\n \"messages\": {\n \"TelemetryData\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/TelemetryData\"\n }\n }\n }\n },\n \"device-commands\": {\n \"address\": \"devices/{deviceId}/commands\",\n \"parameters\": {\n \"deviceId\": {\n \"description\": \"Unique device identifier\"\n }\n },\n \"messages\": {\n \"DeviceCommand\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/DeviceCommand\"\n }\n }\n }\n }\n },\n \"operations\": {\n \"publishTelemetry\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/device-telemetry\"},\n \"messages\": [{\"$ref\": \"#/channels/device-telemetry/messages/TelemetryData\"}],\n \"bindings\": {\n \"mqtt\": {\n \"qos\": 1,\n \"retain\": false\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"mqtt_publish\"]\n }\n },\n \"subscribeToTelemetry\": {\n \"action\": \"receive\",\n \"channel\": {\"$ref\": \"#/channels/device-telemetry\"},\n \"messages\": [{\"$ref\": \"#/channels/device-telemetry/messages/TelemetryData\"}],\n \"bindings\": {\n \"mqtt\": {\n \"qos\": 1\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"mqtt_subscribe\"]\n }\n },\n \"sendCommand\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/device-commands\"},\n \"messages\": [{\"$ref\": \"#/channels/device-commands/messages/DeviceCommand\"}],\n \"bindings\": {\n \"mqtt\": {\n \"qos\": 2,\n \"retain\": true\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"mqtt_publish\"]\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"TelemetryData\": {\n \"type\": \"object\",\n \"properties\": {\n \"deviceId\": {\"type\": \"string\"},\n \"sensorType\": {\"type\": \"string\"},\n \"value\": {\"type\": \"number\"},\n \"unit\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"},\n \"location\": {\n \"type\": \"object\",\n \"properties\": {\n \"latitude\": {\"type\": \"number\"},\n \"longitude\": {\"type\": \"number\"}\n }\n }\n },\n \"required\": [\"deviceId\", \"sensorType\", \"value\", \"timestamp\"]\n },\n \"DeviceCommand\": {\n \"type\": \"object\",\n \"properties\": {\n \"command\": {\"type\": \"string\"},\n \"parameters\": {\"type\": \"object\"},\n \"commandId\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"}\n },\n \"required\": [\"command\", \"commandId\"]\n }\n }\n }\n}\n```\n\n### AMQP\n\nGenerate AMQP producers and consumers for message queuing.\n\n#### Example: Order Processing Queue\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Order Processing Queue\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"order-queue\": {\n \"address\": \"orders.processing\",\n \"messages\": {\n \"OrderMessage\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/Order\"\n },\n \"headers\": {\n \"$ref\": \"#/components/schemas/MessageHeaders\"\n }\n }\n }\n },\n \"order-dlq\": {\n \"address\": \"orders.dead-letter\",\n \"messages\": {\n \"FailedOrderMessage\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/FailedOrder\"\n }\n }\n }\n }\n },\n \"operations\": {\n \"publishOrder\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/order-queue\"},\n \"messages\": [{\"$ref\": \"#/channels/order-queue/messages/OrderMessage\"}],\n \"bindings\": {\n \"amqp\": {\n \"exchange\": {\n \"name\": \"orders\",\n \"type\": \"topic\",\n \"durable\": true\n },\n \"routingKey\": \"order.created\"\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"amqp_publish\"]\n }\n },\n \"consumeOrders\": {\n \"action\": \"receive\",\n \"channel\": {\"$ref\": \"#/channels/order-queue\"},\n \"messages\": [{\"$ref\": \"#/channels/order-queue/messages/OrderMessage\"}],\n \"bindings\": {\n \"amqp\": {\n \"queue\": {\n \"name\": \"order-processing-queue\",\n \"durable\": true,\n \"exclusive\": false,\n \"autoDelete\": false\n },\n \"ack\": true\n }\n },\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"amqp_consume\"]\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"Order\": {\n \"type\": \"object\",\n \"properties\": {\n \"orderId\": {\"type\": \"string\"},\n \"customerId\": {\"type\": \"string\"},\n \"items\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"productId\": {\"type\": \"string\"},\n \"quantity\": {\"type\": \"integer\"},\n \"price\": {\"type\": \"number\"}\n }\n }\n },\n \"totalAmount\": {\"type\": \"number\"},\n \"currency\": {\"type\": \"string\"},\n \"orderDate\": {\"type\": \"string\", \"format\": \"date-time\"}\n },\n \"required\": [\"orderId\", \"customerId\", \"items\", \"totalAmount\"]\n },\n \"FailedOrder\": {\n \"type\": \"object\",\n \"properties\": {\n \"orderId\": {\"type\": \"string\"},\n \"error\": {\"type\": \"string\"},\n \"retryCount\": {\"type\": \"integer\"},\n \"failedAt\": {\"type\": \"string\", \"format\": \"date-time\"}\n }\n },\n \"MessageHeaders\": {\n \"type\": \"object\",\n \"properties\": {\n \"messageId\": {\"type\": \"string\"},\n \"correlationId\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"},\n \"priority\": {\"type\": \"integer\", \"minimum\": 0, \"maximum\": 255}\n }\n }\n }\n }\n}\n```\n\n### EventSource\n\nGenerate Server-Sent Events (SSE) implementations for real-time updates.\n\n#### Example: Real-time Notifications\n\n```json\n{\n \"asyncapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"Real-time Notifications\",\n \"version\": \"1.0.0\"\n },\n \"channels\": {\n \"user-notifications\": {\n \"address\": \"/events/users/{userId}/notifications\",\n \"parameters\": {\n \"userId\": {\n \"description\": \"User identifier for targeted notifications\"\n }\n },\n \"messages\": {\n \"Notification\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/Notification\"\n }\n },\n \"SystemAlert\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/SystemAlert\"\n }\n }\n }\n },\n \"live-updates\": {\n \"address\": \"/events/live/{topic}\",\n \"parameters\": {\n \"topic\": {\n \"enum\": [\"stock-prices\", \"sports-scores\", \"weather-alerts\"]\n }\n },\n \"messages\": {\n \"LiveUpdate\": {\n \"payload\": {\n \"$ref\": \"#/components/schemas/LiveUpdate\"\n }\n }\n }\n }\n },\n \"operations\": {\n \"streamUserNotifications\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/user-notifications\"},\n \"messages\": [\n {\"$ref\": \"#/channels/user-notifications/messages/Notification\"},\n {\"$ref\": \"#/channels/user-notifications/messages/SystemAlert\"}\n ],\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"event_source_express\"]\n }\n },\n \"streamLiveUpdates\": {\n \"action\": \"send\",\n \"channel\": {\"$ref\": \"#/channels/live-updates\"},\n \"messages\": [{\"$ref\": \"#/channels/live-updates/messages/LiveUpdate\"}],\n \"x-the-codegen-project\": {\n \"functionTypeMapping\": [\"event_source_express\"]\n }\n }\n },\n \"components\": {\n \"schemas\": {\n \"Notification\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\"type\": \"string\"},\n \"userId\": {\"type\": \"string\"},\n \"type\": {\"type\": \"string\", \"enum\": [\"info\", \"warning\", \"error\", \"success\"]},\n \"title\": {\"type\": \"string\"},\n \"message\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"},\n \"actionUrl\": {\"type\": \"string\", \"format\": \"uri\"},\n \"read\": {\"type\": \"boolean\", \"default\": false}\n },\n \"required\": [\"id\", \"userId\", \"type\", \"title\", \"message\", \"timestamp\"]\n },\n \"SystemAlert\": {\n \"type\": \"object\",\n \"properties\": {\n \"alertId\": {\"type\": \"string\"},\n \"severity\": {\"type\": \"string\", \"enum\": [\"low\", \"medium\", \"high\", \"critical\"]},\n \"service\": {\"type\": \"string\"},\n \"message\": {\"type\": \"string\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"},\n \"resolved\": {\"type\": \"boolean\", \"default\": false}\n },\n \"required\": [\"alertId\", \"severity\", \"service\", \"message\", \"timestamp\"]\n },\n \"LiveUpdate\": {\n \"type\": \"object\",\n \"properties\": {\n \"topic\": {\"type\": \"string\"},\n \"data\": {\"type\": \"object\"},\n \"timestamp\": {\"type\": \"string\", \"format\": \"date-time\"},\n \"sequence\": {\"type\": \"integer\"}\n },\n \"required\": [\"topic\", \"data\", \"timestamp\"]\n }\n }\n }\n}\n```\n\n## FAQ\n\n### How does it relate to AsyncAPI Generator and templates?\nIt is fairly similar in functionality except in some key areas.\n\nTemplates are similar to presets except you can bind presets together to make it easier to render code down stream.\n\nThe AsyncAPI Generator is like the core of the Codegen Project, however it does not enable different inputs than AsyncAPI documents. \n\n### Can I mix multiple protocols in one document?\nYes! You can define operations with different protocol bindings in the same AsyncAPI document. Use the `x-the-codegen-project` extension to specify which generators to use for each operation.\n\n### How do I handle versioning?\n\nShort answer: Use the `info.version` field in your AsyncAPI document and consider using separate documents for major version changes. You can also use channel addressing patterns to include version information.\n\nLong answer: It's hard to version APIs, there are tons of resources how to handle versioning of your API which is far beyond what we can offer here.\n\n### Can I customize the generated code structure?\nYes, use the `x-the-codegen-project` extension properties to customize channel names, function mappings, and other generation aspects or the configuration file [while taking a look at the different generators](../generators).", + }, + "inputs/jsonschema": { + title: "JSON Schema", + content: "# JSON Schema\n\nJSON Schema input support enables you to generate TypeScript models directly from JSON Schema documents. This is particularly useful when you have standalone JSON Schema files that define your data structures.\n\n## Supported Generators\n\nThe JSON Schema input type supports the following generators:\n\n| Preset | JSON Schema |\n|-----------|---------|\n| [`models`](../generators/models.md) | ✅ |\n| [`custom`](../generators/custom.md) | ✅ |\n\n## Configuration\n\n### Basic Configuration\n\n```js\nexport default {\n inputType: 'jsonschema',\n inputPath: './user-schema.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n outputPath: './src/models'\n }\n ]\n};\n```\n\n### Advanced Configuration with Modelina Options\n\n```js\nexport default {\n inputType: 'jsonschema',\n inputPath: './complex-schema.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n outputPath: './src/models',\n options: {\n modelType: 'class',\n enumType: 'enum',\n mapType: 'record',\n rawPropertyNames: false,\n useJavascriptReservedKeywords: false\n },\n renderers: [\n {\n class: {\n property: ({ content, property }) => {\n return `/** ${property.property.description || 'Auto-generated property'} */\\n${content}`;\n }\n }\n }\n ]\n }\n ]\n};\n```\n\n## Examples\n\n### Simple User Schema\n\n**Input: `user-schema.json`**\n```json\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"title\": \"User\",\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"format\": \"uuid\"\n },\n \"name\": {\n \"type\": \"string\"\n },\n \"email\": {\n \"type\": \"string\",\n \"format\": \"email\"\n },\n \"age\": {\n \"type\": \"integer\",\n \"minimum\": 0\n }\n },\n \"required\": [\"id\", \"name\", \"email\"]\n}\n```\n\n**Configuration: `codegen.mjs`**\n```js\nexport default {\n inputType: 'jsonschema',\n inputPath: './user-schema.json',\n language: 'typescript',\n generators: [\n {\n preset: 'models',\n outputPath: './src/models'\n }\n ]\n};\n```\n\n**Generated Output: `src/models/User.ts`**\n```typescript\nexport class User {\n private _id: string;\n private _name: string;\n private _email: string;\n private _age?: number;\n\n constructor(input: {\n id: string,\n name: string,\n email: string,\n age?: number,\n }) {\n this._id = input.id;\n this._name = input.name;\n this._email = input.email;\n this._age = input.age;\n }\n\n get id(): string { return this._id; }\n get name(): string { return this._name; }\n get email(): string { return this._email; }\n get age(): number | undefined { return this._age; }\n\n public marshal(): string {\n return JSON.stringify({\n id: this.id,\n name: this.name,\n email: this.email,\n age: this.age,\n });\n }\n\n public static unmarshal(json: string): User {\n const obj = JSON.parse(json);\n return new User(obj);\n }\n}\n```\n\n### Complex Schema with Definitions\n\n**Input: `complex-schema.json`**\n```json\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"Address\": {\n \"type\": \"object\",\n \"properties\": {\n \"street\": { \"type\": \"string\" },\n \"city\": { \"type\": \"string\" },\n \"zipCode\": { \"type\": \"string\" }\n },\n \"required\": [\"street\", \"city\", \"zipCode\"]\n }\n },\n \"type\": \"object\",\n \"properties\": {\n \"person\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": { \"type\": \"string\" },\n \"address\": { \"$ref\": \"#/definitions/Address\" }\n },\n \"required\": [\"name\"]\n }\n }\n}\n```\n\nThis will generate both `Person` and `Address` classes with proper type relationships.\n\n## File Format Support\n\n### JSON Format\n```json\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"example\": { \"type\": \"string\" }\n }\n}\n```\n\n### YAML Format\n```yaml\n$schema: \"http://json-schema.org/draft-07/schema#\"\ntype: object\nproperties:\n example:\n type: string\n```", + }, + "inputs/openapi": { + title: "OpenAPI", + content: "# OpenAPI\n\nInput support; `openapi`\n\n- OpenAPI 3.0.x\n- OpenAPI 3.1.x\n- OpenAPI 2.0.0 (Swagger)\n\n| **Presets** | OpenAPI | \n|---|---|\n| [`payloads`](../generators/payloads.md) | ✅ |\n| [`parameters`](../generators/parameters.md) | ✅ |\n| [`headers`](../generators/headers.md) | ✅ |\n| [`types`](../generators/types.md) | ✅ |\n| [`channels`](../generators/channels.md) | ❌ |\n| [`client`](../generators/client.md) | ❌ |\n| [`custom`](../generators/custom.md) | ✅ |\n| [`models`](../generators/custom.md) | ✅ |\n\n## Basic Usage\n\n### Configuration\n\nCreate a configuration file that specifies OpenAPI as the input type:\n\n```json\n{\n \"inputType\": \"openapi\",\n \"inputPath\": \"./api/openapi.yaml\",\n \"language\": \"typescript\",\n \"generators\": [ ... ]\n}\n```\n\n## Advanced Features\n\n### External References\n\nThe OpenAPI parser automatically resolves external `$ref` references:\n\n```yaml\ncomponents:\n schemas:\n Pet:\n $ref: './schemas/pet.yaml#/Pet'\n User:\n $ref: 'https://api.example.com/schemas/user.json#/User'\n```\n\n### OpenAPI 3.1 Features\n\nFull support for OpenAPI 3.1 features including:\n\n- JSON Schema 2020-12 compatibility\n- `const` keyword\n- `if`/`then`/`else` conditionals\n- Enhanced `examples` support\n\n## Troubleshooting\n\n## FAQ\n\n### Can I use both OpenAPI and AsyncAPI in the same project?\n\nYes! You can have separate configuration files for each input type and generate code to different output directories.\n\n### Can I customize the generated code?\n\nYes, use the [custom generator](../generators/custom) preset to create your own generation logic.", + }, + "integrations/nextjs": { + title: "Next.JS", + content: "---\r\nsidebar_position: 99\r\n---\r\n\r\n# Next.JS\r\nSimple Next.JS server that use AsyncAPI as input to generate payload models that is serialized and printed on the website, works both client and server side.\r\n\r\n[See the project in action 🎬](https://github.com/the-codegen-project/cli/tree/main/examples/typescript-nextjs)", + }, + "integrations/typescript": { + title: "TypeScript Project", + content: "---\r\nsidebar_position: 99\r\n---\r\n\r\n# TypeScript Project\r\n\r\nSimple TypeScript library that use AsyncAPI as input to generate payload models that is serialized and printed in the console.\r\n\r\n[See the project in action 🎬](https://github.com/the-codegen-project/cli/tree/main/examples/typescript-library)", + }, + "migrations": { + title: "Migrations", + content: "# Migrations\nThese are the migration documents\n- [v0, migrating between v0 versions](v0.md) can be found here.", + }, + "migrations/v0": { + title: "Migrating between v0", + content: "\n\n\n\n- [Migrating between v0](#migrating-between-v0)\n * [Breaking Changes 0.39.0](#breaking-changes-0390)\n + [Functions Parameters](#functions-parameters)\n * [Breaking Changes 0.55.1](#breaking-changes-0551)\n * [Breaking Changes 0.61.0](#breaking-changes-0610)\n + [Channels Multi-File Output](#channels-multi-file-output)\n\n\n\n# Migrating between v0\nThese are all the breaking changes in v0 and how to migrate between them\n\n## Breaking Changes 0.39.0\n\n### Functions Parameters\n\nAll TypeScript functions now [use object parameters](../architectural-decisions/typescript.md#28042025) instead of regular parameters. This change affects `channels` and `client` generators across all protocols.\n\nBefore:\n```typescript\n// Publishing\nawait jetStreamPublishToSendUserSignedup(message, parameters, js);\nawait publishToSendUserSignedup(message, parameters, connection);\n\n// Subscribing\nconst subscriber = await jetStreamPullSubscribeToReceiveUserSignedup(\n onDataCallback,\n parameters,\n js,\n config\n);\n```\n\nAfter:\n```typescript\n// Publishing\nawait jetStreamPublishToSendUserSignedup({\n message,\n parameters,\n js\n});\nawait publishToSendUserSignedup({\n message,\n parameters,\n connection\n});\n\n// Subscribing\nconst subscriber = await jetStreamPullSubscribeToReceiveUserSignedup({\n onDataCallback,\n parameters,\n js,\n config\n});\n```\n\n## Breaking Changes 0.55.1\n\nWe upgraded the AsyncAPI Modelina dependency to the `next` version so for the next few versions it will contain breaking changes as we continue to improve the tool.\n\n## Breaking Changes 0.61.0\n\n### Channels Multi-File Output\n\nThe `channels` generator now outputs one file per protocol instead of a single file with a `Protocols` object. This change improves tree-shaking, reduces bundle size, and provides better code organization.\n\n**Before (v0.60.x and earlier):**\n```typescript\n// Single file with Protocols object containing all protocols\nimport { Protocols } from './channels/index';\nconst { nats } = Protocols;\nconst { publishToSendUserSignedup, subscribeToReceiveUserSignedup } = nats;\n\n// Or destructure directly\nconst { nats: { publishToSendUserSignedup } } = Protocols;\n```\n\n**After (v0.61.0+):**\n```typescript\n// Option 1: Import specific functions directly from protocol file\nimport {\n publishToSendUserSignedup,\n subscribeToReceiveUserSignedup\n} from './channels/nats';\n\n// Option 2: Import the entire protocol as a namespace\nimport * as nats from './channels/nats';\nnats.publishToSendUserSignedup({ ... });\n\n// Option 3: Import from index (protocols are re-exported as namespaces)\nimport { nats, kafka, mqtt } from './channels/index';\nnats.publishToSendUserSignedup({ ... });\n```\n\n**New file structure:**\n```\noutputPath/\n├── index.ts # Re-exports all protocol namespaces\n├── nats.ts # NATS-specific functions\n├── kafka.ts # Kafka-specific functions\n├── mqtt.ts # MQTT-specific functions\n├── amqp.ts # AMQP-specific functions\n├── event_source.ts # EventSource-specific functions\n├── http_client.ts # HTTP client-specific functions\n└── websocket.ts # WebSocket-specific functions\n```\n\n**Migration steps:**\n1. Replace `import { Protocols } from './channels'` with direct imports from protocol files\n2. Remove destructuring of the `Protocols` object\n3. Update function calls - functions are now standalone exports, not object properties\n4. Optionally use namespace imports (`import * as nats from './channels/nats'`) to keep similar syntax", + }, + "protocols/amqp": { + title: "AMQP", + content: "# AMQP\n`AMQP` is currently available through the generators ([channels](#channels)):\n\n| **Languages** | Publish exchange | Publish queue | Subscribe queue | Subscribe exchange |\n|---|---|---|---|---|\n| TypeScript | ✅ | ✅ | ✅ | |\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md).\n\n## Channels\nRead more about the [channels](../generators/channels.md) generator here before continuing.\n\nThis generator provides support functions for each resource ensuring you the right payload and parameter are used. \n\n\n\n \n \n \n \n\n\n \n \n \n \n\n
Input (AsyncAPI)Using the code
\n\n```yaml\nasyncapi: 3.0.0\ninfo:\n title: Account Service\n version: 1.0.0\n description: This service is in charge of processing user signups\nchannels:\n userSignups:\n address: user/signedup\n messages:\n userSignedup:\n $ref: '#/components/messages/UserSignedUp'\noperations:\n publishUserSignups:\n action: send\n channel:\n $ref: '#/channels/userSignups'\n consumeUserSignups:\n action: receive\n channel:\n $ref: '#/channels/userSignups'\ncomponents:\n messages:\n UserSignedUp:\n payload:\n type: object\n properties:\n displayName:\n type: string\n description: Name of the user\n email:\n type: string\n format: email\n description: Email of the user\n```\n\n\n```ts\nimport * as Amqp from 'amqplib';\n// Location depends on the payload generator configurations\nimport { UserSignedup } from './__gen__/payloads/UserSignedup';\n// Location depends on the channel generator configurations\nimport { Protocols } from './__gen__/channels';\nconst { amqp } = Protocols;\nconst { publishToPublishUserSignupsExchange, publishToPublishUserSignupsQueue, subscribeToConsumeUserSignupsQueue } = amqp;\n\n/**\n * Setup the regular client\n */\nconst client = await Amqp.connect('amqp://localhost');\nconst myPayload = new UserSignedup({displayName: 'test', email: 'test@test.dk'});\n\n// Use exchange\nawait publishToPublishUserSignupsExchange(myPayload, client);\n\n// Use queue\nawait publishToPublishUserSignupsQueue(myPayload, client);\nawait subscribeToConsumeUserSignupsQueue((message) => {\n console.log(`Received message: ${message.displayName}, ${message.email}`);\n}, client);\n```\t\n
", + }, + "protocols/eventsource": { + title: "EventSource", + content: "# EventSource\n`Event Source` is currently available through the generators ([channels](#channels)):\n\n| **Languages** | [client](#client) | [server](#server) |\n|---|---|---|\n| TypeScript | ✅ | ✅ |\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md).\n\n## Client\n\nThe client generated code is to listen for events from the server and act accordingly. \n\n## Server\n\nThe server generated code is to listen for clients making the connection and being ready to receive events. \n\n## Channels\nRead more about the [channels](../generators/channels.md) generator here before continuing.\n\nThis generator provides support functions for each resource ensuring you the right payload and parameter are used. \n\n\n\n \n \n \n \n\n\n \n \n \n \n\n
Input (AsyncAPI)Using the code
\n\n```yaml\nasyncapi: 3.0.0\ninfo:\n title: Account Service\n version: 1.0.0\n description: This service is in charge of processing user signups\nchannels:\n userSignups:\n address: user/signedup\n messages:\n userSignedup:\n $ref: '#/components/messages/UserSignedUp'\noperations:\n consumeUserSignups:\n action: receive\n channel:\n $ref: '#/channels/userSignups'\ncomponents:\n messages:\n UserSignedUp:\n payload:\n type: object\n properties:\n displayName:\n type: string\n description: Name of the user\n email:\n type: string\n format: email\n description: Email of the user\n\n```\n\n\n```ts\nimport express, { Router } from 'express'\n// Location depends on the payload generator configurations\nimport { UserSignedup } from './__gen__/payloads/UserSignedup';\n// Location depends on the channel generator configurations\nimport { Protocols } from './__gen__/channels';\nconst { event_source_client } = Protocols;\nconst { listenForUserSignedup } = event_source_client;\nconst listenCallback = async (\n messageEvent: UserSignedUp | null, \n parameters: UserSignedUpParameters | null,\n error?: string\n) => {\n // Do stuff once you receive the event from the server\n};\nlistenForUserSignedup(listenCallback, {baseUrl: 'http://localhost:3000'})\n\n// Use express to listen for clients registering for events\nconst router = Router()\nconst app = express()\napp.use(express.json({ limit: '3000kb' }))\napp.use(express.urlencoded({ extended: true }))\nregisterSendUserSignedup(router, (req, res, next, parameters, sendEvent) => {\n //Do stuff when client starts listening to the event.\n //For example send a message to the client\n const testMessage = new UserSignedup({displayName: 'test', email: 'test@test.dk'});\n sendEvent(testMessage);\n})\napp.use(router)\napp.listen(3000)\n```\n
", + }, + "protocols/http_client": { + title: "HTTP(S)", + content: "# HTTP(S)\n\nHTTP client generator creates type-safe functions for making HTTP requests based on your API specification. It supports various authentication methods, pagination, retry logic, and extensibility hooks.\n\nIt is currently available through the generators ([channels](../generators/channels.md)):\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md). [Requires HTTP `method` binding for operation and `statusCode` for messages](../inputs/asyncapi.md#http-client).\n\n## TypeScript\n\n| **Feature** | Is supported? |\n|---|---|\n| Download | ❌ |\n| Upload | ❌ |\n| Offset based Pagination | ✅ |\n| Cursor based Pagination | ✅ |\n| Page based Pagination | ✅ |\n| Range based Pagination | ✅ |\n| Retry with backoff | ✅ |\n| OAuth2 Authorization code | ❌ (browser-only) |\n| OAuth2 Implicit | ❌ (browser-only) |\n| OAuth2 Password | ✅ |\n| OAuth2 Client Credentials | ✅ |\n| OAuth2 Token Refresh | ✅ |\n| Username/password Authentication | ✅ |\n| Bearer Authentication | ✅ |\n| Basic Authentication | ✅ |\n| API Key Authentication | ✅ |\n| Request/Response Hooks | ✅ |\n| XML Based API | ❌ |\n| JSON Based API | ✅ |\n| POST | ✅ |\n| GET | ✅ |\n| PATCH | ✅ |\n| DELETE | ✅ |\n| PUT | ✅ |\n| HEAD | ✅ |\n| OPTIONS | ✅ |\n\n## Channels\n\nRead more about the [channels generator here](../generators/channels.md).\n\n\n\n \n \n \n \n\n\n \n \n \n \n\n
Input (AsyncAPI)Using the code
\n\n```yaml\nasyncapi: 3.0.0\ninfo:\n title: User API\n version: 1.0.0\nchannels:\n ping:\n address: /ping\n messages:\n pingRequest:\n $ref: '#/components/messages/PingRequest'\n pongResponse:\n $ref: '#/components/messages/PongResponse'\noperations:\n postPing:\n action: send\n channel:\n $ref: '#/channels/ping'\n bindings:\n http:\n method: POST\n reply:\n channel:\n $ref: '#/channels/ping'\n messages:\n - $ref: '#/channels/ping/messages/pongResponse'\ncomponents:\n messages:\n PingRequest:\n payload:\n type: object\n properties:\n message:\n type: string\n PongResponse:\n payload:\n type: object\n properties:\n response:\n type: string\n bindings:\n http:\n statusCode: 200\n```\n\n\n```ts\n// Location depends on the payload generator configurations\nimport { Ping } from './__gen__/payloads/Ping';\nimport { Pong } from './__gen__/payloads/Pong';\n// Location depends on the channel generator configurations\nimport { Protocols } from './__gen__/channels';\nconst { http_client } = Protocols;\nconst { postPingPostRequest } = http_client;\n\n// Create a request payload\nconst pingMessage = new Ping({ message: 'Hello!' });\n\n// Make a simple request\nconst response = await postPingPostRequest({\n payload: pingMessage,\n server: 'https://api.example.com'\n});\n\n// Access the response\nconsole.log(response.data.response); // The deserialized Pong\nconsole.log(response.status); // 200\nconsole.log(response.headers); // Response headers\nconsole.log(response.rawData); // Raw JSON response\n```\n
\n\n## Authentication\n\nThe HTTP client uses a discriminated union for authentication, providing excellent TypeScript autocomplete support.\n\n### Bearer Token\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'bearer',\n token: 'your-jwt-token'\n }\n});\n```\n\n### Basic Authentication\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'basic',\n username: 'user',\n password: 'pass'\n }\n});\n```\n\n### API Key\n\n```typescript\n// API Key in header (default)\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'apiKey',\n key: 'your-api-key',\n name: 'X-API-Key', // Header name (default: 'X-API-Key')\n in: 'header' // 'header' or 'query'\n }\n});\n\n// API Key in query parameter\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'apiKey',\n key: 'your-api-key',\n name: 'api_key',\n in: 'query'\n }\n});\n```\n\n### OAuth2 Client Credentials\n\nFor server-to-server authentication:\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'oauth2',\n flow: 'client_credentials',\n clientId: 'your-client-id',\n clientSecret: 'your-client-secret',\n tokenUrl: 'https://auth.example.com/oauth/token',\n scopes: ['read', 'write'],\n onTokenRefresh: (tokens) => {\n // Called when tokens are obtained/refreshed\n console.log('New access token:', tokens.accessToken);\n }\n }\n});\n```\n\n### OAuth2 Password Flow\n\nFor legacy applications requiring username/password:\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'oauth2',\n flow: 'password',\n clientId: 'your-client-id',\n username: 'user@example.com',\n password: 'user-password',\n tokenUrl: 'https://auth.example.com/oauth/token',\n onTokenRefresh: (tokens) => {\n // Store tokens for future use\n saveTokens(tokens);\n }\n }\n});\n```\n\n### OAuth2 with Pre-obtained Token\n\nFor tokens obtained via browser-based flows (implicit, authorization code):\n\n```typescript\n// Token obtained from browser OAuth flow\nconst accessToken = getTokenFromBrowserFlow();\n\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n auth: {\n type: 'oauth2',\n accessToken: accessToken,\n refreshToken: refreshToken, // Optional: for auto-refresh on 401\n tokenUrl: 'https://auth.example.com/oauth/token',\n clientId: 'your-client-id',\n onTokenRefresh: (tokens) => {\n // Update stored tokens\n updateStoredTokens(tokens);\n }\n }\n});\n```\n\n## Pagination\n\nThe HTTP client supports multiple pagination strategies. Pagination parameters can be placed in query parameters or headers.\n\n### Offset-based Pagination\n\n```typescript\nconst response = await getItemsRequest({\n server: 'https://api.example.com',\n pagination: {\n type: 'offset',\n offset: 0,\n limit: 25,\n in: 'query', // 'query' or 'header'\n offsetParam: 'offset', // Query param name (default: 'offset')\n limitParam: 'limit' // Query param name (default: 'limit')\n }\n});\n\n// Navigate pages\nif (response.hasNextPage?.()) {\n const nextPage = await response.getNextPage?.();\n}\n```\n\n### Cursor-based Pagination\n\n```typescript\nconst response = await getItemsRequest({\n server: 'https://api.example.com',\n pagination: {\n type: 'cursor',\n cursor: undefined, // First page\n limit: 25,\n cursorParam: 'cursor'\n }\n});\n\n// Get next page using cursor from response\nif (response.pagination?.nextCursor) {\n const nextPage = await response.getNextPage?.();\n}\n```\n\n### Page-based Pagination\n\n```typescript\nconst response = await getItemsRequest({\n server: 'https://api.example.com',\n pagination: {\n type: 'page',\n page: 1,\n pageSize: 25,\n pageParam: 'page',\n pageSizeParam: 'per_page'\n }\n});\n```\n\n### Range-based Pagination (RFC 7233)\n\n```typescript\nconst response = await getItemsRequest({\n server: 'https://api.example.com',\n pagination: {\n type: 'range',\n start: 0,\n end: 24,\n unit: 'items', // Range unit (default: 'items')\n rangeHeader: 'Range' // Header name (default: 'Range')\n }\n});\n// Sends: Range: items=0-24\n```\n\n## Retry with Exponential Backoff\n\nConfigure automatic retry for failed requests:\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n retry: {\n maxRetries: 3, // Maximum retry attempts (default: 3)\n initialDelayMs: 1000, // Initial delay before first retry (default: 1000)\n maxDelayMs: 30000, // Maximum delay between retries (default: 30000)\n backoffMultiplier: 2, // Exponential backoff multiplier (default: 2)\n retryableStatusCodes: [408, 429, 500, 502, 503, 504], // Status codes to retry\n retryOnNetworkError: true, // Retry on network failures\n onRetry: (attempt, delay, error) => {\n console.log(`Retry attempt ${attempt} after ${delay}ms: ${error.message}`);\n }\n }\n});\n```\n\n## Request/Response Hooks\n\nCustomize request behavior with hooks:\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n hooks: {\n // Modify request before sending\n beforeRequest: async (params) => {\n console.log('Making request to:', params.url);\n // Add custom header\n return {\n ...params,\n headers: {\n ...params.headers,\n 'X-Request-ID': generateRequestId()\n }\n };\n },\n\n // Replace the default fetch implementation\n makeRequest: async (params) => {\n // Use axios, got, or any HTTP client\n const axiosResponse = await axios({\n url: params.url,\n method: params.method,\n headers: params.headers,\n data: params.body\n });\n return {\n ok: axiosResponse.status >= 200 && axiosResponse.status < 300,\n status: axiosResponse.status,\n statusText: axiosResponse.statusText,\n headers: axiosResponse.headers,\n json: () => axiosResponse.data\n };\n },\n\n // Process response after receiving\n afterResponse: async (response, params) => {\n console.log(`Response ${response.status} from ${params.url}`);\n return response;\n },\n\n // Handle errors\n onError: async (error, params) => {\n console.error(`Request failed: ${error.message}`);\n // Optionally transform the error\n return error;\n }\n }\n});\n```\n\n## Path Parameters\n\nFor operations with path parameters, the generator creates typed parameter classes:\n\n```typescript\nimport { UserItemsParameters } from './__gen__/parameters/UserItemsParameters';\n\n// Create parameters with type safety\nconst params = new UserItemsParameters({\n userId: 'user-123',\n itemId: 456\n});\n\nconst response = await getGetUserItem({\n server: 'https://api.example.com',\n parameters: params // Replaces {userId} and {itemId} in path\n});\n```\n\n## Typed Headers\n\nFor operations with defined headers, the generator creates typed header classes:\n\n```typescript\nimport { ItemRequestHeaders } from './__gen__/headers/ItemRequestHeaders';\n\nconst headers = new ItemRequestHeaders({\n xCorrelationId: 'corr-123',\n xRequestId: 'req-456'\n});\n\nconst response = await putUpdateUserItem({\n server: 'https://api.example.com',\n parameters: params,\n payload: itemData,\n requestHeaders: headers // Type-safe headers\n});\n```\n\n## Additional Headers and Query Parameters\n\nAdd custom headers or query parameters to any request:\n\n```typescript\nconst response = await postPingPostRequest({\n payload: message,\n server: 'https://api.example.com',\n additionalHeaders: {\n 'X-Custom-Header': 'value',\n 'Accept-Language': 'en-US'\n },\n queryParams: {\n include: 'metadata',\n format: 'detailed'\n }\n});\n```\n\n## Multi-Status Responses\n\nFor operations that return different payloads based on status code, the generator creates union types:\n\n```yaml\n# AsyncAPI spec with multiple response types\noperations:\n getItem:\n reply:\n messages:\n - $ref: '#/components/messages/ItemResponse' # 200\n - $ref: '#/components/messages/NotFoundError' # 404\n```\n\n```typescript\nconst response = await getItemRequest({\n server: 'https://api.example.com',\n parameters: params\n});\n\n// Response type is union: ItemResponse | NotFoundError\n// Use response.status to discriminate\nif (response.status === 200) {\n console.log('Item:', response.data); // ItemResponse\n} else if (response.status === 404) {\n console.log('Not found:', response.data); // NotFoundError\n}\n```", + }, + "protocols/kafka": { + title: "Kafka", + content: "# Kafka\nKafka is currently supported through the following generators ([channels](#channels)):\n\n| **Languages** | Publish | Subscribe\n|---|---|---|\n| TypeScript | ✅ | ✅ |\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md). If you use \n\n## Channels\nRead more about the [channels](../generators/channels.md) generator here before continuing.\n\nThis generator provides support functions for each resource ensuring you the right payload and parameter are used. \n\n\n \n \n \n \n\n\n \n \n \n \n\n
Input (AsyncAPI)Using the code
\n\n```yaml\nasyncapi: 3.0.0\ninfo:\n title: Account Service\n version: 1.0.0\n description: This service is in charge of processing user signups\nchannels:\n userSignups:\n address: user/signedup\n messages:\n userSignedup:\n $ref: '#/components/messages/UserSignedUp'\noperations:\n publishUserSignups:\n action: send\n channel:\n $ref: '#/channels/userSignups'\n consumeUserSignups:\n action: receive\n channel:\n $ref: '#/channels/userSignups'\ncomponents:\n messages:\n UserSignedUp:\n payload:\n type: object\n properties:\n displayName:\n type: string\n description: Name of the user\n email:\n type: string\n format: email\n description: Email of the user\n\n```\n\n\n```ts\nimport { Kafka } from 'kafkajs';\n// Location depends on the payload generator configurations\nimport { UserSignedup } from './__gen__/payloads/UserSignedup';\n// Location depends on the channel generator configurations\nimport { Protocols } from './__gen__/channels';\nconst { kafka } = Protocols;\nconst { consumeFromConsumeUserSignups, produceToPublishUserSignups } = kafka;\n\n/**\n * Setup the regular client\n */\nconst kafkaClient = new Kafka({\n clientId: 'test',\n brokers: ['localhost:9093'],\n});\n\nconst myPayload = new UserSignedup({displayName: 'test', email: 'test@test.dk'});\n\n// Consume the messages with the generated channel function\nconst consumerCallback = async (\n err,\n msg: UserSignedUp | undefined, \n parameters: UserSignedUpParameters | undefined, \n kafkaMsg: EachMessagePayload | undefined\n ) => {\n // Do stuff once you consumer from the topic\n};\nconst consumer = await consumeFromConsumeUserSignups(\n consumerCallback,\n myParameters, \n kafkaClient, \n {\n fromBeginning: true, \n groupId: 'testId1'\n }\n);\n\n// Produce the messages with the generated channel function\nconst producer = await produceToPublishUserSignups(myPayload, kafkaClient);\n```\t\n
", + }, + "protocols/mqtt": { + title: "MQTT", + content: "# MQTT\n`MQTT` is currently available through the generators ([channels](#channels)):\n\n| **Languages** | publish | subscribe |\n|---|---|---|\n| TypeScript | ✅ | ✅ |\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md).\n\n## ⚠️ Important: MQTT v5 Required for Headers\n\nWhen using headers with MQTT, you MUST configure your MQTT client to use protocol version 5:\n\n```typescript\n// ✅ REQUIRED for header support\nconst client = await MqttClient.connectAsync(\"mqtt://0.0.0.0:1883\", { \n protocolVersion: 5 \n});\n\n// ❌ Will NOT work with headers (defaults to MQTT v3.1.1)\nconst client = await MqttClient.connectAsync(\"mqtt://0.0.0.0:1883\");\n```\n\n**Why MQTT v5 is Required:**\n- MQTT v3.1.1 (default) does not support user properties\n- MQTT v5 introduces user properties which are used for header transmission\n- Both publish and subscribe operations require MQTT v5 for full header functionality\n\n## Channels\nRead more about the [channels](../generators/channels.md) generator here before continuing.\n\nThis generator provides support functions for each resource ensuring you the right payload and parameter are used. \n\n\n\n \n \n \n \n\n\n \n \n \n \n\n
Input (AsyncAPI)Using the code
\n\n```yaml\nasyncapi: 3.0.0\ninfo:\n title: Account Service\n version: 1.0.0\n description: This service is in charge of processing user signups\nchannels:\n userSignups:\n address: user/signedup\n messages:\n userSignedup:\n $ref: '#/components/messages/UserSignedUp'\noperations:\n publishUserSignups:\n action: send\n channel:\n $ref: '#/channels/userSignups'\n consumeUserSignups:\n action: receive\n channel:\n $ref: '#/channels/userSignups'\ncomponents:\n messages:\n UserSignedUp:\n payload:\n type: object\n properties:\n displayName:\n type: string\n description: Name of the user\n email:\n type: string\n format: email\n description: Email of the user\n\n```\n\n\n```ts\nimport * as MqttClient from 'mqtt';\n// Location depends on the payload generator configurations\nimport { UserSignedup } from './__gen__/payloads/UserSignedup';\n// Location depends on the header generator configurations (if using headers)\nimport { UserSignedUpHeaders } from './__gen__/headers/UserSignedUpHeaders';\n// Location depends on the channel generator configurations\nimport { Protocols } from './__gen__/channels';\nconst { mqtt } = Protocols;\nconst { publishToUserSignedup, subscribeToConsumeUserSignups } = mqtt;\n\n/**\n * Setup the MQTT client with v5 protocol for header support\n */\nconst client = await MqttClient.connectAsync(\"mqtt://0.0.0.0:1883\", { \n protocolVersion: 5 // REQUIRED for headers\n});\n\nconst myPayload = new UserSignedup({displayName: 'test', email: 'test@test.dk'});\nconst myHeaders = new UserSignedUpHeaders({ xTestHeader: 'my-header-value' });\n\n// Subscribe to messages with the generated channel function\nawait subscribeToConsumeUserSignups({\n onDataCallback: (params) => {\n const { err, msg, headers, mqttMsg } = params;\n if (err) {\n console.error('Error receiving message:', err);\n return;\n }\n console.log('Received message:', msg);\n console.log('Received headers:', headers); // Available with MQTT v5\n console.log('Raw MQTT packet:', mqttMsg);\n },\n mqtt: client\n});\n\n// Publish messages with the generated channel function\nawait publishToUserSignedup({\n message: myPayload,\n headers: myHeaders, // Headers sent as MQTT v5 user properties\n mqtt: client\n});\n```\t\n
", + }, + "protocols/nats": { + title: "NATS", + content: "# NATS\n\n[NATS is an open-source, high-performance messaging system designed for cloud-native, distributed systems, and Internet of Things (IoT) applications](https://nats.io/). Developed by Synadia, NATS stands out for its simplicity, lightweight architecture, and low latency, making it ideal for real-time messaging. It supports a variety of messaging patterns including publish-subscribe, request-reply, and queueing. \n\nIt is one of the first protocols for The Codegen Project to support, here is what is currently available through the generators ([channels](../generators/channels.md) and [client](../generators/client.md)):\n\n| **Languages** | Core publish | Core subscribe | JetStream publish | JetStream pull subscribe | JetStream push subscription\n|---|---|---|---|---|---|\n| TypeScript | ✅ | ✅ | ✅ | ✅ | ✅ |\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md).", + }, + "protocols/websocket": { + title: "WebSocket", + content: "# WebSocket\n\nWebSocket is currently supported through the following generators ([channels](#channels)):\n\n| **Languages** | Client Publish | Client Subscribe | Server Register |\n|---|---|---|---|\n| TypeScript | ✅ | ✅ | ✅ |\n\nAll of this is available through [AsyncAPI](../inputs/asyncapi.md).\n\n## ⚠️ Important: External Connection Management\n\nThe WebSocket generator assumes that WebSocket connections are managed externally by your application. The generated functions accept already-connected WebSocket instances and focus on message handling rather than connection establishment.\n\n```typescript\n// ✅ You manage the connection\nconst clientWs = new WebSocket('ws://localhost:8080/user/events');\nconst server = new WebSocketServer({ port: 8080 });\n\n// ✅ Generated functions use your connections\nawait publishMessage({ message, parameters, ws: clientWs });\nregisterHandler({ wss: server, onConnection, onMessage });\n```\n\n**Why External Connection Management:**\n- Gives you full control over connection lifecycle\n- Allows custom authentication and authorization\n- Enables connection pooling and reconnection strategies\n- Separates transport concerns from message handling\n\n## Channels\n\nRead more about the [channels](../generators/channels.md) generator here before continuing.\n\nThis generator provides support functions for each resource ensuring you use the right payload and parameters.\n\n\n\n \n \n \n \n\n\n \n \n\n\n\n
Input (AsyncAPI)Using the code
\n\n```yaml\nasyncapi: 3.0.0\ninfo:\n title: User Service\n version: 1.0.0\n description: WebSocket-based user event system\nchannels:\n userEvents:\n address: user/events/{userId}\n parameters:\n userId:\n description: The user identifier\n messages:\n userSignedUp:\n $ref: '#/components/messages/UserSignedUp'\noperations:\n sendUserEvent:\n action: send\n channel:\n $ref: '#/channels/userEvents'\n receiveUserEvent:\n action: receive\n channel:\n $ref: '#/channels/userEvents'\ncomponents:\n messages:\n UserSignedUp:\n payload:\n type: object\n properties:\n userId:\n type: string\n email:\n type: string\n required:\n - userId\n - email\n```\n\n\n\n**Client-side publishing:**\n```typescript\nimport { publishToSendUserEvent } from './channels';\nimport { UserSignedUp } from './payloads';\nimport { UserEventsParameters } from './parameters';\nimport WebSocket from 'ws';\n\n// Create connection (your responsibility)\nconst ws = new WebSocket('ws://localhost:8080/user/events/user123');\n\nawait ws.on('open', async () => {\n // Use generated publish function\n await publishToSendUserEvent({\n message: new UserSignedUp({\n userId: 'user123',\n email: 'user@example.com'\n }),\n parameters: new UserEventsParameters({\n userId: 'user123'\n }),\n ws\n });\n});\n```\n\n**Client-side subscribing:**\n```typescript\nimport { subscribeToReceiveUserEvent } from './channels';\n\nws.on('open', () => {\n // Set up subscription\n subscribeToReceiveUserEvent({\n onDataCallback: (params) => {\n const { err, msg, parameters, ws } = params;\n if (err) {\n console.error('Error:', err);\n return;\n }\n \n console.log('Received:', msg?.marshal());\n console.log('User ID:', parameters?.userId);\n },\n parameters: new UserEventsParameters({\n userId: 'user123'\n }),\n ws\n });\n});\n```\n\n**Server-side handling:**\n```typescript\nimport { registerSendUserEvent } from './channels';\nimport WebSocket from 'ws';\n\n// Create server (your responsibility)\nconst wss = new WebSocket.WebSocketServer({ port: 8080 });\n\n// Register message handler\nregisterSendUserEvent({\n wss,\n onConnection: (params) => {\n const { parameters, ws, request } = params;\n console.log(`User ${parameters.userId} connected`);\n \n // Perform authentication, logging, etc.\n },\n onMessage: (params) => {\n const { message, ws } = params;\n console.log('Received message:', message.marshal());\n \n // Process the message\n // Send response if needed\n const response = new UserSignedUp({\n userId: message.userId,\n email: 'updated@example.com'\n });\n ws.send(response.marshal());\n }\n});\n```\n\n
\n\n## Function Types\n\nThe WebSocket generator creates three types of functions:\n\n### Client Functions\n\n**Publish Functions** (`publishTo*`):\n- Send messages from client to server\n- Require connected WebSocket instance\n- Handle message serialization automatically\n- Return Promise for async operation\n\n**Subscribe Functions** (`subscribeTo*`):\n- Listen for messages from server\n- Set up message handlers on WebSocket\n- Handle message parsing and validation\n- Support error handling through callbacks\n\n### Server Functions\n\n**Register Functions** (`register*`):\n- Handle incoming client connections\n- Process messages from clients\n- Support both connection and message callbacks\n- Enable URL parameter extraction\n\n## URL Pattern Matching\n\nThe WebSocket generator automatically creates URL pattern matching for channels with parameters:\n\n```yaml\n# AsyncAPI Channel\nchannels:\n userEvents:\n address: user/events/{userId}/{eventType}\n```\n\n```typescript\n// Generated pattern matching\n// Matches: /user/events/123/signup\n// Extracts: userId=\"123\", eventType=\"signup\"\n\nregisterSendUserEvent({\n wss,\n onConnection: (params) => {\n // Parameters automatically extracted from URL\n const { parameters } = params;\n console.log(parameters.userId); // \"123\"\n console.log(parameters.eventType); // \"signup\"\n },\n onMessage: (params) => {\n // Handle the message\n }\n});\n```\n\n## Error Handling\n\nThe WebSocket generator includes comprehensive error handling:\n\n```typescript\n// Client-side error handling\nsubscribeToReceiveUserEvent({\n onDataCallback: (params) => {\n const { err, msg } = params;\n if (err) {\n // Handle parsing errors, validation errors, etc.\n console.error('Message error:', err.message);\n return;\n }\n // Process successful message\n },\n ws\n});\n\n// Connection state checking\nawait publishToSendUserEvent({\n message,\n parameters,\n ws // Function checks if WebSocket is open\n});\n```\n\n## Best Practices\n\n### Connection Management\n```typescript\n// ✅ Handle connection lifecycle\nconst ws = new WebSocket('ws://localhost:8080/user/events/123');\n\nws.on('open', () => {\n // Set up subscriptions after connection opens\n subscribeToReceiveUserEvent({ ... });\n});\n\nws.on('close', (code, reason) => {\n // Handle disconnection, implement reconnection logic\n});\n\nws.on('error', (error) => {\n // Handle connection errors\n});\n```\n\n### Message Validation\n```typescript\n// ✅ Use validation in production\nsubscribeToReceiveUserEvent({\n onDataCallback: (params) => {\n const { err, msg } = params;\n if (err) {\n // Generated functions include validation errors\n console.error('Invalid message received:', err);\n return;\n }\n // Message is guaranteed to be valid\n },\n skipMessageValidation: false, // Enable validation (default)\n ws\n});\n```\n\n### Server Setup\n```typescript\n// ✅ Proper server setup with error handling\nconst wss = new WebSocket.WebSocketServer({ \n port: 8080,\n verifyClient: (info) => {\n // Implement authentication logic\n return true;\n }\n});\n\nregisterSendUserEvent({\n wss,\n onConnection: (params) => {\n const { parameters, ws, request } = params;\n \n // Validate parameters\n if (!parameters.userId) {\n ws.close(1008, 'Invalid user ID');\n return;\n }\n \n // Set up user session\n },\n onMessage: (params) => {\n const { message, ws } = params;\n \n try {\n // Process message safely\n } catch (error) {\n ws.close(1011, 'Processing error');\n }\n }\n});\n```\n\n## Dependencies\n\nThe generated WebSocket code requires the `ws` library:\n\n```bash\nnpm install ws\nnpm install @types/ws # For TypeScript projects\n```", + }, + "index": { + title: "Documentation", + content: "# Documentation\n\n\n\n\n\n- [Configurations](#configurations)\n- [Getting Started](#getting-started)\n- [Contributing](#contributing)\n- [Usage](#usage)\n- [Generators](#generators)\n- [Architectural Decisions](#architectural-decisions)\n- [Inputs](#inputs)\n- [Protocols](#protocols)\n- [Migrations](#migrations)\n- [Telemetry](#telemetry)\n\n\n\nThis document gives the overview of all the available documentation for The Codegen Project.\n\n### [Configurations](./configurations.md)\nContains all the information about the how the configuration file works and how it's loaded and in what order.\n\n### [Getting Started](./getting-started/)\nGet started in 5 minutes :fire:\n\n### [Contributing](./contributing.md)\nGet an overview of how to contribute to the project\n\n### [Usage](./usage.md)\nContains all the information about what the CLI can do and how.\n\n### [Generators](./generators/README.md)\nFor all available generators, this document describes what is possible.\n\n### Architectural Decisions\nIf there has been a decision about certain technical solutions it will be marked in the architectural decision document.\n- [TypeScript](./architectural-decisions/typescript.md)\n\n### Inputs\nEach input has its own limitations, corner cases, and features; thus, each has separate documentation.\n- [AsyncAPI](./inputs/asyncapi.md)\n- [OpenAPI](./inputs/openapi.md)\n- [JSON Schema](./inputs/jsonschema.md)\n\n### Protocols\nEach protocol has its own limitations, corner cases, and features; thus, each has separate documentation.\n- [NATS](./protocols/nats.md)\n- [AMQP](./protocols/amqp.md)\n- [Kafka](./protocols/kafka.md)\n- [MQTT](./protocols/mqtt.md)\n- [EventSource](./protocols/eventsource.md)\n- [HTTP Client](./protocols/http_client.md)\n- [WebSocket client and server](./protocols/websocket.md)\n\n### [Migrations](./migrations/README.md)\nGet an overview of how to contribute to the project\n\n### [Telemetry](./telemetry.md)\nGet an overview of how telemetry works for this project", + }, + "telemetry": { + title: "Telemetry", + content: "# Telemetry\n\nThe Codegen Project CLI collects **anonymous** usage data to help us understand how the tool is being used and make data-driven improvements.\n\n## Privacy First\n\nWe take your privacy seriously. Here's what we collect and what we don't:\n\n### ✅ What We Collect\n\n- **Command usage**: Which commands you run (e.g., `generate`, `init`)\n- **Generator types**: Which generators you use (e.g., `payloads`, `channels`)\n- **Input source types**: Whether you use remote URLs, local relative paths, or absolute paths (not the actual paths)\n- **Feature usage**: Which flags and options you use\n- **Error categories**: Types of errors that occur (not error messages or stack traces)\n- **System information**: CLI version, Node.js version, OS platform\n- **Execution metrics**: Command duration and success rates\n\n### ❌ What We DON'T Collect\n\n- ❌ File paths or file names\n- ❌ Actual URLs or file locations\n- ❌ File contents or schema details\n- ❌ Project names\n- ❌ User names or emails\n- ❌ API keys or credentials\n- ❌ IP addresses (anonymized by analytics provider)\n- ❌ Hostnames\n- ❌ Environment variable values\n- ❌ Git repository information\n- ❌ Custom schema structures\n\n## Managing Telemetry\n\n### Check Status\n\nView your current telemetry settings:\n\n```bash\ncodegen telemetry status\n```\n\nThis shows:\n- Whether telemetry is enabled or disabled\n- Configuration file location\n- What data is collected\n- Environment variable overrides\n\n### Disable Telemetry\n\nYou can disable telemetry in several ways:\n\n#### Option 1: Using the CLI command\n\n```bash\ncodegen telemetry disable\n```\n\n#### Option 2: Environment variable (permanent)\n\nAdd to your shell profile (`.bashrc`, `.zshrc`, etc.):\n\n```bash\nexport CODEGEN_TELEMETRY_DISABLED=1\n```\n\nOr use the standard DO_NOT_TRACK variable:\n\n```bash\nexport DO_NOT_TRACK=1\n```\n\n#### Option 3: Environment variable (per-command)\n\n```bash\nCODEGEN_TELEMETRY_DISABLED=1 codegen generate\n```\n\n#### Option 4: Project-level configuration\n\nIn your `codegen.config.js`:\n\n```javascript\nexport default {\n inputType: 'asyncapi',\n inputPath: './asyncapi.yaml',\n generators: [/* ... */],\n \n // Disable telemetry for this project\n telemetry: {\n enabled: false\n }\n}\n```\n\n### Re-enable Telemetry\n\n```bash\ncodegen telemetry enable\n```\n\n## First-Run Notice\n\nWhen you run any command for the first time, you'll see a notice about telemetry:\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│ │\n│ The Codegen Project CLI collects anonymous usage data │\n│ to help us improve the tool. │\n│ │\n│ To disable: codegen telemetry disable │\n│ Learn more: https://the-codegen-project.org/docs/telemetry │\n│ │\n└─────────────────────────────────────────────────────────────┘\n```\n\nThis notice is shown only once. Telemetry is **opt-out by default**, meaning it's enabled unless you explicitly disable it.\n\n## Debug Mode\n\nTo see what telemetry data is being sent:\n\n```bash\nCODEGEN_TELEMETRY_DEBUG=1 codegen generate\n```\n\nThis logs telemetry events to the console, including:\n- The event being tracked\n- The telemetry configuration state\n- The full payload being sent to the analytics endpoint\n- HTTP response status (success/failure)\n\nEvents are still sent to the analytics endpoint in debug mode, but you can see exactly what's being transmitted. The events will also appear in **GA4 DebugView** when debug mode is enabled.\n\n## Custom Tracking Endpoint (for Organizations)\n\nOrganizations can point telemetry to their own analytics endpoint using environment variables. These environment variables have the **highest priority** and will override any configuration from project-level config or global config files:\n\n```bash\n# Set custom endpoint (highest priority - overrides all other configs)\nexport CODEGEN_TELEMETRY_ENDPOINT=https://analytics.mycompany.com/telemetry\nexport CODEGEN_TELEMETRY_ID=custom-tracking-id\nexport CODEGEN_TELEMETRY_API_SECRET=your-api-secret\n```\n\n**Configuration Priority Order (highest to lowest):**\n1. **Environment variables** (highest priority):\n - `CODEGEN_TELEMETRY_DISABLED` / `DO_NOT_TRACK` - disable telemetry\n - `CODEGEN_TELEMETRY_ENDPOINT` - custom analytics endpoint\n - `CODEGEN_TELEMETRY_ID` - custom tracking ID\n - `CODEGEN_TELEMETRY_API_SECRET` - custom API secret\n2. **Project-level config** (from `codegen.config.js`)\n3. **Global config file** (`~/.the-codegen-project/config.json`)\n\nExpected endpoint format (GA4 Measurement Protocol compatible):\n\n```\nPOST /telemetry\nContent-Type: application/json\n\n{\n \"client_id\": \"anonymous-uuid\",\n \"events\": [{\n \"name\": \"command_executed\",\n \"params\": {\n \"command\": \"generate\",\n \"flags\": \"watch\",\n \"input_source\": \"local_relative\",\n \"input_type\": \"asyncapi\",\n \"generators\": \"payloads,parameters\",\n \"generator_count\": 2,\n \"duration\": 1234,\n \"success\": true,\n \"cli_version\": \"0.57.0\",\n \"node_version\": \"v18.0.0\",\n \"os\": \"darwin\",\n \"ci\": false,\n \"engagement_time_msec\": \"1234\"\n }\n }]\n}\n```\n\n## Configuration File\n\nTelemetry settings are stored in:\n\n```\n~/.the-codegen-project/config.json\n```\n\nExample configuration:\n\n```json\n{\n \"version\": \"1.0.0\",\n \"telemetry\": {\n \"enabled\": true,\n \"anonymousId\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"endpoint\": \"https://www.google-analytics.com/mp/collect\",\n \"trackingId\": \"G-XXXXXXXXXX\"\n },\n \"hasShownTelemetryNotice\": true,\n \"lastUpdated\": \"2024-12-11T10:30:00Z\"\n}\n```\n\n## Example Telemetry Events\n\n### Command Execution\n\n```javascript\n{\n event: 'command_executed',\n command: 'generate',\n flags: 'watch', // Comma-separated if multiple, 'none' if empty\n input_source: 'local_relative', // Not the actual path!\n input_type: 'asyncapi',\n generators: 'payloads,parameters,channels', // Comma-separated list\n generator_count: 3,\n duration: 1234,\n success: true,\n cli_version: '0.57.0',\n node_version: 'v18.0.0',\n os: 'darwin',\n ci: false,\n engagement_time_msec: '1234' // Same as duration for proper engagement tracking\n}\n```\n\n**Why track generator combinations?** This helps us understand:\n- Which generators are commonly used together\n- Popular generator patterns (e.g., \"payloads + parameters\")\n- If certain generators are always used in isolation\n- Common workflows and use cases\n\n### Generator Usage\n\n```javascript\n{\n event: 'generator_used',\n generator_type: 'payloads',\n input_type: 'asyncapi', // Can be: asyncapi, openapi, jsonschema\n input_source: 'remote_url', // Not the actual URL!\n language: 'typescript',\n options: '{\"includeValidation\":true,\"serializationType\":\"json\"}',\n duration: 500,\n success: true,\n cli_version: '0.57.0',\n node_version: 'v18.0.0',\n os: 'darwin',\n ci: false,\n engagement_time_msec: '500'\n}\n```\n\n**Why track individual generators?** This helps us understand:\n- Which generators are most popular\n- How users configure generators (validation, serialization, etc.)\n- Performance characteristics of each generator\n- Success/failure rates per generator type\n\n**Combined with `command_executed` event**, we get both:\n- **Macro view**: What generators are used together\n- **Micro view**: How each generator is configured\n\n### Init Command\n\n```javascript\n{\n event: 'init_executed',\n config_type: 'esm',\n input_type: 'asyncapi',\n generators: 'payloads,parameters,channels', // Comma-separated list\n language: 'typescript',\n completed: true,\n cli_version: '0.57.0',\n node_version: 'v18.0.0',\n os: 'darwin',\n ci: false,\n engagement_time_msec: '100' // Minimum engagement time\n}\n```\n\n### Error Tracking\n\n```javascript\n{\n event: 'error_occurred',\n command: 'generate',\n error_type: 'configuration_error', // Category only, not actual error message\n cli_version: '0.57.0',\n node_version: 'v18.0.0'\n}\n```\n\n## CI/CD Environments\n\nTelemetry automatically detects CI environments and adjusts behavior:\n\n- **First-run notice is skipped** in CI environments\n- Telemetry still runs by default (to track CI usage patterns)\n- You can disable it with environment variables if needed\n\nDetected CI environments:\n- GitHub Actions\n- GitLab CI\n- CircleCI\n- Travis CI\n- Jenkins\n- Bitbucket Pipelines\n- AWS CodeBuild\n- TeamCity\n- Buildkite\n\n## Privacy & Compliance\n\n### GDPR Compliance\n\nOur telemetry implementation is GDPR compliant:\n\n- ✅ **Lawful Basis**: Legitimate interest (improving software)\n- ✅ **Transparency**: Clear notice on first run\n- ✅ **User Control**: Easy opt-out mechanism\n- ✅ **Data Minimization**: Only collect necessary data\n- ✅ **Purpose Limitation**: Use only for improvement\n- ✅ **Anonymization**: No PII collected\n- ✅ **Right to Object**: Users can disable anytime\n\n### Data Retention\n\nWe store data for 14 months, if you use your own telemetry, then its up to you.\n\n### Anonymous ID\n\nEach installation generates a random UUID (v4) as an anonymous identifier. This ID:\n- Is NOT tied to your identity\n- Cannot be used to identify you personally\n- Is only used to understand usage patterns\n- Can be reset by deleting the config file\n\n## How Telemetry Helps\n\nThe data we collect helps us:\n\n1. **Prioritize features**: Focus on the most-used generators and commands\n2. **Improve reliability**: Identify and fix common error scenarios\n3. **Optimize performance**: Understand typical execution times\n4. **Support platforms**: Know which Node.js versions and OS platforms to support\n5. **Guide documentation**: Understand which features cause confusion\n6. **Understand workflows**: Learn whether users prefer remote URLs, relative paths, or absolute paths\n\n## Technical Details\n\n### Implementation\n\n- **Non-blocking**: Telemetry runs asynchronously and never blocks CLI execution\n- **Fail-safe**: Network errors or timeouts don't affect CLI functionality\n- **Fast timeout**: Telemetry requests timeout after 1 second\n- **Error handling**: All errors are handled gracefully and silently\n\n### Default Analytics Provider\n\nWe use Google Analytics 4 Measurement Protocol by default:\n- Free service with powerful analytics\n- Automatic IP anonymization\n- GDPR compliant\n- No additional infrastructure needed\n\n## Website Analytics\n\nIn addition to CLI telemetry, our documentation website (https://the-codegen-project.org) also uses **Google Analytics 4** to understand how users interact with our documentation.\n\n### What the Website Tracks\n\n- **Page views**: Which documentation pages are viewed\n- **Navigation**: How users navigate through the documentation\n- **Search queries**: What users search for in the docs\n- **Outbound links**: Which external links users click\n- **Time on page**: How long users spend reading documentation\n- **Referral sources**: How users found our documentation\n\n**Note:** The website uses a different Google Analytics property than the CLI telemetry. They are completely separate tracking systems.\n\n### What the Website Does NOT Track\n\n- ❌ Personal information\n- ❌ Form inputs or data\n- ❌ Clipboard contents\n- ❌ Code snippets you copy\n- ❌ IP addresses (anonymized by GA4)\n\n### Website Privacy Controls\n\n**Standard Browser Controls:**\n- Use browser \"Do Not Track\" settings\n- Install privacy extensions (uBlock Origin, Privacy Badger, etc.)\n- Use browser incognito/private mode\n- Disable JavaScript (documentation still accessible)\n\n**Website-Specific Settings:**\n- Our website respects the `DO_NOT_TRACK` browser header\n- No cookies are set for tracking purposes\n- Google Analytics IP anonymization is enabled\n- No third-party tracking scripts beyond GA4\n\n## FAQ\n\n### Q: Will telemetry slow down my CLI?\n\n**A**: No. Telemetry runs asynchronously and doesn't block command execution. Network requests timeout after 1 second and fail silently.\n\n### Q: Can telemetry errors break my CLI?\n\n**A**: No. All telemetry functions are designed to never throw errors. Failures are handled internally and don't affect CLI functionality.\n\n### Q: Does this work behind a corporate proxy?\n\n**A**: Yes. Telemetry respects standard `HTTP_PROXY` and `HTTPS_PROXY` environment variables. If it fails, it fails silently without affecting the CLI.\n\n### Q: Can I see what's being sent?\n\n**A**: Yes! Use debug mode:\n\n```bash\nCODEGEN_TELEMETRY_DEBUG=1 codegen generate\n```\n\n### Q: Why opt-out instead of opt-in?\n\n**A**: Opt-out telemetry provides more representative data about how the tool is actually used, which leads to better improvements for all users. However, we respect your choice to opt-out at any time.\n\n### Q: Is my company's internal tracking supported?\n\n**A**: Yes! Set `CODEGEN_TELEMETRY_ENDPOINT` to your internal analytics service. See the \"Custom Tracking Endpoint\" section above.\n\n### Q: Where is the data sent?\n\n**A**: By default, to Google Analytics 4 (anonymized). You can configure a custom endpoint for organizational tracking.\n\n### Q: Can you track me across projects?\n\n**A**: We use an anonymous UUID that is the same across all your projects (any where you interact with the-codegen-project), but it's not tied to any personal information. You can reset it by deleting `~/.the-codegen-project/config.json`.\n\n### Q: Are CLI telemetry and website analytics linked?\n\n**A**: No. The CLI uses an anonymous UUID that is never shared with the website. Website analytics use standard Google Analytics browser tracking. There is no way to correlate CLI usage with website visits - they are completely independent systems.\n\n## Contact\n\nIf you have questions or concerns about telemetry:\n\n- GitHub Issues: [the-codegen-project/cli](https://github.com/the-codegen-project/cli/issues)", + }, + "usage": { + title: "CLI Usage", + content: "# CLI Usage\n\n\n```sh-session\n$ npm install -g @the-codegen-project/cli\n$ codegen COMMAND\nrunning command...\n$ codegen (--version)\n@the-codegen-project/cli/0.62.1 linux-x64 node-v18.20.8\n$ codegen --help [COMMAND]\nUSAGE\n $ codegen COMMAND\n...\n```\n\n\n## Table of contents\n\n\n* [CLI Usage](#cli-usage)\n\n\n## Commands\n\n\n* [`codegen autocomplete [SHELL]`](#codegen-autocomplete-shell)\n* [`codegen generate [FILE]`](#codegen-generate-file)\n* [`codegen help [COMMAND]`](#codegen-help-command)\n* [`codegen init`](#codegen-init)\n* [`codegen telemetry ACTION`](#codegen-telemetry-action)\n* [`codegen version`](#codegen-version)\n\n## `codegen autocomplete [SHELL]`\n\nDisplay autocomplete installation instructions.\n\n```\nUSAGE\n $ codegen autocomplete [SHELL] [-r]\n\nARGUMENTS\n SHELL (zsh|bash|powershell) Shell type\n\nFLAGS\n -r, --refresh-cache Refresh cache (ignores displaying instructions)\n\nDESCRIPTION\n Display autocomplete installation instructions.\n\nEXAMPLES\n $ codegen autocomplete\n\n $ codegen autocomplete bash\n\n $ codegen autocomplete zsh\n\n $ codegen autocomplete powershell\n\n $ codegen autocomplete --refresh-cache\n```\n\n_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v3.0.18/src/commands/autocomplete/index.ts)_\n\n## `codegen generate [FILE]`\n\nGenerate code based on your configuration, use `init` to get started.\n\n```\nUSAGE\n $ codegen generate [FILE] [--help] [-w] [-p ]\n\nARGUMENTS\n FILE Path or URL to the configuration file, defaults to root of where the command is run\n\nFLAGS\n -p, --watchPath= Optional path to watch for changes when --watch flag is used. If not provided, watches the\n input file from configuration\n -w, --watch Watch for file changes and regenerate code automatically\n --help Show CLI help.\n\nDESCRIPTION\n Generate code based on your configuration, use `init` to get started.\n```\n\n_See code: [src/commands/generate.ts](https://github.com/the-codegen-project/cli/blob/v0.62.1/src/commands/generate.ts)_\n\n## `codegen help [COMMAND]`\n\nDisplay help for codegen.\n\n```\nUSAGE\n $ codegen help [COMMAND...] [-n]\n\nARGUMENTS\n COMMAND... Command to show help for.\n\nFLAGS\n -n, --nested-commands Include all nested commands in the output.\n\nDESCRIPTION\n Display help for codegen.\n```\n\n_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.0.22/src/commands/help.ts)_\n\n## `codegen init`\n\nInitialize The Codegen Project in your project\n\n```\nUSAGE\n $ codegen init [--help] [--input-file ] [--config-name ] [--input-type asyncapi|openapi]\n [--output-directory ] [--config-type esm|json|yaml|ts] [--languages typescript] [--no-tty]\n [--include-payloads] [--include-headers] [--include-client] [--include-parameters] [--include-channels]\n [--gitignore-generated]\n\nFLAGS\n --config-name= [default: codegen] The name to use for the configuration file (dont include file\n extension)\n --config-type=