@eventcatalog/sdk 2.13.2 → 2.14.0

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/dist/index.mjs CHANGED
@@ -1,12 +1,409 @@
1
1
  // src/index.ts
2
2
  import { join as join18 } from "path";
3
3
 
4
+ // src/dsl/utils.ts
5
+ import { globSync } from "glob";
6
+ function serializeBaseFields(resource, indent = " ") {
7
+ const lines = [];
8
+ if (resource.version) {
9
+ lines.push(`${indent}version ${resource.version}`);
10
+ }
11
+ if (resource.name) {
12
+ lines.push(`${indent}name "${resource.name}"`);
13
+ }
14
+ if (resource.summary) {
15
+ lines.push(`${indent}summary "${resource.summary.trim()}"`);
16
+ }
17
+ if (resource.owners && resource.owners.length > 0) {
18
+ for (const owner of resource.owners) {
19
+ lines.push(`${indent}owner ${owner}`);
20
+ }
21
+ }
22
+ if (resource.deprecated === true) {
23
+ lines.push(`${indent}deprecated true`);
24
+ }
25
+ if (resource.draft === true) {
26
+ lines.push(`${indent}draft true`);
27
+ }
28
+ return lines.join("\n");
29
+ }
30
+ function resolveMessageType(catalogDir, id) {
31
+ if (globSync(`**/events/${id}/index.{md,mdx}`, { cwd: catalogDir }).length > 0) return "event";
32
+ if (globSync(`**/commands/${id}/index.{md,mdx}`, { cwd: catalogDir }).length > 0) return "command";
33
+ if (globSync(`**/queries/${id}/index.{md,mdx}`, { cwd: catalogDir }).length > 0) return "query";
34
+ return void 0;
35
+ }
36
+ function serializeChannelRef(channel) {
37
+ let ref = channel.id;
38
+ if (channel.version) ref += `@${channel.version}`;
39
+ return ref;
40
+ }
41
+ function serializeMessagePointers(items, direction, catalogDir, indent = " ") {
42
+ const lines = [];
43
+ for (const item of items) {
44
+ const msgType = resolveMessageType(catalogDir, item.id);
45
+ if (!msgType) continue;
46
+ let ref = `${item.id}`;
47
+ if (item.version) ref += `@${item.version}`;
48
+ const channels = direction === "sends" ? item.to : item.from;
49
+ const channelKeyword = direction === "sends" ? "to" : "from";
50
+ if (channels && channels.length === 1) {
51
+ lines.push(`${indent}${direction} ${msgType} ${ref} ${channelKeyword} ${serializeChannelRef(channels[0])}`);
52
+ } else if (channels && channels.length > 1) {
53
+ const channelRefs = channels.map(serializeChannelRef).join(", ");
54
+ lines.push(`${indent}${direction} ${msgType} ${ref} ${channelKeyword} ${channelRefs}`);
55
+ } else {
56
+ lines.push(`${indent}${direction} ${msgType} ${ref}`);
57
+ }
58
+ }
59
+ return lines.join("\n");
60
+ }
61
+
62
+ // src/dsl/message.ts
63
+ function messageToDSL(resource, type) {
64
+ const body = serializeBaseFields(resource);
65
+ if (!body) {
66
+ return `${type} ${resource.id}`;
67
+ }
68
+ return `${type} ${resource.id} {
69
+ ${body}
70
+ }`;
71
+ }
72
+
73
+ // src/dsl/service.ts
74
+ async function serviceToDSL(resource, options, getMessageFn) {
75
+ const { catalogDir, hydrate = false, _seen = /* @__PURE__ */ new Set() } = options;
76
+ const parts = [];
77
+ if (hydrate && getMessageFn) {
78
+ const allMessages = [...resource.sends || [], ...resource.receives || []];
79
+ for (const msg of allMessages) {
80
+ const key = `${msg.id}@${msg.version || "latest"}`;
81
+ if (_seen.has(key)) continue;
82
+ _seen.add(key);
83
+ const msgType = resolveMessageType(catalogDir, msg.id);
84
+ if (!msgType) continue;
85
+ const msgResource = await getMessageFn(msg.id, msg.version);
86
+ if (msgResource) {
87
+ parts.push(messageToDSL(msgResource, msgType));
88
+ }
89
+ }
90
+ }
91
+ const lines = [];
92
+ const baseFields = serializeBaseFields(resource);
93
+ if (baseFields) lines.push(baseFields);
94
+ if (resource.sends && resource.sends.length > 0) {
95
+ const sendsStr = serializeMessagePointers(resource.sends, "sends", catalogDir);
96
+ if (sendsStr) lines.push(sendsStr);
97
+ }
98
+ if (resource.receives && resource.receives.length > 0) {
99
+ const recvStr = serializeMessagePointers(resource.receives, "receives", catalogDir);
100
+ if (recvStr) lines.push(recvStr);
101
+ }
102
+ if (resource.writesTo && resource.writesTo.length > 0) {
103
+ for (const container of resource.writesTo) {
104
+ let ref = container.id;
105
+ if (container.version) ref += `@${container.version}`;
106
+ lines.push(` writes-to container ${ref}`);
107
+ }
108
+ }
109
+ if (resource.readsFrom && resource.readsFrom.length > 0) {
110
+ for (const container of resource.readsFrom) {
111
+ let ref = container.id;
112
+ if (container.version) ref += `@${container.version}`;
113
+ lines.push(` reads-from container ${ref}`);
114
+ }
115
+ }
116
+ const body = lines.join("\n");
117
+ parts.push(`service ${resource.id} {
118
+ ${body}
119
+ }`);
120
+ return parts.join("\n\n");
121
+ }
122
+
123
+ // src/dsl/channel.ts
124
+ function channelToDSL(resource) {
125
+ const lines = [];
126
+ if (resource.version) {
127
+ lines.push(` version ${resource.version}`);
128
+ }
129
+ if (resource.name) {
130
+ lines.push(` name "${resource.name}"`);
131
+ }
132
+ if (resource.address) {
133
+ lines.push(` address "${resource.address}"`);
134
+ }
135
+ if (resource.protocols && resource.protocols.length > 0) {
136
+ for (const protocol of resource.protocols) {
137
+ lines.push(` protocol "${protocol}"`);
138
+ }
139
+ }
140
+ if (resource.summary) {
141
+ lines.push(` summary "${resource.summary.trim()}"`);
142
+ }
143
+ if (!lines.length) {
144
+ return `channel ${resource.id}`;
145
+ }
146
+ return `channel ${resource.id} {
147
+ ${lines.join("\n")}
148
+ }`;
149
+ }
150
+
151
+ // src/dsl/owner.ts
152
+ function teamToDSL(team) {
153
+ const lines = [];
154
+ if (team.name) lines.push(` name "${team.name}"`);
155
+ if (team.avatarUrl) lines.push(` avatar "${team.avatarUrl}"`);
156
+ if (team.role) lines.push(` role "${team.role}"`);
157
+ if (team.summary) lines.push(` summary "${team.summary}"`);
158
+ if (team.email) lines.push(` email "${team.email}"`);
159
+ if (team.slackDirectMessageUrl) lines.push(` slack "${team.slackDirectMessageUrl}"`);
160
+ return `team ${team.id} {
161
+ ${lines.join("\n")}
162
+ }`;
163
+ }
164
+ function userToDSL(user) {
165
+ const lines = [];
166
+ if (user.name) lines.push(` name "${user.name}"`);
167
+ if (user.avatarUrl) lines.push(` avatar "${user.avatarUrl}"`);
168
+ if (user.role) lines.push(` role "${user.role}"`);
169
+ if (user.email) lines.push(` email "${user.email}"`);
170
+ if (user.slackDirectMessageUrl) lines.push(` slack "${user.slackDirectMessageUrl}"`);
171
+ return `user ${user.id} {
172
+ ${lines.join("\n")}
173
+ }`;
174
+ }
175
+
176
+ // src/dsl/domain.ts
177
+ async function hydrateOwners(owners, resolvers, seen, parts) {
178
+ if (!owners || !resolvers.getTeam || !resolvers.getUser) return;
179
+ for (const ownerId of owners) {
180
+ const key = `owner:${ownerId}`;
181
+ if (seen.has(key)) continue;
182
+ seen.add(key);
183
+ const team = await resolvers.getTeam(ownerId);
184
+ if (team) {
185
+ parts.push(teamToDSL(team));
186
+ continue;
187
+ }
188
+ const user = await resolvers.getUser(ownerId);
189
+ if (user) {
190
+ parts.push(userToDSL(user));
191
+ }
192
+ }
193
+ }
194
+ async function hydrateChannelsFromMessages(messages, resolvers, seen, parts) {
195
+ if (!resolvers.getChannel) return;
196
+ for (const msg of messages) {
197
+ const channels = msg.to || msg.from;
198
+ if (!channels) continue;
199
+ for (const ch of channels) {
200
+ const key = `channel:${ch.id}@${ch.version || "latest"}`;
201
+ if (seen.has(key)) continue;
202
+ seen.add(key);
203
+ const channel = await resolvers.getChannel(ch.id, ch.version);
204
+ if (channel) {
205
+ parts.push(channelToDSL(channel));
206
+ }
207
+ }
208
+ }
209
+ }
210
+ async function buildDomainBody(resource, options, resolvers, keyword) {
211
+ const { catalogDir, hydrate = false, _seen = /* @__PURE__ */ new Set() } = options;
212
+ const topLevelParts = [];
213
+ if (hydrate && resolvers) {
214
+ if (resource.services && resource.services.length > 0 && resolvers.getService) {
215
+ for (const svcRef of resource.services) {
216
+ const svcKey = `service:${svcRef.id}@${svcRef.version || "latest"}`;
217
+ if (_seen.has(svcKey)) continue;
218
+ _seen.add(svcKey);
219
+ const svc = await resolvers.getService(svcRef.id, svcRef.version);
220
+ if (svc) {
221
+ await hydrateOwners(svc.owners, resolvers, _seen, topLevelParts);
222
+ const svcMessages = [...svc.sends || [], ...svc.receives || []];
223
+ await hydrateChannelsFromMessages(svcMessages, resolvers, _seen, topLevelParts);
224
+ const svcDsl = await serviceToDSL(svc, { catalogDir, hydrate: true, _seen }, resolvers.getMessage);
225
+ topLevelParts.push(svcDsl);
226
+ }
227
+ }
228
+ }
229
+ const domainMessages = [...resource.sends || [], ...resource.receives || []];
230
+ await hydrateChannelsFromMessages(domainMessages, resolvers, _seen, topLevelParts);
231
+ if (resolvers.getMessage) {
232
+ for (const msg of domainMessages) {
233
+ const key = `${msg.id}@${msg.version || "latest"}`;
234
+ if (_seen.has(key)) continue;
235
+ _seen.add(key);
236
+ const msgType = resolveMessageType(catalogDir, msg.id);
237
+ if (!msgType) continue;
238
+ const msgResource = await resolvers.getMessage(msg.id, msg.version);
239
+ if (msgResource) {
240
+ topLevelParts.push(messageToDSL(msgResource, msgType));
241
+ }
242
+ }
243
+ }
244
+ }
245
+ const lines = [];
246
+ const baseFields = serializeBaseFields(resource);
247
+ if (baseFields) lines.push(baseFields);
248
+ if (resource.services && resource.services.length > 0) {
249
+ for (const svc of resource.services) {
250
+ let ref = svc.id;
251
+ if (svc.version) ref += `@${svc.version}`;
252
+ lines.push(` service ${ref}`);
253
+ }
254
+ }
255
+ if (resource.sends && resource.sends.length > 0) {
256
+ const sendsStr = serializeMessagePointers(resource.sends, "sends", catalogDir);
257
+ if (sendsStr) lines.push(sendsStr);
258
+ }
259
+ if (resource.receives && resource.receives.length > 0) {
260
+ const recvStr = serializeMessagePointers(resource.receives, "receives", catalogDir);
261
+ if (recvStr) lines.push(recvStr);
262
+ }
263
+ if (resource.domains && resource.domains.length > 0) {
264
+ if (hydrate && resolvers?.getDomain) {
265
+ for (const subRef of resource.domains) {
266
+ const subKey = `domain:${subRef.id}@${subRef.version || "latest"}`;
267
+ if (_seen.has(subKey)) continue;
268
+ _seen.add(subKey);
269
+ const subDomain = await resolvers.getDomain(subRef.id, subRef.version);
270
+ if (subDomain) {
271
+ await hydrateOwners(subDomain.owners, resolvers, _seen, topLevelParts);
272
+ const sub = await buildDomainBody(subDomain, { catalogDir, hydrate, _seen }, resolvers, "subdomain");
273
+ topLevelParts.push(...sub.topLevelParts);
274
+ const indented = sub.block.split("\n").map((line) => ` ${line}`).join("\n");
275
+ lines.push(indented);
276
+ }
277
+ }
278
+ } else {
279
+ for (const sub of resource.domains) {
280
+ let ref = sub.id;
281
+ if (sub.version) ref += `@${sub.version}`;
282
+ lines.push(` subdomain ${ref}`);
283
+ }
284
+ }
285
+ }
286
+ const body = lines.join("\n");
287
+ const block = `${keyword} ${resource.id} {
288
+ ${body}
289
+ }`;
290
+ return { topLevelParts, block };
291
+ }
292
+ async function domainToDSL(resource, options, resolvers) {
293
+ const { catalogDir, hydrate = false, _seen = /* @__PURE__ */ new Set() } = options;
294
+ const result = await buildDomainBody(resource, { catalogDir, hydrate, _seen }, resolvers, "domain");
295
+ const parts = [...result.topLevelParts, result.block];
296
+ return parts.join("\n\n");
297
+ }
298
+
299
+ // src/dsl/index.ts
300
+ function getMessage(resolvers, catalogDir) {
301
+ return async (id, version) => {
302
+ const msgType = resolveMessageType(catalogDir, id);
303
+ if (!msgType) return void 0;
304
+ switch (msgType) {
305
+ case "event":
306
+ return resolvers.getEvent(id, version);
307
+ case "command":
308
+ return resolvers.getCommand(id, version);
309
+ case "query":
310
+ return resolvers.getQuery(id, version);
311
+ }
312
+ };
313
+ }
314
+ async function hydrateChannels(resource, resolvers, seen, parts) {
315
+ const allMessages = [...resource.sends || [], ...resource.receives || []];
316
+ for (const msg of allMessages) {
317
+ const channels = "to" in msg ? msg.to : "from" in msg ? msg.from : void 0;
318
+ if (!channels) continue;
319
+ for (const ch of channels) {
320
+ const key = `channel:${ch.id}@${ch.version || "latest"}`;
321
+ if (seen.has(key)) continue;
322
+ seen.add(key);
323
+ const channel = await resolvers.getChannel(ch.id, ch.version);
324
+ if (channel) {
325
+ parts.push(channelToDSL(channel));
326
+ }
327
+ }
328
+ }
329
+ }
330
+ async function hydrateOwners2(owners, resolvers, seen, parts) {
331
+ if (!owners) return;
332
+ for (const ownerId of owners) {
333
+ const key = `owner:${ownerId}`;
334
+ if (seen.has(key)) continue;
335
+ seen.add(key);
336
+ const team = await resolvers.getTeam(ownerId);
337
+ if (team) {
338
+ parts.push(teamToDSL(team));
339
+ continue;
340
+ }
341
+ const user = await resolvers.getUser(ownerId);
342
+ if (user) {
343
+ parts.push(userToDSL(user));
344
+ }
345
+ }
346
+ }
347
+ var toDSL = (catalogDir, resolvers) => async (resource, options) => {
348
+ const resources = Array.isArray(resource) ? resource : [resource];
349
+ const seen = /* @__PURE__ */ new Set();
350
+ const parts = [];
351
+ for (const res of resources) {
352
+ const key = `${options.type}:${res.id}@${res.version || "latest"}`;
353
+ if (seen.has(key)) continue;
354
+ seen.add(key);
355
+ switch (options.type) {
356
+ case "event":
357
+ case "command":
358
+ case "query":
359
+ if (options.hydrate) {
360
+ await hydrateOwners2(res.owners, resolvers, seen, parts);
361
+ }
362
+ parts.push(messageToDSL(res, options.type));
363
+ break;
364
+ case "service":
365
+ if (options.hydrate) {
366
+ await hydrateOwners2(res.owners, resolvers, seen, parts);
367
+ await hydrateChannels(res, resolvers, seen, parts);
368
+ }
369
+ parts.push(
370
+ await serviceToDSL(
371
+ res,
372
+ { catalogDir, hydrate: options.hydrate, _seen: seen },
373
+ getMessage(resolvers, catalogDir)
374
+ )
375
+ );
376
+ break;
377
+ case "domain":
378
+ if (options.hydrate) {
379
+ await hydrateOwners2(res.owners, resolvers, seen, parts);
380
+ }
381
+ parts.push(
382
+ await domainToDSL(
383
+ res,
384
+ { catalogDir, hydrate: options.hydrate, _seen: seen },
385
+ {
386
+ getService: resolvers.getService,
387
+ getDomain: resolvers.getDomain,
388
+ getMessage: getMessage(resolvers, catalogDir),
389
+ getChannel: resolvers.getChannel,
390
+ getTeam: resolvers.getTeam,
391
+ getUser: resolvers.getUser
392
+ }
393
+ )
394
+ );
395
+ break;
396
+ }
397
+ }
398
+ return parts.join("\n\n");
399
+ };
400
+
4
401
  // src/events.ts
5
402
  import fs2 from "fs/promises";
6
403
  import { join as join3 } from "path";
7
404
 
8
405
  // src/internal/utils.ts
9
- import { globSync } from "glob";
406
+ import { globSync as globSync2 } from "glob";
10
407
  import fsSync from "fs";
11
408
  import { copy } from "fs-extra";
12
409
  import { join, dirname, normalize, resolve, relative } from "path";
@@ -57,7 +454,7 @@ var getFiles = async (pattern, ignore = "") => {
57
454
  let relativePattern = relative(absoluteBaseDir, normalizedInputPattern);
58
455
  relativePattern = relativePattern.replace(/\\/g, "/");
59
456
  const ignoreList = Array.isArray(ignore) ? ignore : [ignore];
60
- const files = globSync(relativePattern, {
457
+ const files = globSync2(relativePattern, {
61
458
  cwd: absoluteBaseDir,
62
459
  ignore: ["node_modules/**", ...ignoreList],
63
460
  absolute: true,
@@ -459,7 +856,18 @@ var getServiceByPath = (directory) => async (path6) => {
459
856
  };
460
857
  var getServices = (directory) => async (options) => getResources(directory, {
461
858
  type: "services",
462
- ignore: ["**/events/**", "**/commands/**", "**/queries/**", "**/entities/**", "**/subdomains/**/entities/**"],
859
+ ignore: [
860
+ "**/events/**",
861
+ "**/commands/**",
862
+ "**/queries/**",
863
+ "**/entities/**",
864
+ "**/channels/**",
865
+ "**/containers/**",
866
+ "**/data-products/**",
867
+ "**/data-stores/**",
868
+ "**/flows/**",
869
+ "**/subdomains/**/entities/**"
870
+ ],
463
871
  ...options
464
872
  });
465
873
  var writeService = (directory) => async (service, options = {
@@ -822,8 +1230,8 @@ var addMessageToChannel = (directory, collection) => async (id, _message, versio
822
1230
  writeMessage: writeQuery
823
1231
  }
824
1232
  };
825
- const { getMessage, rmMessageById, writeMessage } = functions[collection];
826
- const message = await getMessage(directory)(_message.id, _message.version);
1233
+ const { getMessage: getMessage2, rmMessageById, writeMessage } = functions[collection];
1234
+ const message = await getMessage2(directory)(_message.id, _message.version);
827
1235
  const messagePath = await getResourcePath(directory, _message.id, _message.version);
828
1236
  const extension = extname2(messagePath?.fullPath || "");
829
1237
  if (!message) throw new Error(`Message ${_message.id} with version ${_message.version} not found`);
@@ -2394,7 +2802,35 @@ var src_default = (path6) => {
2394
2802
  * @param version - Optional version of the diagram to add the file to
2395
2803
  * @returns
2396
2804
  */
2397
- addFileToDiagram: addFileToDiagram(join18(path6))
2805
+ addFileToDiagram: addFileToDiagram(join18(path6)),
2806
+ /**
2807
+ * ================================
2808
+ * DSL
2809
+ * ================================
2810
+ */
2811
+ /**
2812
+ * Converts catalog resources to EventCatalog DSL (.ec) format strings.
2813
+ *
2814
+ * @param resource - A resource or array of resources to convert
2815
+ * @param options - Options including type ('event'|'command'|'query'|'service'|'domain') and optional hydrate flag
2816
+ * @returns A DSL string representation
2817
+ *
2818
+ * @example
2819
+ * ```ts
2820
+ * const dsl = await sdk.toDSL(event, { type: 'event' });
2821
+ * const dsl = await sdk.toDSL(services, { type: 'service', hydrate: true });
2822
+ * ```
2823
+ */
2824
+ toDSL: toDSL(join18(path6), {
2825
+ getEvent: getEvent(join18(path6)),
2826
+ getCommand: getCommand(join18(path6)),
2827
+ getQuery: getQuery(join18(path6)),
2828
+ getService: getService(join18(path6)),
2829
+ getDomain: getDomain(join18(path6, "domains")),
2830
+ getChannel: getChannel(join18(path6)),
2831
+ getTeam: getTeam(join18(path6, "teams")),
2832
+ getUser: getUser(join18(path6, "users"))
2833
+ })
2398
2834
  };
2399
2835
  };
2400
2836
  export {