@eventcatalog/core 4.3.2 → 4.3.3

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.
Files changed (46) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-T6UEDS2J.js → chunk-2IGBU3PN.js} +1 -1
  6. package/dist/{chunk-YBNWZEFQ.js → chunk-HR6KK56F.js} +1 -1
  7. package/dist/{chunk-25RQCO2H.js → chunk-OWE74ZHY.js} +3 -3
  8. package/dist/{chunk-W4UBACYZ.js → chunk-PXBG5QKU.js} +1 -1
  9. package/dist/{chunk-BYH34SOZ.js → chunk-UW52WRSH.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/docs/api/02-config.md +24 -0
  13. package/dist/docs/cli/governance.md +1 -1
  14. package/dist/docs/cli/snapshots.md +1 -1
  15. package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +56 -7
  16. package/dist/docs/development/ask-your-architecture/03-mcp-server/introduction.md +8 -3
  17. package/dist/docs/development/deployment/build-ssr-mode.md +1 -9
  18. package/dist/docs/development/developer-tools/api-catalog.md +1 -1
  19. package/dist/docs/development/guides/98-versioning-resources.md +15 -0
  20. package/dist/docs/development/guides/resources/entities/03-model-entity-relationships.md +26 -6
  21. package/dist/docs/development/guides/resources/entities/05-entity-maps.md +2 -0
  22. package/dist/docs/development/guides/resources/entities/06-reference.md +25 -2
  23. package/dist/docs/development/guides/resources/services/07-versioning-and-lifecycle/01-version-services.md +2 -0
  24. package/dist/docs/plugins/asyncapi/02-plugin-configuration.md +30 -2
  25. package/dist/docs/plugins/asyncapi/03-features.md +39 -26
  26. package/dist/docs/plugins/asyncapi/03a-workflows.md +61 -2
  27. package/dist/docs/plugins/openapi/02-plugin-configuration.md +1 -2
  28. package/dist/docs/plugins/openapi/03-features.md +8 -6
  29. package/dist/docs/plugins/openapi/03a-workflows.md +79 -18
  30. package/dist/eventcatalog.cjs +1 -1
  31. package/dist/eventcatalog.js +9 -9
  32. package/dist/generate.cjs +1 -1
  33. package/dist/generate.js +3 -3
  34. package/dist/utils/cli-logger.cjs +1 -1
  35. package/dist/utils/cli-logger.js +2 -2
  36. package/eventcatalog/src/components/LatestVersionRedirect.astro +26 -0
  37. package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +25 -0
  38. package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +25 -0
  39. package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +25 -0
  40. package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +25 -0
  41. package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +25 -0
  42. package/eventcatalog/src/pages/docs/services/_latest-version-route.ts +42 -0
  43. package/eventcatalog/src/stores/sidebar-store/builders/service.ts +9 -12
  44. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +4 -2
  45. package/package.json +3 -3
  46. package/dist/docs/cli/import.md +0 -26
@@ -15,7 +15,7 @@ import TabItem from '@theme/TabItem';
15
15
  | Feature | Use cases |
16
16
  |---------|-----------|
17
17
  | [Mapping messages events, commands or queries](#mapping-messages-events-commands-or-queries) | AsyncAPI does not distinguish between commands, events and queries, everything is a message. Using the EventCatalog custom AsyncAPI [extension](https://www.asyncapi.com/docs/concepts/asyncapi-document/extending-specification) `x-eventcatalog-message-type` you can specify if your messages are events, command or queries. |
18
- | [Defining message ownership roles](#defining-message-ownership-roles) | AsyncAPI specification files do not define who owns the message and it's contract. Your AsyncAPI file my define messages your service consumes and produces but that service may not be the service that owns it's contract. (e.g An order service that consumes a Payment event. The order service (AsyncAPI file) would specify it consumes the payment event, but it is not the owner of the contract or event). |
18
+ | [Defining message ownership roles](#defining-message-ownership-roles) | Decide which service owns a shared message contract. By default, `send`/`publish` operations own the contract and `receive`/`subscribe` operations reference it. Use `x-eventcatalog-role` when you need to override that behavior. |
19
19
  | [Defining message versions](#defining-message-versions) | In EventCatalog you can version your domains, services and all your messages. This can be useful as you can specify which version of a message your service produces or consumes. |
20
20
  | [Mapping channels into EventCatalog](#mapping-channels-into-eventcatalog) | EventCatalog supports [Channels](/docs/development/guides/resources/messages/message-channels/introduction) ([see demo](https://demo.eventcatalog.dev/docs/channels/inventory.%7Benv%7D.events/1.0.0)). This let's you document how messages or organized and transported in your event-driven architecture. |
21
21
  | [Creating draft domains, services and messages](#creating-draft-domains-services-and-messages) | You can create draft domains, services and messages in EventCatalog from your AsyncAPI files. This will be used to mark the resources as draft in EventCatalog. |
@@ -53,47 +53,60 @@ You can see more [examples of the extension on the demo project](https://github.
53
53
 
54
54
  <AddedIn version="2.4.0" pkg="@eventcatalog/generator-asyncapi" url="https://github.com/event-catalog/generators/releases/tag/v"/>
55
55
 
56
- AsyncAPI specification files do not define who owns the message and it's contract. Your AsyncAPI file my define messages your service consumes and produces but that service may not be the service that owns it's contract. (e.g An order service that consumes a Payment event. The order service (AsyncAPI file) would specify it consumes the payment event, but it is not the owner of the contract or event).
56
+ EventCatalog stores a message as one shared contract, even when several AsyncAPI files reference it. The generator therefore needs to decide which service can create or update that contract.
57
57
 
58
- By default when you integrate your AsyncAPI files into EventCatalog, EventCatalog will assume your service (AsyncAPI file) owns the messages and will document them this way.
58
+ By default, ownership follows the AsyncAPI operation direction:
59
59
 
60
- If you want to define ownership of messages in your files you can use the `x-eventcatalog-role` extension in your AsyncAPI files. This let's you specify if your service is a provider (owner) or the message or just a consumer (client) of the message.
60
+ | AsyncAPI operation | Relationship to the contract | Generator behavior |
61
+ |--------------------|------------------------------|--------------------|
62
+ | `send` or `publish` | Owner | Creates the message and may update an existing contract. |
63
+ | `receive` or `subscribe` | Reference | Uses the existing contract without overwriting it. If the message does not exist, the receiver creates a fallback from its own definition. |
61
64
 
62
- The available extension values are:
65
+ The service relationship is recorded independently of ownership. Owners are added to the service's `sends`, while references are added to its `receives`.
63
66
 
64
- - `provider`: Generator will generate a new message considering the service is the owner of message contract (`default`).
65
- - `client`: Generator will consider the message as a sent/received message in service but will NOT generate or modify the message in EventCatalog.
67
+ :::info Default changed in generator v7
68
+ Before v7, every message without an explicit role was treated as owned by the service being generated. This meant a consumer could overwrite the producer's summary or schema.
66
69
 
67
- ```js title="x-eventcatalog-role example"
68
- components:
69
- messages:
70
- SendOrderConfirmation:
71
- description: 'Command received to ask for sending an Order confirmation notification'
72
- x-eventcatalog-role: client // Define the ownership. This example shows the service is a client of the message and does not own the message.
73
- ```
70
+ To retain the previous ownership behavior for a receiving operation, set `x-eventcatalog-role: provider` explicitly.
71
+ :::
74
72
 
75
- You can see more [examples of the extension on the demo project](https://github.com/event-catalog/generators/tree/main/examples/generator-asyncapi/tree/main/asyncapi-files).
73
+ #### Receiver fallbacks
74
+
75
+ If a receiving service is generated before the message exists, EventCatalog creates a fallback beneath that service. This keeps consumer-only catalogs working.
76
76
 
77
- AsyncAPI specification files do not define who owns the message and it's contract. Your AsyncAPI file my define messages your service consumes and produces but that service may not be the service that owns it's contract. (e.g An order service that consumes a Payment event. The order service (AsyncAPI file) would specify it consumes the payment event, but it is not the owner of the contract or event).
77
+ When the producing service is generated later for the same message and version, its definition becomes authoritative and the fallback is moved to the producer's message location. This makes the final contract independent of whether the producer or consumer was generated first.
78
78
 
79
- By default when you integrate your AsyncAPI files into EventCatalog, EventCatalog will assume your service (AsyncAPI file) owns the messages and will document them this way.
79
+ #### Override the inferred role
80
80
 
81
- If you want to define ownership of messages in your files you can use the `x-eventcatalog-role` extension in your AsyncAPI files. This let's you specify if your service is a provider (owner) or the message or just a consumer (client) of the message.
81
+ Use `x-eventcatalog-role` on an operation or message when operation direction does not express the ownership you need:
82
82
 
83
- The available extension values are:
83
+ - `provider`: The service owns the message contract and may create or update it.
84
+ - `client`: The contract is external to the service. The generator records the `sends` or `receives` relationship but does not create or modify the message documentation.
84
85
 
85
- - `provider`: Generator will generate a new message considering the service is the owner of message contract (`default`).
86
- - `client`: Generator will consider the message as a sent/received message in service but will NOT generate or modify the message in EventCatalog.
86
+ An operation-level role takes precedence over a message-level role. This is useful when a shared message has a different role for each operation or service.
87
87
 
88
- ```js title="x-eventcatalog-role example"
88
+ ```yaml title="Receiving service that explicitly owns the contract"
89
+ operations:
90
+ receiveOrderPlaced:
91
+ action: receive
92
+ x-eventcatalog-role: provider
93
+ channel:
94
+ $ref: '#/channels/orders'
95
+ messages:
96
+ - $ref: '#/channels/orders/messages/OrderPlaced'
97
+ ```
98
+
99
+ You can also set the role on the message:
100
+
101
+ ```yaml title="External message"
89
102
  components:
90
103
  messages:
91
- SendOrderConfirmation:
92
- description: 'Command received to ask for sending an Order confirmation notification'
93
- x-eventcatalog-role: client // Define the ownership. This example shows the service is a client of the message and does not own the message.
104
+ PaymentProcessed:
105
+ description: Payment processed by an external provider
106
+ x-eventcatalog-role: client
94
107
  ```
95
108
 
96
- You can see more [examples of the extension on the demo project](https://github.com/event-catalog/generators/tree/main/examples/generator-asyncapi/tree/main/asyncapi-files).
109
+ See [Shared message contracts across producer and consumer specs](/docs/plugins/asyncapi/03a-workflows#shared-message-contracts-across-producer-and-consumer-specs) for a complete workflow.
97
110
 
98
111
  ### Defining message versions
99
112
 
@@ -16,6 +16,8 @@ Many companies have different needs, so we have provided a few different workflo
16
16
 
17
17
  - [Simple mapping between AsyncAPI files and EventCatalog services](#simple-mapping-between-asyncapi-files-and-eventcatalog-services)
18
18
  - Map a single AsyncAPI file to a single EventCatalog service
19
+ - [Shared message contracts across producer and consumer specs](#shared-message-contracts-across-producer-and-consumer-specs)
20
+ - Keep the producer's message contract authoritative when several services reference it
19
21
  - [Independent message versions from your AsyncAPI file](#independent-message-versions-from-your-asyncapi-file)
20
22
  - Version your messages independently of the service version
21
23
  - [Mapping multiple AsyncAPI files to a single EventCatalog service](#mapping-multiple-asyncapi-files-to-a-single-eventcatalog-service)
@@ -48,7 +50,64 @@ generators: [
48
50
  ],
49
51
  ```
50
52
 
51
- ### Independent message versions from your AsyncAPI file
53
+ ## Shared message contracts across producer and consumer specs
54
+
55
+ This workflow is useful when several AsyncAPI files describe the same message. For example, an order service may send `OrderPlaced` while notification and fulfillment services receive it.
56
+
57
+ EventCatalog stores one shared message contract for a given message ID and version. Without an explicit `x-eventcatalog-role`, the generator uses the AsyncAPI operation direction to determine how each service relates to that contract:
58
+
59
+ - `send` or `publish` operations own the message contract and may update it.
60
+ - `receive` or `subscribe` operations reference an existing contract without overwriting it.
61
+ - A receiver creates a fallback from its own AsyncAPI definition only when the message does not exist yet.
62
+ - If the producer is generated after a receiver fallback, the producer's definition replaces the fallback and becomes authoritative.
63
+
64
+ The services can be configured in either order:
65
+
66
+ ```js title="eventcatalog.config.js"
67
+ generators: [
68
+ [
69
+ '@eventcatalog/generator-asyncapi',
70
+ {
71
+ services: [
72
+ { path: path.join(__dirname, 'asyncapi-files', 'notification-service.yml'), id: 'notification-service' },
73
+ { path: path.join(__dirname, 'asyncapi-files', 'order-service.yml'), id: 'order-service' },
74
+ ],
75
+ },
76
+ ],
77
+ ],
78
+ ```
79
+
80
+ Given these operations, `order-service` owns `OrderPlaced` and `notification-service` references it:
81
+
82
+ ```yaml title="order-service.yml"
83
+ operations:
84
+ sendOrderPlaced:
85
+ action: send
86
+ channel:
87
+ $ref: '#/channels/orders'
88
+ messages:
89
+ - $ref: '#/channels/orders/messages/OrderPlaced'
90
+ ```
91
+
92
+ ```yaml title="notification-service.yml"
93
+ operations:
94
+ receiveOrderPlaced:
95
+ action: receive
96
+ channel:
97
+ $ref: '#/channels/orders'
98
+ messages:
99
+ - $ref: '#/channels/orders/messages/OrderPlaced'
100
+ ```
101
+
102
+ Both services remain connected to the message: the producer has an entry in `sends` and the consumer has an entry in `receives`. Only the producer can replace the shared contract.
103
+
104
+ :::tip Explicit ownership
105
+ If a receiving operation should own the contract, add `x-eventcatalog-role: provider` to that operation or message. Use `x-eventcatalog-role: client` when a sending or receiving service should only record its relationship and must never create the message documentation.
106
+
107
+ Read [Defining message ownership roles](/docs/plugins/asyncapi/features#defining-message-ownership-roles) for the complete rules.
108
+ :::
109
+
110
+ ## Independent message versions from your AsyncAPI file
52
111
 
53
112
  This is useful if you want to version your messages separately from the AsyncAPI file.
54
113
 
@@ -150,4 +209,4 @@ generators: [
150
209
  },
151
210
  ],
152
211
  ],
153
- ```
212
+ ```
@@ -33,7 +33,7 @@ Each service in the `services` array requires the following properties:
33
33
  |----------|------|----------|-------------|
34
34
  | `id` | string | Yes | EventCatalog ID for the service. |
35
35
  | `path` | string or string[] | Yes | Path/s to your OpenAPI file or remote URL to the OpenAPI file. v6.0.0 introduced the ability to map multiple OpenAPI files to a single service. |
36
- | `version` | string | No | Version for the generated EventCatalog service and messages. If not provided, the version from the OpenAPI `info.version` field is used. |
36
+ | `version` | string | No | Version for the generated EventCatalog service and messages. If not provided, the OpenAPI `info.version` is used. For a `path` array, the highest `info.version` is selected using semantic version ordering and all specifications are attached to that service version. |
37
37
  | `name` | string | No | Display name for the service. If not provided, the specification will be used. _Added in v7.4.3_|
38
38
  | `summary` | string | No | Short summary of the service. If not provided, the specification will be used. _Added in v7.4.3_|
39
39
  | `owners` | string[] | No | Owners of the service. You can assign EventCatalog users or teams to services. |
@@ -330,4 +330,3 @@ or refer to the [OpenAPI examples on GitHub](https://github.com/event-catalog/ge
330
330
 
331
331
 
332
332
 
333
-
@@ -294,6 +294,8 @@ This can be useful if you want to introduce a new endpoint, but warn users that
294
294
 
295
295
  If your service exposes multiple APIs, you can map many OpenAPI files to a single service.
296
296
 
297
+ One service configuration entry represents one EventCatalog service version. Every file in the `path` array is attached to that service version.
298
+
297
299
  ```js title="eventcatalog.config.js"
298
300
  // ..rest of file
299
301
  generators: [
@@ -304,10 +306,10 @@ generators: [
304
306
  // Here we map two OpenAPI files to a single service
305
307
  {
306
308
  path: [
307
- path.join(__dirname, 'openapi-files', 'orders-service-v1.yml'),
308
- path.join(__dirname, 'openapi-files', 'orders-service-v2.yml')
309
+ path.join(__dirname, 'openapi-files', 'orders-public-api.yml'),
310
+ path.join(__dirname, 'openapi-files', 'orders-admin-api.yml')
309
311
  ],
310
- id: 'orders-service', owners: ['dboyne', 'team-1']
312
+ id: 'orders-service', version: '3.0.0', owners: ['dboyne', 'team-1']
311
313
  },
312
314
  ],
313
315
  // You can also set owners to the domain, this does not cascade to the services or messages
@@ -323,11 +325,11 @@ generators: [
323
325
 
324
326
  #### How does mapping multiple OpenAPI files to a single service work?
325
327
 
326
- The OpenAPI plugin will parse all the files in the `path` array. The are ordered by version (info.version).
328
+ The OpenAPI plugin parses every file in the `path` array and adds every specification to the same service version.
327
329
 
328
- Old versions are parsed first and versioned in your catalog along side the messages.
330
+ When `version` is configured on the service entry, it is used for the service and its generated messages. When it is omitted, the highest OpenAPI `info.version` in the array is selected using semantic version ordering.
329
331
 
330
- The latest version is parsed last and will be used as the current version in your catalog.
332
+ The files in a `path` array do not create separate historical service records. To model service history, configure a separate service entry with an explicit `version` for each OpenAPI file. See [OpenAPI workflows](/docs/plugins/openapi/03a-workflows#creating-versioned-service-records) for examples.
331
333
 
332
334
  You can try this demo out for yourself by running the [mapping-many-openapi-files-to-a-service example](https://github.com/event-catalog/generators/tree/main/examples/generator-openapi/mapping-many-openapi-files-to-a-service).
333
335
 
@@ -19,7 +19,11 @@ Many companies have different needs, so we have provided a few different workflo
19
19
  - [Independent message versions from your OpenAPI file](#independent-message-versions-from-your-openapi-file)
20
20
  - Version your messages independently of the service version
21
21
  - [Mapping multiple OpenAPI files to a single EventCatalog service](#mapping-multiple-openapi-files-to-a-single-eventcatalog-service)
22
- - Map multiple OpenAPI files to a single EventCatalog service
22
+ - Attach multiple OpenAPI specifications to one service version
23
+ - [Creating versioned service records](#creating-versioned-service-records)
24
+ - Use separate service entries when each specification represents a service version
25
+ - [Versioning a service on a later generator run](#versioning-a-service-on-a-later-generator-run)
26
+ - Preserve the current service as history when a newer version is generated
23
27
  - [Mapping OpenAPI and AsyncAPI files to the same EventCatalog service](#mapping-openapi-and-asyncapi-files-to-the-same-eventcatalog-service)
24
28
  - Map an OpenAPI and AsyncAPI file to the same EventCatalog service
25
29
 
@@ -29,7 +33,9 @@ _If we are missing a workflow that you think is useful, please raise an [issue o
29
33
 
30
34
  This is the simplest workflow and is useful if you have a single OpenAPI file per service.
31
35
 
32
- EventCatalog will parse your OpenAPI file and map it's specification to the service you define.
36
+ EventCatalog will parse your OpenAPI file and map its specification to the service you define.
37
+
38
+ If you do not configure a service `version`, the OpenAPI document's `info.version` is used.
33
39
 
34
40
  This will document your service, and the messages it produces and consumes.
35
41
 
@@ -79,44 +85,99 @@ paths:
79
85
 
80
86
  In the example above, the message `listOrders` will be versioned as `2.0.0` and all other messages will be versioned as `1.0.0`.
81
87
 
82
- You can read more about message versioning in the [Features](/docs/plugins/openapi/features#defining-message-versions) section.
88
+ You can read more about message versioning in the [Features](/docs/plugins/openapi/features#custom-versioning-with-x-eventcatalog-message-version) section.
83
89
 
84
90
  ## Mapping multiple OpenAPI files to a single EventCatalog service
85
91
 
86
- This is useful if you have a single service that produces and consumes multiple OpenAPI versions.
92
+ This is useful when one version of a service exposes multiple OpenAPI contracts, for example a public API and an administration API.
93
+
94
+ Use a `path` array in one service entry. One service entry represents one EventCatalog service version, so every OpenAPI file in the array is attached to that version and rendered in the service's `specifications` list.
95
+
96
+ ```js title="eventcatalog.config.js"
97
+ // ...rest of file
98
+ generators: [
99
+ [
100
+ '@eventcatalog/generator-openapi',
101
+ {
102
+ services: [
103
+ {
104
+ id: 'orders-service',
105
+ version: '3.0.0',
106
+ path: [
107
+ path.join(__dirname, 'openapi-files', 'orders-public-api.yml'),
108
+ path.join(__dirname, 'openapi-files', 'orders-admin-api.yml'),
109
+ ],
110
+ },
111
+ ],
112
+ },
113
+ ],
114
+ ],
115
+ ```
116
+
117
+ The generated service contains both specifications:
118
+
119
+ ```yaml
120
+ id: orders-service
121
+ version: 3.0.0
122
+ specifications:
123
+ - type: openapi
124
+ path: orders-public-api.yml
125
+ name: Orders Public API
126
+ - type: openapi
127
+ path: orders-admin-api.yml
128
+ name: Orders Admin API
129
+ ```
130
+
131
+ :::info Choosing the service version
132
+ When `version` is configured on the service entry, it takes precedence over the `info.version` values in the OpenAPI files.
87
133
 
88
- Some people call this a "polyglot" service, as it produces and consumes multiple APIs.
134
+ When `version` is omitted, the generator uses the highest OpenAPI `info.version` from the `path` array. Versions are compared semantically, so `10.0.0` is considered newer than `2.0.0`. Every specification in the array is then attached to that selected service version.
135
+
136
+ A `path` array does not create historical service records. Use separate service entries if the files represent different versions of the service.
137
+ :::
138
+
139
+ ## Creating versioned service records
140
+
141
+ Use a separate service entry for each service version when you want to keep multiple versions in EventCatalog. Set `version` explicitly to make the intended service lifecycle clear.
89
142
 
90
143
  ```js title="eventcatalog.config.js"
91
144
  // ...rest of file
92
145
  generators: [
93
146
  [
94
- '@eventcatalog/generator-asyncapi',
147
+ '@eventcatalog/generator-openapi',
95
148
  {
96
149
  services: [
97
- // Version 1 of the OpenAPI file
98
- { path: path.join(__dirname, 'openapi-files', 'orders-service-v1.yml'), id: 'orders-service' },
99
- // Version 2 of the AsyncAPI File
100
- { path: path.join(__dirname, 'openapi-files', 'orders-service-v2.yml'), id: 'orders-service' },
150
+ {
151
+ id: 'orders-service',
152
+ version: '1.0.0',
153
+ path: path.join(__dirname, 'openapi-files', 'orders-service-v1.yml'),
154
+ },
155
+ {
156
+ id: 'orders-service',
157
+ version: '2.0.0',
158
+ path: path.join(__dirname, 'openapi-files', 'orders-service-v2.yml'),
159
+ },
101
160
  ],
102
161
  },
103
162
  ],
104
163
  ],
105
164
  ```
106
165
 
107
- :::info Versioning
108
- When you map multiple versions of an OpenAPI file to a single service, the `version` property in your OpenAPI files needs to be the same.
166
+ In this example, `2.0.0` becomes the current service. Version `1.0.0` and its OpenAPI specification are stored as a historical service record under `versioned/1.0.0`. Each service version references only the OpenAPI specification configured for that entry.
109
167
 
110
- If they are different, EventCatalog will version the previous versions of your service.
168
+ You can also use a `path` array inside either entry if that particular service version has multiple OpenAPI specifications.
111
169
 
112
- In the example above, the `version` property in the OpenAPI files is `1.0.0` and `2.0.0`.
170
+ ## Versioning a service on a later generator run
113
171
 
114
- EventCatalog will version the service as `1.0.0` and `2.0.0`.
172
+ You do not need to keep every historical version in `eventcatalog.config.js` forever. If `orders-service` is generated as `1.0.0` today and the configured OpenAPI file changes to `2.0.0` later, running the generator again will:
115
173
 
116
- The messages in the service will be versioned as `1.0.0` and `2.0.0`.
174
+ 1. Move the existing `1.0.0` service and its OpenAPI specification into the service's versioned history.
175
+ 2. Generate `2.0.0` as the current service with the new OpenAPI specification.
117
176
 
118
- This allows you to track the history of your service and the messages in it.
177
+ This works when the service version comes from OpenAPI `info.version` or from an explicit `version` in the service configuration. If you configure `version`, update it when you intend to create a new service version.
119
178
 
179
+ :::tip Multiple contracts versus service history
180
+ Use one entry with `path: []` for multiple contracts belonging to the same service version. Use separate entries, or update the version on a later run, to create service history.
120
181
  :::
121
182
 
122
183
  ## Mapping OpenAPI and AsyncAPI files to the same EventCatalog service
@@ -150,4 +211,4 @@ generators: [
150
211
  },
151
212
  ],
152
213
  ],
153
- ```
214
+ ```
@@ -144,7 +144,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
144
144
  var import_picocolors = __toESM(require("picocolors"), 1);
145
145
 
146
146
  // package.json
147
- var version = "4.3.2";
147
+ var version = "4.3.3";
148
148
 
149
149
  // src/constants.ts
150
150
  var VERSION = version;
@@ -1,11 +1,8 @@
1
1
  import {
2
2
  log_build_default
3
- } from "./chunk-25RQCO2H.js";
3
+ } from "./chunk-OWE74ZHY.js";
4
+ import "./chunk-UW52WRSH.js";
4
5
  import "./chunk-K2XIENVT.js";
5
- import {
6
- runMigrations
7
- } from "./chunk-XUAF2H54.js";
8
- import "./chunk-CA4U2JP7.js";
9
6
  import {
10
7
  getProjectOutDir,
11
8
  isAuthEnabled,
@@ -14,10 +11,10 @@ import {
14
11
  } from "./chunk-B7HCX5HM.js";
15
12
  import {
16
13
  generate
17
- } from "./chunk-YBNWZEFQ.js";
14
+ } from "./chunk-HR6KK56F.js";
18
15
  import {
19
16
  logger
20
- } from "./chunk-T6UEDS2J.js";
17
+ } from "./chunk-2IGBU3PN.js";
21
18
  import {
22
19
  resolve_catalog_dependencies_default
23
20
  } from "./chunk-LHR4G2UO.js";
@@ -27,7 +24,10 @@ import {
27
24
  import {
28
25
  watch
29
26
  } from "./chunk-CAJUIMDJ.js";
30
- import "./chunk-BYH34SOZ.js";
27
+ import {
28
+ runMigrations
29
+ } from "./chunk-XUAF2H54.js";
30
+ import "./chunk-CA4U2JP7.js";
31
31
  import {
32
32
  createAstroDevLineFilter,
33
33
  createAstroLineFilter
@@ -38,7 +38,7 @@ import {
38
38
  import "./chunk-W5JQON7Z.js";
39
39
  import {
40
40
  VERSION
41
- } from "./chunk-W4UBACYZ.js";
41
+ } from "./chunk-PXBG5QKU.js";
42
42
  import {
43
43
  linkCoreNodeModules,
44
44
  resolveInstalledCoreNodeModules
package/dist/generate.cjs CHANGED
@@ -108,7 +108,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
108
108
  var import_picocolors = __toESM(require("picocolors"), 1);
109
109
 
110
110
  // package.json
111
- var version = "4.3.2";
111
+ var version = "4.3.3";
112
112
 
113
113
  // src/constants.ts
114
114
  var VERSION = version;
package/dist/generate.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generate
3
- } from "./chunk-YBNWZEFQ.js";
4
- import "./chunk-T6UEDS2J.js";
5
- import "./chunk-W4UBACYZ.js";
3
+ } from "./chunk-HR6KK56F.js";
4
+ import "./chunk-2IGBU3PN.js";
5
+ import "./chunk-PXBG5QKU.js";
6
6
  import "./chunk-6QENHZZP.js";
7
7
  export {
8
8
  generate
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(cli_logger_exports);
36
36
  var import_picocolors = __toESM(require("picocolors"), 1);
37
37
 
38
38
  // package.json
39
- var version = "4.3.2";
39
+ var version = "4.3.3";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  logger
3
- } from "../chunk-T6UEDS2J.js";
4
- import "../chunk-W4UBACYZ.js";
3
+ } from "../chunk-2IGBU3PN.js";
4
+ import "../chunk-PXBG5QKU.js";
5
5
  export {
6
6
  logger
7
7
  };
@@ -0,0 +1,26 @@
1
+ ---
2
+ import Seo from '@components/Seo.astro';
3
+
4
+ interface Props {
5
+ redirectUrl: string;
6
+ title: string;
7
+ }
8
+
9
+ const { redirectUrl, title } = Astro.props;
10
+ ---
11
+
12
+ <!doctype html>
13
+ <html lang="en">
14
+ <head>
15
+ <Seo title={title} />
16
+ <meta http-equiv="refresh" content={`0;url=${redirectUrl}`} />
17
+ </head>
18
+ <body>
19
+ <p>You are being redirected to <a href={redirectUrl}>{redirectUrl}</a></p>
20
+ </body>
21
+ </html>
22
+
23
+ <script define:vars={{ redirectUrl }}>
24
+ const fullRedirectUrl = redirectUrl + window.location.search + window.location.hash;
25
+ window.location.replace(fullRedirectUrl);
26
+ </script>
@@ -0,0 +1,25 @@
1
+ ---
2
+ import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
3
+ import { isResourceDocsEnabled, isSSR } from '@utils/feature';
4
+ import { buildUrl } from '@utils/url-builder';
5
+ import { Page as VersionedPage } from '../../../../[type]/[id]/[version]/[docType]/[docId]/_index.data';
6
+ import { getLatestServicePaths, getLatestServiceVersion } from '../../../_latest-version-route';
7
+
8
+ export const prerender = !isSSR();
9
+ export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
10
+
11
+ const { id, docType, docId } = Astro.params;
12
+ const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
13
+
14
+ if (!isResourceDocsEnabled() || !id || !docType || !docId || !version) {
15
+ throw new Response(null, { status: 404, statusText: 'Resource documentation not found' });
16
+ }
17
+
18
+ const redirectUrl = buildUrl(`/docs/services/${id}/${version}/${docType}/${docId}`);
19
+
20
+ if (isSSR()) {
21
+ return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
22
+ }
23
+ ---
24
+
25
+ <LatestVersionRedirect title="Latest service documentation" redirectUrl={redirectUrl} />
@@ -0,0 +1,25 @@
1
+ ---
2
+ import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
3
+ import { isSSR } from '@utils/feature';
4
+ import { buildUrl } from '@utils/url-builder';
5
+ import { Page as VersionedPage } from '../../../[type]/[id]/[version]/asyncapi/_[filename].data';
6
+ import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
7
+
8
+ export const prerender = !isSSR();
9
+ export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
10
+
11
+ const { id, filename } = Astro.params;
12
+ const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
13
+
14
+ if (!id || !filename || !version) {
15
+ throw new Response(null, { status: 404, statusText: 'AsyncAPI specification not found' });
16
+ }
17
+
18
+ const redirectUrl = buildUrl(`/docs/services/${id}/${version}/asyncapi/${filename}`);
19
+
20
+ if (isSSR()) {
21
+ return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
22
+ }
23
+ ---
24
+
25
+ <LatestVersionRedirect title="Latest AsyncAPI specification" redirectUrl={redirectUrl} />
@@ -0,0 +1,25 @@
1
+ ---
2
+ import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
3
+ import { isChangelogEnabled, isSSR } from '@utils/feature';
4
+ import { buildUrl } from '@utils/url-builder';
5
+ import { Page as VersionedPage } from '../../../[type]/[id]/[version]/changelog/_index.data';
6
+ import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
7
+
8
+ export const prerender = !isSSR();
9
+ export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
10
+
11
+ const { id } = Astro.params;
12
+ const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
13
+
14
+ if (!isChangelogEnabled() || !id || !version) {
15
+ throw new Response(null, { status: 404, statusText: 'Changelog not found' });
16
+ }
17
+
18
+ const redirectUrl = buildUrl(`/docs/services/${id}/${version}/changelog`);
19
+
20
+ if (isSSR()) {
21
+ return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
22
+ }
23
+ ---
24
+
25
+ <LatestVersionRedirect title="Latest service changelog" redirectUrl={redirectUrl} />
@@ -0,0 +1,25 @@
1
+ ---
2
+ import LatestVersionRedirect from '@components/LatestVersionRedirect.astro';
3
+ import { isSSR } from '@utils/feature';
4
+ import { buildUrl } from '@utils/url-builder';
5
+ import { Page as VersionedPage } from '../../../[type]/[id]/[version]/graphql/_[filename].data';
6
+ import { getLatestServicePaths, getLatestServiceVersion } from '../../_latest-version-route';
7
+
8
+ export const prerender = !isSSR();
9
+ export const getStaticPaths = async () => getLatestServicePaths(await VersionedPage.getStaticPaths());
10
+
11
+ const { id, filename } = Astro.params;
12
+ const version = Astro.props.redirectVersion ?? (id ? await getLatestServiceVersion(id) : undefined);
13
+
14
+ if (!id || !filename || !version) {
15
+ throw new Response(null, { status: 404, statusText: 'GraphQL specification not found' });
16
+ }
17
+
18
+ const redirectUrl = buildUrl(`/docs/services/${id}/${version}/graphql/${filename}`);
19
+
20
+ if (isSSR()) {
21
+ return Astro.redirect(`${redirectUrl}${Astro.url.search}${Astro.url.hash}`);
22
+ }
23
+ ---
24
+
25
+ <LatestVersionRedirect title="Latest GraphQL specification" redirectUrl={redirectUrl} />