@eventcatalog/core 4.7.4 → 4.7.6
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/LICENSE +95 -0
- package/README.md +3 -2
- 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-7QTCSRAC.js → chunk-6BBLQLGL.js} +1 -1
- package/dist/chunk-DDALKGTF.js +7 -0
- package/dist/chunk-JJPB6EOZ.js +159 -0
- package/dist/chunk-MLIR3NL4.js +279 -0
- package/dist/{chunk-ZXVQXBTT.js → chunk-MNAPKH63.js} +24 -1
- package/dist/{chunk-TLWM7WRZ.js → chunk-Q534DWPZ.js} +1 -1
- package/dist/{chunk-ZIABX6SP.js → chunk-RZAJNR7O.js} +1 -1
- package/dist/chunk-WRNH5C3U.js +118 -0
- package/dist/{chunk-FDXJIZ74.js → chunk-YXANZO6Y.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +560 -211
- package/dist/eventcatalog.config.d.cts +9 -3
- package/dist/eventcatalog.config.d.ts +9 -3
- package/dist/eventcatalog.js +40 -42
- package/dist/federation/content-cache.d.cts +5 -0
- package/dist/federation/content-cache.d.ts +5 -0
- package/dist/federation/diagnostics.cjs +187 -0
- package/dist/federation/diagnostics.d.cts +28 -0
- package/dist/federation/diagnostics.d.ts +28 -0
- package/dist/federation/diagnostics.js +14 -0
- package/dist/federation/entitlement.cjs +31 -0
- package/dist/federation/entitlement.d.cts +12 -0
- package/dist/federation/entitlement.d.ts +12 -0
- package/dist/federation/entitlement.js +6 -0
- package/dist/federation/federate.cjs +447 -118
- package/dist/federation/federate.d.cts +13 -2
- package/dist/federation/federate.d.ts +13 -2
- package/dist/federation/federate.js +9 -4
- package/dist/federation/filesystem-source-provider.d.cts +5 -0
- package/dist/federation/filesystem-source-provider.d.ts +5 -0
- package/dist/federation/github-source-provider.d.cts +5 -0
- package/dist/federation/github-source-provider.d.ts +5 -0
- package/dist/federation/output-transaction.cjs +152 -0
- package/dist/federation/output-transaction.d.cts +7 -0
- package/dist/federation/output-transaction.d.ts +7 -0
- package/dist/federation/output-transaction.js +6 -0
- package/dist/federation/public-assets.d.cts +5 -0
- package/dist/federation/public-assets.d.ts +5 -0
- package/dist/federation/source-provider.d.cts +5 -0
- package/dist/federation/source-provider.d.ts +5 -0
- package/dist/federation/source-provider.js +2 -2
- package/dist/federation/types.d.cts +5 -0
- package/dist/federation/types.d.ts +5 -0
- package/dist/generate.cjs +24 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +24 -1
- package/dist/utils/cli-logger.d.cts +6 -0
- package/dist/utils/cli-logger.d.ts +6 -0
- package/dist/utils/cli-logger.js +2 -2
- package/package.json +4 -4
- package/dist/chunk-RIUHZZRA.js +0 -204
- package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
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_process2 = require("child_process");
|
|
32
|
-
var
|
|
32
|
+
var import_node_path17 = require("path");
|
|
33
33
|
var import_node_http = __toESM(require("http"), 1);
|
|
34
34
|
var import_fs3 = __toESM(require("fs"), 1);
|
|
35
|
-
var
|
|
35
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
36
36
|
var import_node_url = require("url");
|
|
37
37
|
|
|
38
38
|
// src/generate.js
|
|
@@ -144,7 +144,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
144
144
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
145
145
|
|
|
146
146
|
// package.json
|
|
147
|
-
var version = "4.7.
|
|
147
|
+
var version = "4.7.6";
|
|
148
148
|
|
|
149
149
|
// src/constants.ts
|
|
150
150
|
var VERSION = version;
|
|
@@ -183,6 +183,29 @@ var logger = {
|
|
|
183
183
|
warning: (message, tag = "warn") => {
|
|
184
184
|
console.log(formatMessage(tag, message, import_picocolors.default.yellow));
|
|
185
185
|
},
|
|
186
|
+
line: (message = "") => {
|
|
187
|
+
console.log(message);
|
|
188
|
+
},
|
|
189
|
+
diagnostic: (severity, message, rule, attributes = []) => {
|
|
190
|
+
const isError = severity === "error";
|
|
191
|
+
const color = isError ? import_picocolors.default.red : import_picocolors.default.yellow;
|
|
192
|
+
const icon = isError ? "\u2716" : "\u26A0";
|
|
193
|
+
const label = `${icon} ${severity.padEnd(7)} ${message}`;
|
|
194
|
+
const ruleSpacing = " ".repeat(Math.max(2, 72 - label.length));
|
|
195
|
+
console.log(` ${color(icon)} ${color(severity.padEnd(7))} ${message}${ruleSpacing}${import_picocolors.default.gray(rule)}`);
|
|
196
|
+
for (const attribute of attributes) {
|
|
197
|
+
console.log(` - ${import_picocolors.default.dim(`${attribute.label}:`)} ${attribute.value}`);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
diagnosticSummary: (errors, warnings) => {
|
|
201
|
+
const total = errors + warnings;
|
|
202
|
+
const color = errors > 0 ? import_picocolors.default.red : import_picocolors.default.yellow;
|
|
203
|
+
const icon = errors > 0 ? "\u2716" : "\u26A0";
|
|
204
|
+
const problems = `${total} problem${total === 1 ? "" : "s"}`;
|
|
205
|
+
const errorCount = `${errors} error${errors === 1 ? "" : "s"}`;
|
|
206
|
+
const warningCount = `${warnings} warning${warnings === 1 ? "" : "s"}`;
|
|
207
|
+
console.log(color(`${icon} ${problems} (${errorCount}, ${warningCount})`));
|
|
208
|
+
},
|
|
186
209
|
dim: (message) => {
|
|
187
210
|
console.log(import_picocolors.default.dim(message));
|
|
188
211
|
}
|
|
@@ -389,7 +412,7 @@ var reportCloudResourceInventory = async (configFile, resourceCounts) => {
|
|
|
389
412
|
})
|
|
390
413
|
});
|
|
391
414
|
};
|
|
392
|
-
var main = async (projectDir, { isEventCatalogStarterEnabled: isEventCatalogStarterEnabled2, isEventCatalogScaleEnabled:
|
|
415
|
+
var main = async (projectDir, { isEventCatalogStarterEnabled: isEventCatalogStarterEnabled2, isEventCatalogScaleEnabled: isEventCatalogScaleEnabled3, isBackstagePluginEnabled }) => {
|
|
393
416
|
try {
|
|
394
417
|
await verifyRequiredFieldsAreInCatalogConfigFile(projectDir);
|
|
395
418
|
const configFile = await getEventCatalogConfigFile(projectDir);
|
|
@@ -398,7 +421,7 @@ var main = async (projectDir, { isEventCatalogStarterEnabled: isEventCatalogStar
|
|
|
398
421
|
if (isEventCatalogStarterEnabled2) {
|
|
399
422
|
generatorNames.push("@eventcatalog/eventcatalog-starter");
|
|
400
423
|
}
|
|
401
|
-
if (
|
|
424
|
+
if (isEventCatalogScaleEnabled3) {
|
|
402
425
|
generatorNames.push("@eventcatalog/eventcatalog-scale");
|
|
403
426
|
}
|
|
404
427
|
if (isBackstagePluginEnabled) {
|
|
@@ -1310,22 +1333,22 @@ function getAvroTypeName(type) {
|
|
|
1310
1333
|
function walkAvroRecord(schema, prefix, fields) {
|
|
1311
1334
|
if (!schema.fields || !Array.isArray(schema.fields)) return;
|
|
1312
1335
|
for (const field of schema.fields) {
|
|
1313
|
-
const
|
|
1336
|
+
const path18 = prefix ? `${prefix}.${field.name}` : field.name;
|
|
1314
1337
|
const isOptional = Array.isArray(field.type) && field.type.includes("null");
|
|
1315
1338
|
const typeName = getAvroTypeName(field.type);
|
|
1316
1339
|
fields.push({
|
|
1317
|
-
path:
|
|
1340
|
+
path: path18,
|
|
1318
1341
|
type: typeName,
|
|
1319
1342
|
description: field.doc || "",
|
|
1320
1343
|
required: !isOptional
|
|
1321
1344
|
});
|
|
1322
1345
|
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;
|
|
1323
1346
|
if (innerType) {
|
|
1324
|
-
walkAvroRecord(innerType,
|
|
1347
|
+
walkAvroRecord(innerType, path18, fields);
|
|
1325
1348
|
}
|
|
1326
1349
|
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;
|
|
1327
1350
|
if (arrayType && typeof arrayType.items === "object" && arrayType.items.type === "record") {
|
|
1328
|
-
walkAvroRecord(arrayType.items, `${
|
|
1351
|
+
walkAvroRecord(arrayType.items, `${path18}[]`, fields);
|
|
1329
1352
|
}
|
|
1330
1353
|
}
|
|
1331
1354
|
}
|
|
@@ -1356,32 +1379,32 @@ function walkJsonSchema(node, prefix, requiredList, rootSchema, fields) {
|
|
|
1356
1379
|
}
|
|
1357
1380
|
if (!node.properties) return;
|
|
1358
1381
|
for (const [name, prop] of Object.entries(node.properties)) {
|
|
1359
|
-
const
|
|
1382
|
+
const path18 = prefix ? `${prefix}.${name}` : name;
|
|
1360
1383
|
const isRequired = requiredList.includes(name);
|
|
1361
1384
|
if (prop.$ref) {
|
|
1362
1385
|
const resolved = resolveLocalRef(prop.$ref, rootSchema);
|
|
1363
1386
|
if (resolved) {
|
|
1364
1387
|
const rawRefType = resolved.type || "object";
|
|
1365
1388
|
const type2 = Array.isArray(rawRefType) ? [...rawRefType].sort().join(" | ") : rawRefType;
|
|
1366
|
-
fields.push({ path:
|
|
1389
|
+
fields.push({ path: path18, type: type2, description: resolved.description || "", required: isRequired });
|
|
1367
1390
|
if (resolved.properties) {
|
|
1368
|
-
walkJsonSchema(resolved,
|
|
1391
|
+
walkJsonSchema(resolved, path18, resolved.required || [], rootSchema, fields);
|
|
1369
1392
|
}
|
|
1370
1393
|
} else {
|
|
1371
|
-
fields.push({ path:
|
|
1394
|
+
fields.push({ path: path18, type: "$ref", description: "", required: isRequired });
|
|
1372
1395
|
}
|
|
1373
1396
|
continue;
|
|
1374
1397
|
}
|
|
1375
1398
|
const rawType = prop.type || (prop.enum ? "enum" : prop.$ref ? "$ref" : "object");
|
|
1376
1399
|
const typeList = Array.isArray(rawType) ? rawType : [rawType];
|
|
1377
1400
|
const type = Array.isArray(rawType) ? [...rawType].sort().join(" | ") : rawType;
|
|
1378
|
-
fields.push({ path:
|
|
1401
|
+
fields.push({ path: path18, type, description: prop.description || "", required: isRequired });
|
|
1379
1402
|
if (typeList.includes("object") && prop.properties) {
|
|
1380
|
-
walkJsonSchema(prop,
|
|
1403
|
+
walkJsonSchema(prop, path18, prop.required || [], rootSchema, fields);
|
|
1381
1404
|
}
|
|
1382
1405
|
if (typeList.includes("array") && prop.items) {
|
|
1383
1406
|
if (prop.items.type === "object" && prop.items.properties) {
|
|
1384
|
-
walkJsonSchema(prop.items, `${
|
|
1407
|
+
walkJsonSchema(prop.items, `${path18}[]`, prop.items.required || [], rootSchema, fields);
|
|
1385
1408
|
}
|
|
1386
1409
|
}
|
|
1387
1410
|
}
|
|
@@ -1901,8 +1924,8 @@ var createAstroDevLineFilter = () => {
|
|
|
1901
1924
|
|
|
1902
1925
|
// src/federation/federate.ts
|
|
1903
1926
|
var import_node_crypto4 = require("crypto");
|
|
1904
|
-
var
|
|
1905
|
-
var
|
|
1927
|
+
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
1928
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
1906
1929
|
var import_sdk3 = __toESM(require("@eventcatalog/sdk"), 1);
|
|
1907
1930
|
|
|
1908
1931
|
// src/federation/content-cache.ts
|
|
@@ -1946,28 +1969,299 @@ var createFederationContentCache = (projectDirectory, options = {}) => {
|
|
|
1946
1969
|
};
|
|
1947
1970
|
};
|
|
1948
1971
|
|
|
1949
|
-
// src/federation/
|
|
1950
|
-
var
|
|
1972
|
+
// src/federation/diagnostics.ts
|
|
1973
|
+
var federationRuleDefaults = {
|
|
1974
|
+
"federation/duplicate-source": "error",
|
|
1975
|
+
"federation/type-collision": "error",
|
|
1976
|
+
"federation/pointer-type-mismatch": "error",
|
|
1977
|
+
"federation/facet-disagreement": "error",
|
|
1978
|
+
"federation/asset-collision": "warn",
|
|
1979
|
+
"federation/missing-resource": "warn",
|
|
1980
|
+
"federation/unresolved-version": "warn"
|
|
1981
|
+
};
|
|
1982
|
+
var resolveFederationRules = (rules = {}) => {
|
|
1983
|
+
for (const [rule, level] of Object.entries(rules)) {
|
|
1984
|
+
if (!Object.hasOwn(federationRuleDefaults, rule)) throw new Error(`Unknown federation rule "${rule}".`);
|
|
1985
|
+
if (level !== "off" && level !== "warn" && level !== "error") {
|
|
1986
|
+
throw new Error(`Invalid level "${String(level)}" for federation rule "${rule}". Expected off, warn, or error.`);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
return { ...federationRuleDefaults, ...rules };
|
|
1990
|
+
};
|
|
1991
|
+
var getExternalResource = (external) => `${external.id}${external.version === void 0 ? "" : `@${external.version}`}`;
|
|
1992
|
+
var getVersionedResource = (id, version2) => `${id}${version2 == null ? "" : `@${version2}`}`;
|
|
1993
|
+
var getDocumentedResourceType = (type) => {
|
|
1994
|
+
const usesAn = type === "adr" || type === "agent" || type === "entity" || type === "event";
|
|
1995
|
+
return `documented as ${usesAn ? "an" : "a"} ${type}`;
|
|
1996
|
+
};
|
|
1997
|
+
var getConflictDiagnostic = (conflict, graph) => {
|
|
1998
|
+
const catalogs = { label: "catalogs", value: conflict.sources.join(", ") };
|
|
1999
|
+
switch (conflict.kind) {
|
|
2000
|
+
case "duplicate-source":
|
|
2001
|
+
return {
|
|
2002
|
+
severity: "error",
|
|
2003
|
+
message: "Resource has multiple owners",
|
|
2004
|
+
rule: "federation/duplicate-source",
|
|
2005
|
+
attributes: [
|
|
2006
|
+
{ label: "resource", value: conflict.id },
|
|
2007
|
+
catalogs,
|
|
2008
|
+
{ label: "resolution", value: "assign a single owning catalog" }
|
|
2009
|
+
]
|
|
2010
|
+
};
|
|
2011
|
+
case "type-collision": {
|
|
2012
|
+
const types = [
|
|
2013
|
+
...new Map(
|
|
2014
|
+
graph.entities.filter((entity) => entity.id === conflict.id).map(
|
|
2015
|
+
(entity) => [
|
|
2016
|
+
`${entity.resolvedFrom.source}:${entity.type}`,
|
|
2017
|
+
{ label: entity.resolvedFrom.source, value: getDocumentedResourceType(entity.type) }
|
|
2018
|
+
]
|
|
2019
|
+
)
|
|
2020
|
+
).values()
|
|
2021
|
+
];
|
|
2022
|
+
return {
|
|
2023
|
+
severity: "error",
|
|
2024
|
+
message: "Resource ID has conflicting types",
|
|
2025
|
+
rule: "federation/type-collision",
|
|
2026
|
+
attributes: [{ label: "resource", value: conflict.id }, ...types.length > 0 ? types : [catalogs]]
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
case "pointer-type-mismatch": {
|
|
2030
|
+
const typeMismatch = /^Expected (.+) but found (.+)$/.exec(conflict.detail ?? "");
|
|
2031
|
+
return {
|
|
2032
|
+
severity: "error",
|
|
2033
|
+
message: "Reference type does not match resource",
|
|
2034
|
+
rule: "federation/pointer-type-mismatch",
|
|
2035
|
+
attributes: [
|
|
2036
|
+
{ label: "resource", value: conflict.id },
|
|
2037
|
+
...typeMismatch ? [
|
|
2038
|
+
{ label: "expected type", value: typeMismatch[1] },
|
|
2039
|
+
{ label: "actual type", value: typeMismatch[2] }
|
|
2040
|
+
] : conflict.detail ? [{ label: "detail", value: conflict.detail }] : [],
|
|
2041
|
+
catalogs
|
|
2042
|
+
]
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
case "facet-disagreement":
|
|
2046
|
+
return {
|
|
2047
|
+
severity: "error",
|
|
2048
|
+
message: "Catalogs disagree about this resource",
|
|
2049
|
+
rule: "federation/facet-disagreement",
|
|
2050
|
+
attributes: [
|
|
2051
|
+
{ label: "resource", value: conflict.id },
|
|
2052
|
+
...conflict.detail ? [{ label: "detail", value: conflict.detail }] : [],
|
|
2053
|
+
catalogs
|
|
2054
|
+
]
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
var getFederationDiagnostics = (graph, rules = {}) => {
|
|
2059
|
+
const resolvedRules = resolveFederationRules(rules);
|
|
2060
|
+
const diagnostics = graph.conflicts.map((conflict) => getConflictDiagnostic(conflict, graph));
|
|
2061
|
+
for (const external of graph.externals) {
|
|
2062
|
+
const resource = getExternalResource(external);
|
|
2063
|
+
for (const referencedBy of external.referencedBy) {
|
|
2064
|
+
const referrer = graph.entities.find((entity) => entity.id === referencedBy);
|
|
2065
|
+
diagnostics.push({
|
|
2066
|
+
severity: "warning",
|
|
2067
|
+
message: "Referenced EventCatalog resource does not exist",
|
|
2068
|
+
rule: "federation/missing-resource",
|
|
2069
|
+
attributes: [
|
|
2070
|
+
{ label: "source catalog", value: referrer?.resolvedFrom.source ?? "unknown" },
|
|
2071
|
+
{ label: "referenced by", value: referencedBy },
|
|
2072
|
+
{ label: "missing resource", value: resource }
|
|
2073
|
+
]
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
for (const edge of graph.edges.filter((edge2) => edge2.status === "unresolved")) {
|
|
2078
|
+
const availableVersions = [
|
|
2079
|
+
...new Set(graph.entities.filter((entity) => entity.id === edge.to).map((entity) => entity.version ?? "unversioned"))
|
|
2080
|
+
].sort((left, right) => left.localeCompare(right, void 0, { numeric: true }));
|
|
2081
|
+
diagnostics.push({
|
|
2082
|
+
severity: "warning",
|
|
2083
|
+
message: "Referenced EventCatalog resource version does not exist",
|
|
2084
|
+
rule: "federation/unresolved-version",
|
|
2085
|
+
attributes: [
|
|
2086
|
+
{ label: "source catalog", value: edge.fromResolvedFrom.source },
|
|
2087
|
+
{ label: "referenced by", value: getVersionedResource(edge.from, edge.fromVersion) },
|
|
2088
|
+
{ label: "resource", value: edge.to },
|
|
2089
|
+
{ label: "requested version", value: edge.pointer ?? "unspecified" },
|
|
2090
|
+
{ label: "available versions", value: availableVersions.join(", ") }
|
|
2091
|
+
]
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
for (const warning of graph.warnings) {
|
|
2095
|
+
diagnostics.push({
|
|
2096
|
+
severity: "warning",
|
|
2097
|
+
message: "Asset collision",
|
|
2098
|
+
rule: "federation/asset-collision",
|
|
2099
|
+
attributes: [
|
|
2100
|
+
{ label: "asset", value: warning.path },
|
|
2101
|
+
{ label: "sources", value: warning.sources.join(", ") },
|
|
2102
|
+
{ label: "winner", value: warning.winner },
|
|
2103
|
+
{ label: "resolution", value: "last configured source wins" }
|
|
2104
|
+
]
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
return diagnostics.flatMap((diagnostic) => {
|
|
2108
|
+
const level = resolvedRules[diagnostic.rule];
|
|
2109
|
+
if (level === "off") return [];
|
|
2110
|
+
return [{ ...diagnostic, severity: level === "error" ? "error" : "warning" }];
|
|
2111
|
+
}).sort(
|
|
2112
|
+
(left, right) => (left.severity === right.severity ? 0 : left.severity === "error" ? -1 : 1) || left.rule.localeCompare(right.rule) || (left.attributes[0]?.value ?? "").localeCompare(right.attributes[0]?.value ?? "")
|
|
2113
|
+
);
|
|
2114
|
+
};
|
|
2115
|
+
var getFederationDiagnosticCounts = (diagnostics) => diagnostics.reduce(
|
|
2116
|
+
(counts, diagnostic) => ({
|
|
2117
|
+
errors: counts.errors + (diagnostic.severity === "error" ? 1 : 0),
|
|
2118
|
+
warnings: counts.warnings + (diagnostic.severity === "warning" ? 1 : 0)
|
|
2119
|
+
}),
|
|
2120
|
+
{ errors: 0, warnings: 0 }
|
|
2121
|
+
);
|
|
2122
|
+
var getVisibleFederationDiagnostics = (diagnostics, verbose) => diagnostics.filter((diagnostic) => verbose || diagnostic.severity === "error");
|
|
2123
|
+
|
|
2124
|
+
// src/federation/entitlement.ts
|
|
2125
|
+
var import_license = require("@eventcatalog/license");
|
|
2126
|
+
var isFederationEnabled = () => (0, import_license.isEventCatalogScaleEnabled)();
|
|
2127
|
+
|
|
2128
|
+
// src/federation/output-transaction.ts
|
|
1951
2129
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
1952
2130
|
var import_node_path12 = __toESM(require("path"), 1);
|
|
2131
|
+
var getSafePublicPath = (publicDirectory, relativePath) => {
|
|
2132
|
+
const normalizedPath = import_node_path12.default.posix.normalize(relativePath);
|
|
2133
|
+
const unsafe = relativePath.length === 0 || relativePath.includes("\0") || relativePath.includes("\\") || import_node_path12.default.posix.isAbsolute(relativePath) || /^[a-zA-Z]:\//.test(relativePath) || normalizedPath !== relativePath || normalizedPath === ".." || normalizedPath.startsWith("../");
|
|
2134
|
+
if (unsafe) return void 0;
|
|
2135
|
+
const destinationPath = import_node_path12.default.resolve(publicDirectory, relativePath);
|
|
2136
|
+
const relativeDestinationPath = import_node_path12.default.relative(publicDirectory, destinationPath);
|
|
2137
|
+
return relativeDestinationPath !== "" && relativeDestinationPath !== ".." && !relativeDestinationPath.startsWith(`..${import_node_path12.default.sep}`) && !import_node_path12.default.isAbsolute(relativeDestinationPath) ? destinationPath : void 0;
|
|
2138
|
+
};
|
|
2139
|
+
var beginFederationOutputTransaction = async (projectDirectory, outDir, relativePublicPaths) => {
|
|
2140
|
+
const transactionDirectory = await import_promises5.default.mkdtemp(import_node_path12.default.join(projectDirectory, ".eventcatalog-federation-transaction-"));
|
|
2141
|
+
const previousOutDir = import_node_path12.default.join(transactionDirectory, "federated");
|
|
2142
|
+
const publicBackupDirectory = import_node_path12.default.join(transactionDirectory, "public");
|
|
2143
|
+
const publicDirectory = import_node_path12.default.resolve(projectDirectory, "public");
|
|
2144
|
+
const publicSnapshots = [];
|
|
2145
|
+
const missingPublicDirectories = /* @__PURE__ */ new Set();
|
|
2146
|
+
let hadPreviousOutDir = false;
|
|
2147
|
+
try {
|
|
2148
|
+
for (const relativePath of new Set(relativePublicPaths)) {
|
|
2149
|
+
const destinationPath = getSafePublicPath(publicDirectory, relativePath);
|
|
2150
|
+
if (!destinationPath) continue;
|
|
2151
|
+
try {
|
|
2152
|
+
const stat = await import_promises5.default.lstat(destinationPath);
|
|
2153
|
+
if (!stat.isFile()) continue;
|
|
2154
|
+
const backupPath = import_node_path12.default.join(publicBackupDirectory, `${publicSnapshots.length}`);
|
|
2155
|
+
await import_promises5.default.mkdir(import_node_path12.default.dirname(backupPath), { recursive: true });
|
|
2156
|
+
await import_promises5.default.copyFile(destinationPath, backupPath);
|
|
2157
|
+
publicSnapshots.push({ backupPath, destinationPath, state: "file" });
|
|
2158
|
+
} catch (error) {
|
|
2159
|
+
const code = error.code;
|
|
2160
|
+
if (code === "ENOTDIR") continue;
|
|
2161
|
+
if (code !== "ENOENT") throw error;
|
|
2162
|
+
publicSnapshots.push({ destinationPath, state: "missing" });
|
|
2163
|
+
let directory = import_node_path12.default.dirname(destinationPath);
|
|
2164
|
+
while (directory !== publicDirectory) {
|
|
2165
|
+
try {
|
|
2166
|
+
await import_promises5.default.lstat(directory);
|
|
2167
|
+
break;
|
|
2168
|
+
} catch (directoryError) {
|
|
2169
|
+
const directoryCode = directoryError.code;
|
|
2170
|
+
if (directoryCode === "ENOTDIR") break;
|
|
2171
|
+
if (directoryCode !== "ENOENT") throw directoryError;
|
|
2172
|
+
missingPublicDirectories.add(directory);
|
|
2173
|
+
directory = import_node_path12.default.dirname(directory);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
try {
|
|
2179
|
+
await import_promises5.default.rename(outDir, previousOutDir);
|
|
2180
|
+
hadPreviousOutDir = true;
|
|
2181
|
+
} catch (error) {
|
|
2182
|
+
if (error.code !== "ENOENT") throw error;
|
|
2183
|
+
}
|
|
2184
|
+
} catch (error) {
|
|
2185
|
+
await import_promises5.default.rm(transactionDirectory, { recursive: true, force: true });
|
|
2186
|
+
throw error;
|
|
2187
|
+
}
|
|
2188
|
+
return {
|
|
2189
|
+
commit: async () => {
|
|
2190
|
+
await import_promises5.default.rm(transactionDirectory, { recursive: true, force: true }).catch(() => void 0);
|
|
2191
|
+
},
|
|
2192
|
+
rollback: async () => {
|
|
2193
|
+
const rollbackErrors = [];
|
|
2194
|
+
const attempt = async (operation) => {
|
|
2195
|
+
try {
|
|
2196
|
+
await operation();
|
|
2197
|
+
} catch (error) {
|
|
2198
|
+
rollbackErrors.push(error);
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2201
|
+
await attempt(() => import_promises5.default.rm(outDir, { recursive: true, force: true }));
|
|
2202
|
+
if (hadPreviousOutDir) await attempt(() => import_promises5.default.rename(previousOutDir, outDir));
|
|
2203
|
+
for (const snapshot of publicSnapshots) {
|
|
2204
|
+
if (snapshot.state === "missing") {
|
|
2205
|
+
await attempt(() => import_promises5.default.rm(snapshot.destinationPath, { force: true }));
|
|
2206
|
+
continue;
|
|
2207
|
+
}
|
|
2208
|
+
await attempt(async () => {
|
|
2209
|
+
await import_promises5.default.mkdir(import_node_path12.default.dirname(snapshot.destinationPath), { recursive: true });
|
|
2210
|
+
await import_promises5.default.copyFile(snapshot.backupPath, snapshot.destinationPath);
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
for (const directory of [...missingPublicDirectories].sort((left, right) => right.length - left.length)) {
|
|
2214
|
+
await attempt(async () => {
|
|
2215
|
+
try {
|
|
2216
|
+
await import_promises5.default.rmdir(directory);
|
|
2217
|
+
} catch (error) {
|
|
2218
|
+
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
|
|
2219
|
+
}
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
await attempt(() => import_promises5.default.rm(transactionDirectory, { recursive: true, force: true }));
|
|
2223
|
+
if (rollbackErrors.length > 0) throw new AggregateError(rollbackErrors, "Failed to restore the previous federation output");
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
};
|
|
2227
|
+
var withFederationOutputTransaction = async (projectDirectory, outDir, relativePublicPaths, update) => {
|
|
2228
|
+
const transaction = await beginFederationOutputTransaction(projectDirectory, outDir, relativePublicPaths);
|
|
2229
|
+
try {
|
|
2230
|
+
const result = await update();
|
|
2231
|
+
await transaction.commit();
|
|
2232
|
+
return result;
|
|
2233
|
+
} catch (error) {
|
|
2234
|
+
try {
|
|
2235
|
+
await transaction.rollback();
|
|
2236
|
+
} catch (rollbackError) {
|
|
2237
|
+
throw new AggregateError([error, rollbackError], "Federation failed and the previous output could not be restored");
|
|
2238
|
+
}
|
|
2239
|
+
throw error;
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
|
|
2243
|
+
// src/federation/public-assets.ts
|
|
2244
|
+
var import_node_crypto2 = require("crypto");
|
|
2245
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
2246
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
1953
2247
|
var getContentHash2 = (content) => `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex")}`;
|
|
1954
2248
|
var getFileHash = async (filePath) => {
|
|
1955
2249
|
try {
|
|
1956
|
-
const stat = await
|
|
1957
|
-
return stat.isFile() ? getContentHash2(await
|
|
2250
|
+
const stat = await import_promises6.default.lstat(filePath);
|
|
2251
|
+
return stat.isFile() ? getContentHash2(await import_promises6.default.readFile(filePath)) : void 0;
|
|
1958
2252
|
} catch (error) {
|
|
1959
2253
|
if (error.code === "ENOENT") return void 0;
|
|
1960
2254
|
throw error;
|
|
1961
2255
|
}
|
|
1962
2256
|
};
|
|
1963
2257
|
var getSafePath = (directory, relativePath) => {
|
|
1964
|
-
const normalizedPath =
|
|
1965
|
-
const unsafe = relativePath.length === 0 || relativePath.includes("\0") || relativePath.includes("\\") ||
|
|
2258
|
+
const normalizedPath = import_node_path13.default.posix.normalize(relativePath);
|
|
2259
|
+
const unsafe = relativePath.length === 0 || relativePath.includes("\0") || relativePath.includes("\\") || import_node_path13.default.posix.isAbsolute(relativePath) || /^[a-zA-Z]:\//.test(relativePath) || normalizedPath !== relativePath || normalizedPath === ".." || normalizedPath.startsWith("../");
|
|
1966
2260
|
if (unsafe) return void 0;
|
|
1967
|
-
const resolvedDirectory =
|
|
1968
|
-
const resolvedPath =
|
|
1969
|
-
const relativeResolvedPath =
|
|
1970
|
-
if (relativeResolvedPath === "" || relativeResolvedPath === ".." || relativeResolvedPath.startsWith(`..${
|
|
2261
|
+
const resolvedDirectory = import_node_path13.default.resolve(directory);
|
|
2262
|
+
const resolvedPath = import_node_path13.default.resolve(resolvedDirectory, relativePath);
|
|
2263
|
+
const relativeResolvedPath = import_node_path13.default.relative(resolvedDirectory, resolvedPath);
|
|
2264
|
+
if (relativeResolvedPath === "" || relativeResolvedPath === ".." || relativeResolvedPath.startsWith(`..${import_node_path13.default.sep}`) || import_node_path13.default.isAbsolute(relativeResolvedPath)) {
|
|
1971
2265
|
return void 0;
|
|
1972
2266
|
}
|
|
1973
2267
|
return resolvedPath;
|
|
@@ -1975,23 +2269,23 @@ var getSafePath = (directory, relativePath) => {
|
|
|
1975
2269
|
var listFiles = async (directory, relativeDirectory = "") => {
|
|
1976
2270
|
let entries;
|
|
1977
2271
|
try {
|
|
1978
|
-
entries = await
|
|
2272
|
+
entries = await import_promises6.default.readdir(import_node_path13.default.join(directory, relativeDirectory), { withFileTypes: true });
|
|
1979
2273
|
} catch (error) {
|
|
1980
2274
|
if (error.code === "ENOENT") return [];
|
|
1981
2275
|
throw error;
|
|
1982
2276
|
}
|
|
1983
2277
|
const files = await Promise.all(
|
|
1984
2278
|
entries.map(async (entry) => {
|
|
1985
|
-
const relativePath =
|
|
2279
|
+
const relativePath = import_node_path13.default.join(relativeDirectory, entry.name);
|
|
1986
2280
|
if (entry.isDirectory()) return listFiles(directory, relativePath);
|
|
1987
|
-
return entry.isFile() ? [relativePath.split(
|
|
2281
|
+
return entry.isFile() ? [relativePath.split(import_node_path13.default.sep).join("/")] : [];
|
|
1988
2282
|
})
|
|
1989
2283
|
);
|
|
1990
2284
|
return files.flat().sort();
|
|
1991
2285
|
};
|
|
1992
2286
|
var pathExists = async (filePath) => {
|
|
1993
2287
|
try {
|
|
1994
|
-
await
|
|
2288
|
+
await import_promises6.default.lstat(filePath);
|
|
1995
2289
|
return true;
|
|
1996
2290
|
} catch (error) {
|
|
1997
2291
|
if (error.code === "ENOENT") return false;
|
|
@@ -1999,27 +2293,27 @@ var pathExists = async (filePath) => {
|
|
|
1999
2293
|
}
|
|
2000
2294
|
};
|
|
2001
2295
|
var hasBlockingParent = async (publicDirectory, destinationPath) => {
|
|
2002
|
-
let currentPath =
|
|
2296
|
+
let currentPath = import_node_path13.default.dirname(destinationPath);
|
|
2003
2297
|
while (currentPath !== publicDirectory) {
|
|
2004
2298
|
try {
|
|
2005
|
-
if (!(await
|
|
2299
|
+
if (!(await import_promises6.default.lstat(currentPath)).isDirectory()) return true;
|
|
2006
2300
|
} catch (error) {
|
|
2007
2301
|
if (error.code !== "ENOENT") throw error;
|
|
2008
2302
|
}
|
|
2009
|
-
currentPath =
|
|
2303
|
+
currentPath = import_node_path13.default.dirname(currentPath);
|
|
2010
2304
|
}
|
|
2011
2305
|
return false;
|
|
2012
2306
|
};
|
|
2013
2307
|
var pruneEmptyDirectories = async (publicDirectory, filePath) => {
|
|
2014
|
-
let currentPath =
|
|
2308
|
+
let currentPath = import_node_path13.default.dirname(filePath);
|
|
2015
2309
|
while (currentPath !== publicDirectory) {
|
|
2016
2310
|
try {
|
|
2017
|
-
await
|
|
2311
|
+
await import_promises6.default.rmdir(currentPath);
|
|
2018
2312
|
} catch (error) {
|
|
2019
2313
|
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
|
|
2020
2314
|
if (error.code === "ENOTEMPTY") return;
|
|
2021
2315
|
}
|
|
2022
|
-
currentPath =
|
|
2316
|
+
currentPath = import_node_path13.default.dirname(currentPath);
|
|
2023
2317
|
}
|
|
2024
2318
|
};
|
|
2025
2319
|
var composePublicAssets = async ({
|
|
@@ -2029,8 +2323,8 @@ var composePublicAssets = async ({
|
|
|
2029
2323
|
previousFiles = {},
|
|
2030
2324
|
collisionPaths = /* @__PURE__ */ new Set()
|
|
2031
2325
|
}) => {
|
|
2032
|
-
const publicDirectory =
|
|
2033
|
-
const federatedPublicDirectory =
|
|
2326
|
+
const publicDirectory = import_node_path13.default.resolve(projectDirectory, "public");
|
|
2327
|
+
const federatedPublicDirectory = import_node_path13.default.resolve(federatedDirectory, "public");
|
|
2034
2328
|
const sourceFiles = await listFiles(federatedPublicDirectory);
|
|
2035
2329
|
const sourceFileSet = new Set(sourceFiles);
|
|
2036
2330
|
const managedFiles = /* @__PURE__ */ new Set();
|
|
@@ -2043,7 +2337,7 @@ var composePublicAssets = async ({
|
|
|
2043
2337
|
if (sourceFileSet.has(relativePath)) continue;
|
|
2044
2338
|
const destinationPath = getSafePath(publicDirectory, relativePath);
|
|
2045
2339
|
if (!destinationPath) continue;
|
|
2046
|
-
await
|
|
2340
|
+
await import_promises6.default.rm(destinationPath, { force: true });
|
|
2047
2341
|
await pruneEmptyDirectories(publicDirectory, destinationPath);
|
|
2048
2342
|
removed += 1;
|
|
2049
2343
|
}
|
|
@@ -2065,31 +2359,31 @@ var composePublicAssets = async ({
|
|
|
2065
2359
|
}
|
|
2066
2360
|
const asset = publicAssetsByPath.get(relativePath);
|
|
2067
2361
|
if (!asset) throw new Error(`Cannot identify the source of federated public asset "${relativePath}"`);
|
|
2068
|
-
const content = await
|
|
2069
|
-
await
|
|
2070
|
-
await
|
|
2362
|
+
const content = await import_promises6.default.readFile(sourcePath);
|
|
2363
|
+
await import_promises6.default.mkdir(import_node_path13.default.dirname(destinationPath), { recursive: true });
|
|
2364
|
+
await import_promises6.default.writeFile(destinationPath, content);
|
|
2071
2365
|
files[relativePath] = { source: asset.resolvedFrom.source, hash: getContentHash2(content) };
|
|
2072
2366
|
copied += 1;
|
|
2073
2367
|
if (collisionPaths.has(`public/${relativePath}`)) overwritten += 1;
|
|
2074
2368
|
}
|
|
2075
|
-
await
|
|
2369
|
+
await import_promises6.default.rm(federatedPublicDirectory, { recursive: true, force: true });
|
|
2076
2370
|
return { files, copied, skipped, overwritten, removed };
|
|
2077
2371
|
};
|
|
2078
2372
|
|
|
2079
2373
|
// src/federation/filesystem-source-provider.ts
|
|
2080
2374
|
var import_node_crypto3 = require("crypto");
|
|
2081
|
-
var
|
|
2082
|
-
var
|
|
2375
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
2376
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
2083
2377
|
var import_sdk = __toESM(require("@eventcatalog/sdk"), 1);
|
|
2084
2378
|
var FILESYSTEM_SOURCE_PREFIX = "file:";
|
|
2085
2379
|
var isWithinDirectory = (directory, target) => {
|
|
2086
|
-
const relativePath =
|
|
2087
|
-
return relativePath === "" || !relativePath.startsWith(`..${
|
|
2380
|
+
const relativePath = import_node_path14.default.relative(directory, target);
|
|
2381
|
+
return relativePath === "" || !relativePath.startsWith(`..${import_node_path14.default.sep}`) && relativePath !== ".." && !import_node_path14.default.isAbsolute(relativePath);
|
|
2088
2382
|
};
|
|
2089
2383
|
var assertPortableRelativePath = (filePath, label, allowRoot = true) => {
|
|
2090
2384
|
const portablePath = filePath.replaceAll("\\", "/");
|
|
2091
|
-
const normalizedPath =
|
|
2092
|
-
const isUnsafe = filePath.includes("\\") || filePath.includes("\0") ||
|
|
2385
|
+
const normalizedPath = import_node_path14.default.posix.normalize(portablePath);
|
|
2386
|
+
const isUnsafe = filePath.includes("\\") || filePath.includes("\0") || import_node_path14.default.posix.isAbsolute(normalizedPath) || /^[a-zA-Z]:\//.test(normalizedPath) || normalizedPath === ".." || normalizedPath.startsWith("../") || !allowRoot && (normalizedPath === "." || normalizedPath === "");
|
|
2093
2387
|
if (isUnsafe) throw new Error(`${label} "${filePath}" escapes its filesystem source`);
|
|
2094
2388
|
return normalizedPath;
|
|
2095
2389
|
};
|
|
@@ -2099,22 +2393,22 @@ var getSourceRoot = (projectDirectory, source) => {
|
|
|
2099
2393
|
}
|
|
2100
2394
|
const locator = source.source.slice(FILESYSTEM_SOURCE_PREFIX.length);
|
|
2101
2395
|
if (!locator.trim()) throw new Error(`Filesystem federation source "${source.id}" requires a path after "file:".`);
|
|
2102
|
-
return
|
|
2396
|
+
return import_node_path14.default.resolve(projectDirectory, locator);
|
|
2103
2397
|
};
|
|
2104
2398
|
var getCatalogDirectory = async (projectDirectory, source) => {
|
|
2105
2399
|
if (source.ref) throw new Error(`Filesystem federation source "${source.id}" does not support "ref".`);
|
|
2106
2400
|
const sourceRoot = getSourceRoot(projectDirectory, source);
|
|
2107
2401
|
const catalogPath = assertPortableRelativePath(source.path ?? ".", "Catalog path");
|
|
2108
|
-
const catalogDirectory =
|
|
2402
|
+
const catalogDirectory = import_node_path14.default.resolve(sourceRoot, ...catalogPath.split("/"));
|
|
2109
2403
|
if (!isWithinDirectory(sourceRoot, catalogDirectory)) {
|
|
2110
2404
|
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
2111
2405
|
}
|
|
2112
2406
|
try {
|
|
2113
|
-
const [realSourceRoot, realCatalogDirectory] = await Promise.all([
|
|
2407
|
+
const [realSourceRoot, realCatalogDirectory] = await Promise.all([import_promises7.default.realpath(sourceRoot), import_promises7.default.realpath(catalogDirectory)]);
|
|
2114
2408
|
if (!isWithinDirectory(realSourceRoot, realCatalogDirectory)) {
|
|
2115
2409
|
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
2116
2410
|
}
|
|
2117
|
-
if (!(await
|
|
2411
|
+
if (!(await import_promises7.default.stat(realCatalogDirectory)).isDirectory()) {
|
|
2118
2412
|
throw new Error(`Filesystem federation source "${source.id}" is not a directory: ${catalogDirectory}`);
|
|
2119
2413
|
}
|
|
2120
2414
|
return realCatalogDirectory;
|
|
@@ -2127,12 +2421,12 @@ var getCatalogDirectory = async (projectDirectory, source) => {
|
|
|
2127
2421
|
};
|
|
2128
2422
|
var getArtifactPath = async (catalogDirectory, source, artifactPath) => {
|
|
2129
2423
|
const normalizedPath = assertPortableRelativePath(artifactPath, "Federated artifact path", false);
|
|
2130
|
-
const filePath =
|
|
2424
|
+
const filePath = import_node_path14.default.resolve(catalogDirectory, ...normalizedPath.split("/"));
|
|
2131
2425
|
if (!isWithinDirectory(catalogDirectory, filePath)) {
|
|
2132
2426
|
throw new Error(`Federated artifact path "${artifactPath}" escapes source "${source.id}"`);
|
|
2133
2427
|
}
|
|
2134
2428
|
try {
|
|
2135
|
-
const realFilePath = await
|
|
2429
|
+
const realFilePath = await import_promises7.default.realpath(filePath);
|
|
2136
2430
|
if (!isWithinDirectory(catalogDirectory, realFilePath)) {
|
|
2137
2431
|
throw new Error(`Federated artifact path "${artifactPath}" escapes source "${source.id}"`);
|
|
2138
2432
|
}
|
|
@@ -2159,15 +2453,15 @@ var createFileSystemSourceProvider = (projectDirectory) => ({
|
|
|
2159
2453
|
},
|
|
2160
2454
|
async fetchContent({ source, path: artifactPath }) {
|
|
2161
2455
|
const catalogDirectory = await getCatalogDirectory(projectDirectory, source);
|
|
2162
|
-
return
|
|
2456
|
+
return import_promises7.default.readFile(await getArtifactPath(catalogDirectory, source, artifactPath));
|
|
2163
2457
|
}
|
|
2164
2458
|
});
|
|
2165
2459
|
|
|
2166
2460
|
// src/federation/github-source-provider.ts
|
|
2167
2461
|
var import_node_child_process = require("child_process");
|
|
2168
|
-
var
|
|
2462
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
2169
2463
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
2170
|
-
var
|
|
2464
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
2171
2465
|
var import_node_util = require("util");
|
|
2172
2466
|
var import_sdk2 = __toESM(require("@eventcatalog/sdk"), 1);
|
|
2173
2467
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
@@ -2178,8 +2472,8 @@ var parseGitHubSource = (source) => {
|
|
|
2178
2472
|
};
|
|
2179
2473
|
var assertSafeCatalogPath = (source) => {
|
|
2180
2474
|
const catalogPath = source.path ?? ".";
|
|
2181
|
-
const normalized =
|
|
2182
|
-
if (catalogPath.includes("\\") ||
|
|
2475
|
+
const normalized = import_node_path15.default.posix.normalize(catalogPath.replaceAll("\\", "/"));
|
|
2476
|
+
if (catalogPath.includes("\\") || import_node_path15.default.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
2183
2477
|
throw new Error(`Catalog path "${catalogPath}" escapes source "${source.id}"`);
|
|
2184
2478
|
}
|
|
2185
2479
|
};
|
|
@@ -2222,8 +2516,8 @@ var getGitEnvironment = (token) => {
|
|
|
2222
2516
|
};
|
|
2223
2517
|
var createCheckout = (executeFile, token) => async (source, ref, callback) => {
|
|
2224
2518
|
const { owner, repository } = parseGitHubSource(source);
|
|
2225
|
-
const catalogPath =
|
|
2226
|
-
const directory = await
|
|
2519
|
+
const catalogPath = import_node_path15.default.posix.normalize(source.path ?? ".");
|
|
2520
|
+
const directory = await import_promises8.default.mkdtemp(import_node_path15.default.join(import_node_os4.default.tmpdir(), "eventcatalog-federation-"));
|
|
2227
2521
|
const env = getGitEnvironment(token);
|
|
2228
2522
|
try {
|
|
2229
2523
|
const git = (args) => executeFile("git", args, { cwd: directory, env, encoding: "utf8" });
|
|
@@ -2237,22 +2531,22 @@ var createCheckout = (executeFile, token) => async (source, ref, callback) => {
|
|
|
2237
2531
|
await git(["checkout", "--quiet", "--detach", "FETCH_HEAD"]);
|
|
2238
2532
|
return await callback(directory);
|
|
2239
2533
|
} finally {
|
|
2240
|
-
await
|
|
2534
|
+
await import_promises8.default.rm(directory, { recursive: true, force: true });
|
|
2241
2535
|
}
|
|
2242
2536
|
};
|
|
2243
2537
|
var generateIndex = async (source, ref, checkout, executeFile) => checkout(source, ref, async (directory) => {
|
|
2244
2538
|
const { stdout } = await executeFile("git", ["rev-parse", "HEAD"], { cwd: directory, encoding: "utf8" });
|
|
2245
2539
|
const commit = stdout.trim();
|
|
2246
|
-
const catalogDirectory =
|
|
2247
|
-
const relativeCatalogDirectory =
|
|
2248
|
-
if (relativeCatalogDirectory.startsWith("..") ||
|
|
2540
|
+
const catalogDirectory = import_node_path15.default.resolve(directory, source.path ?? ".");
|
|
2541
|
+
const relativeCatalogDirectory = import_node_path15.default.relative(directory, catalogDirectory);
|
|
2542
|
+
if (relativeCatalogDirectory.startsWith("..") || import_node_path15.default.isAbsolute(relativeCatalogDirectory)) {
|
|
2249
2543
|
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
2250
2544
|
}
|
|
2251
2545
|
const index = await (0, import_sdk2.default)(catalogDirectory).buildIndex({ source: source.id, commit });
|
|
2252
2546
|
return { bytes: Buffer.from(JSON.stringify(index)), index, commit, generated: true };
|
|
2253
2547
|
});
|
|
2254
2548
|
var fetchPublishedIndex = async (source, ref, fetcher, token) => {
|
|
2255
|
-
const indexPath =
|
|
2549
|
+
const indexPath = import_node_path15.default.posix.join(source.path ?? ".", "catalog.index.json");
|
|
2256
2550
|
const bytes = await fetchBytes(source, ref, indexPath, fetcher, token);
|
|
2257
2551
|
if (!bytes) return void 0;
|
|
2258
2552
|
const index = (0, import_sdk2.parseIndex)(JSON.parse(bytes.toString("utf8")));
|
|
@@ -2275,7 +2569,7 @@ var createGitHubSourceProvider = (options = {}) => {
|
|
|
2275
2569
|
},
|
|
2276
2570
|
async fetchContent({ source, commit, path: artifactPath }) {
|
|
2277
2571
|
assertSafeCatalogPath(source);
|
|
2278
|
-
const catalogPath =
|
|
2572
|
+
const catalogPath = import_node_path15.default.posix.join(source.path ?? ".", artifactPath);
|
|
2279
2573
|
const content = await fetchBytes(source, commit, catalogPath, fetcher, token);
|
|
2280
2574
|
if (!content) throw new Error(`Federated artifact not found for "${source.id}": ${artifactPath}`);
|
|
2281
2575
|
return content;
|
|
@@ -2313,6 +2607,16 @@ ${details}`);
|
|
|
2313
2607
|
this.conflicts = conflicts;
|
|
2314
2608
|
}
|
|
2315
2609
|
};
|
|
2610
|
+
var FederationDiagnosticError = class extends Error {
|
|
2611
|
+
diagnostics;
|
|
2612
|
+
constructor(diagnostics) {
|
|
2613
|
+
const details = diagnostics.map((diagnostic) => `${diagnostic.rule}: ${diagnostic.message}`).join("\n");
|
|
2614
|
+
super(`Federation diagnostics prevent hydration:
|
|
2615
|
+
${details}`);
|
|
2616
|
+
this.name = "FederationDiagnosticError";
|
|
2617
|
+
this.diagnostics = diagnostics;
|
|
2618
|
+
}
|
|
2619
|
+
};
|
|
2316
2620
|
var validateSources = (sources) => {
|
|
2317
2621
|
const ids = /* @__PURE__ */ new Set();
|
|
2318
2622
|
for (const source of sources) {
|
|
@@ -2321,19 +2625,46 @@ var validateSources = (sources) => {
|
|
|
2321
2625
|
ids.add(source.id);
|
|
2322
2626
|
}
|
|
2323
2627
|
};
|
|
2628
|
+
var createDiagnosticGraph = (graph, ownershipGraph, remoteSourceIds, localSourceId) => {
|
|
2629
|
+
const edges = ownershipGraph.edges.filter((edge) => remoteSourceIds.has(edge.fromResolvedFrom.source));
|
|
2630
|
+
const externalsByPointer = /* @__PURE__ */ new Map();
|
|
2631
|
+
for (const edge of edges.filter((edge2) => edge2.status === "external")) {
|
|
2632
|
+
const version2 = edge.pointer ?? void 0;
|
|
2633
|
+
const key = `${edge.to}@${version2 ?? ""}`;
|
|
2634
|
+
const external = externalsByPointer.get(key) ?? {
|
|
2635
|
+
id: edge.to,
|
|
2636
|
+
...version2 === void 0 ? {} : { version: version2 },
|
|
2637
|
+
referencedBy: []
|
|
2638
|
+
};
|
|
2639
|
+
if (!external.referencedBy.includes(edge.from)) external.referencedBy.push(edge.from);
|
|
2640
|
+
externalsByPointer.set(key, external);
|
|
2641
|
+
}
|
|
2642
|
+
return {
|
|
2643
|
+
...graph,
|
|
2644
|
+
entities: [
|
|
2645
|
+
...graph.entities,
|
|
2646
|
+
...ownershipGraph.entities.filter(
|
|
2647
|
+
(entity) => entity.resolvedFrom.source === localSourceId && entity.resolvedFrom.commit === "local"
|
|
2648
|
+
)
|
|
2649
|
+
],
|
|
2650
|
+
edges,
|
|
2651
|
+
conflicts: ownershipGraph.conflicts,
|
|
2652
|
+
externals: [...externalsByPointer.values()]
|
|
2653
|
+
};
|
|
2654
|
+
};
|
|
2324
2655
|
var writeLock = async (lockPath, lock) => {
|
|
2325
2656
|
const temporaryPath = `${lockPath}.tmp-${process.pid}`;
|
|
2326
2657
|
try {
|
|
2327
|
-
await
|
|
2658
|
+
await import_promises9.default.writeFile(temporaryPath, `${JSON.stringify(lock, null, 2)}
|
|
2328
2659
|
`, "utf8");
|
|
2329
|
-
await
|
|
2660
|
+
await import_promises9.default.rename(temporaryPath, lockPath);
|
|
2330
2661
|
} finally {
|
|
2331
|
-
await
|
|
2662
|
+
await import_promises9.default.rm(temporaryPath, { force: true });
|
|
2332
2663
|
}
|
|
2333
2664
|
};
|
|
2334
2665
|
var readLock = async (lockPath) => {
|
|
2335
2666
|
try {
|
|
2336
|
-
return JSON.parse(await
|
|
2667
|
+
return JSON.parse(await import_promises9.default.readFile(lockPath, "utf8"));
|
|
2337
2668
|
} catch (error) {
|
|
2338
2669
|
if (error.code === "ENOENT") return void 0;
|
|
2339
2670
|
throw new Error(`Cannot read federation lock at "${lockPath}"`, { cause: error });
|
|
@@ -2341,7 +2672,7 @@ var readLock = async (lockPath) => {
|
|
|
2341
2672
|
};
|
|
2342
2673
|
var pathExists2 = async (filePath) => {
|
|
2343
2674
|
try {
|
|
2344
|
-
await
|
|
2675
|
+
await import_promises9.default.access(filePath);
|
|
2345
2676
|
return true;
|
|
2346
2677
|
} catch (error) {
|
|
2347
2678
|
if (error.code === "ENOENT") return false;
|
|
@@ -2349,20 +2680,27 @@ var pathExists2 = async (filePath) => {
|
|
|
2349
2680
|
}
|
|
2350
2681
|
};
|
|
2351
2682
|
var cleanupPreviousFederation = async (projectDirectory, onProgress) => {
|
|
2352
|
-
const outDir =
|
|
2353
|
-
const lockPath =
|
|
2683
|
+
const outDir = import_node_path16.default.join(projectDirectory, "federated");
|
|
2684
|
+
const lockPath = import_node_path16.default.join(projectDirectory, "eventcatalog.lock");
|
|
2354
2685
|
const previousLock = await readLock(lockPath);
|
|
2355
2686
|
const hadFederatedOutput = await pathExists2(outDir);
|
|
2356
2687
|
const hadLock = previousLock !== void 0;
|
|
2357
2688
|
if (!hadFederatedOutput && !hadLock) return;
|
|
2358
|
-
|
|
2359
|
-
const publicResult = await composePublicAssets({
|
|
2689
|
+
const publicResult = await withFederationOutputTransaction(
|
|
2360
2690
|
projectDirectory,
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2691
|
+
outDir,
|
|
2692
|
+
Object.keys(previousLock?.publicFiles ?? {}),
|
|
2693
|
+
async () => {
|
|
2694
|
+
const result = await composePublicAssets({
|
|
2695
|
+
projectDirectory,
|
|
2696
|
+
federatedDirectory: outDir,
|
|
2697
|
+
assets: [],
|
|
2698
|
+
previousFiles: previousLock?.publicFiles
|
|
2699
|
+
});
|
|
2700
|
+
await import_promises9.default.rm(lockPath, { force: true });
|
|
2701
|
+
return result;
|
|
2702
|
+
}
|
|
2703
|
+
);
|
|
2366
2704
|
onProgress?.({
|
|
2367
2705
|
type: "cleanup:complete",
|
|
2368
2706
|
federated: hadFederatedOutput,
|
|
@@ -2378,12 +2716,13 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2378
2716
|
await cleanupPreviousFederation(projectDirectory, options.onProgress);
|
|
2379
2717
|
return null;
|
|
2380
2718
|
}
|
|
2381
|
-
if (
|
|
2719
|
+
if (!await isFederationEnabled()) {
|
|
2382
2720
|
throw new Error(
|
|
2383
2721
|
"Cannot federate catalogs: EventCatalog federation is an Enterprise feature. Visit https://www.eventcatalog.dev/pricing to enable federation."
|
|
2384
2722
|
);
|
|
2385
2723
|
}
|
|
2386
2724
|
validateSources(sources);
|
|
2725
|
+
const rules = resolveFederationRules(config.federation?.rules);
|
|
2387
2726
|
if (options.useCache === false) options.onProgress?.({ type: "cache:disabled" });
|
|
2388
2727
|
const provider = options.provider ?? createFederationSourceProvider(projectDirectory);
|
|
2389
2728
|
const resolvedSources = [];
|
|
@@ -2407,8 +2746,8 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2407
2746
|
throw new Error(`Failed to federate source "${source.id}": ${message}`, { cause: error });
|
|
2408
2747
|
}
|
|
2409
2748
|
}
|
|
2410
|
-
const outDir =
|
|
2411
|
-
const lockPath =
|
|
2749
|
+
const outDir = import_node_path16.default.join(projectDirectory, "federated");
|
|
2750
|
+
const lockPath = import_node_path16.default.join(projectDirectory, "eventcatalog.lock");
|
|
2412
2751
|
const previousLock = await readLock(lockPath);
|
|
2413
2752
|
const resources = resolvedSources.reduce((total, source) => total + source.resolved.index.resources.length, 0);
|
|
2414
2753
|
const remoteIndexes = resolvedSources.map(({ resolved }) => resolved.index);
|
|
@@ -2422,60 +2761,79 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2422
2761
|
options.onProgress?.({ type: "local:complete", resources: localIndex.resources.length });
|
|
2423
2762
|
options.onProgress?.({ type: "resolving", resources, localResources: localIndex.resources.length });
|
|
2424
2763
|
const ownershipGraph = (0, import_sdk3.resolve)([localIndex, ...remoteIndexes]);
|
|
2425
|
-
if (ownershipGraph.conflicts.length > 0) {
|
|
2426
|
-
options.onProgress?.({ type: "resolved", graph: ownershipGraph });
|
|
2427
|
-
throw new FederationConflictError(ownershipGraph.conflicts);
|
|
2428
|
-
}
|
|
2429
2764
|
const graph = (0, import_sdk3.resolve)(remoteIndexes);
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2765
|
+
const diagnosticGraph = createDiagnosticGraph(
|
|
2766
|
+
graph,
|
|
2767
|
+
ownershipGraph,
|
|
2768
|
+
new Set(remoteIndexes.map((index) => index.source)),
|
|
2769
|
+
localIndex.source
|
|
2770
|
+
);
|
|
2771
|
+
const diagnostics = getFederationDiagnostics(diagnosticGraph, rules);
|
|
2772
|
+
options.onProgress?.({ type: "resolved", graph, diagnostics });
|
|
2773
|
+
const blockingConflicts = ownershipGraph.conflicts.filter((conflict) => rules[`federation/${conflict.kind}`] === "error");
|
|
2774
|
+
if (blockingConflicts.length > 0) throw new FederationConflictError(blockingConflicts);
|
|
2775
|
+
const blockingDiagnostics = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
2776
|
+
if (blockingDiagnostics.length > 0) throw new FederationDiagnosticError(blockingDiagnostics);
|
|
2777
|
+
const publicPaths = [
|
|
2778
|
+
...Object.keys(previousLock?.publicFiles ?? {}),
|
|
2779
|
+
...graph.assets.filter((asset) => asset.path.startsWith("public/")).map((asset) => asset.path.slice("public/".length))
|
|
2780
|
+
];
|
|
2781
|
+
const { hydrateResult, publicResult } = await withFederationOutputTransaction(
|
|
2782
|
+
projectDirectory,
|
|
2436
2783
|
outDir,
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2784
|
+
publicPaths,
|
|
2785
|
+
async () => {
|
|
2786
|
+
const sourcesById = new Map(sources.map((source) => [source.id, source]));
|
|
2787
|
+
options.onProgress?.({ type: "hydrating", outDir });
|
|
2788
|
+
let hydratedFiles = 0;
|
|
2789
|
+
let cachedFiles = 0;
|
|
2790
|
+
const hydrateResult2 = await (0, import_sdk3.hydrate)(graph, {
|
|
2791
|
+
outDir,
|
|
2792
|
+
cache: createFederationContentCache(projectDirectory, {
|
|
2793
|
+
read: options.useCache !== false,
|
|
2794
|
+
onHit: () => {
|
|
2795
|
+
cachedFiles += 1;
|
|
2796
|
+
options.onProgress?.({ type: "hydrate:cache", files: cachedFiles });
|
|
2797
|
+
}
|
|
2798
|
+
}),
|
|
2799
|
+
fetch: async ({ source: sourceId, commit, path: artifactPath }) => {
|
|
2800
|
+
const source = sourcesById.get(sourceId);
|
|
2801
|
+
if (!source) throw new Error(`Cannot fetch content for unconfigured source "${sourceId}"`);
|
|
2802
|
+
const content = await provider.fetchContent({ source, commit, path: artifactPath });
|
|
2803
|
+
hydratedFiles += 1;
|
|
2804
|
+
options.onProgress?.({ type: "hydrate:file", files: hydratedFiles, source: sourceId, path: artifactPath });
|
|
2805
|
+
return content;
|
|
2806
|
+
}
|
|
2807
|
+
});
|
|
2808
|
+
const publicResult2 = await composePublicAssets({
|
|
2809
|
+
projectDirectory,
|
|
2810
|
+
federatedDirectory: outDir,
|
|
2811
|
+
assets: graph.assets,
|
|
2812
|
+
previousFiles: previousLock?.publicFiles,
|
|
2813
|
+
collisionPaths: new Set(
|
|
2814
|
+
graph.warnings.filter((warning) => warning.kind === "asset-collision").map((warning) => warning.path)
|
|
2815
|
+
)
|
|
2816
|
+
});
|
|
2817
|
+
options.onProgress?.({ type: "public:complete", result: publicResult2 });
|
|
2818
|
+
const resolvedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
2819
|
+
await writeLock(lockPath, {
|
|
2820
|
+
lockVersion: 1,
|
|
2821
|
+
sources: resolvedSources.map(({ config: source, resolved }) => ({
|
|
2822
|
+
id: source.id,
|
|
2823
|
+
digest: `sha256:${(0, import_node_crypto4.createHash)("sha256").update(resolved.bytes).digest("hex")}`,
|
|
2824
|
+
commit: resolved.commit,
|
|
2825
|
+
resolvedAt
|
|
2826
|
+
})).sort((left, right) => left.id.localeCompare(right.id)),
|
|
2827
|
+
publicFiles: publicResult2.files
|
|
2828
|
+
});
|
|
2829
|
+
return { hydrateResult: hydrateResult2, publicResult: publicResult2 };
|
|
2452
2830
|
}
|
|
2453
|
-
|
|
2454
|
-
const publicResult = await composePublicAssets({
|
|
2455
|
-
projectDirectory,
|
|
2456
|
-
federatedDirectory: outDir,
|
|
2457
|
-
assets: graph.assets,
|
|
2458
|
-
previousFiles: previousLock?.publicFiles,
|
|
2459
|
-
collisionPaths: new Set(
|
|
2460
|
-
graph.warnings.filter((warning) => warning.kind === "asset-collision").map((warning) => warning.path)
|
|
2461
|
-
)
|
|
2462
|
-
});
|
|
2463
|
-
options.onProgress?.({ type: "public:complete", result: publicResult });
|
|
2464
|
-
const resolvedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
2465
|
-
await writeLock(lockPath, {
|
|
2466
|
-
lockVersion: 1,
|
|
2467
|
-
sources: resolvedSources.map(({ config: source, resolved }) => ({
|
|
2468
|
-
id: source.id,
|
|
2469
|
-
digest: `sha256:${(0, import_node_crypto4.createHash)("sha256").update(resolved.bytes).digest("hex")}`,
|
|
2470
|
-
commit: resolved.commit,
|
|
2471
|
-
resolvedAt
|
|
2472
|
-
})).sort((left, right) => left.id.localeCompare(right.id)),
|
|
2473
|
-
publicFiles: publicResult.files
|
|
2474
|
-
});
|
|
2831
|
+
);
|
|
2475
2832
|
const result = {
|
|
2476
2833
|
sources: sources.length,
|
|
2477
2834
|
resources,
|
|
2478
2835
|
graph,
|
|
2836
|
+
diagnostics,
|
|
2479
2837
|
hydrate: hydrateResult,
|
|
2480
2838
|
public: publicResult,
|
|
2481
2839
|
outDir,
|
|
@@ -2486,15 +2844,15 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2486
2844
|
};
|
|
2487
2845
|
|
|
2488
2846
|
// src/eventcatalog.ts
|
|
2489
|
-
var
|
|
2490
|
-
var currentDir =
|
|
2847
|
+
var import_license2 = require("@eventcatalog/license");
|
|
2848
|
+
var currentDir = import_node_path18.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
2491
2849
|
var program = new import_commander.Command().version(VERSION);
|
|
2492
|
-
var dir =
|
|
2493
|
-
var core =
|
|
2494
|
-
var eventCatalogDir =
|
|
2850
|
+
var dir = import_node_path18.default.resolve(process.env.PROJECT_DIR || process.cwd());
|
|
2851
|
+
var core = import_node_path18.default.resolve(process.env.CATALOG_DIR || (0, import_node_path17.join)(dir, ".eventcatalog-core"));
|
|
2852
|
+
var eventCatalogDir = import_node_path18.default.resolve((0, import_node_path17.join)(currentDir, "../eventcatalog/"));
|
|
2495
2853
|
var getInstalledEventCatalogVersion = () => {
|
|
2496
2854
|
try {
|
|
2497
|
-
const pkg = import_fs3.default.readFileSync((0,
|
|
2855
|
+
const pkg = import_fs3.default.readFileSync((0, import_node_path17.join)(dir, "package.json"), "utf8");
|
|
2498
2856
|
const json = JSON.parse(pkg);
|
|
2499
2857
|
return json.dependencies["@eventcatalog/core"];
|
|
2500
2858
|
} catch (error) {
|
|
@@ -2560,12 +2918,12 @@ var startDevPrewarm = ({
|
|
|
2560
2918
|
var buildDevSearchIndex = async ({ config }) => {
|
|
2561
2919
|
const result = await buildSearchIndex({
|
|
2562
2920
|
projectDir: dir,
|
|
2563
|
-
outDir:
|
|
2564
|
-
searchOutputPath:
|
|
2921
|
+
outDir: import_node_path18.default.join(core, "public"),
|
|
2922
|
+
searchOutputPath: import_node_path18.default.join(core, "public", "pagefind"),
|
|
2565
2923
|
config,
|
|
2566
2924
|
isServer: false
|
|
2567
2925
|
});
|
|
2568
|
-
logger.info(`Indexed ${result.records} page(s) into ${
|
|
2926
|
+
logger.info(`Indexed ${result.records} page(s) into ${import_node_path18.default.relative(core, result.outputPath)}`, "search");
|
|
2569
2927
|
};
|
|
2570
2928
|
var warnIfIndexedSearchUsesAuth = async () => {
|
|
2571
2929
|
if (!await isAuthEnabled()) {
|
|
@@ -2680,12 +3038,12 @@ var copyCore = () => {
|
|
|
2680
3038
|
import_fs3.default.cpSync(eventCatalogDir, core, {
|
|
2681
3039
|
recursive: true,
|
|
2682
3040
|
filter: (src) => {
|
|
2683
|
-
const relativePath =
|
|
2684
|
-
const pathParts = relativePath.split(
|
|
3041
|
+
const relativePath = import_node_path18.default.relative(eventCatalogDir, src);
|
|
3042
|
+
const pathParts = relativePath.split(import_node_path18.default.sep);
|
|
2685
3043
|
return !pathParts.some((part) => [".astro", "dist", "node_modules"].includes(part));
|
|
2686
3044
|
}
|
|
2687
3045
|
});
|
|
2688
|
-
const coreNodeModules =
|
|
3046
|
+
const coreNodeModules = import_node_path18.default.join(core, "node_modules");
|
|
2689
3047
|
const installedCoreNodeModules = resolveInstalledCoreNodeModules(currentDir);
|
|
2690
3048
|
linkCoreNodeModules({ coreNodeModules, installedCoreNodeModules });
|
|
2691
3049
|
};
|
|
@@ -2744,8 +3102,8 @@ program.command("dev").description("Run development server of EventCatalog").opt
|
|
|
2744
3102
|
logger.info("Setting up EventCatalog...", "eventcatalog");
|
|
2745
3103
|
const isServer = await isOutputServer();
|
|
2746
3104
|
logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
|
|
2747
|
-
if (import_fs3.default.existsSync(
|
|
2748
|
-
import_dotenv.default.config({ path:
|
|
3105
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3106
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2749
3107
|
}
|
|
2750
3108
|
if (options.debug) {
|
|
2751
3109
|
logger.info("Debug mode enabled", "debug");
|
|
@@ -2759,12 +3117,12 @@ program.command("dev").description("Run development server of EventCatalog").opt
|
|
|
2759
3117
|
await runMigrations(dir);
|
|
2760
3118
|
await catalogToAstro(dir, core);
|
|
2761
3119
|
const config = await getEventCatalogConfigFile(dir);
|
|
2762
|
-
const canEmbedPages = await (0,
|
|
3120
|
+
const canEmbedPages = await (0, import_license2.isFeatureEnabled)(
|
|
2763
3121
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
2764
3122
|
process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
|
|
2765
3123
|
);
|
|
2766
|
-
const isEventCatalogStarter = await (0,
|
|
2767
|
-
const isEventCatalogScale = await (0,
|
|
3124
|
+
const isEventCatalogStarter = await (0, import_license2.isEventCatalogStarterEnabled)();
|
|
3125
|
+
const isEventCatalogScale = await (0, import_license2.isEventCatalogScaleEnabled)();
|
|
2768
3126
|
if (isServer) {
|
|
2769
3127
|
try {
|
|
2770
3128
|
logger.info("Building fields index...", "fields");
|
|
@@ -2825,17 +3183,17 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2825
3183
|
logger.info("Building EventCatalog...", "build");
|
|
2826
3184
|
const isServer = await isOutputServer();
|
|
2827
3185
|
logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
|
|
2828
|
-
if (import_fs3.default.existsSync(
|
|
2829
|
-
import_dotenv.default.config({ path:
|
|
3186
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3187
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2830
3188
|
}
|
|
2831
3189
|
await verifyRequiredFieldsAreInCatalogConfigFile(dir);
|
|
2832
3190
|
copyCore();
|
|
2833
|
-
const isBackstagePluginEnabled = await (0,
|
|
3191
|
+
const isBackstagePluginEnabled = await (0, import_license2.isFeatureEnabled)(
|
|
2834
3192
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
2835
3193
|
process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
|
|
2836
3194
|
);
|
|
2837
|
-
const isEventCatalogStarter = await (0,
|
|
2838
|
-
const isEventCatalogScale = await (0,
|
|
3195
|
+
const isEventCatalogStarter = await (0, import_license2.isEventCatalogStarterEnabled)();
|
|
3196
|
+
const isEventCatalogScale = await (0, import_license2.isEventCatalogScaleEnabled)();
|
|
2839
3197
|
const canEmbedPages = isBackstagePluginEnabled || isEventCatalogScale;
|
|
2840
3198
|
await log_build_default(dir, {
|
|
2841
3199
|
isEventCatalogStarterEnabled: isEventCatalogStarter,
|
|
@@ -2876,7 +3234,7 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2876
3234
|
if (await isIndexedSearchEnabled()) {
|
|
2877
3235
|
await warnIfIndexedSearchUsesAuth();
|
|
2878
3236
|
const config = await getEventCatalogConfigFile(dir);
|
|
2879
|
-
const outDir =
|
|
3237
|
+
const outDir = import_node_path18.default.resolve(dir, await getProjectOutDir());
|
|
2880
3238
|
logger.info("Building indexed search...", "search");
|
|
2881
3239
|
const result = await buildSearchIndex({
|
|
2882
3240
|
projectDir: dir,
|
|
@@ -2884,7 +3242,7 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2884
3242
|
config,
|
|
2885
3243
|
isServer
|
|
2886
3244
|
});
|
|
2887
|
-
logger.info(`Indexed ${result.records} page(s) into ${
|
|
3245
|
+
logger.info(`Indexed ${result.records} page(s) into ${import_node_path18.default.relative(dir, result.outputPath)}`, "search");
|
|
2888
3246
|
}
|
|
2889
3247
|
});
|
|
2890
3248
|
var previewCatalog = async ({
|
|
@@ -2911,7 +3269,7 @@ var startServerCatalog = async ({
|
|
|
2911
3269
|
isEventCatalogStarter = false,
|
|
2912
3270
|
isEventCatalogScale = false
|
|
2913
3271
|
}) => {
|
|
2914
|
-
const serverEntryPath =
|
|
3272
|
+
const serverEntryPath = import_node_path18.default.join(dir, "dist", "server", "entry.mjs");
|
|
2915
3273
|
await runCommandWithFilteredOutput({
|
|
2916
3274
|
command: `node "${serverEntryPath}"`,
|
|
2917
3275
|
cwd: core,
|
|
@@ -2928,15 +3286,15 @@ var startServerCatalog = async ({
|
|
|
2928
3286
|
program.command("preview").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
2929
3287
|
logger.welcome();
|
|
2930
3288
|
logger.info("Starting preview of your build...", "preview");
|
|
2931
|
-
if (import_fs3.default.existsSync(
|
|
2932
|
-
import_dotenv.default.config({ path:
|
|
3289
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3290
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2933
3291
|
}
|
|
2934
|
-
const canEmbedPages = await (0,
|
|
3292
|
+
const canEmbedPages = await (0, import_license2.isFeatureEnabled)(
|
|
2935
3293
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
2936
3294
|
process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
|
|
2937
3295
|
);
|
|
2938
|
-
const isEventCatalogStarter = await (0,
|
|
2939
|
-
const isEventCatalogScale = await (0,
|
|
3296
|
+
const isEventCatalogStarter = await (0, import_license2.isEventCatalogStarterEnabled)();
|
|
3297
|
+
const isEventCatalogScale = await (0, import_license2.isEventCatalogScaleEnabled)();
|
|
2940
3298
|
await previewCatalog({
|
|
2941
3299
|
command,
|
|
2942
3300
|
canEmbedPages: canEmbedPages || isEventCatalogScale,
|
|
@@ -2947,15 +3305,15 @@ program.command("preview").description("Serves the contents of your eventcatalog
|
|
|
2947
3305
|
program.command("start").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
2948
3306
|
logger.welcome();
|
|
2949
3307
|
logger.info("Starting preview of your build...", "preview");
|
|
2950
|
-
if (import_fs3.default.existsSync(
|
|
2951
|
-
import_dotenv.default.config({ path:
|
|
3308
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3309
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2952
3310
|
}
|
|
2953
|
-
const canEmbedPages = await (0,
|
|
3311
|
+
const canEmbedPages = await (0, import_license2.isFeatureEnabled)(
|
|
2954
3312
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
2955
3313
|
process.env.EVENTCATALOG_LICENSE_KEY_BACKSTAGE
|
|
2956
3314
|
);
|
|
2957
|
-
const isEventCatalogStarter = await (0,
|
|
2958
|
-
const isEventCatalogScale = await (0,
|
|
3315
|
+
const isEventCatalogStarter = await (0, import_license2.isEventCatalogStarterEnabled)();
|
|
3316
|
+
const isEventCatalogScale = await (0, import_license2.isEventCatalogScaleEnabled)();
|
|
2959
3317
|
const isServerOutput = await isOutputServer();
|
|
2960
3318
|
if (isServerOutput) {
|
|
2961
3319
|
await startServerCatalog({
|
|
@@ -2975,26 +3333,26 @@ program.command("start").description("Serves the contents of your eventcatalog b
|
|
|
2975
3333
|
program.command("export").description("Export your EventCatalog using the SDK dumpCatalog function").option("--include-markdown", "Include markdown content in the export", false).action(async (options) => {
|
|
2976
3334
|
logger.welcome();
|
|
2977
3335
|
logger.info("Exporting EventCatalog...", "export");
|
|
2978
|
-
if (import_fs3.default.existsSync(
|
|
2979
|
-
import_dotenv.default.config({ path:
|
|
3336
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3337
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2980
3338
|
}
|
|
2981
3339
|
const { default: initSDK } = await import("@eventcatalog/sdk");
|
|
2982
3340
|
const sdk = initSDK(dir);
|
|
2983
3341
|
const catalog = await sdk.dumpCatalog({ includeMarkdown: options.includeMarkdown });
|
|
2984
|
-
const exportsDir =
|
|
3342
|
+
const exportsDir = import_node_path18.default.join(dir, "exports");
|
|
2985
3343
|
ensureDir(exportsDir);
|
|
2986
3344
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2987
|
-
const exportFile =
|
|
3345
|
+
const exportFile = import_node_path18.default.join(exportsDir, `catalog-${date}.json`);
|
|
2988
3346
|
import_fs3.default.writeFileSync(exportFile, JSON.stringify(catalog, null, 2), "utf-8");
|
|
2989
3347
|
logger.info(`Catalog exported to ${exportFile}`, "export");
|
|
2990
3348
|
});
|
|
2991
3349
|
program.command("generate [siteDir]").description("Start the generator scripts.").action(async () => {
|
|
2992
|
-
if (import_fs3.default.existsSync(
|
|
2993
|
-
import_dotenv.default.config({ path:
|
|
3350
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3351
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2994
3352
|
}
|
|
2995
3353
|
await generate(dir);
|
|
2996
3354
|
});
|
|
2997
|
-
var reportFederationProgress = (event) => {
|
|
3355
|
+
var reportFederationProgress = (event, verbose = false) => {
|
|
2998
3356
|
switch (event.type) {
|
|
2999
3357
|
case "configured":
|
|
3000
3358
|
if (event.sources > 0)
|
|
@@ -3034,38 +3392,36 @@ var reportFederationProgress = (event) => {
|
|
|
3034
3392
|
);
|
|
3035
3393
|
return;
|
|
3036
3394
|
case "resolved":
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3395
|
+
const counts = getFederationDiagnosticCounts(event.diagnostics);
|
|
3396
|
+
const showDiagnosticDetails = verbose || counts.errors > 0;
|
|
3397
|
+
if (counts.errors === 0) {
|
|
3398
|
+
logger.success(
|
|
3399
|
+
`Graph resolved: ${event.graph.entities.length} remote resources, ${event.graph.edges.length} relationships`,
|
|
3040
3400
|
"federation"
|
|
3041
3401
|
);
|
|
3042
|
-
for (const conflict of event.graph.conflicts.slice(0, 10)) {
|
|
3043
|
-
logger.error(`${conflict.id}: ${conflict.sources.join(", ")}`, "federation");
|
|
3044
|
-
}
|
|
3045
|
-
if (event.graph.conflicts.length > 10) {
|
|
3046
|
-
logger.error(`...and ${event.graph.conflicts.length - 10} more`, "federation");
|
|
3047
|
-
}
|
|
3048
|
-
return;
|
|
3049
3402
|
}
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3403
|
+
if (counts.errors + counts.warnings === 0) return;
|
|
3404
|
+
if (showDiagnosticDetails) {
|
|
3405
|
+
const diagnostics = getVisibleFederationDiagnostics(event.diagnostics, verbose);
|
|
3406
|
+
logger.info("Federation diagnostics", "federation");
|
|
3407
|
+
logger.line();
|
|
3408
|
+
for (const diagnostic of diagnostics) {
|
|
3409
|
+
logger.diagnostic(diagnostic.severity, diagnostic.message, diagnostic.rule, diagnostic.attributes);
|
|
3410
|
+
logger.line();
|
|
3411
|
+
}
|
|
3412
|
+
logger.diagnosticSummary(counts.errors, counts.warnings);
|
|
3413
|
+
} else {
|
|
3055
3414
|
logger.warning(
|
|
3056
|
-
`${
|
|
3415
|
+
`${counts.warnings} problem${counts.warnings === 1 ? "" : "s"} (0 errors, ${counts.warnings} warning${counts.warnings === 1 ? "" : "s"})`,
|
|
3057
3416
|
"federation"
|
|
3058
3417
|
);
|
|
3059
3418
|
}
|
|
3060
|
-
if (
|
|
3061
|
-
logger.warning
|
|
3062
|
-
`${event.graph.externals.length} reference${event.graph.externals.length === 1 ? "" : "s"} remain external`,
|
|
3063
|
-
"federation"
|
|
3064
|
-
);
|
|
3419
|
+
if (!verbose && counts.warnings > 0) {
|
|
3420
|
+
logger.info("Run with --verbose to see warning details.", "federation");
|
|
3065
3421
|
}
|
|
3066
3422
|
return;
|
|
3067
3423
|
case "hydrating":
|
|
3068
|
-
logger.info(`Hydrating federated content into ${
|
|
3424
|
+
logger.info(`Hydrating federated content into ${import_node_path18.default.relative(dir, event.outDir)}/...`, "federation");
|
|
3069
3425
|
return;
|
|
3070
3426
|
case "hydrate:cache":
|
|
3071
3427
|
if (event.files === 1 || event.files % 25 === 0) {
|
|
@@ -3082,34 +3438,27 @@ var reportFederationProgress = (event) => {
|
|
|
3082
3438
|
`Public assets: ${event.result.copied} copied, ${event.result.skipped} preserved from the main catalog, ${event.result.removed} stale removed`,
|
|
3083
3439
|
"federation"
|
|
3084
3440
|
);
|
|
3085
|
-
if (event.result.overwritten > 0) {
|
|
3086
|
-
logger.warning(
|
|
3087
|
-
`${event.result.overwritten} public asset conflict${event.result.overwritten === 1 ? "" : "s"} resolved using the last configured source`,
|
|
3088
|
-
"federation"
|
|
3089
|
-
);
|
|
3090
|
-
}
|
|
3091
3441
|
return;
|
|
3092
3442
|
case "complete":
|
|
3093
3443
|
logger.success(
|
|
3094
3444
|
`Federation complete: ${event.result.sources} sources, ${event.result.resources} remote resources, ${event.result.hydrate.written} files written (${event.result.hydrate.fetched} downloaded, ${event.result.hydrate.written - event.result.hydrate.fetched} cached)`,
|
|
3095
3445
|
"federation"
|
|
3096
3446
|
);
|
|
3097
|
-
logger.info(`
|
|
3447
|
+
logger.info(`Recorded resolved source state in ${import_node_path18.default.relative(dir, event.result.lockPath)}`, "federation");
|
|
3098
3448
|
}
|
|
3099
3449
|
};
|
|
3100
|
-
program.command("federate").description("Fetch, resolve, and hydrate the catalogs configured in federation.sources.").option("--no-cache", "Download all federation content and refresh the cache.").action(async (commandOptions) => {
|
|
3450
|
+
program.command("federate").description("Fetch, resolve, and hydrate the catalogs configured in federation.sources.").option("--no-cache", "Download all federation content and refresh the cache.").option("-v, --verbose", "Show detailed federation diagnostics.").action(async (commandOptions) => {
|
|
3101
3451
|
logger.welcome();
|
|
3102
|
-
if (import_fs3.default.existsSync(
|
|
3103
|
-
import_dotenv.default.config({ path:
|
|
3452
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3453
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
3104
3454
|
}
|
|
3105
3455
|
logger.info("Starting federation...", "federation");
|
|
3106
3456
|
let cleanedPreviousOutput = false;
|
|
3107
3457
|
const result = await federateCatalog(dir, {
|
|
3108
3458
|
useCache: commandOptions.cache,
|
|
3109
|
-
isFederationEnabled: import_license.isEventCatalogScaleEnabled,
|
|
3110
3459
|
onProgress: (event) => {
|
|
3111
3460
|
if (event.type === "cleanup:complete") cleanedPreviousOutput = true;
|
|
3112
|
-
reportFederationProgress(event);
|
|
3461
|
+
reportFederationProgress(event, commandOptions.verbose);
|
|
3113
3462
|
}
|
|
3114
3463
|
});
|
|
3115
3464
|
if (!result && !cleanedPreviousOutput) logger.warning("No federation sources configured; nothing to do.", "federation");
|
|
@@ -3123,6 +3472,6 @@ program.addHelpText(
|
|
|
3123
3472
|
`
|
|
3124
3473
|
);
|
|
3125
3474
|
program.parseAsync().then(() => process.exit(0)).catch((err) => {
|
|
3126
|
-
if (!(err instanceof FederationConflictError)) console.error(err);
|
|
3475
|
+
if (!(err instanceof FederationConflictError) && !(err instanceof FederationDiagnosticError)) console.error(err);
|
|
3127
3476
|
process.exit(1);
|
|
3128
3477
|
});
|