@eventcatalog/core 5.0.0-beta.0 → 5.0.0-beta.10

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 (171) hide show
  1. package/LICENSE +227 -128
  2. package/dist/analytics/analytics.cjs +19 -3
  3. package/dist/analytics/analytics.d.cts +1 -1
  4. package/dist/analytics/analytics.d.ts +1 -1
  5. package/dist/analytics/analytics.js +3 -2
  6. package/dist/analytics/log-build.cjs +72 -35
  7. package/dist/analytics/log-build.js +5 -4
  8. package/dist/analytics/telemetry.cjs +42 -0
  9. package/dist/analytics/telemetry.d.cts +20 -0
  10. package/dist/analytics/telemetry.d.ts +20 -0
  11. package/dist/analytics/telemetry.js +8 -0
  12. package/dist/{chunk-ZHKEARLJ.js → chunk-2LBDRHE4.js} +1 -1
  13. package/dist/{chunk-TQLRG35T.js → chunk-AWOO63ME.js} +71 -45
  14. package/dist/chunk-CPW6G62V.js +17 -0
  15. package/dist/{chunk-LR7AGRHN.js → chunk-CRKJBDN7.js} +56 -33
  16. package/dist/{chunk-CR6AK5LR.js → chunk-EM36OL6A.js} +1 -1
  17. package/dist/{chunk-TWN43V4T.js → chunk-IONYKKOY.js} +1 -1
  18. package/dist/{chunk-WAIADKQE.js → chunk-LJMXOEEC.js} +10 -3
  19. package/dist/constants.cjs +1 -1
  20. package/dist/constants.js +1 -1
  21. package/dist/eventcatalog.cjs +205 -124
  22. package/dist/eventcatalog.config.d.cts +59 -1
  23. package/dist/eventcatalog.config.d.ts +59 -1
  24. package/dist/eventcatalog.js +30 -11
  25. package/dist/federation/federate.js +2 -2
  26. package/dist/generate.cjs +1 -1
  27. package/dist/generate.js +3 -3
  28. package/dist/runtime-dependencies/@hocuspocus__provider.mjs +3 -0
  29. package/dist/runtime-dependencies/@hocuspocus__server.mjs +3 -0
  30. package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +3 -0
  31. package/dist/runtime-dependencies/@mcp-b__webmcp-polyfill.mjs +3 -0
  32. package/dist/runtime-dependencies/manifest.json +8 -1
  33. package/dist/runtime-dependencies/react-hotkeys-hook.mjs +3 -0
  34. package/dist/runtime-dependencies/y-protocols.mjs +3 -0
  35. package/dist/runtime-dependencies/y-protocols__awareness.mjs +3 -0
  36. package/dist/runtime-dependencies/yjs.mjs +3 -0
  37. package/dist/utils/cli-logger.cjs +1 -1
  38. package/dist/utils/cli-logger.js +2 -2
  39. package/dist/utils/license-status.cjs +71 -45
  40. package/dist/utils/license-status.d.cts +14 -5
  41. package/dist/utils/license-status.d.ts +14 -5
  42. package/dist/utils/license-status.js +1 -1
  43. package/eventcatalog/astro.config.mjs +5 -0
  44. package/eventcatalog/integrations/storage.mjs +20 -0
  45. package/eventcatalog/integrations/studio-server.mjs +32 -0
  46. package/eventcatalog/src/components/Grids/utils.tsx +0 -23
  47. package/eventcatalog/src/components/Header.astro +3 -3
  48. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +6 -1
  49. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +12 -4
  50. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +16 -7
  51. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +10 -0
  52. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +4 -62
  53. package/eventcatalog/src/components/Tables/Discover/columns.tsx +17 -150
  54. package/eventcatalog/src/components/Tables/Resources/ResourcesTable.tsx +425 -0
  55. package/eventcatalog/src/components/Tables/RowActionsMenu.tsx +157 -0
  56. package/eventcatalog/src/components/Tables/TablePagination.tsx +82 -0
  57. package/eventcatalog/src/features/ai/chat-api.ts +3 -3
  58. package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +10 -0
  59. package/eventcatalog/src/features/integrations/eventcatalog-features.ts +34 -1
  60. package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +47 -0
  61. package/eventcatalog/src/features/mcp/apps/canvas/styles.css +22 -0
  62. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +276 -0
  63. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +210 -0
  64. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +75 -74
  65. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +2 -1
  66. package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -3
  67. package/eventcatalog/src/features/mcp/mcp-server.ts +36 -5
  68. package/eventcatalog/src/features/server/start.ts +25 -0
  69. package/eventcatalog/src/features/storage/database.ts +88 -0
  70. package/eventcatalog/src/features/storage/migrations.ts +85 -0
  71. package/eventcatalog/src/features/storage/sql/database.sql +5 -0
  72. package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
  73. package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
  74. package/eventcatalog/src/features/storage/sql.ts +28 -0
  75. package/eventcatalog/src/features/studio/agent-choreography.ts +381 -0
  76. package/eventcatalog/src/features/studio/api/canvases-api.ts +170 -0
  77. package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
  78. package/eventcatalog/src/features/studio/api/client.ts +35 -0
  79. package/eventcatalog/src/features/studio/canvas-actions.ts +414 -0
  80. package/eventcatalog/src/features/studio/canvas-doc.ts +414 -0
  81. package/eventcatalog/src/features/studio/canvas-preview.ts +53 -0
  82. package/eventcatalog/src/features/studio/catalog-resources.ts +155 -0
  83. package/eventcatalog/src/features/studio/catalog.ts +171 -0
  84. package/eventcatalog/src/features/studio/clipboard.ts +97 -0
  85. package/eventcatalog/src/features/studio/components/CanvasControls.tsx +154 -0
  86. package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +370 -0
  87. package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +117 -0
  88. package/eventcatalog/src/features/studio/components/Comments.tsx +770 -0
  89. package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +281 -0
  90. package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +144 -0
  91. package/eventcatalog/src/features/studio/components/ContainerChooser.tsx +145 -0
  92. package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +81 -0
  93. package/eventcatalog/src/features/studio/components/DragGhost.tsx +115 -0
  94. package/eventcatalog/src/features/studio/components/JoinForm.tsx +34 -0
  95. package/eventcatalog/src/features/studio/components/LeftPanel.tsx +314 -0
  96. package/eventcatalog/src/features/studio/components/Picture.tsx +21 -0
  97. package/eventcatalog/src/features/studio/components/Presence.tsx +286 -0
  98. package/eventcatalog/src/features/studio/components/PropertiesPanel.tsx +212 -0
  99. package/eventcatalog/src/features/studio/components/ShareDialog.tsx +260 -0
  100. package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +1216 -0
  101. package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
  102. package/eventcatalog/src/features/studio/components/canvas-nodes.tsx +255 -0
  103. package/eventcatalog/src/features/studio/components/icons.ts +53 -0
  104. package/eventcatalog/src/features/studio/components/status.ts +53 -0
  105. package/eventcatalog/src/features/studio/edges.ts +56 -0
  106. package/eventcatalog/src/features/studio/flow-state.ts +92 -0
  107. package/eventcatalog/src/features/studio/grouping.ts +226 -0
  108. package/eventcatalog/src/features/studio/hooks/presence-sender.ts +46 -0
  109. package/eventcatalog/src/features/studio/hooks/presence-store.ts +119 -0
  110. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +347 -0
  111. package/eventcatalog/src/features/studio/hooks/use-comments.ts +54 -0
  112. package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +104 -0
  113. package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +589 -0
  114. package/eventcatalog/src/features/studio/identity.ts +37 -0
  115. package/eventcatalog/src/features/studio/layout.ts +42 -0
  116. package/eventcatalog/src/features/studio/levels.ts +282 -0
  117. package/eventcatalog/src/features/studio/node-types.ts +205 -0
  118. package/eventcatalog/src/features/studio/pages/[id].astro +68 -0
  119. package/eventcatalog/src/features/studio/pages/index.astro +784 -0
  120. package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +27 -0
  121. package/eventcatalog/src/features/studio/placement.ts +197 -0
  122. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +510 -0
  123. package/eventcatalog/src/features/studio/server/runtime.ts +491 -0
  124. package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
  125. package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
  126. package/eventcatalog/src/features/studio/server/storage.ts +90 -0
  127. package/eventcatalog/src/features/studio/server/tool-icons.ts +30 -0
  128. package/eventcatalog/src/features/studio/studio.css +273 -0
  129. package/eventcatalog/src/features/studio/tool-sync.ts +75 -0
  130. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +10 -5
  131. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +52 -63
  132. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +34 -46
  133. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +11 -29
  134. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_ResourcesPage.astro +79 -0
  135. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +32 -14
  136. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +63 -58
  137. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +2 -62
  138. package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +13 -0
  139. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +24 -42
  140. package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +2 -0
  141. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +2 -0
  142. package/eventcatalog/src/stores/sidebar-store/builders/service.ts +23 -23
  143. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +13 -0
  144. package/eventcatalog/src/stores/sidebar-store/builders/system.ts +18 -27
  145. package/eventcatalog/src/stores/sidebar-store/state.ts +5 -0
  146. package/eventcatalog/src/styles/tailwind.css +2 -2
  147. package/eventcatalog/src/utils/collections/channels.ts +56 -1
  148. package/eventcatalog/src/utils/collections/flows.ts +50 -1
  149. package/eventcatalog/src/utils/collections/message-connections.ts +60 -0
  150. package/eventcatalog/src/utils/collections/message-triggers.ts +2 -1
  151. package/eventcatalog/src/utils/collections/resource-owners.ts +193 -0
  152. package/eventcatalog/src/utils/collections/resources.ts +310 -0
  153. package/eventcatalog/src/utils/collections/util.ts +31 -0
  154. package/eventcatalog/src/utils/feature.ts +8 -0
  155. package/eventcatalog/src/utils/git.ts +11 -5
  156. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
  157. package/eventcatalog/src/utils/trial.ts +1 -1
  158. package/package.json +17 -12
  159. package/dist/runtime-dependencies/shelljs.mjs +0 -3
  160. package/eventcatalog/public/studio-bg.png +0 -0
  161. package/eventcatalog/src/components/Grids/DomainGrid.tsx +0 -838
  162. package/eventcatalog/src/components/Grids/MessageGrid.tsx +0 -344
  163. package/eventcatalog/src/components/Grids/SystemGrid.tsx +0 -185
  164. package/eventcatalog/src/components/Grids/components.tsx +0 -170
  165. package/eventcatalog/src/components/Grids/message-link.ts +0 -38
  166. package/eventcatalog/src/components/Studio/StudioPageModal.tsx +0 -150
  167. package/eventcatalog/src/components/Tables/SystemResources/SystemResourcesTable.tsx +0 -294
  168. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.spec.ts +0 -65
  169. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.ts +0 -46
  170. package/eventcatalog/src/pages/studio.astro +0 -316
  171. package/dist/{chunk-AYNZXUB5.js → chunk-KHACCTW2.js} +3 -3
@@ -181,7 +181,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
181
181
  var import_picocolors = __toESM(require("picocolors"), 1);
182
182
 
183
183
  // package.json
184
- var version = "5.0.0-beta.0";
184
+ var version = "5.0.0-beta.10";
185
185
 
186
186
  // src/constants.ts
187
187
  var VERSION = version;
@@ -191,8 +191,8 @@ var getTimestamp = () => {
191
191
  const now = /* @__PURE__ */ new Date();
192
192
  return now.toLocaleTimeString("en-US", { hour12: false });
193
193
  };
194
- var formatMessage = (tag, message, tagColor) => {
195
- return `${import_picocolors.default.dim(getTimestamp())} ${tagColor(`[${tag}]`)} ${message}`;
194
+ var formatMessage = (tag, message2, tagColor) => {
195
+ return `${import_picocolors.default.dim(getTimestamp())} ${tagColor(`[${tag}]`)} ${message2}`;
196
196
  };
197
197
  var logger = {
198
198
  welcome: () => {
@@ -208,28 +208,28 @@ var logger = {
208
208
  );
209
209
  console.log();
210
210
  },
211
- info: (message, tag = "info") => {
212
- console.log(formatMessage(tag, message, import_picocolors.default.blue));
211
+ info: (message2, tag = "info") => {
212
+ console.log(formatMessage(tag, message2, import_picocolors.default.blue));
213
213
  },
214
- success: (message, tag = "success") => {
215
- console.log(formatMessage(tag, message, import_picocolors.default.green));
214
+ success: (message2, tag = "success") => {
215
+ console.log(formatMessage(tag, message2, import_picocolors.default.green));
216
216
  },
217
- error: (message, tag = "error") => {
218
- console.log(formatMessage(tag, message, import_picocolors.default.red));
217
+ error: (message2, tag = "error") => {
218
+ console.log(formatMessage(tag, message2, import_picocolors.default.red));
219
219
  },
220
- warning: (message, tag = "warn") => {
221
- console.log(formatMessage(tag, message, import_picocolors.default.yellow));
220
+ warning: (message2, tag = "warn") => {
221
+ console.log(formatMessage(tag, message2, import_picocolors.default.yellow));
222
222
  },
223
- line: (message = "") => {
224
- console.log(message);
223
+ line: (message2 = "") => {
224
+ console.log(message2);
225
225
  },
226
- diagnostic: (severity, message, rule, attributes = []) => {
226
+ diagnostic: (severity, message2, rule, attributes = []) => {
227
227
  const isError = severity === "error";
228
228
  const color = isError ? import_picocolors.default.red : import_picocolors.default.yellow;
229
229
  const icon = isError ? "\u2716" : "\u26A0";
230
- const label = `${icon} ${severity.padEnd(7)} ${message}`;
230
+ const label = `${icon} ${severity.padEnd(7)} ${message2}`;
231
231
  const ruleSpacing = " ".repeat(Math.max(2, 72 - label.length));
232
- console.log(` ${color(icon)} ${color(severity.padEnd(7))} ${message}${ruleSpacing}${import_picocolors.default.gray(rule)}`);
232
+ console.log(` ${color(icon)} ${color(severity.padEnd(7))} ${message2}${ruleSpacing}${import_picocolors.default.gray(rule)}`);
233
233
  for (const attribute of attributes) {
234
234
  console.log(` - ${import_picocolors.default.dim(`${attribute.label}:`)} ${attribute.value}`);
235
235
  }
@@ -243,8 +243,8 @@ var logger = {
243
243
  const warningCount = `${warnings} warning${warnings === 1 ? "" : "s"}`;
244
244
  console.log(color(`${icon} ${problems} (${errorCount}, ${warningCount})`));
245
245
  },
246
- dim: (message) => {
247
- console.log(import_picocolors.default.dim(message));
246
+ dim: (message2) => {
247
+ console.log(import_picocolors.default.dim(message2));
248
248
  }
249
249
  };
250
250
 
@@ -304,7 +304,24 @@ var generate = async (PROJECT_DIRECTORY) => {
304
304
 
305
305
  // src/analytics/analytics.js
306
306
  var import_os = __toESM(require("os"), 1);
307
- async function raiseEvent(eventData) {
307
+
308
+ // src/analytics/telemetry.ts
309
+ var TELEMETRY_REQUEST_TIMEOUT_MS = 2e3;
310
+ var isOptOutEnvValue = (value) => {
311
+ if (typeof value !== "string") return false;
312
+ const normalized = value.trim().toLowerCase();
313
+ return normalized === "1" || normalized === "true";
314
+ };
315
+ var isTelemetryDisabled = (config, env) => {
316
+ if (isOptOutEnvValue(env.EVENTCATALOG_TELEMETRY_DISABLED)) return true;
317
+ if (isOptOutEnvValue(env.DO_NOT_TRACK)) return true;
318
+ return config?.telemetry === false;
319
+ };
320
+
321
+ // src/analytics/analytics.js
322
+ async function raiseEvent(eventData, config) {
323
+ if (isTelemetryDisabled(config, process.env)) return;
324
+ if (typeof eventData?.command !== "string" || typeof eventData?.cId !== "string") return;
308
325
  const url = "https://queue.simpleanalyticscdn.com/events";
309
326
  const userAgent = `@eventcatalog/eventcatalog@${VERSION} (${import_os.default.platform()}; ${import_os.default.arch()}; Node/${process.version})`;
310
327
  const headers = {
@@ -325,7 +342,8 @@ async function raiseEvent(eventData) {
325
342
  const response = await fetch(url, {
326
343
  method: "POST",
327
344
  headers,
328
- body: JSON.stringify(payload)
345
+ body: JSON.stringify(payload),
346
+ signal: AbortSignal.timeout(TELEMETRY_REQUEST_TIMEOUT_MS)
329
347
  });
330
348
  if (!response.ok) {
331
349
  throw new Error(`Failed to raise analytics event: ${response.status}`);
@@ -402,10 +420,11 @@ function serializeCounts(counts) {
402
420
 
403
421
  // src/utils/license-status.ts
404
422
  var import_boxen = __toESM(require("boxen"), 1);
423
+ var import_picocolors2 = __toESM(require("picocolors"), 1);
405
424
  var import_license2 = require("@eventcatalog/license");
406
425
 
407
426
  // eventcatalog/src/utils/trial.ts
408
- var TRIAL_LENGTH_DAYS = 90;
427
+ var TRIAL_LENGTH_DAYS = 60;
409
428
  var LICENSE_FAQ_URL = "https://www.eventcatalog.dev/license-faq";
410
429
  var LICENSE_RENEW_URL = "https://eventcatalog.cloud";
411
430
  var DAY_IN_MS = 24 * 60 * 60 * 1e3;
@@ -524,75 +543,100 @@ var getLicenseAnalytics = (license = { state: "none" }, tsd) => ({
524
543
  });
525
544
  var formatDate = (date) => date.toLocaleDateString(void 0, { dateStyle: "medium" });
526
545
  var formatDays = (days) => `${days} ${days === 1 ? "day" : "days"}`;
527
- var getTrialLine = (tsd, now) => {
546
+ var PLAIN = { bold: (text) => text, dim: (text) => text, link: (text) => text };
547
+ var COLOURS = { bold: import_picocolors2.default.bold, dim: import_picocolors2.default.dim, link: (text) => import_picocolors2.default.cyan(text) };
548
+ var render = ({ headline, rows, links }, colours) => {
549
+ const width = Math.max(0, ...[...rows, ...links].map(([label]) => label.length));
550
+ const row = ([label, value]) => `${colours.dim(label.padEnd(width))} ${value}`;
551
+ return [
552
+ colours.bold(headline),
553
+ ...rows.length ? ["", ...rows.map(row)] : [],
554
+ ...links.length ? ["", ...links.map(([label, url]) => row([label, colours.link(url)]))] : []
555
+ ].join("\n");
556
+ };
557
+ var message = (content) => ({
558
+ ...content,
559
+ text: render(content, PLAIN)
560
+ });
561
+ var getTrialRow = (tsd, now) => {
528
562
  const trial = getTrialStatus(tsd, now);
529
563
  if (!trial) return;
530
- return trial.ended ? `Your ${TRIAL_LENGTH_DAYS}-day EventCatalog trial ended on ${formatDate(trial.endsAt)}.` : `${formatDays(trial.daysLeft)} left of your ${TRIAL_LENGTH_DAYS}-day EventCatalog trial (ends ${formatDate(trial.endsAt)}).`;
564
+ return [
565
+ "Trial",
566
+ trial.ended ? `Ended ${formatDate(trial.endsAt)}` : `${formatDays(trial.daysLeft)} left of ${TRIAL_LENGTH_DAYS} (ends ${formatDate(trial.endsAt)})`
567
+ ];
531
568
  };
532
569
  var getLicenseStatusMessage = (license, tsd, now = Date.now()) => {
570
+ const licensing = ["Licensing", LICENSE_FAQ_URL];
571
+ const renew = ["Renew", LICENSE_RENEW_URL];
572
+ const licensedTo = "org" in license && license.org ? [["Licensed to", license.org]] : [];
533
573
  if (license.state === "licensed") {
534
574
  const daysLeft = Math.max(0, Math.ceil((license.expiresAt.getTime() - now) / DAY_IN_MS2));
535
- const licensedTo = license.org ? `Licensed to ${license.org}` : "Licensed";
536
575
  if (daysLeft < LICENSE_REMINDER_DAYS) {
537
- return {
538
- title: "EventCatalog Commercial License",
576
+ return message({
577
+ title: "EventCatalog license",
539
578
  color: "yellow",
540
- text: [
541
- licensedTo,
542
- `Your license expires in ${formatDays(daysLeft)}, on ${formatDate(license.expiresAt)}.`,
543
- `Renew it to keep using EventCatalog commercially: ${LICENSE_RENEW_URL}`
544
- ].join("\n")
545
- };
579
+ headline: `Your commercial license expires in ${formatDays(daysLeft)}`,
580
+ rows: [...licensedTo, ["Expires", formatDate(license.expiresAt)]],
581
+ links: [renew]
582
+ });
546
583
  }
547
- return {
548
- title: "EventCatalog Commercial License",
584
+ return message({
585
+ title: "EventCatalog license",
549
586
  color: "green",
550
- text: `${licensedTo}
551
- Valid until ${formatDate(license.expiresAt)} (${formatDays(daysLeft)} left)`
552
- };
587
+ headline: "Commercial license active",
588
+ rows: [...licensedTo, ["Valid until", `${formatDate(license.expiresAt)} (${formatDays(daysLeft)} left)`]],
589
+ links: []
590
+ });
553
591
  }
554
592
  if (license.state === "plan") return;
555
- const trialLine = getTrialLine(tsd, now);
593
+ const trialRow = getTrialRow(tsd, now);
594
+ const trialRows = trialRow ? [trialRow] : [];
556
595
  if (license.state === "none") {
557
- if (!trialLine) return;
558
- const ended = getTrialStatus(tsd, now)?.ended;
559
- return {
596
+ const trial = getTrialStatus(tsd, now);
597
+ if (!trial || !trialRow) return;
598
+ return message({
560
599
  title: "EventCatalog trial",
561
- color: ended ? "yellow" : "green",
562
- text: `${trialLine}
563
- Learn about licensing: ${LICENSE_FAQ_URL}`
564
- };
600
+ color: trial.ended ? "yellow" : "green",
601
+ headline: trial.ended ? "Your EventCatalog trial has ended" : `You're on the ${TRIAL_LENGTH_DAYS}-day EventCatalog trial`,
602
+ rows: trialRows,
603
+ links: trial.ended ? [["Get a license", LICENSE_RENEW_URL], licensing] : [licensing]
604
+ });
565
605
  }
566
606
  if (license.state === "expired") {
567
- const expiredOn = license.expiresAt ? `expired on ${formatDate(license.expiresAt)} (${formatDays(Math.floor((now - license.expiresAt.getTime()) / DAY_IN_MS2))} ago)` : "has expired";
568
- return {
569
- title: "EventCatalog License Expired",
607
+ const expiredOn = license.expiresAt ? [
608
+ [
609
+ "Expired",
610
+ `${formatDate(license.expiresAt)} (${formatDays(Math.floor((now - license.expiresAt.getTime()) / DAY_IN_MS2))} ago)`
611
+ ]
612
+ ] : [];
613
+ return message({
614
+ title: "EventCatalog license",
570
615
  color: "yellow",
571
- text: [
572
- `Your EventCatalog commercial license${license.org ? ` for ${license.org}` : ""} ${expiredOn}.`,
573
- `Renew it to keep using EventCatalog commercially: ${LICENSE_RENEW_URL}`,
574
- trialLine,
575
- `Learn about licensing: ${LICENSE_FAQ_URL}`
576
- ].filter(Boolean).join("\n")
577
- };
616
+ headline: "Your commercial license has expired",
617
+ rows: [...licensedTo, ...expiredOn, ...trialRows],
618
+ links: [renew, licensing]
619
+ });
578
620
  }
579
- return {
621
+ return message({
580
622
  title: "EventCatalog license",
581
623
  color: "yellow",
582
- text: [`Your license.jwt could not be verified: ${license.reason}.`, trialLine, `Learn about licensing: ${LICENSE_FAQ_URL}`].filter(Boolean).join("\n")
583
- };
624
+ headline: "Your license.jwt couldn't be verified",
625
+ rows: [["Reason", license.reason.charAt(0).toUpperCase() + license.reason.slice(1)], ...trialRows],
626
+ links: [licensing]
627
+ });
584
628
  };
585
629
  var printLicenseStatus = (license, tsd) => {
586
- const message = getLicenseStatusMessage(license, tsd);
587
- if (!message) return;
630
+ const status = getLicenseStatusMessage(license, tsd);
631
+ if (!status) return;
588
632
  console.log(
589
- (0, import_boxen.default)(message.text, {
590
- padding: 1,
633
+ (0, import_boxen.default)(render(status, COLOURS), {
634
+ padding: { top: 1, bottom: 1, left: 2, right: 2 },
591
635
  margin: 1,
592
636
  borderStyle: "round",
593
- borderColor: message.color,
594
- title: message.title,
595
- titleAlignment: "center"
637
+ borderColor: status.color,
638
+ title: status.title,
639
+ titleAlignment: "left"
596
640
  })
597
641
  );
598
642
  };
@@ -652,50 +696,69 @@ var toCloudResourceCounts = (counts) => ({
652
696
  customPages: counts.customPages || 0,
653
697
  customApis: counts.customApis || 0
654
698
  });
655
- var reportCloudResourceInventory = async (configFile, resourceCounts) => {
699
+ var getEnabledCloudAnalytics = (configFile) => {
656
700
  const analytics = configFile.cloud?.analytics;
657
- if (!analytics?.enabled || !analytics.trackingId || !analytics.writeKey) return;
701
+ if (!analytics?.enabled || !analytics.trackingId || !analytics.writeKey) return null;
702
+ return analytics;
703
+ };
704
+ var reportCloudResourceInventory = async (configFile, resourceCounts) => {
705
+ const analytics = getEnabledCloudAnalytics(configFile);
706
+ if (!analytics) return;
658
707
  const endpoint = analytics.endpoint || CLOUD_ANALYTICS_ENDPOINT;
659
- await fetch(endpoint, {
660
- method: "POST",
661
- headers: {
662
- "Content-Type": "application/json",
663
- "X-EventCatalog-Analytics-Key": analytics.writeKey
664
- },
665
- body: JSON.stringify({
666
- trackingId: analytics.trackingId,
667
- event: "catalog.resource_inventory_reported",
668
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
669
- counts: toCloudResourceCounts(resourceCounts)
670
- })
671
- });
708
+ try {
709
+ await fetch(endpoint, {
710
+ method: "POST",
711
+ headers: {
712
+ "Content-Type": "application/json",
713
+ "X-EventCatalog-Analytics-Key": analytics.writeKey
714
+ },
715
+ body: JSON.stringify({
716
+ trackingId: analytics.trackingId,
717
+ event: "catalog.resource_inventory_reported",
718
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
719
+ counts: toCloudResourceCounts(resourceCounts)
720
+ }),
721
+ signal: AbortSignal.timeout(TELEMETRY_REQUEST_TIMEOUT_MS)
722
+ });
723
+ } catch {
724
+ }
672
725
  };
673
726
  var main = async (projectDir, { command = "build", license } = {}) => {
674
727
  try {
675
728
  await verifyRequiredFieldsAreInCatalogConfigFile(projectDir);
676
729
  const configFile = await getEventCatalogConfigFile(projectDir);
730
+ if (isTelemetryDisabled(configFile, process.env)) {
731
+ if (getEnabledCloudAnalytics(configFile)) {
732
+ const resourceCounts2 = await countResources(projectDir);
733
+ await reportCloudResourceInventory(configFile, resourceCounts2);
734
+ }
735
+ return;
736
+ }
677
737
  const { cId, tsd, organizationName, generators = [] } = configFile;
678
738
  const generatorNames = generators.length > 0 ? generators.map((generator) => generator[0]) : ["none"];
679
739
  const features = await getFeatures(configFile);
680
740
  const resourceCounts = await countResources(projectDir);
681
741
  const contentHash = await hashCatalogContent(projectDir);
682
742
  await reportCloudResourceInventory(configFile, resourceCounts);
683
- await raiseEvent({
684
- command,
685
- org: organizationName,
686
- cId,
687
- // Trial start date (unix ms) from eventcatalog.config.js
688
- tsd,
689
- // Commercial license or trial, and when each expires (unix ms)
690
- ...getLicenseAnalytics(license, tsd),
691
- // CI redeploys and human-run builds are different signals; tag rather than suppress
692
- ci: process.env.CI ? "true" : "false",
693
- contentHash,
694
- generators: generatorNames.toString(),
695
- features: Object.keys(features).map((feature) => `${feature}:${features[feature]}`).join(","),
696
- directorySources: serializeDirectorySources(configFile),
697
- resources: serializeCounts(resourceCounts)
698
- });
743
+ await raiseEvent(
744
+ {
745
+ command,
746
+ org: organizationName,
747
+ cId,
748
+ // Trial start date (unix ms) from eventcatalog.config.js
749
+ tsd,
750
+ // Commercial license or trial, and when each expires (unix ms)
751
+ ...getLicenseAnalytics(license, tsd),
752
+ // CI redeploys and human-run builds are different signals; tag rather than suppress
753
+ ci: process.env.CI ? "true" : "false",
754
+ contentHash,
755
+ generators: generatorNames.toString(),
756
+ features: Object.keys(features).map((feature) => `${feature}:${features[feature]}`).join(","),
757
+ directorySources: serializeDirectorySources(configFile),
758
+ resources: serializeCounts(resourceCounts)
759
+ },
760
+ configFile
761
+ );
699
762
  } catch (error) {
700
763
  }
701
764
  };
@@ -835,13 +898,13 @@ var message_channels_to_service_channels_default = async (dir2) => {
835
898
  absolute: true
836
899
  }
837
900
  );
838
- const messagesWithChannels = messages.reduce((acc, message) => {
839
- const file = import_node_fs5.default.readFileSync(message, "utf8");
901
+ const messagesWithChannels = messages.reduce((acc, message2) => {
902
+ const file = import_node_fs5.default.readFileSync(message2, "utf8");
840
903
  const { data } = (0, import_gray_matter3.default)(file);
841
904
  if (data.channels?.length > 0) {
842
905
  acc.push({
843
906
  ...data,
844
- path: message
907
+ path: message2
845
908
  });
846
909
  }
847
910
  return acc;
@@ -852,9 +915,9 @@ var message_channels_to_service_channels_default = async (dir2) => {
852
915
  const servicesWithChannels = services.reduce((acc, service) => {
853
916
  const file = import_node_fs5.default.readFileSync(service, "utf8");
854
917
  const { data } = (0, import_gray_matter3.default)(file);
855
- const isSending = data.sends?.some((send) => messagesWithChannels.some((message) => message.id === send.id));
918
+ const isSending = data.sends?.some((send) => messagesWithChannels.some((message2) => message2.id === send.id));
856
919
  const isReceiving = data.receives?.some(
857
- (receive) => messagesWithChannels.some((message) => message.id === receive.id)
920
+ (receive) => messagesWithChannels.some((message2) => message2.id === receive.id)
858
921
  );
859
922
  if (isSending || isReceiving) {
860
923
  acc.push({
@@ -867,16 +930,16 @@ var message_channels_to_service_channels_default = async (dir2) => {
867
930
  for (const service of servicesWithChannels) {
868
931
  const file = import_node_fs5.default.readFileSync(service.path, "utf8");
869
932
  const { data } = (0, import_gray_matter3.default)(file);
870
- const messagesTheServiceSendsThatNeedUpdating = data.sends?.filter((send) => messagesWithChannels.some((message) => message.id === send.id)) ?? [];
871
- const messagesTheServiceReceivesThatNeedUpdating = data.receives?.filter((receive) => messagesWithChannels.some((message) => message.id === receive.id)) ?? [];
933
+ const messagesTheServiceSendsThatNeedUpdating = data.sends?.filter((send) => messagesWithChannels.some((message2) => message2.id === send.id)) ?? [];
934
+ const messagesTheServiceReceivesThatNeedUpdating = data.receives?.filter((receive) => messagesWithChannels.some((message2) => message2.id === receive.id)) ?? [];
872
935
  if (messagesTheServiceSendsThatNeedUpdating.length > 0 || messagesTheServiceReceivesThatNeedUpdating.length > 0) {
873
936
  const newSends = (data.sends ?? []).map((send) => {
874
- const match = messagesWithChannels.find((message) => message.id === send.id);
937
+ const match = messagesWithChannels.find((message2) => message2.id === send.id);
875
938
  if (!match) return send;
876
939
  return { ...send, to: (match.channels ?? []).filter((channel) => channel !== null) };
877
940
  });
878
941
  const newReceives = (data.receives ?? []).map((receive) => {
879
- const match = messagesWithChannels.find((message) => message.id === receive.id);
942
+ const match = messagesWithChannels.find((message2) => message2.id === receive.id);
880
943
  if (!match) return receive;
881
944
  return { ...receive, from: (match.channels ?? []).filter((channel) => channel !== null) };
882
945
  });
@@ -888,11 +951,11 @@ var message_channels_to_service_channels_default = async (dir2) => {
888
951
  import_node_fs5.default.writeFileSync(service.path, import_gray_matter3.default.stringify(file, newData));
889
952
  }
890
953
  }
891
- for (const message of messagesWithChannels) {
892
- const file = import_node_fs5.default.readFileSync(message.path, "utf8");
954
+ for (const message2 of messagesWithChannels) {
955
+ const file = import_node_fs5.default.readFileSync(message2.path, "utf8");
893
956
  const parsed = (0, import_gray_matter3.default)(file);
894
957
  const { channels, ...newData } = parsed.data;
895
- import_node_fs5.default.writeFileSync(message.path, import_gray_matter3.default.stringify(parsed.content, newData));
958
+ import_node_fs5.default.writeFileSync(message2.path, import_gray_matter3.default.stringify(parsed.content, newData));
896
959
  }
897
960
  console.log("\u2714 Channels migrated to new services API");
898
961
  };
@@ -1054,7 +1117,7 @@ var FieldsDatabase = class _FieldsDatabase {
1054
1117
  format,
1055
1118
  type,
1056
1119
  messageType,
1057
- message,
1120
+ message: message2,
1058
1121
  producer,
1059
1122
  consumer,
1060
1123
  required,
@@ -1088,9 +1151,9 @@ var FieldsDatabase = class _FieldsDatabase {
1088
1151
  conditions.push(`f.message_type IN (${types2.map(() => "?").join(", ")})`);
1089
1152
  bindings.push(...types2);
1090
1153
  }
1091
- if (message) {
1154
+ if (message2) {
1092
1155
  conditions.push(`f.message_id = ?`);
1093
- bindings.push(message);
1156
+ bindings.push(message2);
1094
1157
  }
1095
1158
  if (required) {
1096
1159
  conditions.push(`f.required = 1`);
@@ -1903,8 +1966,8 @@ var KNOWN_EVENTCATALOG_COLLECTIONS = /* @__PURE__ */ new Set([
1903
1966
  var EMPTY_COLLECTION_MESSAGE = /^The collection ("(?:\\.|[^"\\])*") does not exist or is empty\. Please check your content config file for errors\.$/;
1904
1967
  var ANSI_PATTERN = /\u001B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
1905
1968
  var ASTRO_CONTENT_WARN_PREFIX = /^[^[\]]*\[WARN\]\s+\[content\](?:\s+\[[^\]]+\])*\s*$/;
1906
- function collectionNameFromEmptyWarning(message) {
1907
- const match = message.match(EMPTY_COLLECTION_MESSAGE);
1969
+ function collectionNameFromEmptyWarning(message2) {
1970
+ const match = message2.match(EMPTY_COLLECTION_MESSAGE);
1908
1971
  if (!match) return null;
1909
1972
  try {
1910
1973
  const name = JSON.parse(match[1]);
@@ -1913,8 +1976,8 @@ function collectionNameFromEmptyWarning(message) {
1913
1976
  return null;
1914
1977
  }
1915
1978
  }
1916
- function isIntentionalEmptyCollectionMessage(message) {
1917
- const name = collectionNameFromEmptyWarning(message);
1979
+ function isIntentionalEmptyCollectionMessage(message2) {
1980
+ const name = collectionNameFromEmptyWarning(message2);
1918
1981
  return name !== null && KNOWN_EVENTCATALOG_COLLECTIONS.has(name);
1919
1982
  }
1920
1983
  function isIntentionalEmptyCollectionLine(line) {
@@ -1922,8 +1985,8 @@ function isIntentionalEmptyCollectionLine(line) {
1922
1985
  const marker = 'The collection "';
1923
1986
  const start = plain.indexOf(marker);
1924
1987
  if (start === -1) return false;
1925
- const message = plain.slice(start).trim();
1926
- if (!isIntentionalEmptyCollectionMessage(message)) return false;
1988
+ const message2 = plain.slice(start).trim();
1989
+ if (!isIntentionalEmptyCollectionMessage(message2)) return false;
1927
1990
  const before = plain.slice(0, start).trim();
1928
1991
  return before.length === 0 || ASTRO_CONTENT_WARN_PREFIX.test(before);
1929
1992
  }
@@ -2767,8 +2830,8 @@ var federateCatalog = async (projectDirectory, options = {}) => {
2767
2830
  generated: resolved.generated
2768
2831
  });
2769
2832
  } catch (error) {
2770
- const message = error instanceof Error ? error.message : String(error);
2771
- throw new Error(`Failed to federate source "${source.id}": ${message}`, { cause: error });
2833
+ const message2 = error instanceof Error ? error.message : String(error);
2834
+ throw new Error(`Failed to federate source "${source.id}": ${message2}`, { cause: error });
2772
2835
  }
2773
2836
  }
2774
2837
  const outDir = import_node_path19.default.join(projectDirectory, "federated");
@@ -3113,10 +3176,10 @@ Upgrade now: npm i @eventcatalog/core@beta`;
3113
3176
  if (!installedVersion) return;
3114
3177
  const pkg = { name: "@eventcatalog/core", version: installedVersion };
3115
3178
  const notifier = (0, import_update_notifier.default)({ pkg, updateCheckInterval: 0 });
3116
- const message = getEventCatalogUpdateMessage(installedVersion, notifier.update?.latest);
3117
- if (message) {
3179
+ const message2 = getEventCatalogUpdateMessage(installedVersion, notifier.update?.latest);
3180
+ if (message2) {
3118
3181
  console.log(
3119
- (0, import_boxen2.default)(message, {
3182
+ (0, import_boxen2.default)(message2, {
3120
3183
  padding: 1,
3121
3184
  margin: 1,
3122
3185
  align: "center",
@@ -3277,15 +3340,33 @@ var previewCatalog = async ({ command }) => {
3277
3340
  shouldFilterLine: createAstroLineFilter()
3278
3341
  });
3279
3342
  };
3343
+ var SERVER_BOOTSTRAP = `
3344
+ const entry = await import(process.argv[1]);
3345
+ const { server } = entry.startServer();
3346
+ globalThis[Symbol.for('eventcatalog.http-server')] = server.server;
3347
+ const start = process.env.EVENTCATALOG_START_PATH;
3348
+ if (start) {
3349
+ server.server.once('listening', () => {
3350
+ // Where the server listens (HOST can be one address only); loopback when it listens on every address
3351
+ const { address, port } = server.server.address();
3352
+ const host = ['0.0.0.0', '::'].includes(address) ? '127.0.0.1' : address.includes(':') ? '[' + address + ']' : address;
3353
+ fetch('http://' + host + ':' + port + start).catch((error) => console.error('[eventcatalog] Could not start the server:', error.message));
3354
+ });
3355
+ }
3356
+ `;
3280
3357
  var startServerCatalog = async () => {
3281
3358
  const serverEntryPath = import_node_path21.default.join(dir, "dist", "server", "entry.mjs");
3359
+ const config = await getEventCatalogConfigFile(dir);
3360
+ const base = (config?.base || "/").replace(/\/$/, "");
3282
3361
  await runCommandWithFilteredOutput({
3283
3362
  command: process.execPath,
3284
- args: [serverEntryPath],
3363
+ args: ["--input-type=module", "-e", SERVER_BOOTSTRAP, (0, import_node_url2.pathToFileURL)(serverEntryPath).href],
3285
3364
  cwd: dir,
3286
3365
  env: {
3287
3366
  PROJECT_DIR: dir,
3288
- CATALOG_DIR: core
3367
+ CATALOG_DIR: core,
3368
+ ASTRO_NODE_AUTOSTART: "disabled",
3369
+ EVENTCATALOG_START_PATH: `${base}/_eventcatalog/start`
3289
3370
  },
3290
3371
  shouldFilterLine: createAstroLineFilter()
3291
3372
  });
@@ -197,6 +197,38 @@ type McpConfig = {
197
197
  enabled?: boolean;
198
198
  auth?: McpAuthConfig;
199
199
  };
200
+ /**
201
+ * The state EventCatalog keeps for features that need it (today, Studio's canvases). Features always work on a copy
202
+ * held in memory by the server, so storage doesn't slow them down: it's read when the server starts and written a
203
+ * moment after something changes (and as the server stops).
204
+ */
205
+ type StorageConfig =
206
+ /** Kept only while the server runs: lost when it restarts or redeploys (the default) */
207
+ {
208
+ type: 'memory';
209
+ }
210
+ /**
211
+ * One SQLite database file for the whole catalog, brought up to date (migrated) as `eventcatalog dev` and
212
+ * `eventcatalog start` start. Uses Node.js's built-in SQLite, so needs Node.js 22.13 or later.
213
+ */
214
+ | {
215
+ type: 'sqlite';
216
+ /**
217
+ * The database file, relative to the catalog. In a container, put it on a volume (e.g. `/data/eventcatalog.db`)
218
+ * so it survives redeploys.
219
+ * @default '.eventcatalog/eventcatalog.db'
220
+ */
221
+ path?: string;
222
+ };
223
+ type StudioConfig = {
224
+ /**
225
+ * Turns Studio on in production (`output: 'server'`, served with `eventcatalog start`). It's on in the dev server
226
+ * unless this is `false`, which turns it off everywhere. Experimental. With authentication on, only signed-in people
227
+ * can open canvases, and they're shown by the name and picture they signed in with.
228
+ * @default false
229
+ */
230
+ enabled?: boolean;
231
+ };
200
232
  type GA4Config = {
201
233
  measurementId: string;
202
234
  };
@@ -257,6 +289,17 @@ interface Config {
257
289
  };
258
290
  security?: {
259
291
  checkOrigin?: boolean;
292
+ /**
293
+ * Hosts to trust in a proxy's X-Forwarded-Host and X-Forwarded-Proto headers (server output), e.g. when deployed
294
+ * behind Fly.io, Railway or a load balancer that serves HTTPS. Without it, form posts (like starting a canvas in
295
+ * Studio) fail the origin check. Passed to Astro's `security.allowedDomains`.
296
+ * @example [{ hostname: 'catalog.example.com', protocol: 'https' }]
297
+ */
298
+ allowedDomains?: {
299
+ hostname?: string;
300
+ protocol?: string;
301
+ port?: string;
302
+ }[];
260
303
  };
261
304
  /**
262
305
  * Theme for the catalog UI.
@@ -270,6 +313,13 @@ interface Config {
270
313
  theme?: CatalogTheme;
271
314
  auth?: AuthConfig;
272
315
  mcp?: McpConfig;
316
+ /** EventCatalog Studio: collaborative canvases (experimental) */
317
+ studio?: StudioConfig;
318
+ /**
319
+ * Where EventCatalog keeps state between restarts (e.g. Studio's canvases), for every feature that has some.
320
+ * @default { type: 'memory' }
321
+ */
322
+ storage?: StorageConfig;
273
323
  rss?: {
274
324
  enabled?: boolean;
275
325
  limit?: number;
@@ -410,6 +460,14 @@ interface Config {
410
460
  queries?: {
411
461
  tableConfiguration?: TableConfiguration;
412
462
  };
463
+ /**
464
+ * Anonymous telemetry for `eventcatalog dev` and `eventcatalog build`.
465
+ * On by default. Set to false to opt out.
466
+ * `EVENTCATALOG_TELEMETRY_DISABLED` or `DO_NOT_TRACK` set to `1` or `true` also opts out.
467
+ * `cloud.analytics` stays its own opt-in for EventCatalog Cloud.
468
+ * @default true
469
+ */
470
+ telemetry?: boolean;
413
471
  cloud?: EventCatalogCloudConfig;
414
472
  integrations?: IntegrationsConfig;
415
473
  scalarConfiguration?: ScalarConfiguration;
@@ -418,4 +476,4 @@ interface Config {
418
476
  federation?: FederationConfig;
419
477
  }
420
478
 
421
- export type { Config, FederationRuleId, FederationRuleLevel, FederationRulesConfig, FederationSourceConfig };
479
+ export type { Config, FederationRuleId, FederationRuleLevel, FederationRulesConfig, FederationSourceConfig, StorageConfig };