@eventcatalog/core 4.3.1 → 4.3.2

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.
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(analytics_exports);
36
36
  var import_os = __toESM(require("os"), 1);
37
37
 
38
38
  // package.json
39
- var version = "4.3.1";
39
+ var version = "4.3.2";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-GC6SSPJI.js";
4
- import "../chunk-E3KVC4LF.js";
3
+ } from "../chunk-BYH34SOZ.js";
4
+ import "../chunk-W4UBACYZ.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -140,7 +140,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
140
140
  var import_os = __toESM(require("os"), 1);
141
141
 
142
142
  // package.json
143
- var version = "4.3.1";
143
+ var version = "4.3.2";
144
144
 
145
145
  // src/constants.ts
146
146
  var VERSION = version;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-ZYVGVWMQ.js";
3
+ } from "../chunk-25RQCO2H.js";
4
4
  import "../chunk-K2XIENVT.js";
5
- import "../chunk-GC6SSPJI.js";
6
- import "../chunk-E3KVC4LF.js";
5
+ import "../chunk-BYH34SOZ.js";
6
+ import "../chunk-W4UBACYZ.js";
7
7
  import "../chunk-6QENHZZP.js";
8
8
  export {
9
9
  log_build_default as default
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-K2XIENVT.js";
5
5
  import {
6
6
  raiseEvent
7
- } from "./chunk-GC6SSPJI.js";
7
+ } from "./chunk-BYH34SOZ.js";
8
8
  import {
9
9
  getEventCatalogConfigFile,
10
10
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-E3KVC4LF.js";
3
+ } from "./chunk-W4UBACYZ.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import os from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-E3KVC4LF.js";
3
+ } from "./chunk-W4UBACYZ.js";
4
4
 
5
5
  // src/utils/cli-logger.ts
6
6
  import pc from "picocolors";
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "4.3.1";
2
+ var version = "4.3.2";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-GC7WNQP6.js";
3
+ } from "./chunk-T6UEDS2J.js";
4
4
  import {
5
5
  cleanup,
6
6
  getEventCatalogConfigFile
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "4.3.1";
28
+ var version = "4.3.2";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-E3KVC4LF.js";
3
+ } from "./chunk-W4UBACYZ.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -42,8 +42,11 @@ Use these fields on an entity property:
42
42
 
43
43
  - `references`: The id of the entity being referenced.
44
44
  - `referencesIdentifier`: The property on the referenced entity that this property matches.
45
+ - `referenceTarget`: Set to `entity` when the relationship should point to the referenced entity as a whole.
45
46
  - `relationType`: The relationship label, such as `belongsTo`, `hasOne`, `hasMany`, or a business-specific label.
46
47
 
48
+ `referenceTarget` is opt-in. When it is omitted, EventCatalog retains the existing behavior of targeting `referencesIdentifier`, the referenced entity's `identifier`, or its first property.
49
+
47
50
  ## Example relationship
48
51
 
49
52
  ```md title="/entities/order/index.mdx"
@@ -83,6 +86,49 @@ properties:
83
86
 
84
87
  This tells EventCatalog that `order.customerId` references the `customer.customerId` identifier.
85
88
 
89
+ ## Reference a whole entity
90
+
91
+ Use `referenceTarget: entity` when the relationship is to the entity rather than one of its properties.
92
+
93
+ ```yaml
94
+ properties:
95
+ - name: customer
96
+ type: object
97
+ references: customer
98
+ referenceTarget: entity
99
+ relationType: placedBy
100
+ ```
101
+
102
+ This renders the relationship against the `Customer` entity header. Existing relationships that omit `referenceTarget` continue to use property-level targeting.
103
+
104
+ ## Model embedded objects
105
+
106
+ Use nested `properties` for a value object that only exists as part of its parent entity. Embedded objects have no entity id and cannot be referenced independently from elsewhere in the catalog.
107
+
108
+ ```yaml
109
+ properties:
110
+ - name: deliveryAddress
111
+ type: object
112
+ properties:
113
+ - name: line1
114
+ type: string
115
+ required: true
116
+ - name: city
117
+ type: string
118
+ required: true
119
+ - name: adjustments
120
+ type: array
121
+ items:
122
+ type: object
123
+ properties:
124
+ - name: reason
125
+ type: string
126
+ - name: amount
127
+ type: decimal
128
+ ```
129
+
130
+ Embedded properties appear inside their parent entity rather than as separate nodes in entity maps.
131
+
86
132
  ## Relationship direction
87
133
 
88
134
  Define the relationship where the reference exists.
@@ -258,9 +258,13 @@ attachments:
258
258
  | `description` | `string` | Description of the property. |
259
259
  | `references` | `string` | Entity id referenced by this property. |
260
260
  | `referencesIdentifier` | `string` | Identifier property on the referenced entity. |
261
+ | `referenceTarget` | `entity` | Targets the referenced entity as a whole instead of one of its properties. |
261
262
  | `relationType` | `string` | Relationship label shown in entity maps. |
262
263
  | `enum` | `array` | Allowed string values. |
263
- | `items` | `object` | Item type for array properties. |
264
+ | `properties` | `array` | Nested properties for an embedded object. |
265
+ | `items` | `object` | Item type and optional nested properties for array properties. |
266
+
267
+ Property definitions are recursive, so embedded objects and arrays of embedded objects can contain their own `properties`. Embedded objects are owned by their parent entity and are not independently referenceable catalog resources.
264
268
 
265
269
  ## EntityPropertiesTable component
266
270
 
@@ -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.1";
147
+ var version = "4.3.2";
148
148
 
149
149
  // src/constants.ts
150
150
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log_build_default
3
- } from "./chunk-ZYVGVWMQ.js";
3
+ } from "./chunk-25RQCO2H.js";
4
4
  import "./chunk-K2XIENVT.js";
5
5
  import {
6
6
  runMigrations
@@ -14,10 +14,10 @@ import {
14
14
  } from "./chunk-B7HCX5HM.js";
15
15
  import {
16
16
  generate
17
- } from "./chunk-UXHV3GN7.js";
17
+ } from "./chunk-YBNWZEFQ.js";
18
18
  import {
19
19
  logger
20
- } from "./chunk-GC7WNQP6.js";
20
+ } from "./chunk-T6UEDS2J.js";
21
21
  import {
22
22
  resolve_catalog_dependencies_default
23
23
  } from "./chunk-LHR4G2UO.js";
@@ -27,7 +27,7 @@ import {
27
27
  import {
28
28
  watch
29
29
  } from "./chunk-CAJUIMDJ.js";
30
- import "./chunk-GC6SSPJI.js";
30
+ import "./chunk-BYH34SOZ.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-E3KVC4LF.js";
41
+ } from "./chunk-W4UBACYZ.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.1";
111
+ var version = "4.3.2";
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-UXHV3GN7.js";
4
- import "./chunk-GC7WNQP6.js";
5
- import "./chunk-E3KVC4LF.js";
3
+ } from "./chunk-YBNWZEFQ.js";
4
+ import "./chunk-T6UEDS2J.js";
5
+ import "./chunk-W4UBACYZ.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.1";
39
+ var version = "4.3.2";
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-GC7WNQP6.js";
4
- import "../chunk-E3KVC4LF.js";
3
+ } from "../chunk-T6UEDS2J.js";
4
+ import "../chunk-W4UBACYZ.js";
5
5
  export {
6
6
  logger
7
7
  };
@@ -8,12 +8,31 @@ type EntityProperty = {
8
8
  type: string;
9
9
  required?: boolean;
10
10
  description?: string;
11
+ references?: string;
12
+ referencesIdentifier?: string;
13
+ referenceTarget?: 'entity';
11
14
  enum?: string[];
15
+ properties?: EntityProperty[];
12
16
  items?: {
13
17
  type: string;
18
+ properties?: EntityProperty[];
14
19
  };
15
20
  };
16
21
 
22
+ type EntityPropertyRow = {
23
+ property: EntityProperty;
24
+ depth: number;
25
+ };
26
+
27
+ const getNestedProperties = (property: EntityProperty) =>
28
+ property.properties || (property.type === 'array' ? property.items?.properties : undefined) || [];
29
+
30
+ const flattenProperties = (entityProperties: EntityProperty[], depth = 0): EntityPropertyRow[] =>
31
+ entityProperties.flatMap((property) => [{ property, depth }, ...flattenProperties(getNestedProperties(property), depth + 1)]);
32
+
33
+ const getPropertyTypeLabel = (property: EntityProperty) =>
34
+ property.type === 'array' && property.items ? `${property.items.type}[]` : property.type;
35
+
17
36
  // Expects a CollectionEntry for 'entities'.
18
37
  // The actual properties structure might differ slightly from the base type,
19
38
  // so we handle it below.
@@ -22,6 +41,7 @@ export interface Props extends CollectionEntry<'entities'> {}
22
41
  const { data, collection } = Astro.props;
23
42
  // Cast the properties to our expected type for use in the template
24
43
  const properties = data?.properties as EntityProperty[] | undefined;
44
+ const propertyRows = flattenProperties(properties || []);
25
45
  const isComponentEnabled = collection === 'entities';
26
46
  ---
27
47
 
@@ -56,23 +76,23 @@ const isComponentEnabled = collection === 'entities';
56
76
  <th scope="col" class="py-3 px-6 min-w-[250px]">
57
77
  Description
58
78
  </th>
79
+ <th scope="col" class="py-3 px-6 min-w-[180px]">
80
+ Reference
81
+ </th>
59
82
  </tr>
60
83
  </thead>
61
84
  <tbody>
62
- {properties.map((prop) => (
85
+ {propertyRows.map(({ property: prop, depth }) => (
63
86
  <tr class="bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] border-b border-[rgb(var(--ec-page-border))] hover:bg-[rgb(var(--ec-content-hover))] align-top">
64
- <td class="py-4 px-6 font-medium text-[rgb(var(--ec-page-text))] whitespace-nowrap">
87
+ <td
88
+ class="py-4 pr-6 font-medium text-[rgb(var(--ec-page-text))] whitespace-nowrap"
89
+ style={`padding-left: ${24 + depth * 20}px`}
90
+ >
91
+ {depth > 0 && <span class="mr-1 text-[rgb(var(--ec-icon-color))]">↳</span>}
65
92
  <code class="text-sm bg-[rgb(var(--ec-content-hover))] rounded px-1 py-0.5">{prop.name}</code>
66
93
  </td>
67
94
  <td class="py-4 px-6 text-[rgb(var(--ec-page-text-muted))]">
68
- {prop.type === 'array' && prop.items ? (
69
- <span>
70
- array&lt;<code class="text-sm bg-[rgb(var(--ec-content-hover))] rounded px-1 py-0.5">{prop.items.type}</code>
71
- &gt;
72
- </span>
73
- ) : (
74
- <code class="text-sm bg-[rgb(var(--ec-content-hover))] rounded px-1 py-0.5">{prop.type}</code>
75
- )}
95
+ <code class="text-sm bg-[rgb(var(--ec-content-hover))] rounded px-1 py-0.5">{getPropertyTypeLabel(prop)}</code>
76
96
  {prop.enum && (
77
97
  <div class="text-xs text-[rgb(var(--ec-page-text-muted))] mt-2">
78
98
  <span class="font-semibold block mb-1">Enum:</span>
@@ -100,6 +120,22 @@ const isComponentEnabled = collection === 'entities';
100
120
  <td class="py-4 px-6 text-[rgb(var(--ec-page-text-muted))]">
101
121
  {prop.description || <span class="text-[rgb(var(--ec-icon-color))] italic">No description provided.</span>}
102
122
  </td>
123
+ <td class="py-4 px-6 text-[rgb(var(--ec-page-text-muted))]">
124
+ {prop.references ? (
125
+ <div class="flex flex-col gap-1">
126
+ <code class="text-sm bg-[rgb(var(--ec-content-hover))] rounded px-1 py-0.5 w-fit">{prop.references}</code>
127
+ <span class="text-xs">
128
+ {prop.referenceTarget === 'entity'
129
+ ? 'Whole entity'
130
+ : prop.referencesIdentifier
131
+ ? `Property: ${prop.referencesIdentifier}`
132
+ : 'Identifier or first property'}
133
+ </span>
134
+ </div>
135
+ ) : (
136
+ <span class="text-[rgb(var(--ec-icon-color))]">—</span>
137
+ )}
138
+ </td>
103
139
  </tr>
104
140
  ))}
105
141
  </tbody>
@@ -963,6 +963,44 @@ const ubiquitousLanguages = defineCollection({
963
963
  }),
964
964
  });
965
965
 
966
+ interface EntityPropertySchema {
967
+ name: string;
968
+ type: string;
969
+ required?: boolean;
970
+ description?: string;
971
+ references?: string;
972
+ referencesIdentifier?: string;
973
+ referenceTarget?: 'entity';
974
+ relationType?: string;
975
+ enum?: string[];
976
+ properties?: EntityPropertySchema[];
977
+ items?: {
978
+ type: string;
979
+ properties?: EntityPropertySchema[];
980
+ };
981
+ }
982
+
983
+ const entityPropertySchema: z.ZodType<EntityPropertySchema> = z.lazy(() =>
984
+ z.object({
985
+ name: z.string(),
986
+ type: z.string(),
987
+ required: z.boolean().optional(),
988
+ description: z.string().optional(),
989
+ references: z.string().optional(),
990
+ referencesIdentifier: z.string().optional(),
991
+ referenceTarget: z.literal('entity').optional(),
992
+ relationType: z.string().optional(),
993
+ enum: z.array(z.string()).optional(),
994
+ properties: z.array(entityPropertySchema).optional(),
995
+ items: z
996
+ .object({
997
+ type: z.string(),
998
+ properties: z.array(entityPropertySchema).optional(),
999
+ })
1000
+ .optional(),
1001
+ })
1002
+ );
1003
+
966
1004
  const entities = defineCollection({
967
1005
  loader: globWithSafeWatcher({
968
1006
  pattern: withIgnoredBuildArtifacts(['**/entities/*/index.(md|mdx)', '**/entities/*/versioned/*/index.(md|mdx)']),
@@ -976,25 +1014,7 @@ const entities = defineCollection({
976
1014
  .object({
977
1015
  aggregateRoot: z.boolean().optional(),
978
1016
  identifier: z.string().optional(),
979
- properties: z
980
- .array(
981
- z.object({
982
- name: z.string(),
983
- type: z.string(),
984
- required: z.boolean().optional(),
985
- description: z.string().optional(),
986
- references: z.string().optional(),
987
- referencesIdentifier: z.string().optional(),
988
- relationType: z.string().optional(),
989
- enum: z.array(z.string()).optional(),
990
- items: z
991
- .object({
992
- type: z.string(),
993
- })
994
- .optional(),
995
- })
996
- )
997
- .optional(),
1017
+ properties: z.array(entityPropertySchema).optional(),
998
1018
  services: z.array(reference('services')).optional(),
999
1019
  domains: z.array(reference('domains')).optional(),
1000
1020
  detailsPanel: z
@@ -10,12 +10,19 @@ import { getServices, type Service } from '@utils/collections/services';
10
10
 
11
11
  const elk = new ELK();
12
12
 
13
+ export const ENTITY_TARGET_HANDLE_ID = '__eventcatalog-entity-target';
14
+
13
15
  const getReferencedEntityId = (property: any, entityMap: Map<string, Entity[]>) => {
14
16
  if (property.references) return property.references;
15
17
  if (property.type === 'array' && property.items?.type && entityMap.has(property.items.type)) return property.items.type;
16
18
  return undefined;
17
19
  };
18
20
 
21
+ const getReferencePropertyNames = (entity: Entity, entityMap: Map<string, Entity[]>) =>
22
+ entity.data.properties
23
+ ?.filter((property: any) => getReferencedEntityId(property, entityMap))
24
+ .map((property: any) => property.name) ?? [];
25
+
19
26
  const getRelationType = (property: any) => {
20
27
  if (property.relationType) return property.relationType;
21
28
  if (property.type === 'array' && property.items?.type) return 'hasMany';
@@ -64,7 +71,14 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
64
71
  id: nodeId,
65
72
  type: 'entities',
66
73
  position: { x: 0, y: 0 },
67
- data: { label: entity.data.name, entity, domainName: resource?.data.name, domainId: resource?.data.id },
74
+ data: {
75
+ label: entity.data.name,
76
+ entity,
77
+ domainName: resource?.data.name,
78
+ domainId: resource?.data.id,
79
+ entityTargetHandle: ENTITY_TARGET_HANDLE_ID,
80
+ referencePropertyNames: getReferencePropertyNames(entity, entityMap),
81
+ },
68
82
  });
69
83
  }
70
84
 
@@ -116,6 +130,8 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
116
130
  externalToDomain: true,
117
131
  domainName: domainName,
118
132
  domainId: domainId,
133
+ entityTargetHandle: ENTITY_TARGET_HANDLE_ID,
134
+ referencePropertyNames: getReferencePropertyNames(externalEntity, entityMap),
119
135
  },
120
136
  });
121
137
  addedExternalEntities.push(externalEntity);
@@ -151,9 +167,12 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
151
167
  // Use the property name as the source handle
152
168
  const sourceHandle = `${referenceProperty.name}-source`;
153
169
 
154
- // Use referencesIdentifier if provided, otherwise use identifier or first property
170
+ // Whole-entity targeting is explicit so existing catalogs retain their
171
+ // identifier/first-property fallback behavior.
155
172
  let targetHandle = '';
156
- if (referenceProperty.referencesIdentifier) {
173
+ if (referenceProperty.referenceTarget === 'entity') {
174
+ targetHandle = ENTITY_TARGET_HANDLE_ID;
175
+ } else if (referenceProperty.referencesIdentifier) {
157
176
  targetHandle = `${referenceProperty.referencesIdentifier}-target`;
158
177
  } else if (referencedEntity.data.identifier) {
159
178
  targetHandle = `${referencedEntity.data.identifier}-target`;
@@ -211,8 +230,8 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
211
230
  // Prepare ELK graph structure
212
231
  const elkNodes = nodes.map((node: any) => ({
213
232
  id: node.id,
214
- width: 280,
215
- height: 200,
233
+ width: 220,
234
+ height: Math.max(120, 72 + (node.data.entity.data.properties?.length ?? 0) * 40),
216
235
  }));
217
236
 
218
237
  const elkEdges = edges.map((edge: any) => ({
@@ -225,12 +244,10 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
225
244
  id: 'root',
226
245
  layoutOptions: {
227
246
  'elk.algorithm': 'force',
228
- 'elk.force.repulsivePower': '2.0',
229
- 'elk.force.iterations': '500',
230
- 'elk.spacing.nodeNode': '150',
231
- 'elk.spacing.edgeNode': '75',
232
- 'elk.spacing.edgeEdge': '30',
233
- 'elk.padding': '[top=50,left=50,bottom=50,right=50]',
247
+ 'elk.force.iterations': '300',
248
+ 'elk.spacing.nodeNode': '45',
249
+ 'elk.spacing.componentComponent': '50',
250
+ 'elk.padding': '[top=30,left=30,bottom=30,right=30]',
234
251
  'elk.separateConnectedComponents': 'true',
235
252
  },
236
253
  children: elkNodes,
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "SEE LICENSE IN LICENSE",
9
9
  "type": "module",
10
- "version": "4.3.1",
10
+ "version": "4.3.2",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -116,9 +116,9 @@
116
116
  "update-notifier": "^7.3.1",
117
117
  "uuid": "^10.0.0",
118
118
  "zod": "^4.3.6",
119
- "@eventcatalog/sdk": "2.26.2",
120
- "@eventcatalog/visualiser": "^4.1.0",
121
- "@eventcatalog/linter": "1.1.7"
119
+ "@eventcatalog/linter": "1.1.8",
120
+ "@eventcatalog/visualiser": "^4.1.1",
121
+ "@eventcatalog/sdk": "2.26.3"
122
122
  },
123
123
  "devDependencies": {
124
124
  "@astrojs/check": "^0.9.9",