@eventcatalog/core 3.25.6 → 3.26.1
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/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-P23BMUBV.js → chunk-6BTN7CY7.js} +1 -1
- package/dist/{chunk-ZEOK723Y.js → chunk-EL6ZQNAX.js} +1 -1
- package/dist/{chunk-53HXLUNO.js → chunk-LTWPA4SA.js} +1 -1
- package/dist/{chunk-R7P4GTFQ.js → chunk-N3QSCVYA.js} +1 -1
- package/dist/{chunk-2ILJMBQM.js → chunk-Y736FREK.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +562 -19
- package/dist/eventcatalog.js +576 -31
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +2 -1
- package/eventcatalog/integrations/eventcatalog-features.ts +13 -0
- package/eventcatalog/public/icons/graphql.svg +3 -1
- package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +225 -0
- package/eventcatalog/src/components/FieldsExplorer/FieldNodeGraph.tsx +521 -0
- package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +501 -0
- package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +236 -0
- package/eventcatalog/src/enterprise/fields/field-extractor.test.ts +241 -0
- package/eventcatalog/src/enterprise/fields/field-extractor.ts +183 -0
- package/eventcatalog/src/enterprise/fields/field-indexer.ts +131 -0
- package/eventcatalog/src/enterprise/fields/fields-db.test.ts +186 -0
- package/eventcatalog/src/enterprise/fields/fields-db.ts +453 -0
- package/eventcatalog/src/enterprise/fields/pages/api/fields.ts +43 -0
- package/eventcatalog/src/enterprise/fields/pages/fields.astro +19 -0
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +23 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +14 -16
- package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +192 -0
- package/package.json +6 -4
package/dist/eventcatalog.cjs
CHANGED
|
@@ -29,10 +29,10 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
29
29
|
// src/eventcatalog.ts
|
|
30
30
|
var import_commander = require("commander");
|
|
31
31
|
var import_node_child_process = require("child_process");
|
|
32
|
-
var
|
|
32
|
+
var import_node_path8 = require("path");
|
|
33
33
|
var import_node_http = __toESM(require("http"), 1);
|
|
34
34
|
var import_fs2 = __toESM(require("fs"), 1);
|
|
35
|
-
var
|
|
35
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
36
36
|
var import_node_url = require("url");
|
|
37
37
|
|
|
38
38
|
// src/generate.js
|
|
@@ -114,7 +114,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
114
114
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
115
115
|
|
|
116
116
|
// package.json
|
|
117
|
-
var version = "3.
|
|
117
|
+
var version = "3.26.1";
|
|
118
118
|
|
|
119
119
|
// src/constants.ts
|
|
120
120
|
var VERSION = version;
|
|
@@ -699,16 +699,533 @@ var runMigrations = async (dir2) => {
|
|
|
699
699
|
await message_channels_to_service_channels_default(dir2);
|
|
700
700
|
};
|
|
701
701
|
|
|
702
|
+
// eventcatalog/src/enterprise/fields/field-indexer.ts
|
|
703
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
704
|
+
var import_node_fs7 = __toESM(require("fs"), 1);
|
|
705
|
+
|
|
706
|
+
// eventcatalog/src/enterprise/fields/fields-db.ts
|
|
707
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
708
|
+
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
709
|
+
var SCHEMA_SQL = `
|
|
710
|
+
CREATE TABLE IF NOT EXISTS fields (
|
|
711
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
712
|
+
path TEXT NOT NULL,
|
|
713
|
+
type TEXT NOT NULL,
|
|
714
|
+
description TEXT NOT NULL DEFAULT '',
|
|
715
|
+
required INTEGER NOT NULL DEFAULT 0,
|
|
716
|
+
schema_format TEXT NOT NULL,
|
|
717
|
+
message_id TEXT NOT NULL,
|
|
718
|
+
message_version TEXT NOT NULL,
|
|
719
|
+
message_type TEXT NOT NULL,
|
|
720
|
+
message_name TEXT NOT NULL DEFAULT '',
|
|
721
|
+
message_summary TEXT NOT NULL DEFAULT '',
|
|
722
|
+
message_owners TEXT NOT NULL DEFAULT '[]'
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
CREATE TABLE IF NOT EXISTS message_producers (
|
|
726
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
727
|
+
message_id TEXT NOT NULL,
|
|
728
|
+
message_version TEXT NOT NULL,
|
|
729
|
+
service_id TEXT NOT NULL,
|
|
730
|
+
service_version TEXT NOT NULL,
|
|
731
|
+
service_name TEXT NOT NULL DEFAULT '',
|
|
732
|
+
service_summary TEXT NOT NULL DEFAULT '',
|
|
733
|
+
service_owners TEXT NOT NULL DEFAULT '[]'
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
CREATE TABLE IF NOT EXISTS message_consumers (
|
|
737
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
738
|
+
message_id TEXT NOT NULL,
|
|
739
|
+
message_version TEXT NOT NULL,
|
|
740
|
+
service_id TEXT NOT NULL,
|
|
741
|
+
service_version TEXT NOT NULL,
|
|
742
|
+
service_name TEXT NOT NULL DEFAULT '',
|
|
743
|
+
service_summary TEXT NOT NULL DEFAULT '',
|
|
744
|
+
service_owners TEXT NOT NULL DEFAULT '[]'
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
CREATE INDEX IF NOT EXISTS idx_fields_path ON fields(path);
|
|
748
|
+
CREATE INDEX IF NOT EXISTS idx_fields_message ON fields(message_id, message_version);
|
|
749
|
+
CREATE INDEX IF NOT EXISTS idx_producers_message ON message_producers(message_id, message_version);
|
|
750
|
+
CREATE INDEX IF NOT EXISTS idx_consumers_message ON message_consumers(message_id, message_version);
|
|
751
|
+
`;
|
|
752
|
+
var FTS_SQL = `
|
|
753
|
+
DROP TABLE IF EXISTS fields_fts;
|
|
754
|
+
CREATE VIRTUAL TABLE fields_fts USING fts5(
|
|
755
|
+
path,
|
|
756
|
+
description,
|
|
757
|
+
type,
|
|
758
|
+
content=fields,
|
|
759
|
+
content_rowid=id
|
|
760
|
+
);
|
|
761
|
+
INSERT INTO fields_fts(rowid, path, description, type) SELECT id, path, description, type FROM fields;
|
|
762
|
+
`;
|
|
763
|
+
var FieldsDatabase = class {
|
|
764
|
+
db;
|
|
765
|
+
constructor(dbPath, options) {
|
|
766
|
+
if (options?.recreate && import_node_fs6.default.existsSync(dbPath)) {
|
|
767
|
+
import_node_fs6.default.unlinkSync(dbPath);
|
|
768
|
+
}
|
|
769
|
+
this.db = new import_better_sqlite3.default(dbPath);
|
|
770
|
+
this.db.pragma("journal_mode = WAL");
|
|
771
|
+
this.db.exec(SCHEMA_SQL);
|
|
772
|
+
}
|
|
773
|
+
insertField(field) {
|
|
774
|
+
this.db.prepare(
|
|
775
|
+
`INSERT INTO fields (path, type, description, required, schema_format, message_id, message_version, message_type, message_name, message_summary, message_owners)
|
|
776
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
777
|
+
).run(
|
|
778
|
+
field.path,
|
|
779
|
+
field.type,
|
|
780
|
+
field.description,
|
|
781
|
+
field.required ? 1 : 0,
|
|
782
|
+
field.schemaFormat,
|
|
783
|
+
field.messageId,
|
|
784
|
+
field.messageVersion,
|
|
785
|
+
field.messageType,
|
|
786
|
+
field.messageName || "",
|
|
787
|
+
field.messageSummary || "",
|
|
788
|
+
JSON.stringify(field.messageOwners || [])
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
insertProducer(messageId, messageVersion, serviceId, serviceVersion, serviceName, serviceSummary, serviceOwners) {
|
|
792
|
+
this.db.prepare(
|
|
793
|
+
`INSERT INTO message_producers (message_id, message_version, service_id, service_version, service_name, service_summary, service_owners)
|
|
794
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
795
|
+
).run(
|
|
796
|
+
messageId,
|
|
797
|
+
messageVersion,
|
|
798
|
+
serviceId,
|
|
799
|
+
serviceVersion,
|
|
800
|
+
serviceName || "",
|
|
801
|
+
serviceSummary || "",
|
|
802
|
+
JSON.stringify(serviceOwners || [])
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
insertConsumer(messageId, messageVersion, serviceId, serviceVersion, serviceName, serviceSummary, serviceOwners) {
|
|
806
|
+
this.db.prepare(
|
|
807
|
+
`INSERT INTO message_consumers (message_id, message_version, service_id, service_version, service_name, service_summary, service_owners)
|
|
808
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
809
|
+
).run(
|
|
810
|
+
messageId,
|
|
811
|
+
messageVersion,
|
|
812
|
+
serviceId,
|
|
813
|
+
serviceVersion,
|
|
814
|
+
serviceName || "",
|
|
815
|
+
serviceSummary || "",
|
|
816
|
+
JSON.stringify(serviceOwners || [])
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
rebuildFts() {
|
|
820
|
+
this.db.exec(FTS_SQL);
|
|
821
|
+
}
|
|
822
|
+
queryFields(params) {
|
|
823
|
+
const {
|
|
824
|
+
q,
|
|
825
|
+
shared,
|
|
826
|
+
conflicting,
|
|
827
|
+
format,
|
|
828
|
+
type,
|
|
829
|
+
messageType,
|
|
830
|
+
message,
|
|
831
|
+
producer,
|
|
832
|
+
consumer,
|
|
833
|
+
required,
|
|
834
|
+
path: fieldPath,
|
|
835
|
+
pageSize = 50,
|
|
836
|
+
cursor
|
|
837
|
+
} = params;
|
|
838
|
+
const conditions = [];
|
|
839
|
+
const bindings = [];
|
|
840
|
+
if (fieldPath) {
|
|
841
|
+
conditions.push(`f.path = ?`);
|
|
842
|
+
bindings.push(fieldPath);
|
|
843
|
+
}
|
|
844
|
+
if (q) {
|
|
845
|
+
conditions.push(`f.id IN (SELECT rowid FROM fields_fts WHERE fields_fts MATCH ?)`);
|
|
846
|
+
const escaped = q.replace(/"/g, '""');
|
|
847
|
+
bindings.push(`"${escaped}" *`);
|
|
848
|
+
}
|
|
849
|
+
if (format) {
|
|
850
|
+
const formats2 = format.split(",").map((f) => f.trim()).filter(Boolean);
|
|
851
|
+
conditions.push(`f.schema_format IN (${formats2.map(() => "?").join(", ")})`);
|
|
852
|
+
bindings.push(...formats2);
|
|
853
|
+
}
|
|
854
|
+
if (type) {
|
|
855
|
+
conditions.push(`f.type = ?`);
|
|
856
|
+
bindings.push(type);
|
|
857
|
+
}
|
|
858
|
+
if (messageType) {
|
|
859
|
+
const types2 = messageType.split(",").map((t) => t.trim()).filter(Boolean);
|
|
860
|
+
conditions.push(`f.message_type IN (${types2.map(() => "?").join(", ")})`);
|
|
861
|
+
bindings.push(...types2);
|
|
862
|
+
}
|
|
863
|
+
if (message) {
|
|
864
|
+
conditions.push(`f.message_id = ?`);
|
|
865
|
+
bindings.push(message);
|
|
866
|
+
}
|
|
867
|
+
if (required) {
|
|
868
|
+
conditions.push(`f.required = 1`);
|
|
869
|
+
}
|
|
870
|
+
if (producer) {
|
|
871
|
+
conditions.push(
|
|
872
|
+
`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 = ?)`
|
|
873
|
+
);
|
|
874
|
+
bindings.push(producer);
|
|
875
|
+
}
|
|
876
|
+
if (consumer) {
|
|
877
|
+
conditions.push(
|
|
878
|
+
`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 = ?)`
|
|
879
|
+
);
|
|
880
|
+
bindings.push(consumer);
|
|
881
|
+
}
|
|
882
|
+
if (shared) {
|
|
883
|
+
const sharedSubquery = `SELECT path FROM fields GROUP BY path HAVING COUNT(DISTINCT message_id || '/' || message_version) > 1`;
|
|
884
|
+
conditions.push(`f.path IN (${sharedSubquery})`);
|
|
885
|
+
}
|
|
886
|
+
if (conflicting) {
|
|
887
|
+
const conflictSubquery = `SELECT path FROM fields GROUP BY path HAVING COUNT(DISTINCT type) > 1`;
|
|
888
|
+
conditions.push(`f.path IN (${conflictSubquery})`);
|
|
889
|
+
}
|
|
890
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
891
|
+
const cursorConditions = [];
|
|
892
|
+
const cursorBindings = [];
|
|
893
|
+
if (cursor) {
|
|
894
|
+
const lastId = decodeCursor(cursor);
|
|
895
|
+
cursorConditions.push(`f.id > ?`);
|
|
896
|
+
cursorBindings.push(lastId);
|
|
897
|
+
}
|
|
898
|
+
const paginationWhere = cursorConditions.length > 0 ? whereClause ? `${whereClause} AND ${cursorConditions.join(" AND ")}` : `WHERE ${cursorConditions.join(" AND ")}` : whereClause;
|
|
899
|
+
const countSql = `SELECT COUNT(*) as cnt FROM fields f ${whereClause}`;
|
|
900
|
+
const total = this.db.prepare(countSql).get(...bindings).cnt;
|
|
901
|
+
const mainSql = `SELECT f.* FROM fields f ${paginationWhere} ORDER BY f.id ASC LIMIT ?`;
|
|
902
|
+
const allBindings = [...bindings, ...cursorBindings, pageSize];
|
|
903
|
+
const rows = this.db.prepare(mainSql).all(...allBindings);
|
|
904
|
+
const usedInStmt = this.db.prepare(
|
|
905
|
+
`SELECT COUNT(DISTINCT message_id || '/' || message_version) as cnt FROM fields WHERE path = ?`
|
|
906
|
+
);
|
|
907
|
+
const conflictsStmt = this.db.prepare(
|
|
908
|
+
`SELECT type, COUNT(DISTINCT message_id || '/' || message_version) as count FROM fields WHERE path = ? GROUP BY type`
|
|
909
|
+
);
|
|
910
|
+
const fields = rows.map((row) => {
|
|
911
|
+
const producers = this.db.prepare(
|
|
912
|
+
`SELECT service_id, service_version, service_name, service_summary, service_owners FROM message_producers WHERE message_id = ? AND message_version = ?`
|
|
913
|
+
).all(row.message_id, row.message_version);
|
|
914
|
+
const consumers = this.db.prepare(
|
|
915
|
+
`SELECT service_id, service_version, service_name, service_summary, service_owners FROM message_consumers WHERE message_id = ? AND message_version = ?`
|
|
916
|
+
).all(row.message_id, row.message_version);
|
|
917
|
+
const parseOwners = (raw) => {
|
|
918
|
+
try {
|
|
919
|
+
return JSON.parse(raw || "[]");
|
|
920
|
+
} catch {
|
|
921
|
+
return [];
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
const usedInCount = usedInStmt.get(row.path).cnt;
|
|
925
|
+
const typeRows = conflictsStmt.all(row.path);
|
|
926
|
+
const conflicts = typeRows.length > 1 ? typeRows.map((r) => ({ type: r.type, count: r.count })) : void 0;
|
|
927
|
+
return {
|
|
928
|
+
id: row.id,
|
|
929
|
+
path: row.path,
|
|
930
|
+
type: row.type,
|
|
931
|
+
description: row.description,
|
|
932
|
+
required: row.required === 1,
|
|
933
|
+
schemaFormat: row.schema_format,
|
|
934
|
+
messageId: row.message_id,
|
|
935
|
+
messageVersion: row.message_version,
|
|
936
|
+
messageType: row.message_type,
|
|
937
|
+
messageName: row.message_name || row.message_id,
|
|
938
|
+
messageSummary: row.message_summary || "",
|
|
939
|
+
messageOwners: parseOwners(row.message_owners),
|
|
940
|
+
usedInCount,
|
|
941
|
+
conflicts,
|
|
942
|
+
producers: producers.map((p) => ({
|
|
943
|
+
id: p.service_id,
|
|
944
|
+
version: p.service_version,
|
|
945
|
+
name: p.service_name || p.service_id,
|
|
946
|
+
summary: p.service_summary || "",
|
|
947
|
+
owners: parseOwners(p.service_owners)
|
|
948
|
+
})),
|
|
949
|
+
consumers: consumers.map((c) => ({
|
|
950
|
+
id: c.service_id,
|
|
951
|
+
version: c.service_version,
|
|
952
|
+
name: c.service_name || c.service_id,
|
|
953
|
+
summary: c.service_summary || "",
|
|
954
|
+
owners: parseOwners(c.service_owners)
|
|
955
|
+
}))
|
|
956
|
+
};
|
|
957
|
+
});
|
|
958
|
+
const formatsFacetSql = `SELECT f.schema_format as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.schema_format`;
|
|
959
|
+
const formats = this.db.prepare(formatsFacetSql).all(...bindings);
|
|
960
|
+
const typesFacetSql = `SELECT f.type as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.type`;
|
|
961
|
+
const types = this.db.prepare(typesFacetSql).all(...bindings);
|
|
962
|
+
const messageTypesFacetSql = `SELECT f.message_type as value, COUNT(*) as count FROM fields f ${whereClause} GROUP BY f.message_type`;
|
|
963
|
+
const messageTypes = this.db.prepare(messageTypesFacetSql).all(...bindings);
|
|
964
|
+
const lastRow = rows[rows.length - 1];
|
|
965
|
+
const nextCursor = lastRow && rows.length === pageSize ? encodeCursor(lastRow.id) : void 0;
|
|
966
|
+
return {
|
|
967
|
+
fields,
|
|
968
|
+
total,
|
|
969
|
+
cursor: nextCursor,
|
|
970
|
+
facets: { formats, types, messageTypes }
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
close() {
|
|
974
|
+
this.db.close();
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
function encodeCursor(id) {
|
|
978
|
+
return Buffer.from(String(id)).toString("base64url");
|
|
979
|
+
}
|
|
980
|
+
function decodeCursor(cursor) {
|
|
981
|
+
return parseInt(Buffer.from(cursor, "base64url").toString(), 10);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// eventcatalog/src/enterprise/fields/field-extractor.ts
|
|
985
|
+
function extractSchemaFieldsDeep(content, format) {
|
|
986
|
+
if (!content) return [];
|
|
987
|
+
if (format === "json-schema") {
|
|
988
|
+
return extractJsonSchemaFields(content);
|
|
989
|
+
}
|
|
990
|
+
if (format === "avro") {
|
|
991
|
+
return extractAvroFields(content);
|
|
992
|
+
}
|
|
993
|
+
if (format === "proto") {
|
|
994
|
+
return extractProtoFields(content);
|
|
995
|
+
}
|
|
996
|
+
return [];
|
|
997
|
+
}
|
|
998
|
+
function extractProtoFields(content) {
|
|
999
|
+
if (!content) return [];
|
|
1000
|
+
const fields = [];
|
|
1001
|
+
const fieldRegex = /^\s*(repeated\s+|optional\s+|required\s+)?(\w+)\s+(\w+)\s*=\s*\d+\s*;(?:\s*\/\/\s*(.*))?/gm;
|
|
1002
|
+
let match;
|
|
1003
|
+
while ((match = fieldRegex.exec(content)) !== null) {
|
|
1004
|
+
const modifier = (match[1] || "").trim();
|
|
1005
|
+
const type = modifier ? `${modifier} ${match[2]}` : match[2];
|
|
1006
|
+
fields.push({
|
|
1007
|
+
path: match[3],
|
|
1008
|
+
type,
|
|
1009
|
+
description: match[4]?.trim() || "",
|
|
1010
|
+
required: modifier === "required"
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
return fields;
|
|
1014
|
+
}
|
|
1015
|
+
function extractAvroFields(content) {
|
|
1016
|
+
try {
|
|
1017
|
+
const schema = JSON.parse(content);
|
|
1018
|
+
const fields = [];
|
|
1019
|
+
walkAvroRecord(schema, "", fields);
|
|
1020
|
+
return fields;
|
|
1021
|
+
} catch {
|
|
1022
|
+
return [];
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function getAvroTypeName(type) {
|
|
1026
|
+
if (typeof type === "string") return type;
|
|
1027
|
+
if (Array.isArray(type)) {
|
|
1028
|
+
return type.map((t) => typeof t === "string" ? t : t.type || "complex").join(" | ");
|
|
1029
|
+
}
|
|
1030
|
+
if (typeof type === "object" && type !== null) {
|
|
1031
|
+
if (type.type === "array" && type.items) return `array<${getAvroTypeName(type.items)}>`;
|
|
1032
|
+
if (type.type === "record") return type.name || "record";
|
|
1033
|
+
return type.type || "complex";
|
|
1034
|
+
}
|
|
1035
|
+
return "unknown";
|
|
1036
|
+
}
|
|
1037
|
+
function walkAvroRecord(schema, prefix, fields) {
|
|
1038
|
+
if (!schema.fields || !Array.isArray(schema.fields)) return;
|
|
1039
|
+
for (const field of schema.fields) {
|
|
1040
|
+
const path9 = prefix ? `${prefix}.${field.name}` : field.name;
|
|
1041
|
+
const isOptional = Array.isArray(field.type) && field.type.includes("null");
|
|
1042
|
+
const typeName = getAvroTypeName(field.type);
|
|
1043
|
+
fields.push({
|
|
1044
|
+
path: path9,
|
|
1045
|
+
type: typeName,
|
|
1046
|
+
description: field.doc || "",
|
|
1047
|
+
required: !isOptional
|
|
1048
|
+
});
|
|
1049
|
+
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;
|
|
1050
|
+
if (innerType) {
|
|
1051
|
+
walkAvroRecord(innerType, path9, fields);
|
|
1052
|
+
}
|
|
1053
|
+
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;
|
|
1054
|
+
if (arrayType && typeof arrayType.items === "object" && arrayType.items.type === "record") {
|
|
1055
|
+
walkAvroRecord(arrayType.items, `${path9}[]`, fields);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
function extractJsonSchemaFields(content) {
|
|
1060
|
+
try {
|
|
1061
|
+
const schema = JSON.parse(content);
|
|
1062
|
+
const fields = [];
|
|
1063
|
+
walkJsonSchema(schema, "", schema.required || [], schema, fields);
|
|
1064
|
+
return fields;
|
|
1065
|
+
} catch {
|
|
1066
|
+
return [];
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
function walkJsonSchema(node, prefix, requiredList, rootSchema, fields) {
|
|
1070
|
+
if (node.allOf && Array.isArray(node.allOf)) {
|
|
1071
|
+
const merged = { type: "object", properties: {}, required: [] };
|
|
1072
|
+
for (let sub of node.allOf) {
|
|
1073
|
+
if (sub.$ref) {
|
|
1074
|
+
const resolved = resolveLocalRef(sub.$ref, rootSchema);
|
|
1075
|
+
if (resolved) sub = resolved;
|
|
1076
|
+
else continue;
|
|
1077
|
+
}
|
|
1078
|
+
Object.assign(merged.properties, sub.properties || {});
|
|
1079
|
+
merged.required.push(...sub.required || []);
|
|
1080
|
+
}
|
|
1081
|
+
walkJsonSchema(merged, prefix, merged.required, rootSchema, fields);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
if (!node.properties) return;
|
|
1085
|
+
for (const [name, prop] of Object.entries(node.properties)) {
|
|
1086
|
+
const path9 = prefix ? `${prefix}.${name}` : name;
|
|
1087
|
+
const isRequired = requiredList.includes(name);
|
|
1088
|
+
if (prop.$ref) {
|
|
1089
|
+
const resolved = resolveLocalRef(prop.$ref, rootSchema);
|
|
1090
|
+
if (resolved) {
|
|
1091
|
+
const type2 = resolved.type || "object";
|
|
1092
|
+
fields.push({ path: path9, type: type2, description: resolved.description || "", required: isRequired });
|
|
1093
|
+
if (resolved.properties) {
|
|
1094
|
+
walkJsonSchema(resolved, path9, resolved.required || [], rootSchema, fields);
|
|
1095
|
+
}
|
|
1096
|
+
} else {
|
|
1097
|
+
fields.push({ path: path9, type: "$ref", description: "", required: isRequired });
|
|
1098
|
+
}
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
const type = prop.type || (prop.enum ? "enum" : prop.$ref ? "$ref" : "object");
|
|
1102
|
+
fields.push({ path: path9, type, description: prop.description || "", required: isRequired });
|
|
1103
|
+
if (prop.type === "object" && prop.properties) {
|
|
1104
|
+
walkJsonSchema(prop, path9, prop.required || [], rootSchema, fields);
|
|
1105
|
+
}
|
|
1106
|
+
if (prop.type === "array" && prop.items) {
|
|
1107
|
+
if (prop.items.type === "object" && prop.items.properties) {
|
|
1108
|
+
walkJsonSchema(prop.items, `${path9}[]`, prop.items.required || [], rootSchema, fields);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
function resolveLocalRef(ref, rootSchema) {
|
|
1114
|
+
if (!ref.startsWith("#/")) return null;
|
|
1115
|
+
const parts = ref.replace("#/", "").split("/");
|
|
1116
|
+
let current = rootSchema;
|
|
1117
|
+
for (const part of parts) {
|
|
1118
|
+
current = current?.[part];
|
|
1119
|
+
if (!current) return null;
|
|
1120
|
+
}
|
|
1121
|
+
return current;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// eventcatalog/src/enterprise/fields/field-indexer.ts
|
|
1125
|
+
function detectFormat(fileName) {
|
|
1126
|
+
const ext = import_node_path7.default.extname(fileName).toLowerCase();
|
|
1127
|
+
if (ext === ".proto") return "proto";
|
|
1128
|
+
if (ext === ".avro" || ext === ".avsc") return "avro";
|
|
1129
|
+
return "json-schema";
|
|
1130
|
+
}
|
|
1131
|
+
async function buildFieldsIndex(catalogDir, outputDir) {
|
|
1132
|
+
const sdkModule = await import("@eventcatalog/sdk");
|
|
1133
|
+
const sdk = sdkModule.default(catalogDir);
|
|
1134
|
+
const dbDir = import_node_path7.default.join(outputDir || catalogDir, ".eventcatalog");
|
|
1135
|
+
const dbPath = import_node_path7.default.join(dbDir, "fields.db");
|
|
1136
|
+
if (!import_node_fs7.default.existsSync(dbDir)) {
|
|
1137
|
+
import_node_fs7.default.mkdirSync(dbDir, { recursive: true });
|
|
1138
|
+
}
|
|
1139
|
+
const db = new FieldsDatabase(dbPath, { recreate: true });
|
|
1140
|
+
const warnings = [];
|
|
1141
|
+
try {
|
|
1142
|
+
const [events, commands, queries] = await Promise.all([
|
|
1143
|
+
sdk.getEvents({ latestOnly: true }),
|
|
1144
|
+
sdk.getCommands({ latestOnly: true }),
|
|
1145
|
+
sdk.getQueries({ latestOnly: true })
|
|
1146
|
+
]);
|
|
1147
|
+
const collections = [
|
|
1148
|
+
{ entries: events, type: "event" },
|
|
1149
|
+
{ entries: commands, type: "command" },
|
|
1150
|
+
{ entries: queries, type: "query" }
|
|
1151
|
+
];
|
|
1152
|
+
for (const { entries, type } of collections) {
|
|
1153
|
+
for (const entry of entries) {
|
|
1154
|
+
const msgId = entry.id;
|
|
1155
|
+
const msgVersion = entry.version;
|
|
1156
|
+
let schemaData;
|
|
1157
|
+
try {
|
|
1158
|
+
schemaData = await sdk.getSchemaForMessage(msgId, msgVersion);
|
|
1159
|
+
} catch {
|
|
1160
|
+
continue;
|
|
1161
|
+
}
|
|
1162
|
+
if (!schemaData) continue;
|
|
1163
|
+
const { schema: content, fileName } = schemaData;
|
|
1164
|
+
const format = detectFormat(fileName);
|
|
1165
|
+
try {
|
|
1166
|
+
const fields = extractSchemaFieldsDeep(content, format);
|
|
1167
|
+
for (const field of fields) {
|
|
1168
|
+
db.insertField({
|
|
1169
|
+
path: field.path,
|
|
1170
|
+
type: field.type,
|
|
1171
|
+
description: field.description,
|
|
1172
|
+
required: field.required,
|
|
1173
|
+
schemaFormat: format,
|
|
1174
|
+
messageId: msgId,
|
|
1175
|
+
messageVersion: msgVersion,
|
|
1176
|
+
messageType: type,
|
|
1177
|
+
messageName: entry.name || msgId,
|
|
1178
|
+
messageSummary: entry.summary || "",
|
|
1179
|
+
messageOwners: entry.owners || []
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
const { producers, consumers } = await sdk.getProducersAndConsumersForMessage(msgId, msgVersion);
|
|
1183
|
+
for (const producer of producers) {
|
|
1184
|
+
db.insertProducer(
|
|
1185
|
+
msgId,
|
|
1186
|
+
msgVersion,
|
|
1187
|
+
producer.id,
|
|
1188
|
+
producer.version,
|
|
1189
|
+
producer.name || producer.id,
|
|
1190
|
+
producer.summary || "",
|
|
1191
|
+
producer.owners || []
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
for (const consumer of consumers) {
|
|
1195
|
+
db.insertConsumer(
|
|
1196
|
+
msgId,
|
|
1197
|
+
msgVersion,
|
|
1198
|
+
consumer.id,
|
|
1199
|
+
consumer.version,
|
|
1200
|
+
consumer.name || consumer.id,
|
|
1201
|
+
consumer.summary || "",
|
|
1202
|
+
consumer.owners || []
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
} catch (err) {
|
|
1206
|
+
warnings.push({ messageId: msgId, version: msgVersion, error: err.message });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
db.rebuildFts();
|
|
1211
|
+
db.close();
|
|
1212
|
+
return { dbPath, warnings };
|
|
1213
|
+
} catch (err) {
|
|
1214
|
+
db.close();
|
|
1215
|
+
throw err;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
702
1219
|
// src/eventcatalog.ts
|
|
703
1220
|
var import_license = require("@eventcatalog/license");
|
|
704
|
-
var currentDir =
|
|
1221
|
+
var currentDir = import_node_path9.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
705
1222
|
var program = new import_commander.Command().version(VERSION);
|
|
706
|
-
var dir =
|
|
707
|
-
var core =
|
|
708
|
-
var eventCatalogDir =
|
|
1223
|
+
var dir = import_node_path9.default.resolve(process.env.PROJECT_DIR || process.cwd());
|
|
1224
|
+
var core = import_node_path9.default.resolve(process.env.CATALOG_DIR || (0, import_node_path8.join)(dir, ".eventcatalog-core"));
|
|
1225
|
+
var eventCatalogDir = import_node_path9.default.resolve((0, import_node_path8.join)(currentDir, "../eventcatalog/"));
|
|
709
1226
|
var getInstalledEventCatalogVersion = () => {
|
|
710
1227
|
try {
|
|
711
|
-
const pkg = import_fs2.default.readFileSync((0,
|
|
1228
|
+
const pkg = import_fs2.default.readFileSync((0, import_node_path8.join)(dir, "package.json"), "utf8");
|
|
712
1229
|
const json = JSON.parse(pkg);
|
|
713
1230
|
return json.dependencies["@eventcatalog/core"];
|
|
714
1231
|
} catch (error) {
|
|
@@ -913,8 +1430,8 @@ program.command("dev").description("Run development server of EventCatalog").opt
|
|
|
913
1430
|
logger.info("Setting up EventCatalog...", "eventcatalog");
|
|
914
1431
|
const isServer = await isOutputServer();
|
|
915
1432
|
logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
|
|
916
|
-
if (import_fs2.default.existsSync(
|
|
917
|
-
import_dotenv.default.config({ path:
|
|
1433
|
+
if (import_fs2.default.existsSync(import_node_path9.default.join(dir, ".env"))) {
|
|
1434
|
+
import_dotenv.default.config({ path: import_node_path9.default.join(dir, ".env") });
|
|
918
1435
|
}
|
|
919
1436
|
if (options.debug) {
|
|
920
1437
|
logger.info("Debug mode enabled", "debug");
|
|
@@ -933,6 +1450,19 @@ program.command("dev").description("Run development server of EventCatalog").opt
|
|
|
933
1450
|
);
|
|
934
1451
|
const isEventCatalogStarter = await (0, import_license.isEventCatalogStarterEnabled)();
|
|
935
1452
|
const isEventCatalogScale = await (0, import_license.isEventCatalogScaleEnabled)();
|
|
1453
|
+
if (isServer) {
|
|
1454
|
+
try {
|
|
1455
|
+
logger.info("Building fields index...", "fields");
|
|
1456
|
+
const { warnings } = await buildFieldsIndex(dir, core);
|
|
1457
|
+
if (warnings.length > 0) {
|
|
1458
|
+
logger.info(`Fields index built with ${warnings.length} warning(s)`, "fields");
|
|
1459
|
+
} else {
|
|
1460
|
+
logger.info("Fields index built successfully", "fields");
|
|
1461
|
+
}
|
|
1462
|
+
} catch (err) {
|
|
1463
|
+
logger.info(`Failed to build fields index: ${err.message}`, "fields");
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
936
1466
|
checkForUpdate();
|
|
937
1467
|
let watchUnsub;
|
|
938
1468
|
try {
|
|
@@ -973,8 +1503,8 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
973
1503
|
logger.info("Building EventCatalog...", "build");
|
|
974
1504
|
const isServer = await isOutputServer();
|
|
975
1505
|
logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
|
|
976
|
-
if (import_fs2.default.existsSync(
|
|
977
|
-
import_dotenv.default.config({ path:
|
|
1506
|
+
if (import_fs2.default.existsSync(import_node_path9.default.join(dir, ".env"))) {
|
|
1507
|
+
import_dotenv.default.config({ path: import_node_path9.default.join(dir, ".env") });
|
|
978
1508
|
}
|
|
979
1509
|
await verifyRequiredFieldsAreInCatalogConfigFile(dir);
|
|
980
1510
|
copyCore();
|
|
@@ -993,6 +1523,19 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
993
1523
|
await resolve_catalog_dependencies_default(dir, core);
|
|
994
1524
|
await runMigrations(dir);
|
|
995
1525
|
await catalogToAstro(dir, core);
|
|
1526
|
+
if (isServer) {
|
|
1527
|
+
try {
|
|
1528
|
+
logger.info("Building fields index...", "fields");
|
|
1529
|
+
const { warnings } = await buildFieldsIndex(dir, core);
|
|
1530
|
+
if (warnings.length > 0) {
|
|
1531
|
+
logger.info(`Fields index built with ${warnings.length} warning(s)`, "fields");
|
|
1532
|
+
} else {
|
|
1533
|
+
logger.info("Fields index built successfully", "fields");
|
|
1534
|
+
}
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
logger.info(`Failed to build fields index: ${err.message}`, "fields");
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
996
1539
|
checkForUpdate();
|
|
997
1540
|
const args = command.args.join(" ").trim();
|
|
998
1541
|
await runCommandWithFilteredOutput({
|
|
@@ -1028,7 +1571,7 @@ var startServerCatalog = ({
|
|
|
1028
1571
|
isEventCatalogStarter = false,
|
|
1029
1572
|
isEventCatalogScale = false
|
|
1030
1573
|
}) => {
|
|
1031
|
-
const serverEntryPath =
|
|
1574
|
+
const serverEntryPath = import_node_path9.default.join(dir, "dist", "server", "entry.mjs");
|
|
1032
1575
|
(0, import_node_child_process.execSync)(
|
|
1033
1576
|
`cross-env PROJECT_DIR='${dir}' CATALOG_DIR='${core}' ENABLE_EMBED=${canEmbedPages} EVENTCATALOG_STARTER=${isEventCatalogStarter} EVENTCATALOG_SCALE=${isEventCatalogScale} node "${serverEntryPath}"`,
|
|
1034
1577
|
{
|
|
@@ -1040,8 +1583,8 @@ var startServerCatalog = ({
|
|
|
1040
1583
|
program.command("preview").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
1041
1584
|
logger.welcome();
|
|
1042
1585
|
logger.info("Starting preview of your build...", "preview");
|
|
1043
|
-
if (import_fs2.default.existsSync(
|
|
1044
|
-
import_dotenv.default.config({ path:
|
|
1586
|
+
if (import_fs2.default.existsSync(import_node_path9.default.join(dir, ".env"))) {
|
|
1587
|
+
import_dotenv.default.config({ path: import_node_path9.default.join(dir, ".env") });
|
|
1045
1588
|
}
|
|
1046
1589
|
const canEmbedPages = await (0, import_license.isFeatureEnabled)(
|
|
1047
1590
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
@@ -1054,8 +1597,8 @@ program.command("preview").description("Serves the contents of your eventcatalog
|
|
|
1054
1597
|
program.command("start").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
1055
1598
|
logger.welcome();
|
|
1056
1599
|
logger.info("Starting preview of your build...", "preview");
|
|
1057
|
-
if (import_fs2.default.existsSync(
|
|
1058
|
-
import_dotenv.default.config({ path:
|
|
1600
|
+
if (import_fs2.default.existsSync(import_node_path9.default.join(dir, ".env"))) {
|
|
1601
|
+
import_dotenv.default.config({ path: import_node_path9.default.join(dir, ".env") });
|
|
1059
1602
|
}
|
|
1060
1603
|
const canEmbedPages = await (0, import_license.isFeatureEnabled)(
|
|
1061
1604
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
@@ -1081,8 +1624,8 @@ program.command("start").description("Serves the contents of your eventcatalog b
|
|
|
1081
1624
|
}
|
|
1082
1625
|
});
|
|
1083
1626
|
program.command("generate [siteDir]").description("Start the generator scripts.").action(async () => {
|
|
1084
|
-
if (import_fs2.default.existsSync(
|
|
1085
|
-
import_dotenv.default.config({ path:
|
|
1627
|
+
if (import_fs2.default.existsSync(import_node_path9.default.join(dir, ".env"))) {
|
|
1628
|
+
import_dotenv.default.config({ path: import_node_path9.default.join(dir, ".env") });
|
|
1086
1629
|
}
|
|
1087
1630
|
await generate(dir);
|
|
1088
1631
|
});
|