@amqp-contract/asyncapi 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/index.cjs +228 -92
- package/dist/index.d.cts +127 -120
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +127 -120
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +228 -91
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +165 -0
- package/package.json +16 -4
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["channels: Record<string, AsyncAPIChannel>","operations: Record<string, AsyncAPIOperation>","messages: Record<string, AsyncAPIMessageRef>","binding: {\n amqp?: {\n is: \"queue\";\n queue?: {\n name: string;\n durable?: boolean;\n exclusive?: boolean;\n autoDelete?: boolean;\n };\n };\n }","binding: {\n amqp?: {\n is: \"routingKey\";\n exchange?: {\n name: string;\n type: \"topic\" | \"direct\" | \"fanout\" | \"headers\";\n durable?: boolean;\n autoDelete?: boolean;\n };\n };\n }","result: AsyncAPIDocument"],"sources":["../src/schema-converter.ts","../src/generator.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { AsyncAPISchema } from \"./generator.js\";\n\n/**\n * Convert a Standard Schema to JSON Schema (AsyncAPI format)\n *\n * This is a basic converter that returns a generic object schema.\n * Users should provide their own schema-to-JSON-Schema converter function\n * specific to their schema library.\n *\n * For Zod users, import and use zodToJsonSchema from @amqp-contract/zod\n *\n * @example\n * ```ts\n * import { zodToJsonSchema } from '@amqp-contract/zod';\n * import { generateAsyncAPI } from '@amqp-contract/asyncapi';\n *\n * // Use zodToJsonSchema for converting Zod schemas\n * const jsonSchema = zodToJsonSchema(zodSchema);\n * ```\n */\nexport function standardSchemaToJsonSchema(_schema: StandardSchemaV1): AsyncAPISchema {\n // Basic fallback - returns a generic object schema\n // Users should provide their own converter for better results\n return { type: \"object\" };\n}\n","import type { ContractDefinition } from \"@amqp-contract/contract\";\nimport { standardSchemaToJsonSchema } from \"./schema-converter.js\";\n\n/**\n * AsyncAPI 3.0.0 Specification\n */\nexport interface AsyncAPIDocument {\n asyncapi: \"3.0.0\";\n info: AsyncAPIInfo;\n servers?: Record<string, AsyncAPIServer>;\n channels?: Record<string, AsyncAPIChannel>;\n operations?: Record<string, AsyncAPIOperation>;\n components?: AsyncAPIComponents;\n}\n\nexport interface AsyncAPIInfo {\n title: string;\n version: string;\n description?: string;\n termsOfService?: string;\n contact?: {\n name?: string;\n url?: string;\n email?: string;\n };\n license?: {\n name: string;\n url?: string;\n };\n}\n\nexport interface AsyncAPIServer {\n host: string;\n protocol: string;\n description?: string;\n variables?: Record<string, { default: string; description?: string }>;\n}\n\nexport interface AsyncAPIChannel {\n address: string;\n messages?: Record<string, AsyncAPIMessageRef>;\n description?: string;\n bindings?: {\n amqp?: {\n is: \"queue\" | \"routingKey\";\n queue?: {\n name: string;\n durable?: boolean;\n exclusive?: boolean;\n autoDelete?: boolean;\n };\n exchange?: {\n name: string;\n type: \"topic\" | \"direct\" | \"fanout\" | \"headers\";\n durable?: boolean;\n autoDelete?: boolean;\n };\n };\n };\n}\n\nexport interface AsyncAPIOperation {\n action: \"send\" | \"receive\";\n channel: AsyncAPIRef;\n messages?: AsyncAPIMessageRef[];\n description?: string;\n}\n\nexport interface AsyncAPIMessageRef {\n $ref?: string;\n payload?: AsyncAPISchema;\n contentType?: string;\n name?: string;\n title?: string;\n summary?: string;\n description?: string;\n}\n\nexport interface AsyncAPISchema {\n type?: string;\n properties?: Record<string, AsyncAPISchema>;\n required?: string[];\n items?: AsyncAPISchema;\n $ref?: string;\n description?: string;\n format?: string;\n minimum?: number;\n maximum?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n enum?: unknown[];\n}\n\ninterface AsyncAPIRef {\n $ref: string;\n}\n\nexport interface AsyncAPIComponents {\n messages?: Record<string, AsyncAPIMessageRef>;\n schemas?: Record<string, AsyncAPISchema>;\n}\n\n/**\n * Options for generating AsyncAPI specification\n */\nexport interface GenerateAsyncAPIOptions {\n info: Omit<AsyncAPIInfo, \"title\" | \"version\"> & {\n title?: string;\n version?: string;\n };\n servers?: Record<string, AsyncAPIServer>;\n}\n\n/**\n * Generate AsyncAPI 3.0.0 specification from AMQP contract\n */\nexport function generateAsyncAPI(\n contract: ContractDefinition,\n options: GenerateAsyncAPIOptions,\n): AsyncAPIDocument {\n const channels: Record<string, AsyncAPIChannel> = {};\n const operations: Record<string, AsyncAPIOperation> = {};\n const messages: Record<string, AsyncAPIMessageRef> = {};\n\n // Generate channels from queues\n if (contract.queues) {\n for (const [queueName, queue] of Object.entries(contract.queues)) {\n const binding: {\n amqp?: {\n is: \"queue\";\n queue?: {\n name: string;\n durable?: boolean;\n exclusive?: boolean;\n autoDelete?: boolean;\n };\n };\n } = {\n amqp: {\n is: \"queue\",\n queue: {\n name: queue.name,\n },\n },\n };\n\n if (queue.durable !== undefined) {\n binding.amqp!.queue!.durable = queue.durable;\n }\n if (queue.exclusive !== undefined) {\n binding.amqp!.queue!.exclusive = queue.exclusive;\n }\n if (queue.autoDelete !== undefined) {\n binding.amqp!.queue!.autoDelete = queue.autoDelete;\n }\n\n channels[queueName] = {\n address: queue.name,\n description: `Queue: ${queue.name}`,\n bindings: binding,\n };\n }\n }\n\n // Generate channels from exchanges\n if (contract.exchanges) {\n for (const [exchangeName, exchange] of Object.entries(contract.exchanges)) {\n const binding: {\n amqp?: {\n is: \"routingKey\";\n exchange?: {\n name: string;\n type: \"topic\" | \"direct\" | \"fanout\" | \"headers\";\n durable?: boolean;\n autoDelete?: boolean;\n };\n };\n } = {\n amqp: {\n is: \"routingKey\",\n exchange: {\n name: exchange.name,\n type: exchange.type,\n },\n },\n };\n\n if (exchange.durable !== undefined) {\n binding.amqp!.exchange!.durable = exchange.durable;\n }\n if (exchange.autoDelete !== undefined) {\n binding.amqp!.exchange!.autoDelete = exchange.autoDelete;\n }\n\n channels[exchangeName] = {\n address: exchange.name,\n description: `Exchange: ${exchange.name} (${exchange.type})`,\n bindings: binding,\n };\n }\n }\n\n // Generate operations from publishers\n if (contract.publishers) {\n for (const [publisherName, publisher] of Object.entries(contract.publishers)) {\n const messageName = `${publisherName}Message`;\n\n messages[messageName] = {\n name: messageName,\n title: `${publisherName} message`,\n contentType: \"application/json\",\n payload: standardSchemaToJsonSchema(publisher.message),\n };\n\n operations[publisherName] = {\n action: \"send\",\n channel: { $ref: `#/channels/${publisher.exchange}` },\n messages: [{ $ref: `#/components/messages/${messageName}` }],\n description: `Publish message to ${publisher.exchange}`,\n };\n }\n }\n\n // Generate operations from consumers\n if (contract.consumers) {\n for (const [consumerName, consumer] of Object.entries(contract.consumers)) {\n const messageName = `${consumerName}Message`;\n\n messages[messageName] = {\n name: messageName,\n title: `${consumerName} message`,\n contentType: \"application/json\",\n payload: standardSchemaToJsonSchema(consumer.message),\n };\n\n operations[consumerName] = {\n action: \"receive\",\n channel: { $ref: `#/channels/${consumer.queue}` },\n messages: [{ $ref: `#/components/messages/${messageName}` }],\n description: `Consume message from ${consumer.queue}`,\n };\n }\n }\n\n const result: AsyncAPIDocument = {\n asyncapi: \"3.0.0\",\n info: {\n title: options.info.title ?? \"AMQP Contract API\",\n version: options.info.version ?? \"1.0.0\",\n ...options.info,\n },\n channels,\n operations,\n components: {\n messages,\n },\n };\n\n if (options.servers) {\n result.servers = options.servers;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,2BAA2B,SAA2C;AAGpF,QAAO,EAAE,MAAM,UAAU;;;;;;;;AC6F3B,SAAgB,iBACd,UACA,SACkB;CAClB,MAAMA,WAA4C,EAAE;CACpD,MAAMC,aAAgD,EAAE;CACxD,MAAMC,WAA+C,EAAE;AAGvD,KAAI,SAAS,OACX,MAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,SAAS,OAAO,EAAE;EAChE,MAAMC,UAUF,EACF,MAAM;GACJ,IAAI;GACJ,OAAO,EACL,MAAM,MAAM,MACb;GACF,EACF;AAED,MAAI,MAAM,YAAY,OACpB,SAAQ,KAAM,MAAO,UAAU,MAAM;AAEvC,MAAI,MAAM,cAAc,OACtB,SAAQ,KAAM,MAAO,YAAY,MAAM;AAEzC,MAAI,MAAM,eAAe,OACvB,SAAQ,KAAM,MAAO,aAAa,MAAM;AAG1C,WAAS,aAAa;GACpB,SAAS,MAAM;GACf,aAAa,UAAU,MAAM;GAC7B,UAAU;GACX;;AAKL,KAAI,SAAS,UACX,MAAK,MAAM,CAAC,cAAc,aAAa,OAAO,QAAQ,SAAS,UAAU,EAAE;EACzE,MAAMC,UAUF,EACF,MAAM;GACJ,IAAI;GACJ,UAAU;IACR,MAAM,SAAS;IACf,MAAM,SAAS;IAChB;GACF,EACF;AAED,MAAI,SAAS,YAAY,OACvB,SAAQ,KAAM,SAAU,UAAU,SAAS;AAE7C,MAAI,SAAS,eAAe,OAC1B,SAAQ,KAAM,SAAU,aAAa,SAAS;AAGhD,WAAS,gBAAgB;GACvB,SAAS,SAAS;GAClB,aAAa,aAAa,SAAS,KAAK,IAAI,SAAS,KAAK;GAC1D,UAAU;GACX;;AAKL,KAAI,SAAS,WACX,MAAK,MAAM,CAAC,eAAe,cAAc,OAAO,QAAQ,SAAS,WAAW,EAAE;EAC5E,MAAM,cAAc,GAAG,cAAc;AAErC,WAAS,eAAe;GACtB,MAAM;GACN,OAAO,GAAG,cAAc;GACxB,aAAa;GACb,SAAS,2BAA2B,UAAU,QAAQ;GACvD;AAED,aAAW,iBAAiB;GAC1B,QAAQ;GACR,SAAS,EAAE,MAAM,cAAc,UAAU,YAAY;GACrD,UAAU,CAAC,EAAE,MAAM,yBAAyB,eAAe,CAAC;GAC5D,aAAa,sBAAsB,UAAU;GAC9C;;AAKL,KAAI,SAAS,UACX,MAAK,MAAM,CAAC,cAAc,aAAa,OAAO,QAAQ,SAAS,UAAU,EAAE;EACzE,MAAM,cAAc,GAAG,aAAa;AAEpC,WAAS,eAAe;GACtB,MAAM;GACN,OAAO,GAAG,aAAa;GACvB,aAAa;GACb,SAAS,2BAA2B,SAAS,QAAQ;GACtD;AAED,aAAW,gBAAgB;GACzB,QAAQ;GACR,SAAS,EAAE,MAAM,cAAc,SAAS,SAAS;GACjD,UAAU,CAAC,EAAE,MAAM,yBAAyB,eAAe,CAAC;GAC5D,aAAa,wBAAwB,SAAS;GAC/C;;CAIL,MAAMC,SAA2B;EAC/B,UAAU;EACV,MAAM;GACJ,OAAO,QAAQ,KAAK,SAAS;GAC7B,SAAS,QAAQ,KAAK,WAAW;GACjC,GAAG,QAAQ;GACZ;EACD;EACA;EACA,YAAY,EACV,UACD;EACF;AAED,KAAI,QAAQ,QACV,QAAO,UAAU,QAAQ;AAG3B,QAAO"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["convertedChannels: ChannelsObject","convertedOperations: OperationsObject","convertedMessages: MessagesObject","channelMessages: MessagesObject","channel: ChannelObject","result: Record<string, unknown>"],"sources":["../src/index.ts"],"sourcesContent":["import { ConditionalSchemaConverter, JSONSchema } from \"@orpc/openapi\";\nimport {\n AsyncAPIObject,\n ChannelsObject,\n OperationsObject,\n MessagesObject,\n MessageObject,\n ChannelObject,\n} from \"@asyncapi/parser/esm/spec-types/v3\";\nimport type {\n ContractDefinition,\n MessageDefinition,\n QueueDefinition,\n ExchangeDefinition,\n} from \"@amqp-contract/contract\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Options for configuring the AsyncAPI generator.\n *\n * @example\n * ```typescript\n * import { AsyncAPIGenerator } from '@amqp-contract/asyncapi';\n * import { zodToJsonSchema } from '@orpc/zod';\n *\n * const generator = new AsyncAPIGenerator({\n * schemaConverters: [zodToJsonSchema]\n * });\n * ```\n */\nexport interface AsyncAPIGeneratorOptions {\n /**\n * Schema converters for transforming validation schemas to JSON Schema.\n * Supports Zod, Valibot, ArkType, and other Standard Schema v1 compatible libraries.\n */\n schemaConverters?: ConditionalSchemaConverter[];\n}\n\n/**\n * Options for generating an AsyncAPI document.\n * These correspond to the top-level AsyncAPI document fields.\n */\nexport type AsyncAPIGeneratorGenerateOptions = Pick<AsyncAPIObject, \"id\" | \"info\" | \"servers\">;\n\n/**\n * Generator for creating AsyncAPI 3.0 documentation from AMQP contracts.\n *\n * This class converts contract definitions into AsyncAPI 3.0 specification documents,\n * which can be used for API documentation, code generation, and tooling integration.\n *\n * @example\n * ```typescript\n * import { AsyncAPIGenerator } from '@amqp-contract/asyncapi';\n * import { zodToJsonSchema } from '@orpc/zod';\n * import { z } from 'zod';\n *\n * const contract = defineContract({\n * exchanges: {\n * orders: defineExchange('orders', 'topic', { durable: true })\n * },\n * publishers: {\n * orderCreated: definePublisher('orders', z.object({\n * orderId: z.string(),\n * amount: z.number()\n * }), {\n * routingKey: 'order.created'\n * })\n * }\n * });\n *\n * const generator = new AsyncAPIGenerator({\n * schemaConverters: [zodToJsonSchema]\n * });\n *\n * const asyncapi = await generator.generate(contract, {\n * id: 'urn:com:example:order-service',\n * info: {\n * title: 'Order Service API',\n * version: '1.0.0',\n * description: 'Async API for order processing'\n * },\n * servers: {\n * production: {\n * host: 'rabbitmq.example.com',\n * protocol: 'amqp',\n * protocolVersion: '0.9.1'\n * }\n * }\n * });\n * ```\n */\nexport class AsyncAPIGenerator {\n private readonly converters: ConditionalSchemaConverter[];\n\n /**\n * Create a new AsyncAPI generator instance.\n *\n * @param options - Configuration options including schema converters\n */\n constructor(options: AsyncAPIGeneratorOptions = {}) {\n this.converters = options.schemaConverters ?? [];\n }\n\n /**\n * Generate an AsyncAPI 3.0 document from a contract definition.\n *\n * Converts AMQP exchanges, queues, publishers, and consumers into\n * AsyncAPI channels, operations, and messages with proper JSON Schema\n * validation definitions.\n *\n * @param contract - The AMQP contract definition to convert\n * @param options - AsyncAPI document metadata (id, info, servers)\n * @returns Promise resolving to a complete AsyncAPI 3.0 document\n *\n * @example\n * ```typescript\n * const asyncapi = await generator.generate(contract, {\n * id: 'urn:com:example:api',\n * info: {\n * title: 'My API',\n * version: '1.0.0'\n * },\n * servers: {\n * dev: {\n * host: 'localhost:5672',\n * protocol: 'amqp'\n * }\n * }\n * });\n * ```\n */\n async generate(\n contract: ContractDefinition,\n options: AsyncAPIGeneratorGenerateOptions,\n ): Promise<AsyncAPIObject> {\n const convertedChannels: ChannelsObject = {};\n const convertedOperations: OperationsObject = {};\n const convertedMessages: MessagesObject = {};\n\n // First, collect all messages from publishers and consumers\n const publisherMessages = new Map<string, { message: MessageDefinition; channelKey: string }>();\n const consumerMessages = new Map<string, { message: MessageDefinition; channelKey: string }>();\n\n // Collect messages from publishers\n if (contract.publishers) {\n for (const [publisherName, publisher] of Object.entries(contract.publishers)) {\n const channelKey = this.getExchangeName(publisher.exchange, contract);\n publisherMessages.set(publisherName, { message: publisher.message, channelKey });\n }\n }\n\n // Collect messages from consumers\n if (contract.consumers) {\n for (const [consumerName, consumer] of Object.entries(contract.consumers)) {\n const channelKey = this.getQueueName(consumer.queue, contract);\n consumerMessages.set(consumerName, { message: consumer.message, channelKey });\n }\n }\n\n // Generate channels from queues with their messages\n if (contract.queues) {\n for (const [queueName, queue] of Object.entries(contract.queues)) {\n const channelMessages: MessagesObject = {};\n\n // Add messages from consumers that reference this queue\n for (const [consumerName, { message, channelKey }] of consumerMessages) {\n if (channelKey === queueName) {\n const messageName = `${consumerName}Message`;\n channelMessages[messageName] = await this.convertMessage(message);\n convertedMessages[messageName] = await this.convertMessage(message);\n }\n }\n\n const channel: ChannelObject = {\n ...this.queueToChannel(queue),\n };\n\n if (Object.keys(channelMessages).length > 0) {\n channel.messages = channelMessages;\n }\n\n convertedChannels[queueName] = channel;\n }\n }\n\n // Generate channels from exchanges with their messages\n if (contract.exchanges) {\n for (const [exchangeName, exchange] of Object.entries(contract.exchanges)) {\n const channelMessages: MessagesObject = {};\n\n // Add messages from publishers that reference this exchange\n for (const [publisherName, { message, channelKey }] of publisherMessages) {\n if (channelKey === exchangeName) {\n const messageName = `${publisherName}Message`;\n channelMessages[messageName] = await this.convertMessage(message);\n convertedMessages[messageName] = await this.convertMessage(message);\n }\n }\n\n const channel: ChannelObject = {\n ...this.exchangeToChannel(exchange),\n };\n\n if (Object.keys(channelMessages).length > 0) {\n channel.messages = channelMessages;\n }\n\n convertedChannels[exchangeName] = channel;\n }\n }\n\n // Generate publish operations from publishers\n if (contract.publishers) {\n for (const [publisherName, publisher] of Object.entries(contract.publishers)) {\n const exchangeName = this.getExchangeName(publisher.exchange, contract);\n const messageName = `${publisherName}Message`;\n\n convertedOperations[publisherName] = {\n action: \"send\",\n channel: { $ref: `#/channels/${exchangeName}` },\n messages: [{ $ref: `#/channels/${exchangeName}/messages/${messageName}` }],\n summary: `Publish to ${publisher.exchange.name}`,\n ...(publisher.routingKey && {\n description: `Routing key: ${publisher.routingKey}`,\n }),\n };\n }\n }\n\n // Generate receive operations from consumers\n if (contract.consumers) {\n for (const [consumerName, consumer] of Object.entries(contract.consumers)) {\n const queueName = this.getQueueName(consumer.queue, contract);\n const messageName = `${consumerName}Message`;\n\n convertedOperations[consumerName] = {\n action: \"receive\",\n channel: { $ref: `#/channels/${queueName}` },\n messages: [{ $ref: `#/channels/${queueName}/messages/${messageName}` }],\n summary: `Consume from ${consumer.queue.name}`,\n };\n }\n }\n\n return {\n ...options,\n asyncapi: \"3.0.0\",\n channels: convertedChannels,\n operations: convertedOperations,\n components: {\n messages: convertedMessages,\n },\n };\n }\n\n /**\n * Convert a message definition to AsyncAPI MessageObject\n */\n private async convertMessage(message: MessageDefinition): Promise<MessageObject> {\n const payload = message.payload;\n\n // Convert payload schema\n const payloadJsonSchema = await this.convertSchema(payload, \"input\");\n\n // Build result with required properties\n const result: Record<string, unknown> = {\n payload: payloadJsonSchema,\n contentType: \"application/json\",\n };\n\n // Add optional properties only if they exist\n if (message.headers) {\n const headersJsonSchema = await this.convertSchema(message.headers, \"input\");\n if (headersJsonSchema) {\n result[\"headers\"] = headersJsonSchema;\n }\n }\n\n if (message.summary) {\n result[\"summary\"] = message.summary;\n }\n\n if (message.description) {\n result[\"description\"] = message.description;\n }\n\n return result as MessageObject;\n }\n\n /**\n * Convert a queue definition to AsyncAPI ChannelObject\n */\n private queueToChannel(queue: QueueDefinition): ChannelObject {\n const result: Record<string, unknown> = {\n address: queue.name,\n title: queue.name,\n description: `AMQP Queue: ${queue.name}`,\n bindings: {\n amqp: {\n is: \"queue\",\n queue: {\n name: queue.name,\n durable: queue.durable ?? false,\n exclusive: queue.exclusive ?? false,\n autoDelete: queue.autoDelete ?? false,\n ...(queue.arguments && { vhost: \"/\" }),\n },\n },\n },\n };\n\n return result as ChannelObject;\n }\n\n /**\n * Convert an exchange definition to AsyncAPI ChannelObject\n */\n private exchangeToChannel(exchange: ExchangeDefinition): ChannelObject {\n const result: Record<string, unknown> = {\n address: exchange.name,\n title: exchange.name,\n description: `AMQP Exchange: ${exchange.name} (${exchange.type})`,\n bindings: {\n amqp: {\n is: \"routingKey\",\n exchange: {\n name: exchange.name,\n type: exchange.type,\n durable: exchange.durable ?? false,\n autoDelete: exchange.autoDelete ?? false,\n ...(exchange.arguments && { vhost: \"/\" }),\n },\n },\n },\n };\n\n return result as ChannelObject;\n }\n\n /**\n * Get the name/key of an exchange from the contract\n */\n private getExchangeName(exchange: ExchangeDefinition, contract: ContractDefinition): string {\n if (contract.exchanges) {\n for (const [name, ex] of Object.entries(contract.exchanges)) {\n if (ex === exchange || ex.name === exchange.name) {\n return name;\n }\n }\n }\n return exchange.name;\n }\n\n /**\n * Get the name/key of a queue from the contract\n */\n private getQueueName(queue: QueueDefinition, contract: ContractDefinition): string {\n if (contract.queues) {\n for (const [name, q] of Object.entries(contract.queues)) {\n if (q === queue || q.name === queue.name) {\n return name;\n }\n }\n }\n return queue.name;\n }\n\n /**\n * Convert a Standard Schema to JSON Schema using oRPC converters\n */\n private async convertSchema(\n schema: StandardSchemaV1,\n strategy: \"input\" | \"output\",\n ): Promise<JSONSchema> {\n // Try each converter until one matches\n for (const converter of this.converters) {\n const matches = await converter.condition(schema, { strategy });\n if (matches) {\n const [_required, jsonSchema] = await converter.convert(schema, { strategy });\n return jsonSchema;\n }\n }\n\n // If no converter matches, return a generic object schema\n // This allows the contract to still be generated even without schema converters\n return { type: \"object\" };\n }\n}\n"],"mappings":"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"}
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
**@amqp-contract/asyncapi**
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
# @amqp-contract/asyncapi
|
|
6
|
+
|
|
7
|
+
## Classes
|
|
8
|
+
|
|
9
|
+
### AsyncAPIGenerator
|
|
10
|
+
|
|
11
|
+
Defined in: [index.ts:92](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/asyncapi/src/index.ts#L92)
|
|
12
|
+
|
|
13
|
+
Generator for creating AsyncAPI 3.0 documentation from AMQP contracts.
|
|
14
|
+
|
|
15
|
+
This class converts contract definitions into AsyncAPI 3.0 specification documents,
|
|
16
|
+
which can be used for API documentation, code generation, and tooling integration.
|
|
17
|
+
|
|
18
|
+
#### Example
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { AsyncAPIGenerator } from '@amqp-contract/asyncapi';
|
|
22
|
+
import { zodToJsonSchema } from '@orpc/zod';
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
|
|
25
|
+
const contract = defineContract({
|
|
26
|
+
exchanges: {
|
|
27
|
+
orders: defineExchange('orders', 'topic', { durable: true })
|
|
28
|
+
},
|
|
29
|
+
publishers: {
|
|
30
|
+
orderCreated: definePublisher('orders', z.object({
|
|
31
|
+
orderId: z.string(),
|
|
32
|
+
amount: z.number()
|
|
33
|
+
}), {
|
|
34
|
+
routingKey: 'order.created'
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const generator = new AsyncAPIGenerator({
|
|
40
|
+
schemaConverters: [zodToJsonSchema]
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const asyncapi = await generator.generate(contract, {
|
|
44
|
+
id: 'urn:com:example:order-service',
|
|
45
|
+
info: {
|
|
46
|
+
title: 'Order Service API',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
description: 'Async API for order processing'
|
|
49
|
+
},
|
|
50
|
+
servers: {
|
|
51
|
+
production: {
|
|
52
|
+
host: 'rabbitmq.example.com',
|
|
53
|
+
protocol: 'amqp',
|
|
54
|
+
protocolVersion: '0.9.1'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Constructors
|
|
61
|
+
|
|
62
|
+
##### Constructor
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
new AsyncAPIGenerator(options): AsyncAPIGenerator;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Defined in: [index.ts:100](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/asyncapi/src/index.ts#L100)
|
|
69
|
+
|
|
70
|
+
Create a new AsyncAPI generator instance.
|
|
71
|
+
|
|
72
|
+
###### Parameters
|
|
73
|
+
|
|
74
|
+
| Parameter | Type | Description |
|
|
75
|
+
| ------ | ------ | ------ |
|
|
76
|
+
| `options` | [`AsyncAPIGeneratorOptions`](#asyncapigeneratoroptions) | Configuration options including schema converters |
|
|
77
|
+
|
|
78
|
+
###### Returns
|
|
79
|
+
|
|
80
|
+
[`AsyncAPIGenerator`](#asyncapigenerator)
|
|
81
|
+
|
|
82
|
+
#### Methods
|
|
83
|
+
|
|
84
|
+
##### generate()
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
generate(contract, options): Promise<AsyncAPIObject>;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Defined in: [index.ts:132](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/asyncapi/src/index.ts#L132)
|
|
91
|
+
|
|
92
|
+
Generate an AsyncAPI 3.0 document from a contract definition.
|
|
93
|
+
|
|
94
|
+
Converts AMQP exchanges, queues, publishers, and consumers into
|
|
95
|
+
AsyncAPI channels, operations, and messages with proper JSON Schema
|
|
96
|
+
validation definitions.
|
|
97
|
+
|
|
98
|
+
###### Parameters
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Description |
|
|
101
|
+
| ------ | ------ | ------ |
|
|
102
|
+
| `contract` | `ContractDefinition` | The AMQP contract definition to convert |
|
|
103
|
+
| `options` | [`AsyncAPIGeneratorGenerateOptions`](#asyncapigeneratorgenerateoptions) | AsyncAPI document metadata (id, info, servers) |
|
|
104
|
+
|
|
105
|
+
###### Returns
|
|
106
|
+
|
|
107
|
+
`Promise`\<`AsyncAPIObject`\>
|
|
108
|
+
|
|
109
|
+
Promise resolving to a complete AsyncAPI 3.0 document
|
|
110
|
+
|
|
111
|
+
###### Example
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const asyncapi = await generator.generate(contract, {
|
|
115
|
+
id: 'urn:com:example:api',
|
|
116
|
+
info: {
|
|
117
|
+
title: 'My API',
|
|
118
|
+
version: '1.0.0'
|
|
119
|
+
},
|
|
120
|
+
servers: {
|
|
121
|
+
dev: {
|
|
122
|
+
host: 'localhost:5672',
|
|
123
|
+
protocol: 'amqp'
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Interfaces
|
|
130
|
+
|
|
131
|
+
### AsyncAPIGeneratorOptions
|
|
132
|
+
|
|
133
|
+
Defined in: [index.ts:31](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/asyncapi/src/index.ts#L31)
|
|
134
|
+
|
|
135
|
+
Options for configuring the AsyncAPI generator.
|
|
136
|
+
|
|
137
|
+
#### Example
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { AsyncAPIGenerator } from '@amqp-contract/asyncapi';
|
|
141
|
+
import { zodToJsonSchema } from '@orpc/zod';
|
|
142
|
+
|
|
143
|
+
const generator = new AsyncAPIGenerator({
|
|
144
|
+
schemaConverters: [zodToJsonSchema]
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Properties
|
|
149
|
+
|
|
150
|
+
| Property | Type | Description | Defined in |
|
|
151
|
+
| ------ | ------ | ------ | ------ |
|
|
152
|
+
| <a id="schemaconverters"></a> `schemaConverters?` | `ConditionalSchemaConverter`[] | Schema converters for transforming validation schemas to JSON Schema. Supports Zod, Valibot, ArkType, and other Standard Schema v1 compatible libraries. | [index.ts:36](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/asyncapi/src/index.ts#L36) |
|
|
153
|
+
|
|
154
|
+
## Type Aliases
|
|
155
|
+
|
|
156
|
+
### AsyncAPIGeneratorGenerateOptions
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
type AsyncAPIGeneratorGenerateOptions = Pick<AsyncAPIObject, "id" | "info" | "servers">;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Defined in: [index.ts:43](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/asyncapi/src/index.ts#L43)
|
|
163
|
+
|
|
164
|
+
Options for generating an AsyncAPI document.
|
|
165
|
+
These correspond to the top-level AsyncAPI document fields.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amqp-contract/asyncapi",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "AsyncAPI specification generator for amqp-contract",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amqp",
|
|
@@ -38,23 +38,35 @@
|
|
|
38
38
|
"module": "./dist/index.mjs",
|
|
39
39
|
"types": "./dist/index.d.mts",
|
|
40
40
|
"files": [
|
|
41
|
-
"dist"
|
|
41
|
+
"dist",
|
|
42
|
+
"docs"
|
|
42
43
|
],
|
|
43
44
|
"dependencies": {
|
|
45
|
+
"@orpc/openapi": "1.13.0",
|
|
44
46
|
"@standard-schema/spec": "1.1.0",
|
|
45
|
-
"@amqp-contract/contract": "0.1
|
|
47
|
+
"@amqp-contract/contract": "0.2.1"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
50
|
+
"@asyncapi/parser": "3.4.0",
|
|
51
|
+
"@orpc/arktype": "1.13.0",
|
|
52
|
+
"@orpc/valibot": "1.13.0",
|
|
53
|
+
"@orpc/zod": "1.13.0",
|
|
48
54
|
"@types/node": "25.0.3",
|
|
49
55
|
"@vitest/coverage-v8": "4.0.16",
|
|
56
|
+
"arktype": "2.1.29",
|
|
50
57
|
"tsdown": "0.18.2",
|
|
58
|
+
"typedoc": "0.28.3",
|
|
59
|
+
"typedoc-plugin-markdown": "4.9.0",
|
|
51
60
|
"typescript": "5.9.3",
|
|
61
|
+
"valibot": "1.2.0",
|
|
52
62
|
"vitest": "4.0.16",
|
|
53
63
|
"zod": "4.2.1",
|
|
54
|
-
"@amqp-contract/tsconfig": "0.0.0"
|
|
64
|
+
"@amqp-contract/tsconfig": "0.0.0",
|
|
65
|
+
"@amqp-contract/typedoc": "0.0.1"
|
|
55
66
|
},
|
|
56
67
|
"scripts": {
|
|
57
68
|
"build": "tsdown src/index.ts --format cjs,esm --dts --clean",
|
|
69
|
+
"build:docs": "typedoc",
|
|
58
70
|
"dev": "tsdown src/index.ts --format cjs,esm --dts --watch",
|
|
59
71
|
"test": "vitest run",
|
|
60
72
|
"test:watch": "vitest",
|