Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ dist
coverage
test/config/src/__gen__/
playground
test/codegen/generators/*/output
test/codegen/generators/*/output
.claude
32 changes: 26 additions & 6 deletions docs/generators/channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,34 @@ Depending on which protocol, these are the dependencies:
- `HTTP`: https://github.com/node-fetch/node-fetch v2
- `WebSocket`: https://github.com/websockets/ws v8

For TypeScript what is generated is a single file that include functions to help easier interact with AsyncAPI channels. For example;
For TypeScript, the generator creates one file per protocol plus an index file that re-exports all protocols as namespaces. For example;

```ts
import { Protocols } from 'src/__gen__/index';
const { nats, kafka, mqtt, amqp, event_source, ... } = Protocols;
const { jetStreamPublishTo..., jetStreamPullSubscribeTo..., jetStreamPushSubscriptionFrom..., publishTo..., subscribeTo... } = nats;
// Import specific functions from a protocol file
import {
jetStreamPublishToSendUserSignedup,
subscribeToReceiveUserSignedup,
publishToSendUserSignedup
} from 'src/__gen__/nats';

// Or import the entire protocol namespace
import * as nats from 'src/__gen__/nats';

// Or import all protocols from the index
import { nats, kafka, mqtt, amqp, event_source } from 'src/__gen__/index';
```

First we import the generated file, which is located based on your `outputPath` in the generator options.
The generated file structure is:
```
outputPath/
├── index.ts # Re-exports all protocol namespaces
├── nats.ts # NATS-specific functions
├── kafka.ts # Kafka-specific functions
├── mqtt.ts # MQTT-specific functions
├── amqp.ts # AMQP-specific functions
├── event_source.ts # EventSource-specific functions
├── http_client.ts # HTTP client-specific functions
└── websocket.ts # WebSocket-specific functions
```

Next we import the desired protocol and then we have access to all the support functions. These support functions are an easy way to interact with channels defined in your AsyncAPI document. Take notice it does not care which operations you have defined.
Each protocol file contains standalone exported functions for interacting with channels defined in your AsyncAPI document.
55 changes: 55 additions & 0 deletions docs/migrations/v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ sidebar_position: 99
* [Breaking Changes 0.39.0](#breaking-changes-0390)
+ [Functions Parameters](#functions-parameters)
* [Breaking Changes 0.55.1](#breaking-changes-0551)
* [Breaking Changes 0.61.0](#breaking-changes-0610)
+ [Channels Multi-File Output](#channels-multi-file-output)

<!-- tocstop -->

Expand Down Expand Up @@ -63,6 +65,59 @@ const subscriber = await jetStreamPullSubscribeToReceiveUserSignedup({

We 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.

## Breaking Changes 0.61.0

### Channels Multi-File Output

The `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.

**Before (v0.60.x and earlier):**
```typescript
// Single file with Protocols object containing all protocols
import { Protocols } from './channels/index';
const { nats } = Protocols;
const { publishToSendUserSignedup, subscribeToReceiveUserSignedup } = nats;

// Or destructure directly
const { nats: { publishToSendUserSignedup } } = Protocols;
```

**After (v0.61.0+):**
```typescript
// Option 1: Import specific functions directly from protocol file
import {
publishToSendUserSignedup,
subscribeToReceiveUserSignedup
} from './channels/nats';

// Option 2: Import the entire protocol as a namespace
import * as nats from './channels/nats';
nats.publishToSendUserSignedup({ ... });

// Option 3: Import from index (protocols are re-exported as namespaces)
import { nats, kafka, mqtt } from './channels/index';
nats.publishToSendUserSignedup({ ... });
```

**New file structure:**
```
outputPath/
├── index.ts # Re-exports all protocol namespaces
├── nats.ts # NATS-specific functions
├── kafka.ts # Kafka-specific functions
├── mqtt.ts # MQTT-specific functions
├── amqp.ts # AMQP-specific functions
├── event_source.ts # EventSource-specific functions
├── http_client.ts # HTTP client-specific functions
└── websocket.ts # WebSocket-specific functions
```

**Migration steps:**
1. Replace `import { Protocols } from './channels'` with direct imports from protocol files
2. Remove destructuring of the `Protocols` object
3. Update function calls - functions are now standalone exports, not object properties
4. Optionally use namespace imports (`import * as nats from './channels/nats'`) to keep similar syntax




Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@
"bump:version": "npm --no-git-tag-version --allow-same-version version $VERSION",
"runtime:prepare": "npm link",
"runtime:typescript": "npm run runtime:typescript:setup && npm run runtime:typescript:test",
"runtime:typescript:setup": "npm run runtime:prepare && npm run runtime:services:start && npm run runtime:typescript:generate",
"runtime:typescript:clean": "rimraf test/runtime/typescript/src",
"runtime:typescript:setup": "npm run runtime:typescript:clean && npm run runtime:prepare && npm run runtime:services:start && npm run runtime:typescript:generate",
"runtime:typescript:generate": "cd test/runtime/typescript && npm ci && npm run generate",
"runtime:typescript:test": "cd test/runtime/typescript && npm run test",
"runtime:services:start": "npm run runtime:nats:start && npm run runtime:kafka:start && npm run runtime:mqtt:start && npm run runtime:amqp:start",
Expand All @@ -134,7 +135,7 @@
"runtime:amqp:stop": "cd test/runtime && docker compose -f ./docker-compose-amqp.yml down",
"test:blackbox": "concurrently --group -n typescript \"npm run test:blackbox:typescript\"",
"test:blackbox:typescript": "jest ./test/blackbox/typescript.spec.ts",
"prepare:pr": "npm run build && npm run format && npm run lint:fix && npm run test:update"
"prepare:pr": "npm run build && npm run format && npm run lint:fix && npm run test:update && npm run runtime:typescript:generate"
},
"engines": {
"node": ">=18.0.0"
Expand Down
24 changes: 16 additions & 8 deletions src/codegen/generators/typescript/channels/asyncapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from './types';
import {findNameFromChannel} from '../../../utils';
import {ConstrainedObjectModel, OutputModel} from '@asyncapi/modelina';
import {collectProtocolDependencies} from './utils';
import {generateNatsChannels} from './protocols/nats';
import {generateKafkaChannels} from './protocols/kafka';
import {generateMqttChannels} from './protocols/mqtt';
Expand Down Expand Up @@ -82,14 +83,21 @@ export async function generateTypeScriptChannelsForAsyncAPI(
string,
TypeScriptChannelRenderedFunctionType[]
>,
dependencies: string[]
protocolDependencies: Record<string, string[]>
): Promise<void> {
const {asyncapiDocument} = validateAsyncapiContext(context);
const channels = asyncapiDocument!
.allChannels()
.all()
.filter((channel) => channel.address() && channel.messages().length > 0);

// Collect payload/parameter/header imports for each protocol
for (const protocol of protocolsToUse) {
// eslint-disable-next-line security/detect-object-injection
const deps = protocolDependencies[protocol];
collectProtocolDependencies(payloads, parameters, headers, context, deps);
}

for (const channel of channels) {
const subName = findNameFromChannel(channel);
let parameter: OutputModel | undefined = undefined;
Expand Down Expand Up @@ -123,7 +131,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['nats']
);
break;
case 'kafka':
Expand All @@ -132,7 +140,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['kafka']
);
break;
case 'mqtt':
Expand All @@ -141,7 +149,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['mqtt']
);
break;
case 'amqp':
Expand All @@ -150,7 +158,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['amqp']
);
break;
case 'http_client':
Expand All @@ -159,7 +167,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['http_client']
);
break;
case 'event_source':
Expand All @@ -168,7 +176,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['event_source']
);
break;
case 'websocket':
Expand All @@ -177,7 +185,7 @@ export async function generateTypeScriptChannelsForAsyncAPI(
channel,
protocolCodeFunctions,
externalProtocolFunctionInformation,
dependencies
protocolDependencies['websocket']
);
break;
default:
Expand Down
Loading