@eventcatalog/core 3.25.6 → 3.26.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.
Files changed (35) 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-2ILJMBQM.js → chunk-7CRFNX47.js} +1 -1
  6. package/dist/{chunk-53HXLUNO.js → chunk-ASC3AR2X.js} +1 -1
  7. package/dist/{chunk-ZEOK723Y.js → chunk-FQNBDDUF.js} +1 -1
  8. package/dist/{chunk-P23BMUBV.js → chunk-GCNIIIFG.js} +1 -1
  9. package/dist/{chunk-R7P4GTFQ.js → chunk-XUN32ZVJ.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +562 -19
  13. package/dist/eventcatalog.js +572 -27
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/astro.config.mjs +2 -1
  19. package/eventcatalog/integrations/eventcatalog-features.ts +13 -0
  20. package/eventcatalog/public/icons/graphql.svg +3 -1
  21. package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +225 -0
  22. package/eventcatalog/src/components/FieldsExplorer/FieldNodeGraph.tsx +521 -0
  23. package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +501 -0
  24. package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +236 -0
  25. package/eventcatalog/src/enterprise/fields/field-extractor.test.ts +241 -0
  26. package/eventcatalog/src/enterprise/fields/field-extractor.ts +183 -0
  27. package/eventcatalog/src/enterprise/fields/field-indexer.ts +131 -0
  28. package/eventcatalog/src/enterprise/fields/fields-db.test.ts +186 -0
  29. package/eventcatalog/src/enterprise/fields/fields-db.ts +453 -0
  30. package/eventcatalog/src/enterprise/fields/pages/api/fields.ts +43 -0
  31. package/eventcatalog/src/enterprise/fields/pages/fields.astro +19 -0
  32. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +23 -3
  33. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +14 -16
  34. package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +192 -0
  35. package/package.json +4 -2
@@ -6,8 +6,8 @@ import {
6
6
  } from "./chunk-PLNJC7NZ.js";
7
7
  import {
8
8
  log_build_default
9
- } from "./chunk-ZEOK723Y.js";
10
- import "./chunk-2ILJMBQM.js";
9
+ } from "./chunk-FQNBDDUF.js";
10
+ import "./chunk-7CRFNX47.js";
11
11
  import "./chunk-4UVFXLPI.js";
12
12
  import {
13
13
  runMigrations
@@ -22,13 +22,13 @@ import {
22
22
  } from "./chunk-3KXCGYET.js";
23
23
  import {
24
24
  generate
25
- } from "./chunk-53HXLUNO.js";
25
+ } from "./chunk-ASC3AR2X.js";
26
26
  import {
27
27
  logger
28
- } from "./chunk-R7P4GTFQ.js";
28
+ } from "./chunk-XUN32ZVJ.js";
29
29
  import {
30
30
  VERSION
31
- } from "./chunk-P23BMUBV.js";
31
+ } from "./chunk-GCNIIIFG.js";
32
32
  import {
33
33
  getEventCatalogConfigFile,
34
34
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -39,21 +39,540 @@ import { Command } from "commander";
39
39
  import { execSync, spawn } from "child_process";
40
40
  import { join } from "path";
41
41
  import http from "http";
42
- import fs from "fs";
43
- import path from "path";
42
+ import fs3 from "fs";
43
+ import path2 from "path";
44
44
  import { fileURLToPath } from "url";
45
45
  import boxen from "boxen";
46
46
  import updateNotifier from "update-notifier";
47
47
  import dotenv from "dotenv";
48
+
49
+ // eventcatalog/src/enterprise/fields/field-indexer.ts
50
+ import path from "path";
51
+ import fs2 from "fs";
52
+
53
+ // eventcatalog/src/enterprise/fields/fields-db.ts
54
+ import Database from "better-sqlite3";
55
+ import fs from "fs";
56
+ var SCHEMA_SQL = `
57
+ CREATE TABLE IF NOT EXISTS fields (
58
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
59
+ path TEXT NOT NULL,
60
+ type TEXT NOT NULL,
61
+ description TEXT NOT NULL DEFAULT '',
62
+ required INTEGER NOT NULL DEFAULT 0,
63
+ schema_format TEXT NOT NULL,
64
+ message_id TEXT NOT NULL,
65
+ message_version TEXT NOT NULL,
66
+ message_type TEXT NOT NULL,
67
+ message_name TEXT NOT NULL DEFAULT '',
68
+ message_summary TEXT NOT NULL DEFAULT '',
69
+ message_owners TEXT NOT NULL DEFAULT '[]'
70
+ );
71
+
72
+ CREATE TABLE IF NOT EXISTS message_producers (
73
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
74
+ message_id TEXT NOT NULL,
75
+ message_version TEXT NOT NULL,
76
+ service_id TEXT NOT NULL,
77
+ service_version TEXT NOT NULL,
78
+ service_name TEXT NOT NULL DEFAULT '',
79
+ service_summary TEXT NOT NULL DEFAULT '',
80
+ service_owners TEXT NOT NULL DEFAULT '[]'
81
+ );
82
+
83
+ CREATE TABLE IF NOT EXISTS message_consumers (
84
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
85
+ message_id TEXT NOT NULL,
86
+ message_version TEXT NOT NULL,
87
+ service_id TEXT NOT NULL,
88
+ service_version TEXT NOT NULL,
89
+ service_name TEXT NOT NULL DEFAULT '',
90
+ service_summary TEXT NOT NULL DEFAULT '',
91
+ service_owners TEXT NOT NULL DEFAULT '[]'
92
+ );
93
+
94
+ CREATE INDEX IF NOT EXISTS idx_fields_path ON fields(path);
95
+ CREATE INDEX IF NOT EXISTS idx_fields_message ON fields(message_id, message_version);
96
+ CREATE INDEX IF NOT EXISTS idx_producers_message ON message_producers(message_id, message_version);
97
+ CREATE INDEX IF NOT EXISTS idx_consumers_message ON message_consumers(message_id, message_version);
98
+ `;
99
+ var FTS_SQL = `
100
+ DROP TABLE IF EXISTS fields_fts;
101
+ CREATE VIRTUAL TABLE fields_fts USING fts5(
102
+ path,
103
+ description,
104
+ type,
105
+ content=fields,
106
+ content_rowid=id
107
+ );
108
+ INSERT INTO fields_fts(rowid, path, description, type) SELECT id, path, description, type FROM fields;
109
+ `;
110
+ var FieldsDatabase = class {
111
+ db;
112
+ constructor(dbPath, options) {
113
+ if (options?.recreate && fs.existsSync(dbPath)) {
114
+ fs.unlinkSync(dbPath);
115
+ }
116
+ this.db = new Database(dbPath);
117
+ this.db.pragma("journal_mode = WAL");
118
+ this.db.exec(SCHEMA_SQL);
119
+ }
120
+ insertField(field) {
121
+ this.db.prepare(
122
+ `INSERT INTO fields (path, type, description, required, schema_format, message_id, message_version, message_type, message_name, message_summary, message_owners)
123
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
124
+ ).run(
125
+ field.path,
126
+ field.type,
127
+ field.description,
128
+ field.required ? 1 : 0,
129
+ field.schemaFormat,
130
+ field.messageId,
131
+ field.messageVersion,
132
+ field.messageType,
133
+ field.messageName || "",
134
+ field.messageSummary || "",
135
+ JSON.stringify(field.messageOwners || [])
136
+ );
137
+ }
138
+ insertProducer(messageId, messageVersion, serviceId, serviceVersion, serviceName, serviceSummary, serviceOwners) {
139
+ this.db.prepare(
140
+ `INSERT INTO message_producers (message_id, message_version, service_id, service_version, service_name, service_summary, service_owners)
141
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
142
+ ).run(
143
+ messageId,
144
+ messageVersion,
145
+ serviceId,
146
+ serviceVersion,
147
+ serviceName || "",
148
+ serviceSummary || "",
149
+ JSON.stringify(serviceOwners || [])
150
+ );
151
+ }
152
+ insertConsumer(messageId, messageVersion, serviceId, serviceVersion, serviceName, serviceSummary, serviceOwners) {
153
+ this.db.prepare(
154
+ `INSERT INTO message_consumers (message_id, message_version, service_id, service_version, service_name, service_summary, service_owners)
155
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
156
+ ).run(
157
+ messageId,
158
+ messageVersion,
159
+ serviceId,
160
+ serviceVersion,
161
+ serviceName || "",
162
+ serviceSummary || "",
163
+ JSON.stringify(serviceOwners || [])
164
+ );
165
+ }
166
+ rebuildFts() {
167
+ this.db.exec(FTS_SQL);
168
+ }
169
+ queryFields(params) {
170
+ const {
171
+ q,
172
+ shared,
173
+ conflicting,
174
+ format,
175
+ type,
176
+ messageType,
177
+ message,
178
+ producer,
179
+ consumer,
180
+ required,
181
+ path: fieldPath,
182
+ pageSize = 50,
183
+ cursor
184
+ } = params;
185
+ const conditions = [];
186
+ const bindings = [];
187
+ if (fieldPath) {
188
+ conditions.push(`f.path = ?`);
189
+ bindings.push(fieldPath);
190
+ }
191
+ if (q) {
192
+ conditions.push(`f.id IN (SELECT rowid FROM fields_fts WHERE fields_fts MATCH ?)`);
193
+ const escaped = q.replace(/"/g, '""');
194
+ bindings.push(`"${escaped}" *`);
195
+ }
196
+ if (format) {
197
+ const formats2 = format.split(",").map((f) => f.trim()).filter(Boolean);
198
+ conditions.push(`f.schema_format IN (${formats2.map(() => "?").join(", ")})`);
199
+ bindings.push(...formats2);
200
+ }
201
+ if (type) {
202
+ conditions.push(`f.type = ?`);
203
+ bindings.push(type);
204
+ }
205
+ if (messageType) {
206
+ const types2 = messageType.split(",").map((t) => t.trim()).filter(Boolean);
207
+ conditions.push(`f.message_type IN (${types2.map(() => "?").join(", ")})`);
208
+ bindings.push(...types2);
209
+ }
210
+ if (message) {
211
+ conditions.push(`f.message_id = ?`);
212
+ bindings.push(message);
213
+ }
214
+ if (required) {
215
+ conditions.push(`f.required = 1`);
216
+ }
217
+ if (producer) {
218
+ conditions.push(
219
+ `EXISTS (SELECT 1 FROM message_producers p WHERE p.message_id = f.message_id AND p.message_version = f.message_version AND p.service_id = ?)`
220
+ );
221
+ bindings.push(producer);
222
+ }
223
+ if (consumer) {
224
+ conditions.push(
225
+ `EXISTS (SELECT 1 FROM message_consumers c WHERE c.message_id = f.message_id AND c.message_version = f.message_version AND c.service_id = ?)`
226
+ );
227
+ bindings.push(consumer);
228
+ }
229
+ if (shared) {
230
+ const sharedSubquery = `SELECT path FROM fields GROUP BY path HAVING COUNT(DISTINCT message_id || '/' || message_version) > 1`;
231
+ conditions.push(`f.path IN (${sharedSubquery})`);
232
+ }
233
+ if (conflicting) {
234
+ const conflictSubquery = `SELECT path FROM fields GROUP BY path HAVING COUNT(DISTINCT type) > 1`;
235
+ conditions.push(`f.path IN (${conflictSubquery})`);
236
+ }
237
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
238
+ const cursorConditions = [];
239
+ const cursorBindings = [];
240
+ if (cursor) {
241
+ const lastId = decodeCursor(cursor);
242
+ cursorConditions.push(`f.id > ?`);
243
+ cursorBindings.push(lastId);
244
+ }
245
+ const paginationWhere = cursorConditions.length > 0 ? whereClause ? `${whereClause} AND ${cursorConditions.join(" AND ")}` : `WHERE ${cursorConditions.join(" AND ")}` : whereClause;
246
+ const countSql = `SELECT COUNT(*) as cnt FROM fields f ${whereClause}`;
247
+ const total = this.db.prepare(countSql).get(...bindings).cnt;
248
+ const mainSql = `SELECT f.* FROM fields f ${paginationWhere} ORDER BY f.id ASC LIMIT ?`;
249
+ const allBindings = [...bindings, ...cursorBindings, pageSize];
250
+ const rows = this.db.prepare(mainSql).all(...allBindings);
251
+ const usedInStmt = this.db.prepare(
252
+ `SELECT COUNT(DISTINCT message_id || '/' || message_version) as cnt FROM fields WHERE path = ?`
253
+ );
254
+ const conflictsStmt = this.db.prepare(
255
+ `SELECT type, COUNT(DISTINCT message_id || '/' || message_version) as count FROM fields WHERE path = ? GROUP BY type`
256
+ );
257
+ const fields = rows.map((row) => {
258
+ const producers = this.db.prepare(
259
+ `SELECT service_id, service_version, service_name, service_summary, service_owners FROM message_producers WHERE message_id = ? AND message_version = ?`
260
+ ).all(row.message_id, row.message_version);
261
+ const consumers = this.db.prepare(
262
+ `SELECT service_id, service_version, service_name, service_summary, service_owners FROM message_consumers WHERE message_id = ? AND message_version = ?`
263
+ ).all(row.message_id, row.message_version);
264
+ const parseOwners = (raw) => {
265
+ try {
266
+ return JSON.parse(raw || "[]");
267
+ } catch {
268
+ return [];
269
+ }
270
+ };
271
+ const usedInCount = usedInStmt.get(row.path).cnt;
272
+ const typeRows = conflictsStmt.all(row.path);
273
+ const conflicts = typeRows.length > 1 ? typeRows.map((r) => ({ type: r.type, count: r.count })) : void 0;
274
+ return {
275
+ id: row.id,
276
+ path: row.path,
277
+ type: row.type,
278
+ description: row.description,
279
+ required: row.required === 1,
280
+ schemaFormat: row.schema_format,
281
+ messageId: row.message_id,
282
+ messageVersion: row.message_version,
283
+ messageType: row.message_type,
284
+ messageName: row.message_name || row.message_id,
285
+ messageSummary: row.message_summary || "",
286
+ messageOwners: parseOwners(row.message_owners),
287
+ usedInCount,
288
+ conflicts,
289
+ producers: producers.map((p) => ({
290
+ id: p.service_id,
291
+ version: p.service_version,
292
+ name: p.service_name || p.service_id,
293
+ summary: p.service_summary || "",
294
+ owners: parseOwners(p.service_owners)
295
+ })),
296
+ consumers: consumers.map((c) => ({
297
+ id: c.service_id,
298
+ version: c.service_version,
299
+ name: c.service_name || c.service_id,
300
+ summary: c.service_summary || "",
301
+ owners: parseOwners(c.service_owners)
302
+ }))
303
+ };
304
+ });
305
+ const formatsFacetSql = `SELECT f.schema_format as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.schema_format`;
306
+ const formats = this.db.prepare(formatsFacetSql).all(...bindings);
307
+ const typesFacetSql = `SELECT f.type as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.type`;
308
+ const types = this.db.prepare(typesFacetSql).all(...bindings);
309
+ const messageTypesFacetSql = `SELECT f.message_type as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.message_type`;
310
+ const messageTypes = this.db.prepare(messageTypesFacetSql).all(...bindings);
311
+ const lastRow = rows[rows.length - 1];
312
+ const nextCursor = lastRow && rows.length === pageSize ? encodeCursor(lastRow.id) : void 0;
313
+ return {
314
+ fields,
315
+ total,
316
+ cursor: nextCursor,
317
+ facets: { formats, types, messageTypes }
318
+ };
319
+ }
320
+ close() {
321
+ this.db.close();
322
+ }
323
+ };
324
+ function encodeCursor(id) {
325
+ return Buffer.from(String(id)).toString("base64url");
326
+ }
327
+ function decodeCursor(cursor) {
328
+ return parseInt(Buffer.from(cursor, "base64url").toString(), 10);
329
+ }
330
+
331
+ // eventcatalog/src/enterprise/fields/field-extractor.ts
332
+ function extractSchemaFieldsDeep(content, format) {
333
+ if (!content) return [];
334
+ if (format === "json-schema") {
335
+ return extractJsonSchemaFields(content);
336
+ }
337
+ if (format === "avro") {
338
+ return extractAvroFields(content);
339
+ }
340
+ if (format === "proto") {
341
+ return extractProtoFields(content);
342
+ }
343
+ return [];
344
+ }
345
+ function extractProtoFields(content) {
346
+ if (!content) return [];
347
+ const fields = [];
348
+ const fieldRegex = /^\s*(repeated\s+|optional\s+|required\s+)?(\w+)\s+(\w+)\s*=\s*\d+\s*;(?:\s*\/\/\s*(.*))?/gm;
349
+ let match;
350
+ while ((match = fieldRegex.exec(content)) !== null) {
351
+ const modifier = (match[1] || "").trim();
352
+ const type = modifier ? `${modifier} ${match[2]}` : match[2];
353
+ fields.push({
354
+ path: match[3],
355
+ type,
356
+ description: match[4]?.trim() || "",
357
+ required: modifier === "required"
358
+ });
359
+ }
360
+ return fields;
361
+ }
362
+ function extractAvroFields(content) {
363
+ try {
364
+ const schema = JSON.parse(content);
365
+ const fields = [];
366
+ walkAvroRecord(schema, "", fields);
367
+ return fields;
368
+ } catch {
369
+ return [];
370
+ }
371
+ }
372
+ function getAvroTypeName(type) {
373
+ if (typeof type === "string") return type;
374
+ if (Array.isArray(type)) {
375
+ return type.map((t) => typeof t === "string" ? t : t.type || "complex").join(" | ");
376
+ }
377
+ if (typeof type === "object" && type !== null) {
378
+ if (type.type === "array" && type.items) return `array<${getAvroTypeName(type.items)}>`;
379
+ if (type.type === "record") return type.name || "record";
380
+ return type.type || "complex";
381
+ }
382
+ return "unknown";
383
+ }
384
+ function walkAvroRecord(schema, prefix, fields) {
385
+ if (!schema.fields || !Array.isArray(schema.fields)) return;
386
+ for (const field of schema.fields) {
387
+ const path3 = prefix ? `${prefix}.${field.name}` : field.name;
388
+ const isOptional = Array.isArray(field.type) && field.type.includes("null");
389
+ const typeName = getAvroTypeName(field.type);
390
+ fields.push({
391
+ path: path3,
392
+ type: typeName,
393
+ description: field.doc || "",
394
+ required: !isOptional
395
+ });
396
+ const innerType = Array.isArray(field.type) ? field.type.find((t) => typeof t === "object" && t.type === "record") : typeof field.type === "object" && field.type.type === "record" ? field.type : null;
397
+ if (innerType) {
398
+ walkAvroRecord(innerType, path3, fields);
399
+ }
400
+ const arrayType = Array.isArray(field.type) ? field.type.find((t) => typeof t === "object" && t.type === "array") : typeof field.type === "object" && field.type.type === "array" ? field.type : null;
401
+ if (arrayType && typeof arrayType.items === "object" && arrayType.items.type === "record") {
402
+ walkAvroRecord(arrayType.items, `${path3}[]`, fields);
403
+ }
404
+ }
405
+ }
406
+ function extractJsonSchemaFields(content) {
407
+ try {
408
+ const schema = JSON.parse(content);
409
+ const fields = [];
410
+ walkJsonSchema(schema, "", schema.required || [], schema, fields);
411
+ return fields;
412
+ } catch {
413
+ return [];
414
+ }
415
+ }
416
+ function walkJsonSchema(node, prefix, requiredList, rootSchema, fields) {
417
+ if (node.allOf && Array.isArray(node.allOf)) {
418
+ const merged = { type: "object", properties: {}, required: [] };
419
+ for (let sub of node.allOf) {
420
+ if (sub.$ref) {
421
+ const resolved = resolveLocalRef(sub.$ref, rootSchema);
422
+ if (resolved) sub = resolved;
423
+ else continue;
424
+ }
425
+ Object.assign(merged.properties, sub.properties || {});
426
+ merged.required.push(...sub.required || []);
427
+ }
428
+ walkJsonSchema(merged, prefix, merged.required, rootSchema, fields);
429
+ return;
430
+ }
431
+ if (!node.properties) return;
432
+ for (const [name, prop] of Object.entries(node.properties)) {
433
+ const path3 = prefix ? `${prefix}.${name}` : name;
434
+ const isRequired = requiredList.includes(name);
435
+ if (prop.$ref) {
436
+ const resolved = resolveLocalRef(prop.$ref, rootSchema);
437
+ if (resolved) {
438
+ const type2 = resolved.type || "object";
439
+ fields.push({ path: path3, type: type2, description: resolved.description || "", required: isRequired });
440
+ if (resolved.properties) {
441
+ walkJsonSchema(resolved, path3, resolved.required || [], rootSchema, fields);
442
+ }
443
+ } else {
444
+ fields.push({ path: path3, type: "$ref", description: "", required: isRequired });
445
+ }
446
+ continue;
447
+ }
448
+ const type = prop.type || (prop.enum ? "enum" : prop.$ref ? "$ref" : "object");
449
+ fields.push({ path: path3, type, description: prop.description || "", required: isRequired });
450
+ if (prop.type === "object" && prop.properties) {
451
+ walkJsonSchema(prop, path3, prop.required || [], rootSchema, fields);
452
+ }
453
+ if (prop.type === "array" && prop.items) {
454
+ if (prop.items.type === "object" && prop.items.properties) {
455
+ walkJsonSchema(prop.items, `${path3}[]`, prop.items.required || [], rootSchema, fields);
456
+ }
457
+ }
458
+ }
459
+ }
460
+ function resolveLocalRef(ref, rootSchema) {
461
+ if (!ref.startsWith("#/")) return null;
462
+ const parts = ref.replace("#/", "").split("/");
463
+ let current = rootSchema;
464
+ for (const part of parts) {
465
+ current = current?.[part];
466
+ if (!current) return null;
467
+ }
468
+ return current;
469
+ }
470
+
471
+ // eventcatalog/src/enterprise/fields/field-indexer.ts
472
+ function detectFormat(fileName) {
473
+ const ext = path.extname(fileName).toLowerCase();
474
+ if (ext === ".proto") return "proto";
475
+ if (ext === ".avro" || ext === ".avsc") return "avro";
476
+ return "json-schema";
477
+ }
478
+ async function buildFieldsIndex(catalogDir, outputDir) {
479
+ const sdkModule = await import("@eventcatalog/sdk");
480
+ const sdk = sdkModule.default(catalogDir);
481
+ const dbDir = path.join(outputDir || catalogDir, ".eventcatalog");
482
+ const dbPath = path.join(dbDir, "fields.db");
483
+ if (!fs2.existsSync(dbDir)) {
484
+ fs2.mkdirSync(dbDir, { recursive: true });
485
+ }
486
+ const db = new FieldsDatabase(dbPath, { recreate: true });
487
+ const warnings = [];
488
+ try {
489
+ const [events, commands, queries] = await Promise.all([
490
+ sdk.getEvents({ latestOnly: true }),
491
+ sdk.getCommands({ latestOnly: true }),
492
+ sdk.getQueries({ latestOnly: true })
493
+ ]);
494
+ const collections = [
495
+ { entries: events, type: "event" },
496
+ { entries: commands, type: "command" },
497
+ { entries: queries, type: "query" }
498
+ ];
499
+ for (const { entries, type } of collections) {
500
+ for (const entry of entries) {
501
+ const msgId = entry.id;
502
+ const msgVersion = entry.version;
503
+ let schemaData;
504
+ try {
505
+ schemaData = await sdk.getSchemaForMessage(msgId, msgVersion);
506
+ } catch {
507
+ continue;
508
+ }
509
+ if (!schemaData) continue;
510
+ const { schema: content, fileName } = schemaData;
511
+ const format = detectFormat(fileName);
512
+ try {
513
+ const fields = extractSchemaFieldsDeep(content, format);
514
+ for (const field of fields) {
515
+ db.insertField({
516
+ path: field.path,
517
+ type: field.type,
518
+ description: field.description,
519
+ required: field.required,
520
+ schemaFormat: format,
521
+ messageId: msgId,
522
+ messageVersion: msgVersion,
523
+ messageType: type,
524
+ messageName: entry.name || msgId,
525
+ messageSummary: entry.summary || "",
526
+ messageOwners: entry.owners || []
527
+ });
528
+ }
529
+ const { producers, consumers } = await sdk.getProducersAndConsumersForMessage(msgId, msgVersion);
530
+ for (const producer of producers) {
531
+ db.insertProducer(
532
+ msgId,
533
+ msgVersion,
534
+ producer.id,
535
+ producer.version,
536
+ producer.name || producer.id,
537
+ producer.summary || "",
538
+ producer.owners || []
539
+ );
540
+ }
541
+ for (const consumer of consumers) {
542
+ db.insertConsumer(
543
+ msgId,
544
+ msgVersion,
545
+ consumer.id,
546
+ consumer.version,
547
+ consumer.name || consumer.id,
548
+ consumer.summary || "",
549
+ consumer.owners || []
550
+ );
551
+ }
552
+ } catch (err) {
553
+ warnings.push({ messageId: msgId, version: msgVersion, error: err.message });
554
+ }
555
+ }
556
+ }
557
+ db.rebuildFts();
558
+ db.close();
559
+ return { dbPath, warnings };
560
+ } catch (err) {
561
+ db.close();
562
+ throw err;
563
+ }
564
+ }
565
+
566
+ // src/eventcatalog.ts
48
567
  import { isEventCatalogStarterEnabled, isEventCatalogScaleEnabled, isFeatureEnabled } from "@eventcatalog/license";
49
- var currentDir = path.dirname(fileURLToPath(import.meta.url));
568
+ var currentDir = path2.dirname(fileURLToPath(import.meta.url));
50
569
  var program = new Command().version(VERSION);
51
- var dir = path.resolve(process.env.PROJECT_DIR || process.cwd());
52
- var core = path.resolve(process.env.CATALOG_DIR || join(dir, ".eventcatalog-core"));
53
- var eventCatalogDir = path.resolve(join(currentDir, "../eventcatalog/"));
570
+ var dir = path2.resolve(process.env.PROJECT_DIR || process.cwd());
571
+ var core = path2.resolve(process.env.CATALOG_DIR || join(dir, ".eventcatalog-core"));
572
+ var eventCatalogDir = path2.resolve(join(currentDir, "../eventcatalog/"));
54
573
  var getInstalledEventCatalogVersion = () => {
55
574
  try {
56
- const pkg = fs.readFileSync(join(dir, "package.json"), "utf8");
575
+ const pkg = fs3.readFileSync(join(dir, "package.json"), "utf8");
57
576
  const json = JSON.parse(pkg);
58
577
  return json.dependencies["@eventcatalog/core"];
59
578
  } catch (error) {
@@ -62,8 +581,8 @@ var getInstalledEventCatalogVersion = () => {
62
581
  };
63
582
  program.name("eventcatalog").description("Documentation tool for event-driven architectures");
64
583
  var ensureDir = (dir2) => {
65
- if (!fs.existsSync(dir2)) {
66
- fs.mkdirSync(dir2);
584
+ if (!fs3.existsSync(dir2)) {
585
+ fs3.mkdirSync(dir2);
67
586
  }
68
587
  };
69
588
  var resolveDevPort = async ({ projectDir }) => {
@@ -196,7 +715,7 @@ var copyCore = () => {
196
715
  if (eventCatalogDir === core) {
197
716
  return;
198
717
  }
199
- fs.cpSync(eventCatalogDir, core, {
718
+ fs3.cpSync(eventCatalogDir, core, {
200
719
  recursive: true,
201
720
  filter: (src) => {
202
721
  return true;
@@ -204,7 +723,7 @@ var copyCore = () => {
204
723
  });
205
724
  };
206
725
  var clearCore = () => {
207
- if (fs.existsSync(core)) fs.rmSync(core, { recursive: true });
726
+ if (fs3.existsSync(core)) fs3.rmSync(core, { recursive: true });
208
727
  };
209
728
  var checkForUpdate = () => {
210
729
  const installedVersion = getInstalledEventCatalogVersion();
@@ -258,8 +777,8 @@ program.command("dev").description("Run development server of EventCatalog").opt
258
777
  logger.info("Setting up EventCatalog...", "eventcatalog");
259
778
  const isServer = await isOutputServer();
260
779
  logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
261
- if (fs.existsSync(path.join(dir, ".env"))) {
262
- dotenv.config({ path: path.join(dir, ".env") });
780
+ if (fs3.existsSync(path2.join(dir, ".env"))) {
781
+ dotenv.config({ path: path2.join(dir, ".env") });
263
782
  }
264
783
  if (options.debug) {
265
784
  logger.info("Debug mode enabled", "debug");
@@ -278,6 +797,19 @@ program.command("dev").description("Run development server of EventCatalog").opt
278
797
  );
279
798
  const isEventCatalogStarter = await isEventCatalogStarterEnabled();
280
799
  const isEventCatalogScale = await isEventCatalogScaleEnabled();
800
+ if (isServer) {
801
+ try {
802
+ logger.info("Building fields index...", "fields");
803
+ const { warnings } = await buildFieldsIndex(dir, core);
804
+ if (warnings.length > 0) {
805
+ logger.info(`Fields index built with ${warnings.length} warning(s)`, "fields");
806
+ } else {
807
+ logger.info("Fields index built successfully", "fields");
808
+ }
809
+ } catch (err) {
810
+ logger.info(`Failed to build fields index: ${err.message}`, "fields");
811
+ }
812
+ }
281
813
  checkForUpdate();
282
814
  let watchUnsub;
283
815
  try {
@@ -318,8 +850,8 @@ program.command("build").description("Run build of EventCatalog").action(async (
318
850
  logger.info("Building EventCatalog...", "build");
319
851
  const isServer = await isOutputServer();
320
852
  logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
321
- if (fs.existsSync(path.join(dir, ".env"))) {
322
- dotenv.config({ path: path.join(dir, ".env") });
853
+ if (fs3.existsSync(path2.join(dir, ".env"))) {
854
+ dotenv.config({ path: path2.join(dir, ".env") });
323
855
  }
324
856
  await verifyRequiredFieldsAreInCatalogConfigFile(dir);
325
857
  copyCore();
@@ -338,6 +870,19 @@ program.command("build").description("Run build of EventCatalog").action(async (
338
870
  await resolve_catalog_dependencies_default(dir, core);
339
871
  await runMigrations(dir);
340
872
  await catalogToAstro(dir, core);
873
+ if (isServer) {
874
+ try {
875
+ logger.info("Building fields index...", "fields");
876
+ const { warnings } = await buildFieldsIndex(dir, core);
877
+ if (warnings.length > 0) {
878
+ logger.info(`Fields index built with ${warnings.length} warning(s)`, "fields");
879
+ } else {
880
+ logger.info("Fields index built successfully", "fields");
881
+ }
882
+ } catch (err) {
883
+ logger.info(`Failed to build fields index: ${err.message}`, "fields");
884
+ }
885
+ }
341
886
  checkForUpdate();
342
887
  const args = command.args.join(" ").trim();
343
888
  await runCommandWithFilteredOutput({
@@ -373,7 +918,7 @@ var startServerCatalog = ({
373
918
  isEventCatalogStarter = false,
374
919
  isEventCatalogScale = false
375
920
  }) => {
376
- const serverEntryPath = path.join(dir, "dist", "server", "entry.mjs");
921
+ const serverEntryPath = path2.join(dir, "dist", "server", "entry.mjs");
377
922
  execSync(
378
923
  `cross-env PROJECT_DIR='${dir}' CATALOG_DIR='${core}' ENABLE_EMBED=${canEmbedPages} EVENTCATALOG_STARTER=${isEventCatalogStarter} EVENTCATALOG_SCALE=${isEventCatalogScale} node "${serverEntryPath}"`,
379
924
  {
@@ -385,8 +930,8 @@ var startServerCatalog = ({
385
930
  program.command("preview").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
386
931
  logger.welcome();
387
932
  logger.info("Starting preview of your build...", "preview");
388
- if (fs.existsSync(path.join(dir, ".env"))) {
389
- dotenv.config({ path: path.join(dir, ".env") });
933
+ if (fs3.existsSync(path2.join(dir, ".env"))) {
934
+ dotenv.config({ path: path2.join(dir, ".env") });
390
935
  }
391
936
  const canEmbedPages = await isFeatureEnabled(
392
937
  "@eventcatalog/backstage-plugin-eventcatalog",
@@ -399,8 +944,8 @@ program.command("preview").description("Serves the contents of your eventcatalog
399
944
  program.command("start").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
400
945
  logger.welcome();
401
946
  logger.info("Starting preview of your build...", "preview");
402
- if (fs.existsSync(path.join(dir, ".env"))) {
403
- dotenv.config({ path: path.join(dir, ".env") });
947
+ if (fs3.existsSync(path2.join(dir, ".env"))) {
948
+ dotenv.config({ path: path2.join(dir, ".env") });
404
949
  }
405
950
  const canEmbedPages = await isFeatureEnabled(
406
951
  "@eventcatalog/backstage-plugin-eventcatalog",
@@ -426,8 +971,8 @@ program.command("start").description("Serves the contents of your eventcatalog b
426
971
  }
427
972
  });
428
973
  program.command("generate [siteDir]").description("Start the generator scripts.").action(async () => {
429
- if (fs.existsSync(path.join(dir, ".env"))) {
430
- dotenv.config({ path: path.join(dir, ".env") });
974
+ if (fs3.existsSync(path2.join(dir, ".env"))) {
975
+ dotenv.config({ path: path2.join(dir, ".env") });
431
976
  }
432
977
  await generate(dir);
433
978
  });