@eventcatalog/core 4.7.4 → 4.7.5
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-FDXJIZ74.js → chunk-2BYTIH4L.js} +1 -1
- package/dist/{chunk-7QTCSRAC.js → chunk-EUZT4RKY.js} +1 -1
- package/dist/{chunk-RIUHZZRA.js → chunk-GYYYAUJD.js} +128 -56
- package/dist/chunk-JJPB6EOZ.js +159 -0
- package/dist/{chunk-ZIABX6SP.js → chunk-QJRY6Q7S.js} +1 -1
- package/dist/{chunk-ZXVQXBTT.js → chunk-QQPH6RCB.js} +24 -1
- package/dist/chunk-WRNH5C3U.js +118 -0
- package/dist/{chunk-TLWM7WRZ.js → chunk-ZBPMCEXS.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +540 -194
- package/dist/eventcatalog.config.d.cts +9 -3
- package/dist/eventcatalog.config.d.ts +9 -3
- package/dist/eventcatalog.js +37 -39
- package/dist/federation/diagnostics.cjs +187 -0
- package/dist/federation/diagnostics.d.cts +23 -0
- package/dist/federation/diagnostics.d.ts +23 -0
- package/dist/federation/diagnostics.js +14 -0
- package/dist/federation/federate.cjs +442 -117
- package/dist/federation/federate.d.cts +8 -1
- package/dist/federation/federate.d.ts +8 -1
- package/dist/federation/federate.js +6 -2
- package/dist/federation/output-transaction.cjs +152 -0
- package/dist/federation/output-transaction.d.cts +3 -0
- package/dist/federation/output-transaction.d.ts +3 -0
- package/dist/federation/output-transaction.js +6 -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 +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.5";
|
|
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
|
}
|
|
@@ -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,295 @@ 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/output-transaction.ts
|
|
1951
2125
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
1952
2126
|
var import_node_path12 = __toESM(require("path"), 1);
|
|
2127
|
+
var getSafePublicPath = (publicDirectory, relativePath) => {
|
|
2128
|
+
const normalizedPath = import_node_path12.default.posix.normalize(relativePath);
|
|
2129
|
+
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("../");
|
|
2130
|
+
if (unsafe) return void 0;
|
|
2131
|
+
const destinationPath = import_node_path12.default.resolve(publicDirectory, relativePath);
|
|
2132
|
+
const relativeDestinationPath = import_node_path12.default.relative(publicDirectory, destinationPath);
|
|
2133
|
+
return relativeDestinationPath !== "" && relativeDestinationPath !== ".." && !relativeDestinationPath.startsWith(`..${import_node_path12.default.sep}`) && !import_node_path12.default.isAbsolute(relativeDestinationPath) ? destinationPath : void 0;
|
|
2134
|
+
};
|
|
2135
|
+
var beginFederationOutputTransaction = async (projectDirectory, outDir, relativePublicPaths) => {
|
|
2136
|
+
const transactionDirectory = await import_promises5.default.mkdtemp(import_node_path12.default.join(projectDirectory, ".eventcatalog-federation-transaction-"));
|
|
2137
|
+
const previousOutDir = import_node_path12.default.join(transactionDirectory, "federated");
|
|
2138
|
+
const publicBackupDirectory = import_node_path12.default.join(transactionDirectory, "public");
|
|
2139
|
+
const publicDirectory = import_node_path12.default.resolve(projectDirectory, "public");
|
|
2140
|
+
const publicSnapshots = [];
|
|
2141
|
+
const missingPublicDirectories = /* @__PURE__ */ new Set();
|
|
2142
|
+
let hadPreviousOutDir = false;
|
|
2143
|
+
try {
|
|
2144
|
+
for (const relativePath of new Set(relativePublicPaths)) {
|
|
2145
|
+
const destinationPath = getSafePublicPath(publicDirectory, relativePath);
|
|
2146
|
+
if (!destinationPath) continue;
|
|
2147
|
+
try {
|
|
2148
|
+
const stat = await import_promises5.default.lstat(destinationPath);
|
|
2149
|
+
if (!stat.isFile()) continue;
|
|
2150
|
+
const backupPath = import_node_path12.default.join(publicBackupDirectory, `${publicSnapshots.length}`);
|
|
2151
|
+
await import_promises5.default.mkdir(import_node_path12.default.dirname(backupPath), { recursive: true });
|
|
2152
|
+
await import_promises5.default.copyFile(destinationPath, backupPath);
|
|
2153
|
+
publicSnapshots.push({ backupPath, destinationPath, state: "file" });
|
|
2154
|
+
} catch (error) {
|
|
2155
|
+
const code = error.code;
|
|
2156
|
+
if (code === "ENOTDIR") continue;
|
|
2157
|
+
if (code !== "ENOENT") throw error;
|
|
2158
|
+
publicSnapshots.push({ destinationPath, state: "missing" });
|
|
2159
|
+
let directory = import_node_path12.default.dirname(destinationPath);
|
|
2160
|
+
while (directory !== publicDirectory) {
|
|
2161
|
+
try {
|
|
2162
|
+
await import_promises5.default.lstat(directory);
|
|
2163
|
+
break;
|
|
2164
|
+
} catch (directoryError) {
|
|
2165
|
+
const directoryCode = directoryError.code;
|
|
2166
|
+
if (directoryCode === "ENOTDIR") break;
|
|
2167
|
+
if (directoryCode !== "ENOENT") throw directoryError;
|
|
2168
|
+
missingPublicDirectories.add(directory);
|
|
2169
|
+
directory = import_node_path12.default.dirname(directory);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
try {
|
|
2175
|
+
await import_promises5.default.rename(outDir, previousOutDir);
|
|
2176
|
+
hadPreviousOutDir = true;
|
|
2177
|
+
} catch (error) {
|
|
2178
|
+
if (error.code !== "ENOENT") throw error;
|
|
2179
|
+
}
|
|
2180
|
+
} catch (error) {
|
|
2181
|
+
await import_promises5.default.rm(transactionDirectory, { recursive: true, force: true });
|
|
2182
|
+
throw error;
|
|
2183
|
+
}
|
|
2184
|
+
return {
|
|
2185
|
+
commit: async () => {
|
|
2186
|
+
await import_promises5.default.rm(transactionDirectory, { recursive: true, force: true }).catch(() => void 0);
|
|
2187
|
+
},
|
|
2188
|
+
rollback: async () => {
|
|
2189
|
+
const rollbackErrors = [];
|
|
2190
|
+
const attempt = async (operation) => {
|
|
2191
|
+
try {
|
|
2192
|
+
await operation();
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
rollbackErrors.push(error);
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
await attempt(() => import_promises5.default.rm(outDir, { recursive: true, force: true }));
|
|
2198
|
+
if (hadPreviousOutDir) await attempt(() => import_promises5.default.rename(previousOutDir, outDir));
|
|
2199
|
+
for (const snapshot of publicSnapshots) {
|
|
2200
|
+
if (snapshot.state === "missing") {
|
|
2201
|
+
await attempt(() => import_promises5.default.rm(snapshot.destinationPath, { force: true }));
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
await attempt(async () => {
|
|
2205
|
+
await import_promises5.default.mkdir(import_node_path12.default.dirname(snapshot.destinationPath), { recursive: true });
|
|
2206
|
+
await import_promises5.default.copyFile(snapshot.backupPath, snapshot.destinationPath);
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
for (const directory of [...missingPublicDirectories].sort((left, right) => right.length - left.length)) {
|
|
2210
|
+
await attempt(async () => {
|
|
2211
|
+
try {
|
|
2212
|
+
await import_promises5.default.rmdir(directory);
|
|
2213
|
+
} catch (error) {
|
|
2214
|
+
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
|
|
2215
|
+
}
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
await attempt(() => import_promises5.default.rm(transactionDirectory, { recursive: true, force: true }));
|
|
2219
|
+
if (rollbackErrors.length > 0) throw new AggregateError(rollbackErrors, "Failed to restore the previous federation output");
|
|
2220
|
+
}
|
|
2221
|
+
};
|
|
2222
|
+
};
|
|
2223
|
+
var withFederationOutputTransaction = async (projectDirectory, outDir, relativePublicPaths, update) => {
|
|
2224
|
+
const transaction = await beginFederationOutputTransaction(projectDirectory, outDir, relativePublicPaths);
|
|
2225
|
+
try {
|
|
2226
|
+
const result = await update();
|
|
2227
|
+
await transaction.commit();
|
|
2228
|
+
return result;
|
|
2229
|
+
} catch (error) {
|
|
2230
|
+
try {
|
|
2231
|
+
await transaction.rollback();
|
|
2232
|
+
} catch (rollbackError) {
|
|
2233
|
+
throw new AggregateError([error, rollbackError], "Federation failed and the previous output could not be restored");
|
|
2234
|
+
}
|
|
2235
|
+
throw error;
|
|
2236
|
+
}
|
|
2237
|
+
};
|
|
2238
|
+
|
|
2239
|
+
// src/federation/public-assets.ts
|
|
2240
|
+
var import_node_crypto2 = require("crypto");
|
|
2241
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
2242
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
1953
2243
|
var getContentHash2 = (content) => `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex")}`;
|
|
1954
2244
|
var getFileHash = async (filePath) => {
|
|
1955
2245
|
try {
|
|
1956
|
-
const stat = await
|
|
1957
|
-
return stat.isFile() ? getContentHash2(await
|
|
2246
|
+
const stat = await import_promises6.default.lstat(filePath);
|
|
2247
|
+
return stat.isFile() ? getContentHash2(await import_promises6.default.readFile(filePath)) : void 0;
|
|
1958
2248
|
} catch (error) {
|
|
1959
2249
|
if (error.code === "ENOENT") return void 0;
|
|
1960
2250
|
throw error;
|
|
1961
2251
|
}
|
|
1962
2252
|
};
|
|
1963
2253
|
var getSafePath = (directory, relativePath) => {
|
|
1964
|
-
const normalizedPath =
|
|
1965
|
-
const unsafe = relativePath.length === 0 || relativePath.includes("\0") || relativePath.includes("\\") ||
|
|
2254
|
+
const normalizedPath = import_node_path13.default.posix.normalize(relativePath);
|
|
2255
|
+
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
2256
|
if (unsafe) return void 0;
|
|
1967
|
-
const resolvedDirectory =
|
|
1968
|
-
const resolvedPath =
|
|
1969
|
-
const relativeResolvedPath =
|
|
1970
|
-
if (relativeResolvedPath === "" || relativeResolvedPath === ".." || relativeResolvedPath.startsWith(`..${
|
|
2257
|
+
const resolvedDirectory = import_node_path13.default.resolve(directory);
|
|
2258
|
+
const resolvedPath = import_node_path13.default.resolve(resolvedDirectory, relativePath);
|
|
2259
|
+
const relativeResolvedPath = import_node_path13.default.relative(resolvedDirectory, resolvedPath);
|
|
2260
|
+
if (relativeResolvedPath === "" || relativeResolvedPath === ".." || relativeResolvedPath.startsWith(`..${import_node_path13.default.sep}`) || import_node_path13.default.isAbsolute(relativeResolvedPath)) {
|
|
1971
2261
|
return void 0;
|
|
1972
2262
|
}
|
|
1973
2263
|
return resolvedPath;
|
|
@@ -1975,23 +2265,23 @@ var getSafePath = (directory, relativePath) => {
|
|
|
1975
2265
|
var listFiles = async (directory, relativeDirectory = "") => {
|
|
1976
2266
|
let entries;
|
|
1977
2267
|
try {
|
|
1978
|
-
entries = await
|
|
2268
|
+
entries = await import_promises6.default.readdir(import_node_path13.default.join(directory, relativeDirectory), { withFileTypes: true });
|
|
1979
2269
|
} catch (error) {
|
|
1980
2270
|
if (error.code === "ENOENT") return [];
|
|
1981
2271
|
throw error;
|
|
1982
2272
|
}
|
|
1983
2273
|
const files = await Promise.all(
|
|
1984
2274
|
entries.map(async (entry) => {
|
|
1985
|
-
const relativePath =
|
|
2275
|
+
const relativePath = import_node_path13.default.join(relativeDirectory, entry.name);
|
|
1986
2276
|
if (entry.isDirectory()) return listFiles(directory, relativePath);
|
|
1987
|
-
return entry.isFile() ? [relativePath.split(
|
|
2277
|
+
return entry.isFile() ? [relativePath.split(import_node_path13.default.sep).join("/")] : [];
|
|
1988
2278
|
})
|
|
1989
2279
|
);
|
|
1990
2280
|
return files.flat().sort();
|
|
1991
2281
|
};
|
|
1992
2282
|
var pathExists = async (filePath) => {
|
|
1993
2283
|
try {
|
|
1994
|
-
await
|
|
2284
|
+
await import_promises6.default.lstat(filePath);
|
|
1995
2285
|
return true;
|
|
1996
2286
|
} catch (error) {
|
|
1997
2287
|
if (error.code === "ENOENT") return false;
|
|
@@ -1999,27 +2289,27 @@ var pathExists = async (filePath) => {
|
|
|
1999
2289
|
}
|
|
2000
2290
|
};
|
|
2001
2291
|
var hasBlockingParent = async (publicDirectory, destinationPath) => {
|
|
2002
|
-
let currentPath =
|
|
2292
|
+
let currentPath = import_node_path13.default.dirname(destinationPath);
|
|
2003
2293
|
while (currentPath !== publicDirectory) {
|
|
2004
2294
|
try {
|
|
2005
|
-
if (!(await
|
|
2295
|
+
if (!(await import_promises6.default.lstat(currentPath)).isDirectory()) return true;
|
|
2006
2296
|
} catch (error) {
|
|
2007
2297
|
if (error.code !== "ENOENT") throw error;
|
|
2008
2298
|
}
|
|
2009
|
-
currentPath =
|
|
2299
|
+
currentPath = import_node_path13.default.dirname(currentPath);
|
|
2010
2300
|
}
|
|
2011
2301
|
return false;
|
|
2012
2302
|
};
|
|
2013
2303
|
var pruneEmptyDirectories = async (publicDirectory, filePath) => {
|
|
2014
|
-
let currentPath =
|
|
2304
|
+
let currentPath = import_node_path13.default.dirname(filePath);
|
|
2015
2305
|
while (currentPath !== publicDirectory) {
|
|
2016
2306
|
try {
|
|
2017
|
-
await
|
|
2307
|
+
await import_promises6.default.rmdir(currentPath);
|
|
2018
2308
|
} catch (error) {
|
|
2019
2309
|
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
|
|
2020
2310
|
if (error.code === "ENOTEMPTY") return;
|
|
2021
2311
|
}
|
|
2022
|
-
currentPath =
|
|
2312
|
+
currentPath = import_node_path13.default.dirname(currentPath);
|
|
2023
2313
|
}
|
|
2024
2314
|
};
|
|
2025
2315
|
var composePublicAssets = async ({
|
|
@@ -2029,8 +2319,8 @@ var composePublicAssets = async ({
|
|
|
2029
2319
|
previousFiles = {},
|
|
2030
2320
|
collisionPaths = /* @__PURE__ */ new Set()
|
|
2031
2321
|
}) => {
|
|
2032
|
-
const publicDirectory =
|
|
2033
|
-
const federatedPublicDirectory =
|
|
2322
|
+
const publicDirectory = import_node_path13.default.resolve(projectDirectory, "public");
|
|
2323
|
+
const federatedPublicDirectory = import_node_path13.default.resolve(federatedDirectory, "public");
|
|
2034
2324
|
const sourceFiles = await listFiles(federatedPublicDirectory);
|
|
2035
2325
|
const sourceFileSet = new Set(sourceFiles);
|
|
2036
2326
|
const managedFiles = /* @__PURE__ */ new Set();
|
|
@@ -2043,7 +2333,7 @@ var composePublicAssets = async ({
|
|
|
2043
2333
|
if (sourceFileSet.has(relativePath)) continue;
|
|
2044
2334
|
const destinationPath = getSafePath(publicDirectory, relativePath);
|
|
2045
2335
|
if (!destinationPath) continue;
|
|
2046
|
-
await
|
|
2336
|
+
await import_promises6.default.rm(destinationPath, { force: true });
|
|
2047
2337
|
await pruneEmptyDirectories(publicDirectory, destinationPath);
|
|
2048
2338
|
removed += 1;
|
|
2049
2339
|
}
|
|
@@ -2065,31 +2355,31 @@ var composePublicAssets = async ({
|
|
|
2065
2355
|
}
|
|
2066
2356
|
const asset = publicAssetsByPath.get(relativePath);
|
|
2067
2357
|
if (!asset) throw new Error(`Cannot identify the source of federated public asset "${relativePath}"`);
|
|
2068
|
-
const content = await
|
|
2069
|
-
await
|
|
2070
|
-
await
|
|
2358
|
+
const content = await import_promises6.default.readFile(sourcePath);
|
|
2359
|
+
await import_promises6.default.mkdir(import_node_path13.default.dirname(destinationPath), { recursive: true });
|
|
2360
|
+
await import_promises6.default.writeFile(destinationPath, content);
|
|
2071
2361
|
files[relativePath] = { source: asset.resolvedFrom.source, hash: getContentHash2(content) };
|
|
2072
2362
|
copied += 1;
|
|
2073
2363
|
if (collisionPaths.has(`public/${relativePath}`)) overwritten += 1;
|
|
2074
2364
|
}
|
|
2075
|
-
await
|
|
2365
|
+
await import_promises6.default.rm(federatedPublicDirectory, { recursive: true, force: true });
|
|
2076
2366
|
return { files, copied, skipped, overwritten, removed };
|
|
2077
2367
|
};
|
|
2078
2368
|
|
|
2079
2369
|
// src/federation/filesystem-source-provider.ts
|
|
2080
2370
|
var import_node_crypto3 = require("crypto");
|
|
2081
|
-
var
|
|
2082
|
-
var
|
|
2371
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
2372
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
2083
2373
|
var import_sdk = __toESM(require("@eventcatalog/sdk"), 1);
|
|
2084
2374
|
var FILESYSTEM_SOURCE_PREFIX = "file:";
|
|
2085
2375
|
var isWithinDirectory = (directory, target) => {
|
|
2086
|
-
const relativePath =
|
|
2087
|
-
return relativePath === "" || !relativePath.startsWith(`..${
|
|
2376
|
+
const relativePath = import_node_path14.default.relative(directory, target);
|
|
2377
|
+
return relativePath === "" || !relativePath.startsWith(`..${import_node_path14.default.sep}`) && relativePath !== ".." && !import_node_path14.default.isAbsolute(relativePath);
|
|
2088
2378
|
};
|
|
2089
2379
|
var assertPortableRelativePath = (filePath, label, allowRoot = true) => {
|
|
2090
2380
|
const portablePath = filePath.replaceAll("\\", "/");
|
|
2091
|
-
const normalizedPath =
|
|
2092
|
-
const isUnsafe = filePath.includes("\\") || filePath.includes("\0") ||
|
|
2381
|
+
const normalizedPath = import_node_path14.default.posix.normalize(portablePath);
|
|
2382
|
+
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
2383
|
if (isUnsafe) throw new Error(`${label} "${filePath}" escapes its filesystem source`);
|
|
2094
2384
|
return normalizedPath;
|
|
2095
2385
|
};
|
|
@@ -2099,22 +2389,22 @@ var getSourceRoot = (projectDirectory, source) => {
|
|
|
2099
2389
|
}
|
|
2100
2390
|
const locator = source.source.slice(FILESYSTEM_SOURCE_PREFIX.length);
|
|
2101
2391
|
if (!locator.trim()) throw new Error(`Filesystem federation source "${source.id}" requires a path after "file:".`);
|
|
2102
|
-
return
|
|
2392
|
+
return import_node_path14.default.resolve(projectDirectory, locator);
|
|
2103
2393
|
};
|
|
2104
2394
|
var getCatalogDirectory = async (projectDirectory, source) => {
|
|
2105
2395
|
if (source.ref) throw new Error(`Filesystem federation source "${source.id}" does not support "ref".`);
|
|
2106
2396
|
const sourceRoot = getSourceRoot(projectDirectory, source);
|
|
2107
2397
|
const catalogPath = assertPortableRelativePath(source.path ?? ".", "Catalog path");
|
|
2108
|
-
const catalogDirectory =
|
|
2398
|
+
const catalogDirectory = import_node_path14.default.resolve(sourceRoot, ...catalogPath.split("/"));
|
|
2109
2399
|
if (!isWithinDirectory(sourceRoot, catalogDirectory)) {
|
|
2110
2400
|
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
2111
2401
|
}
|
|
2112
2402
|
try {
|
|
2113
|
-
const [realSourceRoot, realCatalogDirectory] = await Promise.all([
|
|
2403
|
+
const [realSourceRoot, realCatalogDirectory] = await Promise.all([import_promises7.default.realpath(sourceRoot), import_promises7.default.realpath(catalogDirectory)]);
|
|
2114
2404
|
if (!isWithinDirectory(realSourceRoot, realCatalogDirectory)) {
|
|
2115
2405
|
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
2116
2406
|
}
|
|
2117
|
-
if (!(await
|
|
2407
|
+
if (!(await import_promises7.default.stat(realCatalogDirectory)).isDirectory()) {
|
|
2118
2408
|
throw new Error(`Filesystem federation source "${source.id}" is not a directory: ${catalogDirectory}`);
|
|
2119
2409
|
}
|
|
2120
2410
|
return realCatalogDirectory;
|
|
@@ -2127,12 +2417,12 @@ var getCatalogDirectory = async (projectDirectory, source) => {
|
|
|
2127
2417
|
};
|
|
2128
2418
|
var getArtifactPath = async (catalogDirectory, source, artifactPath) => {
|
|
2129
2419
|
const normalizedPath = assertPortableRelativePath(artifactPath, "Federated artifact path", false);
|
|
2130
|
-
const filePath =
|
|
2420
|
+
const filePath = import_node_path14.default.resolve(catalogDirectory, ...normalizedPath.split("/"));
|
|
2131
2421
|
if (!isWithinDirectory(catalogDirectory, filePath)) {
|
|
2132
2422
|
throw new Error(`Federated artifact path "${artifactPath}" escapes source "${source.id}"`);
|
|
2133
2423
|
}
|
|
2134
2424
|
try {
|
|
2135
|
-
const realFilePath = await
|
|
2425
|
+
const realFilePath = await import_promises7.default.realpath(filePath);
|
|
2136
2426
|
if (!isWithinDirectory(catalogDirectory, realFilePath)) {
|
|
2137
2427
|
throw new Error(`Federated artifact path "${artifactPath}" escapes source "${source.id}"`);
|
|
2138
2428
|
}
|
|
@@ -2159,15 +2449,15 @@ var createFileSystemSourceProvider = (projectDirectory) => ({
|
|
|
2159
2449
|
},
|
|
2160
2450
|
async fetchContent({ source, path: artifactPath }) {
|
|
2161
2451
|
const catalogDirectory = await getCatalogDirectory(projectDirectory, source);
|
|
2162
|
-
return
|
|
2452
|
+
return import_promises7.default.readFile(await getArtifactPath(catalogDirectory, source, artifactPath));
|
|
2163
2453
|
}
|
|
2164
2454
|
});
|
|
2165
2455
|
|
|
2166
2456
|
// src/federation/github-source-provider.ts
|
|
2167
2457
|
var import_node_child_process = require("child_process");
|
|
2168
|
-
var
|
|
2458
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
2169
2459
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
2170
|
-
var
|
|
2460
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
2171
2461
|
var import_node_util = require("util");
|
|
2172
2462
|
var import_sdk2 = __toESM(require("@eventcatalog/sdk"), 1);
|
|
2173
2463
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
@@ -2178,8 +2468,8 @@ var parseGitHubSource = (source) => {
|
|
|
2178
2468
|
};
|
|
2179
2469
|
var assertSafeCatalogPath = (source) => {
|
|
2180
2470
|
const catalogPath = source.path ?? ".";
|
|
2181
|
-
const normalized =
|
|
2182
|
-
if (catalogPath.includes("\\") ||
|
|
2471
|
+
const normalized = import_node_path15.default.posix.normalize(catalogPath.replaceAll("\\", "/"));
|
|
2472
|
+
if (catalogPath.includes("\\") || import_node_path15.default.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
2183
2473
|
throw new Error(`Catalog path "${catalogPath}" escapes source "${source.id}"`);
|
|
2184
2474
|
}
|
|
2185
2475
|
};
|
|
@@ -2222,8 +2512,8 @@ var getGitEnvironment = (token) => {
|
|
|
2222
2512
|
};
|
|
2223
2513
|
var createCheckout = (executeFile, token) => async (source, ref, callback) => {
|
|
2224
2514
|
const { owner, repository } = parseGitHubSource(source);
|
|
2225
|
-
const catalogPath =
|
|
2226
|
-
const directory = await
|
|
2515
|
+
const catalogPath = import_node_path15.default.posix.normalize(source.path ?? ".");
|
|
2516
|
+
const directory = await import_promises8.default.mkdtemp(import_node_path15.default.join(import_node_os4.default.tmpdir(), "eventcatalog-federation-"));
|
|
2227
2517
|
const env = getGitEnvironment(token);
|
|
2228
2518
|
try {
|
|
2229
2519
|
const git = (args) => executeFile("git", args, { cwd: directory, env, encoding: "utf8" });
|
|
@@ -2237,22 +2527,22 @@ var createCheckout = (executeFile, token) => async (source, ref, callback) => {
|
|
|
2237
2527
|
await git(["checkout", "--quiet", "--detach", "FETCH_HEAD"]);
|
|
2238
2528
|
return await callback(directory);
|
|
2239
2529
|
} finally {
|
|
2240
|
-
await
|
|
2530
|
+
await import_promises8.default.rm(directory, { recursive: true, force: true });
|
|
2241
2531
|
}
|
|
2242
2532
|
};
|
|
2243
2533
|
var generateIndex = async (source, ref, checkout, executeFile) => checkout(source, ref, async (directory) => {
|
|
2244
2534
|
const { stdout } = await executeFile("git", ["rev-parse", "HEAD"], { cwd: directory, encoding: "utf8" });
|
|
2245
2535
|
const commit = stdout.trim();
|
|
2246
|
-
const catalogDirectory =
|
|
2247
|
-
const relativeCatalogDirectory =
|
|
2248
|
-
if (relativeCatalogDirectory.startsWith("..") ||
|
|
2536
|
+
const catalogDirectory = import_node_path15.default.resolve(directory, source.path ?? ".");
|
|
2537
|
+
const relativeCatalogDirectory = import_node_path15.default.relative(directory, catalogDirectory);
|
|
2538
|
+
if (relativeCatalogDirectory.startsWith("..") || import_node_path15.default.isAbsolute(relativeCatalogDirectory)) {
|
|
2249
2539
|
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
2250
2540
|
}
|
|
2251
2541
|
const index = await (0, import_sdk2.default)(catalogDirectory).buildIndex({ source: source.id, commit });
|
|
2252
2542
|
return { bytes: Buffer.from(JSON.stringify(index)), index, commit, generated: true };
|
|
2253
2543
|
});
|
|
2254
2544
|
var fetchPublishedIndex = async (source, ref, fetcher, token) => {
|
|
2255
|
-
const indexPath =
|
|
2545
|
+
const indexPath = import_node_path15.default.posix.join(source.path ?? ".", "catalog.index.json");
|
|
2256
2546
|
const bytes = await fetchBytes(source, ref, indexPath, fetcher, token);
|
|
2257
2547
|
if (!bytes) return void 0;
|
|
2258
2548
|
const index = (0, import_sdk2.parseIndex)(JSON.parse(bytes.toString("utf8")));
|
|
@@ -2275,7 +2565,7 @@ var createGitHubSourceProvider = (options = {}) => {
|
|
|
2275
2565
|
},
|
|
2276
2566
|
async fetchContent({ source, commit, path: artifactPath }) {
|
|
2277
2567
|
assertSafeCatalogPath(source);
|
|
2278
|
-
const catalogPath =
|
|
2568
|
+
const catalogPath = import_node_path15.default.posix.join(source.path ?? ".", artifactPath);
|
|
2279
2569
|
const content = await fetchBytes(source, commit, catalogPath, fetcher, token);
|
|
2280
2570
|
if (!content) throw new Error(`Federated artifact not found for "${source.id}": ${artifactPath}`);
|
|
2281
2571
|
return content;
|
|
@@ -2313,6 +2603,16 @@ ${details}`);
|
|
|
2313
2603
|
this.conflicts = conflicts;
|
|
2314
2604
|
}
|
|
2315
2605
|
};
|
|
2606
|
+
var FederationDiagnosticError = class extends Error {
|
|
2607
|
+
diagnostics;
|
|
2608
|
+
constructor(diagnostics) {
|
|
2609
|
+
const details = diagnostics.map((diagnostic) => `${diagnostic.rule}: ${diagnostic.message}`).join("\n");
|
|
2610
|
+
super(`Federation diagnostics prevent hydration:
|
|
2611
|
+
${details}`);
|
|
2612
|
+
this.name = "FederationDiagnosticError";
|
|
2613
|
+
this.diagnostics = diagnostics;
|
|
2614
|
+
}
|
|
2615
|
+
};
|
|
2316
2616
|
var validateSources = (sources) => {
|
|
2317
2617
|
const ids = /* @__PURE__ */ new Set();
|
|
2318
2618
|
for (const source of sources) {
|
|
@@ -2321,19 +2621,46 @@ var validateSources = (sources) => {
|
|
|
2321
2621
|
ids.add(source.id);
|
|
2322
2622
|
}
|
|
2323
2623
|
};
|
|
2624
|
+
var createDiagnosticGraph = (graph, ownershipGraph, remoteSourceIds, localSourceId) => {
|
|
2625
|
+
const edges = ownershipGraph.edges.filter((edge) => remoteSourceIds.has(edge.fromResolvedFrom.source));
|
|
2626
|
+
const externalsByPointer = /* @__PURE__ */ new Map();
|
|
2627
|
+
for (const edge of edges.filter((edge2) => edge2.status === "external")) {
|
|
2628
|
+
const version2 = edge.pointer ?? void 0;
|
|
2629
|
+
const key = `${edge.to}@${version2 ?? ""}`;
|
|
2630
|
+
const external = externalsByPointer.get(key) ?? {
|
|
2631
|
+
id: edge.to,
|
|
2632
|
+
...version2 === void 0 ? {} : { version: version2 },
|
|
2633
|
+
referencedBy: []
|
|
2634
|
+
};
|
|
2635
|
+
if (!external.referencedBy.includes(edge.from)) external.referencedBy.push(edge.from);
|
|
2636
|
+
externalsByPointer.set(key, external);
|
|
2637
|
+
}
|
|
2638
|
+
return {
|
|
2639
|
+
...graph,
|
|
2640
|
+
entities: [
|
|
2641
|
+
...graph.entities,
|
|
2642
|
+
...ownershipGraph.entities.filter(
|
|
2643
|
+
(entity) => entity.resolvedFrom.source === localSourceId && entity.resolvedFrom.commit === "local"
|
|
2644
|
+
)
|
|
2645
|
+
],
|
|
2646
|
+
edges,
|
|
2647
|
+
conflicts: ownershipGraph.conflicts,
|
|
2648
|
+
externals: [...externalsByPointer.values()]
|
|
2649
|
+
};
|
|
2650
|
+
};
|
|
2324
2651
|
var writeLock = async (lockPath, lock) => {
|
|
2325
2652
|
const temporaryPath = `${lockPath}.tmp-${process.pid}`;
|
|
2326
2653
|
try {
|
|
2327
|
-
await
|
|
2654
|
+
await import_promises9.default.writeFile(temporaryPath, `${JSON.stringify(lock, null, 2)}
|
|
2328
2655
|
`, "utf8");
|
|
2329
|
-
await
|
|
2656
|
+
await import_promises9.default.rename(temporaryPath, lockPath);
|
|
2330
2657
|
} finally {
|
|
2331
|
-
await
|
|
2658
|
+
await import_promises9.default.rm(temporaryPath, { force: true });
|
|
2332
2659
|
}
|
|
2333
2660
|
};
|
|
2334
2661
|
var readLock = async (lockPath) => {
|
|
2335
2662
|
try {
|
|
2336
|
-
return JSON.parse(await
|
|
2663
|
+
return JSON.parse(await import_promises9.default.readFile(lockPath, "utf8"));
|
|
2337
2664
|
} catch (error) {
|
|
2338
2665
|
if (error.code === "ENOENT") return void 0;
|
|
2339
2666
|
throw new Error(`Cannot read federation lock at "${lockPath}"`, { cause: error });
|
|
@@ -2341,7 +2668,7 @@ var readLock = async (lockPath) => {
|
|
|
2341
2668
|
};
|
|
2342
2669
|
var pathExists2 = async (filePath) => {
|
|
2343
2670
|
try {
|
|
2344
|
-
await
|
|
2671
|
+
await import_promises9.default.access(filePath);
|
|
2345
2672
|
return true;
|
|
2346
2673
|
} catch (error) {
|
|
2347
2674
|
if (error.code === "ENOENT") return false;
|
|
@@ -2349,20 +2676,27 @@ var pathExists2 = async (filePath) => {
|
|
|
2349
2676
|
}
|
|
2350
2677
|
};
|
|
2351
2678
|
var cleanupPreviousFederation = async (projectDirectory, onProgress) => {
|
|
2352
|
-
const outDir =
|
|
2353
|
-
const lockPath =
|
|
2679
|
+
const outDir = import_node_path16.default.join(projectDirectory, "federated");
|
|
2680
|
+
const lockPath = import_node_path16.default.join(projectDirectory, "eventcatalog.lock");
|
|
2354
2681
|
const previousLock = await readLock(lockPath);
|
|
2355
2682
|
const hadFederatedOutput = await pathExists2(outDir);
|
|
2356
2683
|
const hadLock = previousLock !== void 0;
|
|
2357
2684
|
if (!hadFederatedOutput && !hadLock) return;
|
|
2358
|
-
|
|
2359
|
-
const publicResult = await composePublicAssets({
|
|
2685
|
+
const publicResult = await withFederationOutputTransaction(
|
|
2360
2686
|
projectDirectory,
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2687
|
+
outDir,
|
|
2688
|
+
Object.keys(previousLock?.publicFiles ?? {}),
|
|
2689
|
+
async () => {
|
|
2690
|
+
const result = await composePublicAssets({
|
|
2691
|
+
projectDirectory,
|
|
2692
|
+
federatedDirectory: outDir,
|
|
2693
|
+
assets: [],
|
|
2694
|
+
previousFiles: previousLock?.publicFiles
|
|
2695
|
+
});
|
|
2696
|
+
await import_promises9.default.rm(lockPath, { force: true });
|
|
2697
|
+
return result;
|
|
2698
|
+
}
|
|
2699
|
+
);
|
|
2366
2700
|
onProgress?.({
|
|
2367
2701
|
type: "cleanup:complete",
|
|
2368
2702
|
federated: hadFederatedOutput,
|
|
@@ -2384,6 +2718,7 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2384
2718
|
);
|
|
2385
2719
|
}
|
|
2386
2720
|
validateSources(sources);
|
|
2721
|
+
const rules = resolveFederationRules(config.federation?.rules);
|
|
2387
2722
|
if (options.useCache === false) options.onProgress?.({ type: "cache:disabled" });
|
|
2388
2723
|
const provider = options.provider ?? createFederationSourceProvider(projectDirectory);
|
|
2389
2724
|
const resolvedSources = [];
|
|
@@ -2407,8 +2742,8 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2407
2742
|
throw new Error(`Failed to federate source "${source.id}": ${message}`, { cause: error });
|
|
2408
2743
|
}
|
|
2409
2744
|
}
|
|
2410
|
-
const outDir =
|
|
2411
|
-
const lockPath =
|
|
2745
|
+
const outDir = import_node_path16.default.join(projectDirectory, "federated");
|
|
2746
|
+
const lockPath = import_node_path16.default.join(projectDirectory, "eventcatalog.lock");
|
|
2412
2747
|
const previousLock = await readLock(lockPath);
|
|
2413
2748
|
const resources = resolvedSources.reduce((total, source) => total + source.resolved.index.resources.length, 0);
|
|
2414
2749
|
const remoteIndexes = resolvedSources.map(({ resolved }) => resolved.index);
|
|
@@ -2422,60 +2757,79 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2422
2757
|
options.onProgress?.({ type: "local:complete", resources: localIndex.resources.length });
|
|
2423
2758
|
options.onProgress?.({ type: "resolving", resources, localResources: localIndex.resources.length });
|
|
2424
2759
|
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
2760
|
const graph = (0, import_sdk3.resolve)(remoteIndexes);
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2761
|
+
const diagnosticGraph = createDiagnosticGraph(
|
|
2762
|
+
graph,
|
|
2763
|
+
ownershipGraph,
|
|
2764
|
+
new Set(remoteIndexes.map((index) => index.source)),
|
|
2765
|
+
localIndex.source
|
|
2766
|
+
);
|
|
2767
|
+
const diagnostics = getFederationDiagnostics(diagnosticGraph, rules);
|
|
2768
|
+
options.onProgress?.({ type: "resolved", graph, diagnostics });
|
|
2769
|
+
const blockingConflicts = ownershipGraph.conflicts.filter((conflict) => rules[`federation/${conflict.kind}`] === "error");
|
|
2770
|
+
if (blockingConflicts.length > 0) throw new FederationConflictError(blockingConflicts);
|
|
2771
|
+
const blockingDiagnostics = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
2772
|
+
if (blockingDiagnostics.length > 0) throw new FederationDiagnosticError(blockingDiagnostics);
|
|
2773
|
+
const publicPaths = [
|
|
2774
|
+
...Object.keys(previousLock?.publicFiles ?? {}),
|
|
2775
|
+
...graph.assets.filter((asset) => asset.path.startsWith("public/")).map((asset) => asset.path.slice("public/".length))
|
|
2776
|
+
];
|
|
2777
|
+
const { hydrateResult, publicResult } = await withFederationOutputTransaction(
|
|
2778
|
+
projectDirectory,
|
|
2436
2779
|
outDir,
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2780
|
+
publicPaths,
|
|
2781
|
+
async () => {
|
|
2782
|
+
const sourcesById = new Map(sources.map((source) => [source.id, source]));
|
|
2783
|
+
options.onProgress?.({ type: "hydrating", outDir });
|
|
2784
|
+
let hydratedFiles = 0;
|
|
2785
|
+
let cachedFiles = 0;
|
|
2786
|
+
const hydrateResult2 = await (0, import_sdk3.hydrate)(graph, {
|
|
2787
|
+
outDir,
|
|
2788
|
+
cache: createFederationContentCache(projectDirectory, {
|
|
2789
|
+
read: options.useCache !== false,
|
|
2790
|
+
onHit: () => {
|
|
2791
|
+
cachedFiles += 1;
|
|
2792
|
+
options.onProgress?.({ type: "hydrate:cache", files: cachedFiles });
|
|
2793
|
+
}
|
|
2794
|
+
}),
|
|
2795
|
+
fetch: async ({ source: sourceId, commit, path: artifactPath }) => {
|
|
2796
|
+
const source = sourcesById.get(sourceId);
|
|
2797
|
+
if (!source) throw new Error(`Cannot fetch content for unconfigured source "${sourceId}"`);
|
|
2798
|
+
const content = await provider.fetchContent({ source, commit, path: artifactPath });
|
|
2799
|
+
hydratedFiles += 1;
|
|
2800
|
+
options.onProgress?.({ type: "hydrate:file", files: hydratedFiles, source: sourceId, path: artifactPath });
|
|
2801
|
+
return content;
|
|
2802
|
+
}
|
|
2803
|
+
});
|
|
2804
|
+
const publicResult2 = await composePublicAssets({
|
|
2805
|
+
projectDirectory,
|
|
2806
|
+
federatedDirectory: outDir,
|
|
2807
|
+
assets: graph.assets,
|
|
2808
|
+
previousFiles: previousLock?.publicFiles,
|
|
2809
|
+
collisionPaths: new Set(
|
|
2810
|
+
graph.warnings.filter((warning) => warning.kind === "asset-collision").map((warning) => warning.path)
|
|
2811
|
+
)
|
|
2812
|
+
});
|
|
2813
|
+
options.onProgress?.({ type: "public:complete", result: publicResult2 });
|
|
2814
|
+
const resolvedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
2815
|
+
await writeLock(lockPath, {
|
|
2816
|
+
lockVersion: 1,
|
|
2817
|
+
sources: resolvedSources.map(({ config: source, resolved }) => ({
|
|
2818
|
+
id: source.id,
|
|
2819
|
+
digest: `sha256:${(0, import_node_crypto4.createHash)("sha256").update(resolved.bytes).digest("hex")}`,
|
|
2820
|
+
commit: resolved.commit,
|
|
2821
|
+
resolvedAt
|
|
2822
|
+
})).sort((left, right) => left.id.localeCompare(right.id)),
|
|
2823
|
+
publicFiles: publicResult2.files
|
|
2824
|
+
});
|
|
2825
|
+
return { hydrateResult: hydrateResult2, publicResult: publicResult2 };
|
|
2452
2826
|
}
|
|
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
|
-
});
|
|
2827
|
+
);
|
|
2475
2828
|
const result = {
|
|
2476
2829
|
sources: sources.length,
|
|
2477
2830
|
resources,
|
|
2478
2831
|
graph,
|
|
2832
|
+
diagnostics,
|
|
2479
2833
|
hydrate: hydrateResult,
|
|
2480
2834
|
public: publicResult,
|
|
2481
2835
|
outDir,
|
|
@@ -2487,14 +2841,14 @@ var federateCatalog = async (projectDirectory, options = {}) => {
|
|
|
2487
2841
|
|
|
2488
2842
|
// src/eventcatalog.ts
|
|
2489
2843
|
var import_license = require("@eventcatalog/license");
|
|
2490
|
-
var currentDir =
|
|
2844
|
+
var currentDir = import_node_path18.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
2491
2845
|
var program = new import_commander.Command().version(VERSION);
|
|
2492
|
-
var dir =
|
|
2493
|
-
var core =
|
|
2494
|
-
var eventCatalogDir =
|
|
2846
|
+
var dir = import_node_path18.default.resolve(process.env.PROJECT_DIR || process.cwd());
|
|
2847
|
+
var core = import_node_path18.default.resolve(process.env.CATALOG_DIR || (0, import_node_path17.join)(dir, ".eventcatalog-core"));
|
|
2848
|
+
var eventCatalogDir = import_node_path18.default.resolve((0, import_node_path17.join)(currentDir, "../eventcatalog/"));
|
|
2495
2849
|
var getInstalledEventCatalogVersion = () => {
|
|
2496
2850
|
try {
|
|
2497
|
-
const pkg = import_fs3.default.readFileSync((0,
|
|
2851
|
+
const pkg = import_fs3.default.readFileSync((0, import_node_path17.join)(dir, "package.json"), "utf8");
|
|
2498
2852
|
const json = JSON.parse(pkg);
|
|
2499
2853
|
return json.dependencies["@eventcatalog/core"];
|
|
2500
2854
|
} catch (error) {
|
|
@@ -2560,12 +2914,12 @@ var startDevPrewarm = ({
|
|
|
2560
2914
|
var buildDevSearchIndex = async ({ config }) => {
|
|
2561
2915
|
const result = await buildSearchIndex({
|
|
2562
2916
|
projectDir: dir,
|
|
2563
|
-
outDir:
|
|
2564
|
-
searchOutputPath:
|
|
2917
|
+
outDir: import_node_path18.default.join(core, "public"),
|
|
2918
|
+
searchOutputPath: import_node_path18.default.join(core, "public", "pagefind"),
|
|
2565
2919
|
config,
|
|
2566
2920
|
isServer: false
|
|
2567
2921
|
});
|
|
2568
|
-
logger.info(`Indexed ${result.records} page(s) into ${
|
|
2922
|
+
logger.info(`Indexed ${result.records} page(s) into ${import_node_path18.default.relative(core, result.outputPath)}`, "search");
|
|
2569
2923
|
};
|
|
2570
2924
|
var warnIfIndexedSearchUsesAuth = async () => {
|
|
2571
2925
|
if (!await isAuthEnabled()) {
|
|
@@ -2680,12 +3034,12 @@ var copyCore = () => {
|
|
|
2680
3034
|
import_fs3.default.cpSync(eventCatalogDir, core, {
|
|
2681
3035
|
recursive: true,
|
|
2682
3036
|
filter: (src) => {
|
|
2683
|
-
const relativePath =
|
|
2684
|
-
const pathParts = relativePath.split(
|
|
3037
|
+
const relativePath = import_node_path18.default.relative(eventCatalogDir, src);
|
|
3038
|
+
const pathParts = relativePath.split(import_node_path18.default.sep);
|
|
2685
3039
|
return !pathParts.some((part) => [".astro", "dist", "node_modules"].includes(part));
|
|
2686
3040
|
}
|
|
2687
3041
|
});
|
|
2688
|
-
const coreNodeModules =
|
|
3042
|
+
const coreNodeModules = import_node_path18.default.join(core, "node_modules");
|
|
2689
3043
|
const installedCoreNodeModules = resolveInstalledCoreNodeModules(currentDir);
|
|
2690
3044
|
linkCoreNodeModules({ coreNodeModules, installedCoreNodeModules });
|
|
2691
3045
|
};
|
|
@@ -2744,8 +3098,8 @@ program.command("dev").description("Run development server of EventCatalog").opt
|
|
|
2744
3098
|
logger.info("Setting up EventCatalog...", "eventcatalog");
|
|
2745
3099
|
const isServer = await isOutputServer();
|
|
2746
3100
|
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:
|
|
3101
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3102
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2749
3103
|
}
|
|
2750
3104
|
if (options.debug) {
|
|
2751
3105
|
logger.info("Debug mode enabled", "debug");
|
|
@@ -2825,8 +3179,8 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2825
3179
|
logger.info("Building EventCatalog...", "build");
|
|
2826
3180
|
const isServer = await isOutputServer();
|
|
2827
3181
|
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:
|
|
3182
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3183
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2830
3184
|
}
|
|
2831
3185
|
await verifyRequiredFieldsAreInCatalogConfigFile(dir);
|
|
2832
3186
|
copyCore();
|
|
@@ -2876,7 +3230,7 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2876
3230
|
if (await isIndexedSearchEnabled()) {
|
|
2877
3231
|
await warnIfIndexedSearchUsesAuth();
|
|
2878
3232
|
const config = await getEventCatalogConfigFile(dir);
|
|
2879
|
-
const outDir =
|
|
3233
|
+
const outDir = import_node_path18.default.resolve(dir, await getProjectOutDir());
|
|
2880
3234
|
logger.info("Building indexed search...", "search");
|
|
2881
3235
|
const result = await buildSearchIndex({
|
|
2882
3236
|
projectDir: dir,
|
|
@@ -2884,7 +3238,7 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2884
3238
|
config,
|
|
2885
3239
|
isServer
|
|
2886
3240
|
});
|
|
2887
|
-
logger.info(`Indexed ${result.records} page(s) into ${
|
|
3241
|
+
logger.info(`Indexed ${result.records} page(s) into ${import_node_path18.default.relative(dir, result.outputPath)}`, "search");
|
|
2888
3242
|
}
|
|
2889
3243
|
});
|
|
2890
3244
|
var previewCatalog = async ({
|
|
@@ -2911,7 +3265,7 @@ var startServerCatalog = async ({
|
|
|
2911
3265
|
isEventCatalogStarter = false,
|
|
2912
3266
|
isEventCatalogScale = false
|
|
2913
3267
|
}) => {
|
|
2914
|
-
const serverEntryPath =
|
|
3268
|
+
const serverEntryPath = import_node_path18.default.join(dir, "dist", "server", "entry.mjs");
|
|
2915
3269
|
await runCommandWithFilteredOutput({
|
|
2916
3270
|
command: `node "${serverEntryPath}"`,
|
|
2917
3271
|
cwd: core,
|
|
@@ -2928,8 +3282,8 @@ var startServerCatalog = async ({
|
|
|
2928
3282
|
program.command("preview").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
2929
3283
|
logger.welcome();
|
|
2930
3284
|
logger.info("Starting preview of your build...", "preview");
|
|
2931
|
-
if (import_fs3.default.existsSync(
|
|
2932
|
-
import_dotenv.default.config({ path:
|
|
3285
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3286
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2933
3287
|
}
|
|
2934
3288
|
const canEmbedPages = await (0, import_license.isFeatureEnabled)(
|
|
2935
3289
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
@@ -2947,8 +3301,8 @@ program.command("preview").description("Serves the contents of your eventcatalog
|
|
|
2947
3301
|
program.command("start").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
2948
3302
|
logger.welcome();
|
|
2949
3303
|
logger.info("Starting preview of your build...", "preview");
|
|
2950
|
-
if (import_fs3.default.existsSync(
|
|
2951
|
-
import_dotenv.default.config({ path:
|
|
3304
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3305
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2952
3306
|
}
|
|
2953
3307
|
const canEmbedPages = await (0, import_license.isFeatureEnabled)(
|
|
2954
3308
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
@@ -2975,26 +3329,26 @@ program.command("start").description("Serves the contents of your eventcatalog b
|
|
|
2975
3329
|
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
3330
|
logger.welcome();
|
|
2977
3331
|
logger.info("Exporting EventCatalog...", "export");
|
|
2978
|
-
if (import_fs3.default.existsSync(
|
|
2979
|
-
import_dotenv.default.config({ path:
|
|
3332
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3333
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2980
3334
|
}
|
|
2981
3335
|
const { default: initSDK } = await import("@eventcatalog/sdk");
|
|
2982
3336
|
const sdk = initSDK(dir);
|
|
2983
3337
|
const catalog = await sdk.dumpCatalog({ includeMarkdown: options.includeMarkdown });
|
|
2984
|
-
const exportsDir =
|
|
3338
|
+
const exportsDir = import_node_path18.default.join(dir, "exports");
|
|
2985
3339
|
ensureDir(exportsDir);
|
|
2986
3340
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2987
|
-
const exportFile =
|
|
3341
|
+
const exportFile = import_node_path18.default.join(exportsDir, `catalog-${date}.json`);
|
|
2988
3342
|
import_fs3.default.writeFileSync(exportFile, JSON.stringify(catalog, null, 2), "utf-8");
|
|
2989
3343
|
logger.info(`Catalog exported to ${exportFile}`, "export");
|
|
2990
3344
|
});
|
|
2991
3345
|
program.command("generate [siteDir]").description("Start the generator scripts.").action(async () => {
|
|
2992
|
-
if (import_fs3.default.existsSync(
|
|
2993
|
-
import_dotenv.default.config({ path:
|
|
3346
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3347
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
2994
3348
|
}
|
|
2995
3349
|
await generate(dir);
|
|
2996
3350
|
});
|
|
2997
|
-
var reportFederationProgress = (event) => {
|
|
3351
|
+
var reportFederationProgress = (event, verbose = false) => {
|
|
2998
3352
|
switch (event.type) {
|
|
2999
3353
|
case "configured":
|
|
3000
3354
|
if (event.sources > 0)
|
|
@@ -3034,38 +3388,36 @@ var reportFederationProgress = (event) => {
|
|
|
3034
3388
|
);
|
|
3035
3389
|
return;
|
|
3036
3390
|
case "resolved":
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3391
|
+
const counts = getFederationDiagnosticCounts(event.diagnostics);
|
|
3392
|
+
const showDiagnosticDetails = verbose || counts.errors > 0;
|
|
3393
|
+
if (counts.errors === 0) {
|
|
3394
|
+
logger.success(
|
|
3395
|
+
`Graph resolved: ${event.graph.entities.length} remote resources, ${event.graph.edges.length} relationships`,
|
|
3040
3396
|
"federation"
|
|
3041
3397
|
);
|
|
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
3398
|
}
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3399
|
+
if (counts.errors + counts.warnings === 0) return;
|
|
3400
|
+
if (showDiagnosticDetails) {
|
|
3401
|
+
const diagnostics = getVisibleFederationDiagnostics(event.diagnostics, verbose);
|
|
3402
|
+
logger.info("Federation diagnostics", "federation");
|
|
3403
|
+
logger.line();
|
|
3404
|
+
for (const diagnostic of diagnostics) {
|
|
3405
|
+
logger.diagnostic(diagnostic.severity, diagnostic.message, diagnostic.rule, diagnostic.attributes);
|
|
3406
|
+
logger.line();
|
|
3407
|
+
}
|
|
3408
|
+
logger.diagnosticSummary(counts.errors, counts.warnings);
|
|
3409
|
+
} else {
|
|
3055
3410
|
logger.warning(
|
|
3056
|
-
`${
|
|
3411
|
+
`${counts.warnings} problem${counts.warnings === 1 ? "" : "s"} (0 errors, ${counts.warnings} warning${counts.warnings === 1 ? "" : "s"})`,
|
|
3057
3412
|
"federation"
|
|
3058
3413
|
);
|
|
3059
3414
|
}
|
|
3060
|
-
if (
|
|
3061
|
-
logger.warning
|
|
3062
|
-
`${event.graph.externals.length} reference${event.graph.externals.length === 1 ? "" : "s"} remain external`,
|
|
3063
|
-
"federation"
|
|
3064
|
-
);
|
|
3415
|
+
if (!verbose && counts.warnings > 0) {
|
|
3416
|
+
logger.info("Run with --verbose to see warning details.", "federation");
|
|
3065
3417
|
}
|
|
3066
3418
|
return;
|
|
3067
3419
|
case "hydrating":
|
|
3068
|
-
logger.info(`Hydrating federated content into ${
|
|
3420
|
+
logger.info(`Hydrating federated content into ${import_node_path18.default.relative(dir, event.outDir)}/...`, "federation");
|
|
3069
3421
|
return;
|
|
3070
3422
|
case "hydrate:cache":
|
|
3071
3423
|
if (event.files === 1 || event.files % 25 === 0) {
|
|
@@ -3082,25 +3434,19 @@ var reportFederationProgress = (event) => {
|
|
|
3082
3434
|
`Public assets: ${event.result.copied} copied, ${event.result.skipped} preserved from the main catalog, ${event.result.removed} stale removed`,
|
|
3083
3435
|
"federation"
|
|
3084
3436
|
);
|
|
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
3437
|
return;
|
|
3092
3438
|
case "complete":
|
|
3093
3439
|
logger.success(
|
|
3094
3440
|
`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
3441
|
"federation"
|
|
3096
3442
|
);
|
|
3097
|
-
logger.info(`
|
|
3443
|
+
logger.info(`Recorded resolved source state in ${import_node_path18.default.relative(dir, event.result.lockPath)}`, "federation");
|
|
3098
3444
|
}
|
|
3099
3445
|
};
|
|
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) => {
|
|
3446
|
+
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
3447
|
logger.welcome();
|
|
3102
|
-
if (import_fs3.default.existsSync(
|
|
3103
|
-
import_dotenv.default.config({ path:
|
|
3448
|
+
if (import_fs3.default.existsSync(import_node_path18.default.join(dir, ".env"))) {
|
|
3449
|
+
import_dotenv.default.config({ path: import_node_path18.default.join(dir, ".env") });
|
|
3104
3450
|
}
|
|
3105
3451
|
logger.info("Starting federation...", "federation");
|
|
3106
3452
|
let cleanedPreviousOutput = false;
|
|
@@ -3109,7 +3455,7 @@ program.command("federate").description("Fetch, resolve, and hydrate the catalog
|
|
|
3109
3455
|
isFederationEnabled: import_license.isEventCatalogScaleEnabled,
|
|
3110
3456
|
onProgress: (event) => {
|
|
3111
3457
|
if (event.type === "cleanup:complete") cleanedPreviousOutput = true;
|
|
3112
|
-
reportFederationProgress(event);
|
|
3458
|
+
reportFederationProgress(event, commandOptions.verbose);
|
|
3113
3459
|
}
|
|
3114
3460
|
});
|
|
3115
3461
|
if (!result && !cleanedPreviousOutput) logger.warning("No federation sources configured; nothing to do.", "federation");
|
|
@@ -3123,6 +3469,6 @@ program.addHelpText(
|
|
|
3123
3469
|
`
|
|
3124
3470
|
);
|
|
3125
3471
|
program.parseAsync().then(() => process.exit(0)).catch((err) => {
|
|
3126
|
-
if (!(err instanceof FederationConflictError)) console.error(err);
|
|
3472
|
+
if (!(err instanceof FederationConflictError) && !(err instanceof FederationDiagnosticError)) console.error(err);
|
|
3127
3473
|
process.exit(1);
|
|
3128
3474
|
});
|