@eventcatalog/core 4.6.1 → 4.6.3
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-352FGP6W.js +123 -0
- package/dist/{chunk-N3YKQTGX.js → chunk-4BBXOL6K.js} +1 -1
- package/dist/{chunk-Y7JJ2B3L.js → chunk-54KFOI6Z.js} +3 -3
- package/dist/chunk-5EPNFDT5.js +133 -0
- package/dist/{chunk-MTXVQ4VP.js → chunk-FZJDCQZE.js} +1 -1
- package/dist/{chunk-X7XI7NEP.js → chunk-LB5VXS6X.js} +1 -1
- package/dist/{chunk-U6VOVEVZ.js → chunk-PWY4SP6L.js} +1 -1
- package/dist/chunk-R7Z5ALYI.js +44 -0
- package/dist/chunk-ZR6AH5Z2.js +204 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +651 -51
- package/dist/eventcatalog.config.d.cts +18 -1
- package/dist/eventcatalog.config.d.ts +18 -1
- package/dist/eventcatalog.js +134 -7
- package/dist/federation/content-cache.cjs +78 -0
- package/dist/federation/content-cache.d.cts +9 -0
- package/dist/federation/content-cache.d.ts +9 -0
- package/dist/federation/content-cache.js +6 -0
- package/dist/federation/federate.cjs +588 -0
- package/dist/federation/federate.d.cts +81 -0
- package/dist/federation/federate.d.ts +81 -0
- package/dist/federation/federate.js +12 -0
- package/dist/federation/github-source-provider.cjs +157 -0
- package/dist/federation/github-source-provider.d.cts +24 -0
- package/dist/federation/github-source-provider.d.ts +24 -0
- package/dist/federation/github-source-provider.js +6 -0
- package/dist/federation/public-assets.cjs +167 -0
- package/dist/federation/public-assets.d.cts +24 -0
- package/dist/federation/public-assets.d.ts +24 -0
- package/dist/federation/public-assets.js +6 -0
- package/dist/federation/types.cjs +18 -0
- package/dist/federation/types.d.cts +19 -0
- package/dist/federation/types.d.ts +19 -0
- package/dist/federation/types.js +0 -0
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/package.json +8 -8
package/dist/eventcatalog.cjs
CHANGED
|
@@ -28,11 +28,11 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
28
28
|
|
|
29
29
|
// src/eventcatalog.ts
|
|
30
30
|
var import_commander = require("commander");
|
|
31
|
-
var
|
|
32
|
-
var
|
|
31
|
+
var import_node_child_process2 = require("child_process");
|
|
32
|
+
var import_node_path14 = 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_path15 = __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.6.
|
|
147
|
+
var version = "4.6.3";
|
|
148
148
|
|
|
149
149
|
// src/constants.ts
|
|
150
150
|
var VERSION = version;
|
|
@@ -1256,22 +1256,22 @@ function getAvroTypeName(type) {
|
|
|
1256
1256
|
function walkAvroRecord(schema, prefix, fields) {
|
|
1257
1257
|
if (!schema.fields || !Array.isArray(schema.fields)) return;
|
|
1258
1258
|
for (const field of schema.fields) {
|
|
1259
|
-
const
|
|
1259
|
+
const path15 = prefix ? `${prefix}.${field.name}` : field.name;
|
|
1260
1260
|
const isOptional = Array.isArray(field.type) && field.type.includes("null");
|
|
1261
1261
|
const typeName = getAvroTypeName(field.type);
|
|
1262
1262
|
fields.push({
|
|
1263
|
-
path:
|
|
1263
|
+
path: path15,
|
|
1264
1264
|
type: typeName,
|
|
1265
1265
|
description: field.doc || "",
|
|
1266
1266
|
required: !isOptional
|
|
1267
1267
|
});
|
|
1268
1268
|
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;
|
|
1269
1269
|
if (innerType) {
|
|
1270
|
-
walkAvroRecord(innerType,
|
|
1270
|
+
walkAvroRecord(innerType, path15, fields);
|
|
1271
1271
|
}
|
|
1272
1272
|
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;
|
|
1273
1273
|
if (arrayType && typeof arrayType.items === "object" && arrayType.items.type === "record") {
|
|
1274
|
-
walkAvroRecord(arrayType.items, `${
|
|
1274
|
+
walkAvroRecord(arrayType.items, `${path15}[]`, fields);
|
|
1275
1275
|
}
|
|
1276
1276
|
}
|
|
1277
1277
|
}
|
|
@@ -1302,32 +1302,32 @@ function walkJsonSchema(node, prefix, requiredList, rootSchema, fields) {
|
|
|
1302
1302
|
}
|
|
1303
1303
|
if (!node.properties) return;
|
|
1304
1304
|
for (const [name, prop] of Object.entries(node.properties)) {
|
|
1305
|
-
const
|
|
1305
|
+
const path15 = prefix ? `${prefix}.${name}` : name;
|
|
1306
1306
|
const isRequired = requiredList.includes(name);
|
|
1307
1307
|
if (prop.$ref) {
|
|
1308
1308
|
const resolved = resolveLocalRef(prop.$ref, rootSchema);
|
|
1309
1309
|
if (resolved) {
|
|
1310
1310
|
const rawRefType = resolved.type || "object";
|
|
1311
1311
|
const type2 = Array.isArray(rawRefType) ? [...rawRefType].sort().join(" | ") : rawRefType;
|
|
1312
|
-
fields.push({ path:
|
|
1312
|
+
fields.push({ path: path15, type: type2, description: resolved.description || "", required: isRequired });
|
|
1313
1313
|
if (resolved.properties) {
|
|
1314
|
-
walkJsonSchema(resolved,
|
|
1314
|
+
walkJsonSchema(resolved, path15, resolved.required || [], rootSchema, fields);
|
|
1315
1315
|
}
|
|
1316
1316
|
} else {
|
|
1317
|
-
fields.push({ path:
|
|
1317
|
+
fields.push({ path: path15, type: "$ref", description: "", required: isRequired });
|
|
1318
1318
|
}
|
|
1319
1319
|
continue;
|
|
1320
1320
|
}
|
|
1321
1321
|
const rawType = prop.type || (prop.enum ? "enum" : prop.$ref ? "$ref" : "object");
|
|
1322
1322
|
const typeList = Array.isArray(rawType) ? rawType : [rawType];
|
|
1323
1323
|
const type = Array.isArray(rawType) ? [...rawType].sort().join(" | ") : rawType;
|
|
1324
|
-
fields.push({ path:
|
|
1324
|
+
fields.push({ path: path15, type, description: prop.description || "", required: isRequired });
|
|
1325
1325
|
if (typeList.includes("object") && prop.properties) {
|
|
1326
|
-
walkJsonSchema(prop,
|
|
1326
|
+
walkJsonSchema(prop, path15, prop.required || [], rootSchema, fields);
|
|
1327
1327
|
}
|
|
1328
1328
|
if (typeList.includes("array") && prop.items) {
|
|
1329
1329
|
if (prop.items.type === "object" && prop.items.properties) {
|
|
1330
|
-
walkJsonSchema(prop.items, `${
|
|
1330
|
+
walkJsonSchema(prop.items, `${path15}[]`, prop.items.required || [], rootSchema, fields);
|
|
1331
1331
|
}
|
|
1332
1332
|
}
|
|
1333
1333
|
}
|
|
@@ -1845,16 +1845,496 @@ var createAstroDevLineFilter = () => {
|
|
|
1845
1845
|
};
|
|
1846
1846
|
};
|
|
1847
1847
|
|
|
1848
|
+
// src/federation/federate.ts
|
|
1849
|
+
var import_node_crypto3 = require("crypto");
|
|
1850
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
1851
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
1852
|
+
var import_sdk2 = __toESM(require("@eventcatalog/sdk"), 1);
|
|
1853
|
+
|
|
1854
|
+
// src/federation/content-cache.ts
|
|
1855
|
+
var import_node_crypto = require("crypto");
|
|
1856
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
1857
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
1858
|
+
var getContentHash = (content) => `sha256:${(0, import_node_crypto.createHash)("sha256").update(content).digest("hex")}`;
|
|
1859
|
+
var isContentHash = (key) => /^sha256:[a-f0-9]{64}$/i.test(key);
|
|
1860
|
+
var createFederationContentCache = (projectDirectory, options = {}) => {
|
|
1861
|
+
const cacheDirectory = import_node_path10.default.join(projectDirectory, ".eventcatalog-cache", "federation", "content");
|
|
1862
|
+
const getCachePath = (key) => import_node_path10.default.join(cacheDirectory, encodeURIComponent(key));
|
|
1863
|
+
return {
|
|
1864
|
+
async get(key) {
|
|
1865
|
+
if (options.read === false || !isContentHash(key)) return void 0;
|
|
1866
|
+
const cachePath = getCachePath(key);
|
|
1867
|
+
try {
|
|
1868
|
+
const content = await import_promises3.default.readFile(cachePath);
|
|
1869
|
+
if (getContentHash(content) !== key) {
|
|
1870
|
+
await import_promises3.default.rm(cachePath, { force: true });
|
|
1871
|
+
return void 0;
|
|
1872
|
+
}
|
|
1873
|
+
options.onHit?.(key);
|
|
1874
|
+
return content;
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
if (error.code === "ENOENT") return void 0;
|
|
1877
|
+
throw error;
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
async set(key, content) {
|
|
1881
|
+
if (!isContentHash(key) || getContentHash(content) !== key) return;
|
|
1882
|
+
await import_promises3.default.mkdir(cacheDirectory, { recursive: true });
|
|
1883
|
+
const cachePath = getCachePath(key);
|
|
1884
|
+
const temporaryPath = `${cachePath}.tmp-${process.pid}-${(0, import_node_crypto.randomUUID)()}`;
|
|
1885
|
+
try {
|
|
1886
|
+
await import_promises3.default.writeFile(temporaryPath, content);
|
|
1887
|
+
await import_promises3.default.rename(temporaryPath, cachePath);
|
|
1888
|
+
} finally {
|
|
1889
|
+
await import_promises3.default.rm(temporaryPath, { force: true });
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
};
|
|
1893
|
+
};
|
|
1894
|
+
|
|
1895
|
+
// src/federation/github-source-provider.ts
|
|
1896
|
+
var import_node_child_process = require("child_process");
|
|
1897
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
1898
|
+
var import_node_os4 = __toESM(require("os"), 1);
|
|
1899
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
1900
|
+
var import_node_util = require("util");
|
|
1901
|
+
var import_sdk = __toESM(require("@eventcatalog/sdk"), 1);
|
|
1902
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
1903
|
+
var parseGitHubSource = (source) => {
|
|
1904
|
+
const match = /^github:([^/]+)\/(.+)$/.exec(source.source);
|
|
1905
|
+
if (!match) throw new Error(`Unsupported federation source "${source.source}". Expected github:owner/repository.`);
|
|
1906
|
+
return { owner: match[1], repository: match[2] };
|
|
1907
|
+
};
|
|
1908
|
+
var assertSafeCatalogPath = (source) => {
|
|
1909
|
+
const catalogPath = source.path ?? ".";
|
|
1910
|
+
const normalized = import_node_path11.default.posix.normalize(catalogPath.replaceAll("\\", "/"));
|
|
1911
|
+
if (catalogPath.includes("\\") || import_node_path11.default.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
1912
|
+
throw new Error(`Catalog path "${catalogPath}" escapes source "${source.id}"`);
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
var encodePath = (value) => value.split("/").filter(Boolean).map(encodeURIComponent).join("/");
|
|
1916
|
+
var rawUrl = (source, ref, filePath) => {
|
|
1917
|
+
const { owner, repository } = parseGitHubSource(source);
|
|
1918
|
+
return `https://raw.githubusercontent.com/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/${encodeURIComponent(
|
|
1919
|
+
ref
|
|
1920
|
+
)}/${encodePath(filePath)}`;
|
|
1921
|
+
};
|
|
1922
|
+
var contentsApiUrl = (source, ref, filePath) => {
|
|
1923
|
+
const { owner, repository } = parseGitHubSource(source);
|
|
1924
|
+
return `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/contents/${encodePath(
|
|
1925
|
+
filePath
|
|
1926
|
+
)}?ref=${encodeURIComponent(ref)}`;
|
|
1927
|
+
};
|
|
1928
|
+
var fetchBytes = async (source, ref, filePath, fetcher, token) => {
|
|
1929
|
+
const url = token ? contentsApiUrl(source, ref, filePath) : rawUrl(source, ref, filePath);
|
|
1930
|
+
const response = token ? await fetcher(url, {
|
|
1931
|
+
headers: {
|
|
1932
|
+
Accept: "application/vnd.github.raw+json",
|
|
1933
|
+
Authorization: `Bearer ${token}`,
|
|
1934
|
+
"X-GitHub-Api-Version": "2026-03-10"
|
|
1935
|
+
}
|
|
1936
|
+
}) : await fetcher(url);
|
|
1937
|
+
if (response.status === 404) return void 0;
|
|
1938
|
+
if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
1939
|
+
return Buffer.from(await response.arrayBuffer());
|
|
1940
|
+
};
|
|
1941
|
+
var getGitEnvironment = (token) => {
|
|
1942
|
+
if (!token) return process.env;
|
|
1943
|
+
const inheritedCount = Number.parseInt(process.env.GIT_CONFIG_COUNT ?? "0", 10);
|
|
1944
|
+
const configIndex = Number.isInteger(inheritedCount) && inheritedCount >= 0 ? inheritedCount : 0;
|
|
1945
|
+
return {
|
|
1946
|
+
...process.env,
|
|
1947
|
+
GIT_CONFIG_COUNT: String(configIndex + 1),
|
|
1948
|
+
[`GIT_CONFIG_KEY_${configIndex}`]: "http.https://github.com/.extraheader",
|
|
1949
|
+
[`GIT_CONFIG_VALUE_${configIndex}`]: `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`
|
|
1950
|
+
};
|
|
1951
|
+
};
|
|
1952
|
+
var createCheckout = (executeFile, token) => async (source, ref, callback) => {
|
|
1953
|
+
const { owner, repository } = parseGitHubSource(source);
|
|
1954
|
+
const catalogPath = import_node_path11.default.posix.normalize(source.path ?? ".");
|
|
1955
|
+
const directory = await import_promises4.default.mkdtemp(import_node_path11.default.join(import_node_os4.default.tmpdir(), "eventcatalog-federation-"));
|
|
1956
|
+
const env = getGitEnvironment(token);
|
|
1957
|
+
try {
|
|
1958
|
+
const git = (args) => executeFile("git", args, { cwd: directory, env, encoding: "utf8" });
|
|
1959
|
+
await git(["init", "--quiet"]);
|
|
1960
|
+
await git(["remote", "add", "origin", `https://github.com/${owner}/${repository}.git`]);
|
|
1961
|
+
if (catalogPath !== ".") {
|
|
1962
|
+
await git(["sparse-checkout", "init", "--cone"]);
|
|
1963
|
+
await git(["sparse-checkout", "set", catalogPath]);
|
|
1964
|
+
}
|
|
1965
|
+
await git(["fetch", "--quiet", "--depth", "1", "origin", ref]);
|
|
1966
|
+
await git(["checkout", "--quiet", "--detach", "FETCH_HEAD"]);
|
|
1967
|
+
return await callback(directory);
|
|
1968
|
+
} finally {
|
|
1969
|
+
await import_promises4.default.rm(directory, { recursive: true, force: true });
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1972
|
+
var generateIndex = async (source, ref, checkout, executeFile) => checkout(source, ref, async (directory) => {
|
|
1973
|
+
const { stdout } = await executeFile("git", ["rev-parse", "HEAD"], { cwd: directory, encoding: "utf8" });
|
|
1974
|
+
const commit = stdout.trim();
|
|
1975
|
+
const catalogDirectory = import_node_path11.default.resolve(directory, source.path ?? ".");
|
|
1976
|
+
const relativeCatalogDirectory = import_node_path11.default.relative(directory, catalogDirectory);
|
|
1977
|
+
if (relativeCatalogDirectory.startsWith("..") || import_node_path11.default.isAbsolute(relativeCatalogDirectory)) {
|
|
1978
|
+
throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
|
|
1979
|
+
}
|
|
1980
|
+
const index = await (0, import_sdk.default)(catalogDirectory).buildIndex({ source: source.id, commit });
|
|
1981
|
+
return { bytes: Buffer.from(JSON.stringify(index)), index, commit, generated: true };
|
|
1982
|
+
});
|
|
1983
|
+
var fetchPublishedIndex = async (source, ref, fetcher, token) => {
|
|
1984
|
+
const indexPath = import_node_path11.default.posix.join(source.path ?? ".", "catalog.index.json");
|
|
1985
|
+
const bytes = await fetchBytes(source, ref, indexPath, fetcher, token);
|
|
1986
|
+
if (!bytes) return void 0;
|
|
1987
|
+
const index = (0, import_sdk.parseIndex)(JSON.parse(bytes.toString("utf8")));
|
|
1988
|
+
if (index.source !== source.id) {
|
|
1989
|
+
throw new Error(`Published index source "${index.source}" does not match configured id "${source.id}"`);
|
|
1990
|
+
}
|
|
1991
|
+
return { bytes, index, commit: index.commit, generated: false };
|
|
1992
|
+
};
|
|
1993
|
+
var createGitHubSourceProvider = (options = {}) => {
|
|
1994
|
+
const fetcher = options.fetch ?? ((url, init) => fetch(url, init));
|
|
1995
|
+
const executeFile = options.execFile ?? ((file, args, execOptions) => execFileAsync(file, args, execOptions));
|
|
1996
|
+
const configuredToken = options.token ?? process.env.EVENTCATALOG_GITHUB_TOKEN ?? process.env.GITHUB_TOKEN;
|
|
1997
|
+
const token = configuredToken?.trim() || void 0;
|
|
1998
|
+
const checkout = options.checkout ?? createCheckout(executeFile, token);
|
|
1999
|
+
return {
|
|
2000
|
+
async resolve(source) {
|
|
2001
|
+
assertSafeCatalogPath(source);
|
|
2002
|
+
const ref = source.ref ?? "main";
|
|
2003
|
+
return await fetchPublishedIndex(source, ref, fetcher, token) ?? generateIndex(source, ref, checkout, executeFile);
|
|
2004
|
+
},
|
|
2005
|
+
async fetchContent({ source, commit, path: artifactPath }) {
|
|
2006
|
+
assertSafeCatalogPath(source);
|
|
2007
|
+
const catalogPath = import_node_path11.default.posix.join(source.path ?? ".", artifactPath);
|
|
2008
|
+
const content = await fetchBytes(source, commit, catalogPath, fetcher, token);
|
|
2009
|
+
if (!content) throw new Error(`Federated artifact not found for "${source.id}": ${artifactPath}`);
|
|
2010
|
+
return content;
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
};
|
|
2014
|
+
|
|
2015
|
+
// src/federation/public-assets.ts
|
|
2016
|
+
var import_node_crypto2 = require("crypto");
|
|
2017
|
+
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
2018
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
2019
|
+
var getContentHash2 = (content) => `sha256:${(0, import_node_crypto2.createHash)("sha256").update(content).digest("hex")}`;
|
|
2020
|
+
var getFileHash = async (filePath) => {
|
|
2021
|
+
try {
|
|
2022
|
+
const stat = await import_promises5.default.lstat(filePath);
|
|
2023
|
+
return stat.isFile() ? getContentHash2(await import_promises5.default.readFile(filePath)) : void 0;
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
if (error.code === "ENOENT") return void 0;
|
|
2026
|
+
throw error;
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
var getSafePath = (directory, relativePath) => {
|
|
2030
|
+
const normalizedPath = import_node_path12.default.posix.normalize(relativePath);
|
|
2031
|
+
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("../");
|
|
2032
|
+
if (unsafe) return void 0;
|
|
2033
|
+
const resolvedDirectory = import_node_path12.default.resolve(directory);
|
|
2034
|
+
const resolvedPath = import_node_path12.default.resolve(resolvedDirectory, relativePath);
|
|
2035
|
+
const relativeResolvedPath = import_node_path12.default.relative(resolvedDirectory, resolvedPath);
|
|
2036
|
+
if (relativeResolvedPath === "" || relativeResolvedPath === ".." || relativeResolvedPath.startsWith(`..${import_node_path12.default.sep}`) || import_node_path12.default.isAbsolute(relativeResolvedPath)) {
|
|
2037
|
+
return void 0;
|
|
2038
|
+
}
|
|
2039
|
+
return resolvedPath;
|
|
2040
|
+
};
|
|
2041
|
+
var listFiles = async (directory, relativeDirectory = "") => {
|
|
2042
|
+
let entries;
|
|
2043
|
+
try {
|
|
2044
|
+
entries = await import_promises5.default.readdir(import_node_path12.default.join(directory, relativeDirectory), { withFileTypes: true });
|
|
2045
|
+
} catch (error) {
|
|
2046
|
+
if (error.code === "ENOENT") return [];
|
|
2047
|
+
throw error;
|
|
2048
|
+
}
|
|
2049
|
+
const files = await Promise.all(
|
|
2050
|
+
entries.map(async (entry) => {
|
|
2051
|
+
const relativePath = import_node_path12.default.join(relativeDirectory, entry.name);
|
|
2052
|
+
if (entry.isDirectory()) return listFiles(directory, relativePath);
|
|
2053
|
+
return entry.isFile() ? [relativePath.split(import_node_path12.default.sep).join("/")] : [];
|
|
2054
|
+
})
|
|
2055
|
+
);
|
|
2056
|
+
return files.flat().sort();
|
|
2057
|
+
};
|
|
2058
|
+
var pathExists = async (filePath) => {
|
|
2059
|
+
try {
|
|
2060
|
+
await import_promises5.default.lstat(filePath);
|
|
2061
|
+
return true;
|
|
2062
|
+
} catch (error) {
|
|
2063
|
+
if (error.code === "ENOENT") return false;
|
|
2064
|
+
throw error;
|
|
2065
|
+
}
|
|
2066
|
+
};
|
|
2067
|
+
var hasBlockingParent = async (publicDirectory, destinationPath) => {
|
|
2068
|
+
let currentPath = import_node_path12.default.dirname(destinationPath);
|
|
2069
|
+
while (currentPath !== publicDirectory) {
|
|
2070
|
+
try {
|
|
2071
|
+
if (!(await import_promises5.default.lstat(currentPath)).isDirectory()) return true;
|
|
2072
|
+
} catch (error) {
|
|
2073
|
+
if (error.code !== "ENOENT") throw error;
|
|
2074
|
+
}
|
|
2075
|
+
currentPath = import_node_path12.default.dirname(currentPath);
|
|
2076
|
+
}
|
|
2077
|
+
return false;
|
|
2078
|
+
};
|
|
2079
|
+
var pruneEmptyDirectories = async (publicDirectory, filePath) => {
|
|
2080
|
+
let currentPath = import_node_path12.default.dirname(filePath);
|
|
2081
|
+
while (currentPath !== publicDirectory) {
|
|
2082
|
+
try {
|
|
2083
|
+
await import_promises5.default.rmdir(currentPath);
|
|
2084
|
+
} catch (error) {
|
|
2085
|
+
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
|
|
2086
|
+
if (error.code === "ENOTEMPTY") return;
|
|
2087
|
+
}
|
|
2088
|
+
currentPath = import_node_path12.default.dirname(currentPath);
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
var composePublicAssets = async ({
|
|
2092
|
+
projectDirectory,
|
|
2093
|
+
federatedDirectory,
|
|
2094
|
+
assets,
|
|
2095
|
+
previousFiles = {},
|
|
2096
|
+
collisionPaths = /* @__PURE__ */ new Set()
|
|
2097
|
+
}) => {
|
|
2098
|
+
const publicDirectory = import_node_path12.default.resolve(projectDirectory, "public");
|
|
2099
|
+
const federatedPublicDirectory = import_node_path12.default.resolve(federatedDirectory, "public");
|
|
2100
|
+
const sourceFiles = await listFiles(federatedPublicDirectory);
|
|
2101
|
+
const sourceFileSet = new Set(sourceFiles);
|
|
2102
|
+
const managedFiles = /* @__PURE__ */ new Set();
|
|
2103
|
+
for (const [relativePath, previousFile] of Object.entries(previousFiles)) {
|
|
2104
|
+
const destinationPath = getSafePath(publicDirectory, relativePath);
|
|
2105
|
+
if (destinationPath && await getFileHash(destinationPath) === previousFile.hash) managedFiles.add(relativePath);
|
|
2106
|
+
}
|
|
2107
|
+
let removed = 0;
|
|
2108
|
+
for (const relativePath of managedFiles) {
|
|
2109
|
+
if (sourceFileSet.has(relativePath)) continue;
|
|
2110
|
+
const destinationPath = getSafePath(publicDirectory, relativePath);
|
|
2111
|
+
if (!destinationPath) continue;
|
|
2112
|
+
await import_promises5.default.rm(destinationPath, { force: true });
|
|
2113
|
+
await pruneEmptyDirectories(publicDirectory, destinationPath);
|
|
2114
|
+
removed += 1;
|
|
2115
|
+
}
|
|
2116
|
+
const publicAssetsByPath = new Map(
|
|
2117
|
+
assets.filter((asset) => asset.path.startsWith("public/")).map((asset) => [asset.path.slice("public/".length), asset])
|
|
2118
|
+
);
|
|
2119
|
+
const files = {};
|
|
2120
|
+
let copied = 0;
|
|
2121
|
+
let skipped = 0;
|
|
2122
|
+
let overwritten = 0;
|
|
2123
|
+
for (const relativePath of sourceFiles) {
|
|
2124
|
+
const sourcePath = getSafePath(federatedPublicDirectory, relativePath);
|
|
2125
|
+
const destinationPath = getSafePath(publicDirectory, relativePath);
|
|
2126
|
+
if (!sourcePath || !destinationPath) throw new Error(`Unsafe federated public asset path "${relativePath}"`);
|
|
2127
|
+
const mainCatalogOwnsPath = await pathExists(destinationPath) && !managedFiles.has(relativePath) || await hasBlockingParent(publicDirectory, destinationPath);
|
|
2128
|
+
if (mainCatalogOwnsPath) {
|
|
2129
|
+
skipped += 1;
|
|
2130
|
+
continue;
|
|
2131
|
+
}
|
|
2132
|
+
const asset = publicAssetsByPath.get(relativePath);
|
|
2133
|
+
if (!asset) throw new Error(`Cannot identify the source of federated public asset "${relativePath}"`);
|
|
2134
|
+
const content = await import_promises5.default.readFile(sourcePath);
|
|
2135
|
+
await import_promises5.default.mkdir(import_node_path12.default.dirname(destinationPath), { recursive: true });
|
|
2136
|
+
await import_promises5.default.writeFile(destinationPath, content);
|
|
2137
|
+
files[relativePath] = { source: asset.resolvedFrom.source, hash: getContentHash2(content) };
|
|
2138
|
+
copied += 1;
|
|
2139
|
+
if (collisionPaths.has(`public/${relativePath}`)) overwritten += 1;
|
|
2140
|
+
}
|
|
2141
|
+
await import_promises5.default.rm(federatedPublicDirectory, { recursive: true, force: true });
|
|
2142
|
+
return { files, copied, skipped, overwritten, removed };
|
|
2143
|
+
};
|
|
2144
|
+
|
|
2145
|
+
// src/federation/federate.ts
|
|
2146
|
+
var FederationConflictError = class extends Error {
|
|
2147
|
+
conflicts;
|
|
2148
|
+
constructor(conflicts) {
|
|
2149
|
+
const details = conflicts.map((conflict) => `${conflict.id}: ${conflict.sources.join(", ")}`).join("\n");
|
|
2150
|
+
super(`Federation conflicts prevent hydration:
|
|
2151
|
+
${details}`);
|
|
2152
|
+
this.name = "FederationConflictError";
|
|
2153
|
+
this.conflicts = conflicts;
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
var validateSources = (sources) => {
|
|
2157
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2158
|
+
for (const source of sources) {
|
|
2159
|
+
if (!source.id?.trim()) throw new Error("Every federation source requires a stable id.");
|
|
2160
|
+
if (ids.has(source.id)) throw new Error(`Federation source id "${source.id}" is configured more than once.`);
|
|
2161
|
+
ids.add(source.id);
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
var writeLock = async (lockPath, lock) => {
|
|
2165
|
+
const temporaryPath = `${lockPath}.tmp-${process.pid}`;
|
|
2166
|
+
try {
|
|
2167
|
+
await import_promises6.default.writeFile(temporaryPath, `${JSON.stringify(lock, null, 2)}
|
|
2168
|
+
`, "utf8");
|
|
2169
|
+
await import_promises6.default.rename(temporaryPath, lockPath);
|
|
2170
|
+
} finally {
|
|
2171
|
+
await import_promises6.default.rm(temporaryPath, { force: true });
|
|
2172
|
+
}
|
|
2173
|
+
};
|
|
2174
|
+
var readLock = async (lockPath) => {
|
|
2175
|
+
try {
|
|
2176
|
+
return JSON.parse(await import_promises6.default.readFile(lockPath, "utf8"));
|
|
2177
|
+
} catch (error) {
|
|
2178
|
+
if (error.code === "ENOENT") return void 0;
|
|
2179
|
+
throw new Error(`Cannot read federation lock at "${lockPath}"`, { cause: error });
|
|
2180
|
+
}
|
|
2181
|
+
};
|
|
2182
|
+
var pathExists2 = async (filePath) => {
|
|
2183
|
+
try {
|
|
2184
|
+
await import_promises6.default.access(filePath);
|
|
2185
|
+
return true;
|
|
2186
|
+
} catch (error) {
|
|
2187
|
+
if (error.code === "ENOENT") return false;
|
|
2188
|
+
throw error;
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
2191
|
+
var cleanupPreviousFederation = async (projectDirectory, onProgress) => {
|
|
2192
|
+
const outDir = import_node_path13.default.join(projectDirectory, "federated");
|
|
2193
|
+
const lockPath = import_node_path13.default.join(projectDirectory, "eventcatalog.lock");
|
|
2194
|
+
const previousLock = await readLock(lockPath);
|
|
2195
|
+
const hadFederatedOutput = await pathExists2(outDir);
|
|
2196
|
+
const hadLock = previousLock !== void 0;
|
|
2197
|
+
if (!hadFederatedOutput && !hadLock) return;
|
|
2198
|
+
await import_promises6.default.rm(outDir, { recursive: true, force: true });
|
|
2199
|
+
const publicResult = await composePublicAssets({
|
|
2200
|
+
projectDirectory,
|
|
2201
|
+
federatedDirectory: outDir,
|
|
2202
|
+
assets: [],
|
|
2203
|
+
previousFiles: previousLock?.publicFiles
|
|
2204
|
+
});
|
|
2205
|
+
await import_promises6.default.rm(lockPath, { force: true });
|
|
2206
|
+
onProgress?.({
|
|
2207
|
+
type: "cleanup:complete",
|
|
2208
|
+
federated: hadFederatedOutput,
|
|
2209
|
+
publicFiles: publicResult.removed,
|
|
2210
|
+
lock: hadLock
|
|
2211
|
+
});
|
|
2212
|
+
};
|
|
2213
|
+
var federateCatalog = async (projectDirectory, options = {}) => {
|
|
2214
|
+
const config = await getEventCatalogConfigFile(projectDirectory);
|
|
2215
|
+
const sources = config.federation?.sources ?? [];
|
|
2216
|
+
options.onProgress?.({ type: "configured", sources: sources.length });
|
|
2217
|
+
if (sources.length === 0) {
|
|
2218
|
+
await cleanupPreviousFederation(projectDirectory, options.onProgress);
|
|
2219
|
+
return null;
|
|
2220
|
+
}
|
|
2221
|
+
if (options.isFederationEnabled && !await options.isFederationEnabled()) {
|
|
2222
|
+
throw new Error(
|
|
2223
|
+
"Cannot federate catalogs: EventCatalog federation is an Enterprise feature. Visit https://www.eventcatalog.dev/pricing to enable federation."
|
|
2224
|
+
);
|
|
2225
|
+
}
|
|
2226
|
+
validateSources(sources);
|
|
2227
|
+
if (options.useCache === false) options.onProgress?.({ type: "cache:disabled" });
|
|
2228
|
+
const provider = options.provider ?? createGitHubSourceProvider();
|
|
2229
|
+
const resolvedSources = [];
|
|
2230
|
+
for (const [index, source] of sources.entries()) {
|
|
2231
|
+
const current = index + 1;
|
|
2232
|
+
options.onProgress?.({ type: "source:start", source, current, total: sources.length });
|
|
2233
|
+
try {
|
|
2234
|
+
const resolved = await provider.resolve(source);
|
|
2235
|
+
resolvedSources.push({ config: source, resolved });
|
|
2236
|
+
options.onProgress?.({
|
|
2237
|
+
type: "source:complete",
|
|
2238
|
+
source,
|
|
2239
|
+
current,
|
|
2240
|
+
total: sources.length,
|
|
2241
|
+
commit: resolved.commit,
|
|
2242
|
+
resources: resolved.index.resources.length,
|
|
2243
|
+
generated: resolved.generated
|
|
2244
|
+
});
|
|
2245
|
+
} catch (error) {
|
|
2246
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2247
|
+
throw new Error(`Failed to federate source "${source.id}": ${message}`, { cause: error });
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
const outDir = import_node_path13.default.join(projectDirectory, "federated");
|
|
2251
|
+
const lockPath = import_node_path13.default.join(projectDirectory, "eventcatalog.lock");
|
|
2252
|
+
const previousLock = await readLock(lockPath);
|
|
2253
|
+
const resources = resolvedSources.reduce((total, source) => total + source.resolved.index.resources.length, 0);
|
|
2254
|
+
const remoteIndexes = resolvedSources.map(({ resolved }) => resolved.index);
|
|
2255
|
+
options.onProgress?.({ type: "local:start" });
|
|
2256
|
+
const localIndex = await (0, import_sdk2.default)(projectDirectory).buildIndex({
|
|
2257
|
+
source: config.cId,
|
|
2258
|
+
commit: "local",
|
|
2259
|
+
hashContent: false,
|
|
2260
|
+
includeFederated: false
|
|
2261
|
+
});
|
|
2262
|
+
options.onProgress?.({ type: "local:complete", resources: localIndex.resources.length });
|
|
2263
|
+
options.onProgress?.({ type: "resolving", resources, localResources: localIndex.resources.length });
|
|
2264
|
+
const ownershipGraph = (0, import_sdk2.resolve)([localIndex, ...remoteIndexes]);
|
|
2265
|
+
if (ownershipGraph.conflicts.length > 0) {
|
|
2266
|
+
options.onProgress?.({ type: "resolved", graph: ownershipGraph });
|
|
2267
|
+
throw new FederationConflictError(ownershipGraph.conflicts);
|
|
2268
|
+
}
|
|
2269
|
+
const graph = (0, import_sdk2.resolve)(remoteIndexes);
|
|
2270
|
+
options.onProgress?.({ type: "resolved", graph });
|
|
2271
|
+
const sourcesById = new Map(sources.map((source) => [source.id, source]));
|
|
2272
|
+
options.onProgress?.({ type: "hydrating", outDir });
|
|
2273
|
+
let hydratedFiles = 0;
|
|
2274
|
+
let cachedFiles = 0;
|
|
2275
|
+
const hydrateResult = await (0, import_sdk2.hydrate)(graph, {
|
|
2276
|
+
outDir,
|
|
2277
|
+
cache: createFederationContentCache(projectDirectory, {
|
|
2278
|
+
read: options.useCache !== false,
|
|
2279
|
+
onHit: () => {
|
|
2280
|
+
cachedFiles += 1;
|
|
2281
|
+
options.onProgress?.({ type: "hydrate:cache", files: cachedFiles });
|
|
2282
|
+
}
|
|
2283
|
+
}),
|
|
2284
|
+
modes: Object.fromEntries(sources.map((source) => [source.id, source.mode ?? "hydrate"])),
|
|
2285
|
+
fetch: async ({ source: sourceId, commit, path: artifactPath }) => {
|
|
2286
|
+
const source = sourcesById.get(sourceId);
|
|
2287
|
+
if (!source) throw new Error(`Cannot fetch content for unconfigured source "${sourceId}"`);
|
|
2288
|
+
const content = await provider.fetchContent({ source, commit, path: artifactPath });
|
|
2289
|
+
hydratedFiles += 1;
|
|
2290
|
+
options.onProgress?.({ type: "hydrate:file", files: hydratedFiles, source: sourceId, path: artifactPath });
|
|
2291
|
+
return content;
|
|
2292
|
+
}
|
|
2293
|
+
});
|
|
2294
|
+
const publicResult = await composePublicAssets({
|
|
2295
|
+
projectDirectory,
|
|
2296
|
+
federatedDirectory: outDir,
|
|
2297
|
+
assets: graph.assets,
|
|
2298
|
+
previousFiles: previousLock?.publicFiles,
|
|
2299
|
+
collisionPaths: new Set(
|
|
2300
|
+
graph.warnings.filter((warning) => warning.kind === "asset-collision").map((warning) => warning.path)
|
|
2301
|
+
)
|
|
2302
|
+
});
|
|
2303
|
+
options.onProgress?.({ type: "public:complete", result: publicResult });
|
|
2304
|
+
const resolvedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
2305
|
+
await writeLock(lockPath, {
|
|
2306
|
+
lockVersion: 1,
|
|
2307
|
+
sources: resolvedSources.map(({ config: source, resolved }) => ({
|
|
2308
|
+
id: source.id,
|
|
2309
|
+
digest: `sha256:${(0, import_node_crypto3.createHash)("sha256").update(resolved.bytes).digest("hex")}`,
|
|
2310
|
+
commit: resolved.commit,
|
|
2311
|
+
resolvedAt
|
|
2312
|
+
})).sort((left, right) => left.id.localeCompare(right.id)),
|
|
2313
|
+
publicFiles: publicResult.files
|
|
2314
|
+
});
|
|
2315
|
+
const result = {
|
|
2316
|
+
sources: sources.length,
|
|
2317
|
+
resources,
|
|
2318
|
+
graph,
|
|
2319
|
+
hydrate: hydrateResult,
|
|
2320
|
+
public: publicResult,
|
|
2321
|
+
outDir,
|
|
2322
|
+
lockPath
|
|
2323
|
+
};
|
|
2324
|
+
options.onProgress?.({ type: "complete", result });
|
|
2325
|
+
return result;
|
|
2326
|
+
};
|
|
2327
|
+
|
|
1848
2328
|
// src/eventcatalog.ts
|
|
1849
2329
|
var import_license = require("@eventcatalog/license");
|
|
1850
|
-
var currentDir =
|
|
2330
|
+
var currentDir = import_node_path15.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
1851
2331
|
var program = new import_commander.Command().version(VERSION);
|
|
1852
|
-
var dir =
|
|
1853
|
-
var core =
|
|
1854
|
-
var eventCatalogDir =
|
|
2332
|
+
var dir = import_node_path15.default.resolve(process.env.PROJECT_DIR || process.cwd());
|
|
2333
|
+
var core = import_node_path15.default.resolve(process.env.CATALOG_DIR || (0, import_node_path14.join)(dir, ".eventcatalog-core"));
|
|
2334
|
+
var eventCatalogDir = import_node_path15.default.resolve((0, import_node_path14.join)(currentDir, "../eventcatalog/"));
|
|
1855
2335
|
var getInstalledEventCatalogVersion = () => {
|
|
1856
2336
|
try {
|
|
1857
|
-
const pkg = import_fs3.default.readFileSync((0,
|
|
2337
|
+
const pkg = import_fs3.default.readFileSync((0, import_node_path14.join)(dir, "package.json"), "utf8");
|
|
1858
2338
|
const json = JSON.parse(pkg);
|
|
1859
2339
|
return json.dependencies["@eventcatalog/core"];
|
|
1860
2340
|
} catch (error) {
|
|
@@ -1884,7 +2364,7 @@ var startDevPrewarm = ({
|
|
|
1884
2364
|
initialDelayMs = 500
|
|
1885
2365
|
}) => {
|
|
1886
2366
|
let attempt = 0;
|
|
1887
|
-
const hit = (requestPath) => new Promise((
|
|
2367
|
+
const hit = (requestPath) => new Promise((resolve3) => {
|
|
1888
2368
|
const req = import_node_http.default.get(
|
|
1889
2369
|
{
|
|
1890
2370
|
hostname: "127.0.0.1",
|
|
@@ -1894,13 +2374,13 @@ var startDevPrewarm = ({
|
|
|
1894
2374
|
},
|
|
1895
2375
|
(res) => {
|
|
1896
2376
|
res.resume();
|
|
1897
|
-
|
|
2377
|
+
resolve3(true);
|
|
1898
2378
|
}
|
|
1899
2379
|
);
|
|
1900
|
-
req.on("error", () =>
|
|
2380
|
+
req.on("error", () => resolve3(false));
|
|
1901
2381
|
req.on("timeout", () => {
|
|
1902
2382
|
req.destroy();
|
|
1903
|
-
|
|
2383
|
+
resolve3(false);
|
|
1904
2384
|
});
|
|
1905
2385
|
});
|
|
1906
2386
|
const tick = async () => {
|
|
@@ -1920,12 +2400,12 @@ var startDevPrewarm = ({
|
|
|
1920
2400
|
var buildDevSearchIndex = async ({ config }) => {
|
|
1921
2401
|
const result = await buildSearchIndex({
|
|
1922
2402
|
projectDir: dir,
|
|
1923
|
-
outDir:
|
|
1924
|
-
searchOutputPath:
|
|
2403
|
+
outDir: import_node_path15.default.join(core, "public"),
|
|
2404
|
+
searchOutputPath: import_node_path15.default.join(core, "public", "pagefind"),
|
|
1925
2405
|
config,
|
|
1926
2406
|
isServer: false
|
|
1927
2407
|
});
|
|
1928
|
-
logger.info(`Indexed ${result.records} page(s) into ${
|
|
2408
|
+
logger.info(`Indexed ${result.records} page(s) into ${import_node_path15.default.relative(core, result.outputPath)}`, "search");
|
|
1929
2409
|
};
|
|
1930
2410
|
var warnIfIndexedSearchUsesAuth = async () => {
|
|
1931
2411
|
if (!await isAuthEnabled()) {
|
|
@@ -1982,8 +2462,8 @@ var runCommandWithFilteredOutput = async ({
|
|
|
1982
2462
|
shouldFilterLine,
|
|
1983
2463
|
transformLine = (line) => line
|
|
1984
2464
|
}) => {
|
|
1985
|
-
await new Promise((
|
|
1986
|
-
const child = (0,
|
|
2465
|
+
await new Promise((resolve3, reject) => {
|
|
2466
|
+
const child = (0, import_node_child_process2.spawn)(command, {
|
|
1987
2467
|
cwd,
|
|
1988
2468
|
env: {
|
|
1989
2469
|
...process.env,
|
|
@@ -2025,7 +2505,7 @@ var runCommandWithFilteredOutput = async ({
|
|
|
2025
2505
|
stdoutBuffer = flush(stdoutBuffer, process.stdout, true);
|
|
2026
2506
|
stderrBuffer = flush(stderrBuffer, process.stderr, true);
|
|
2027
2507
|
if (code === 0) {
|
|
2028
|
-
|
|
2508
|
+
resolve3();
|
|
2029
2509
|
return;
|
|
2030
2510
|
}
|
|
2031
2511
|
reject(new Error(`Command failed with exit code ${code}: ${command}`));
|
|
@@ -2040,12 +2520,12 @@ var copyCore = () => {
|
|
|
2040
2520
|
import_fs3.default.cpSync(eventCatalogDir, core, {
|
|
2041
2521
|
recursive: true,
|
|
2042
2522
|
filter: (src) => {
|
|
2043
|
-
const relativePath =
|
|
2044
|
-
const pathParts = relativePath.split(
|
|
2523
|
+
const relativePath = import_node_path15.default.relative(eventCatalogDir, src);
|
|
2524
|
+
const pathParts = relativePath.split(import_node_path15.default.sep);
|
|
2045
2525
|
return !pathParts.some((part) => [".astro", "dist", "node_modules"].includes(part));
|
|
2046
2526
|
}
|
|
2047
2527
|
});
|
|
2048
|
-
const coreNodeModules =
|
|
2528
|
+
const coreNodeModules = import_node_path15.default.join(core, "node_modules");
|
|
2049
2529
|
const installedCoreNodeModules = resolveInstalledCoreNodeModules(currentDir);
|
|
2050
2530
|
linkCoreNodeModules({ coreNodeModules, installedCoreNodeModules });
|
|
2051
2531
|
};
|
|
@@ -2104,8 +2584,8 @@ program.command("dev").description("Run development server of EventCatalog").opt
|
|
|
2104
2584
|
logger.info("Setting up EventCatalog...", "eventcatalog");
|
|
2105
2585
|
const isServer = await isOutputServer();
|
|
2106
2586
|
logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
|
|
2107
|
-
if (import_fs3.default.existsSync(
|
|
2108
|
-
import_dotenv.default.config({ path:
|
|
2587
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2588
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2109
2589
|
}
|
|
2110
2590
|
if (options.debug) {
|
|
2111
2591
|
logger.info("Debug mode enabled", "debug");
|
|
@@ -2185,8 +2665,8 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2185
2665
|
logger.info("Building EventCatalog...", "build");
|
|
2186
2666
|
const isServer = await isOutputServer();
|
|
2187
2667
|
logger.info(isServer ? "EventCatalog is running in Server Mode" : "EventCatalog is running in Static Mode", "config");
|
|
2188
|
-
if (import_fs3.default.existsSync(
|
|
2189
|
-
import_dotenv.default.config({ path:
|
|
2668
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2669
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2190
2670
|
}
|
|
2191
2671
|
await verifyRequiredFieldsAreInCatalogConfigFile(dir);
|
|
2192
2672
|
copyCore();
|
|
@@ -2236,7 +2716,7 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2236
2716
|
if (await isIndexedSearchEnabled()) {
|
|
2237
2717
|
await warnIfIndexedSearchUsesAuth();
|
|
2238
2718
|
const config = await getEventCatalogConfigFile(dir);
|
|
2239
|
-
const outDir =
|
|
2719
|
+
const outDir = import_node_path15.default.resolve(dir, await getProjectOutDir());
|
|
2240
2720
|
logger.info("Building indexed search...", "search");
|
|
2241
2721
|
const result = await buildSearchIndex({
|
|
2242
2722
|
projectDir: dir,
|
|
@@ -2244,7 +2724,7 @@ program.command("build").description("Run build of EventCatalog").action(async (
|
|
|
2244
2724
|
config,
|
|
2245
2725
|
isServer
|
|
2246
2726
|
});
|
|
2247
|
-
logger.info(`Indexed ${result.records} page(s) into ${
|
|
2727
|
+
logger.info(`Indexed ${result.records} page(s) into ${import_node_path15.default.relative(dir, result.outputPath)}`, "search");
|
|
2248
2728
|
}
|
|
2249
2729
|
});
|
|
2250
2730
|
var previewCatalog = async ({
|
|
@@ -2271,7 +2751,7 @@ var startServerCatalog = async ({
|
|
|
2271
2751
|
isEventCatalogStarter = false,
|
|
2272
2752
|
isEventCatalogScale = false
|
|
2273
2753
|
}) => {
|
|
2274
|
-
const serverEntryPath =
|
|
2754
|
+
const serverEntryPath = import_node_path15.default.join(dir, "dist", "server", "entry.mjs");
|
|
2275
2755
|
await runCommandWithFilteredOutput({
|
|
2276
2756
|
command: `node "${serverEntryPath}"`,
|
|
2277
2757
|
cwd: core,
|
|
@@ -2288,8 +2768,8 @@ var startServerCatalog = async ({
|
|
|
2288
2768
|
program.command("preview").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
2289
2769
|
logger.welcome();
|
|
2290
2770
|
logger.info("Starting preview of your build...", "preview");
|
|
2291
|
-
if (import_fs3.default.existsSync(
|
|
2292
|
-
import_dotenv.default.config({ path:
|
|
2771
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2772
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2293
2773
|
}
|
|
2294
2774
|
const canEmbedPages = await (0, import_license.isFeatureEnabled)(
|
|
2295
2775
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
@@ -2307,8 +2787,8 @@ program.command("preview").description("Serves the contents of your eventcatalog
|
|
|
2307
2787
|
program.command("start").description("Serves the contents of your eventcatalog build directory").action(async (options, command) => {
|
|
2308
2788
|
logger.welcome();
|
|
2309
2789
|
logger.info("Starting preview of your build...", "preview");
|
|
2310
|
-
if (import_fs3.default.existsSync(
|
|
2311
|
-
import_dotenv.default.config({ path:
|
|
2790
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2791
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2312
2792
|
}
|
|
2313
2793
|
const canEmbedPages = await (0, import_license.isFeatureEnabled)(
|
|
2314
2794
|
"@eventcatalog/backstage-plugin-eventcatalog",
|
|
@@ -2335,25 +2815,145 @@ program.command("start").description("Serves the contents of your eventcatalog b
|
|
|
2335
2815
|
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) => {
|
|
2336
2816
|
logger.welcome();
|
|
2337
2817
|
logger.info("Exporting EventCatalog...", "export");
|
|
2338
|
-
if (import_fs3.default.existsSync(
|
|
2339
|
-
import_dotenv.default.config({ path:
|
|
2818
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2819
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2340
2820
|
}
|
|
2341
2821
|
const { default: initSDK } = await import("@eventcatalog/sdk");
|
|
2342
2822
|
const sdk = initSDK(dir);
|
|
2343
2823
|
const catalog = await sdk.dumpCatalog({ includeMarkdown: options.includeMarkdown });
|
|
2344
|
-
const exportsDir =
|
|
2824
|
+
const exportsDir = import_node_path15.default.join(dir, "exports");
|
|
2345
2825
|
ensureDir(exportsDir);
|
|
2346
2826
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2347
|
-
const exportFile =
|
|
2827
|
+
const exportFile = import_node_path15.default.join(exportsDir, `catalog-${date}.json`);
|
|
2348
2828
|
import_fs3.default.writeFileSync(exportFile, JSON.stringify(catalog, null, 2), "utf-8");
|
|
2349
2829
|
logger.info(`Catalog exported to ${exportFile}`, "export");
|
|
2350
2830
|
});
|
|
2351
2831
|
program.command("generate [siteDir]").description("Start the generator scripts.").action(async () => {
|
|
2352
|
-
if (import_fs3.default.existsSync(
|
|
2353
|
-
import_dotenv.default.config({ path:
|
|
2832
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2833
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2354
2834
|
}
|
|
2355
2835
|
await generate(dir);
|
|
2356
2836
|
});
|
|
2837
|
+
var reportFederationProgress = (event) => {
|
|
2838
|
+
switch (event.type) {
|
|
2839
|
+
case "configured":
|
|
2840
|
+
if (event.sources > 0)
|
|
2841
|
+
logger.info(`Found ${event.sources} configured source${event.sources === 1 ? "" : "s"}`, "federation");
|
|
2842
|
+
return;
|
|
2843
|
+
case "cleanup:complete":
|
|
2844
|
+
logger.success(
|
|
2845
|
+
`No sources configured; removed previous federation output${event.publicFiles > 0 ? ` and ${event.publicFiles} managed public file${event.publicFiles === 1 ? "" : "s"}` : ""}.`,
|
|
2846
|
+
"federation"
|
|
2847
|
+
);
|
|
2848
|
+
return;
|
|
2849
|
+
case "cache:disabled":
|
|
2850
|
+
logger.info("Content cache disabled; all federated files will be downloaded.", "federation");
|
|
2851
|
+
return;
|
|
2852
|
+
case "source:start":
|
|
2853
|
+
logger.info(`[${event.current}/${event.total}] Fetching and indexing ${event.source.id}...`, "federation");
|
|
2854
|
+
return;
|
|
2855
|
+
case "source:complete":
|
|
2856
|
+
logger.success(
|
|
2857
|
+
`[${event.current}/${event.total}] ${event.source.id}: ${event.resources} resources at ${event.commit.slice(0, 7)}${event.generated ? " (index generated)" : " (published index)"}`,
|
|
2858
|
+
"federation"
|
|
2859
|
+
);
|
|
2860
|
+
return;
|
|
2861
|
+
case "local:start":
|
|
2862
|
+
logger.info("Indexing central catalog ownership...", "federation");
|
|
2863
|
+
return;
|
|
2864
|
+
case "local:complete":
|
|
2865
|
+
logger.success(
|
|
2866
|
+
`Central catalog: ${event.resources} local resource${event.resources === 1 ? "" : "s"} found (excluding federated/)`,
|
|
2867
|
+
"federation"
|
|
2868
|
+
);
|
|
2869
|
+
return;
|
|
2870
|
+
case "resolving":
|
|
2871
|
+
logger.info(
|
|
2872
|
+
`Validating ownership across ${event.localResources} local resource${event.localResources === 1 ? "" : "s"} and ${event.resources} remote resource${event.resources === 1 ? "" : "s"}...`,
|
|
2873
|
+
"federation"
|
|
2874
|
+
);
|
|
2875
|
+
return;
|
|
2876
|
+
case "resolved":
|
|
2877
|
+
if (event.graph.conflicts.length > 0) {
|
|
2878
|
+
logger.error(
|
|
2879
|
+
`${event.graph.conflicts.length} ownership conflict${event.graph.conflicts.length === 1 ? "" : "s"} found`,
|
|
2880
|
+
"federation"
|
|
2881
|
+
);
|
|
2882
|
+
for (const conflict of event.graph.conflicts.slice(0, 10)) {
|
|
2883
|
+
logger.error(`${conflict.id}: ${conflict.sources.join(", ")}`, "federation");
|
|
2884
|
+
}
|
|
2885
|
+
if (event.graph.conflicts.length > 10) {
|
|
2886
|
+
logger.error(`...and ${event.graph.conflicts.length - 10} more`, "federation");
|
|
2887
|
+
}
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
logger.success(
|
|
2891
|
+
`Graph resolved: ${event.graph.entities.length} remote resources, ${event.graph.edges.length} relationships`,
|
|
2892
|
+
"federation"
|
|
2893
|
+
);
|
|
2894
|
+
if (event.graph.warnings.length > 0) {
|
|
2895
|
+
logger.warning(
|
|
2896
|
+
`${event.graph.warnings.length} federation warning${event.graph.warnings.length === 1 ? "" : "s"}`,
|
|
2897
|
+
"federation"
|
|
2898
|
+
);
|
|
2899
|
+
}
|
|
2900
|
+
if (event.graph.externals.length > 0) {
|
|
2901
|
+
logger.warning(
|
|
2902
|
+
`${event.graph.externals.length} reference${event.graph.externals.length === 1 ? "" : "s"} remain external`,
|
|
2903
|
+
"federation"
|
|
2904
|
+
);
|
|
2905
|
+
}
|
|
2906
|
+
return;
|
|
2907
|
+
case "hydrating":
|
|
2908
|
+
logger.info(`Hydrating federated content into ${import_node_path15.default.relative(dir, event.outDir)}/...`, "federation");
|
|
2909
|
+
return;
|
|
2910
|
+
case "hydrate:cache":
|
|
2911
|
+
if (event.files === 1 || event.files % 25 === 0) {
|
|
2912
|
+
logger.info(`Reused ${event.files} cached file${event.files === 1 ? "" : "s"}...`, "federation");
|
|
2913
|
+
}
|
|
2914
|
+
return;
|
|
2915
|
+
case "hydrate:file":
|
|
2916
|
+
if (event.files === 1 || event.files % 25 === 0) {
|
|
2917
|
+
logger.info(`Fetched ${event.files} federated file${event.files === 1 ? "" : "s"}...`, "federation");
|
|
2918
|
+
}
|
|
2919
|
+
return;
|
|
2920
|
+
case "public:complete":
|
|
2921
|
+
logger.success(
|
|
2922
|
+
`Public assets: ${event.result.copied} copied, ${event.result.skipped} preserved from the main catalog, ${event.result.removed} stale removed`,
|
|
2923
|
+
"federation"
|
|
2924
|
+
);
|
|
2925
|
+
if (event.result.overwritten > 0) {
|
|
2926
|
+
logger.warning(
|
|
2927
|
+
`${event.result.overwritten} public asset conflict${event.result.overwritten === 1 ? "" : "s"} resolved using the last configured source`,
|
|
2928
|
+
"federation"
|
|
2929
|
+
);
|
|
2930
|
+
}
|
|
2931
|
+
return;
|
|
2932
|
+
case "complete":
|
|
2933
|
+
logger.success(
|
|
2934
|
+
`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)`,
|
|
2935
|
+
"federation"
|
|
2936
|
+
);
|
|
2937
|
+
logger.info(`Pinned source commits in ${import_node_path15.default.relative(dir, event.result.lockPath)}`, "federation");
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
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) => {
|
|
2941
|
+
logger.welcome();
|
|
2942
|
+
if (import_fs3.default.existsSync(import_node_path15.default.join(dir, ".env"))) {
|
|
2943
|
+
import_dotenv.default.config({ path: import_node_path15.default.join(dir, ".env") });
|
|
2944
|
+
}
|
|
2945
|
+
logger.info("Starting federation...", "federation");
|
|
2946
|
+
let cleanedPreviousOutput = false;
|
|
2947
|
+
const result = await federateCatalog(dir, {
|
|
2948
|
+
useCache: commandOptions.cache,
|
|
2949
|
+
isFederationEnabled: import_license.isEventCatalogScaleEnabled,
|
|
2950
|
+
onProgress: (event) => {
|
|
2951
|
+
if (event.type === "cleanup:complete") cleanedPreviousOutput = true;
|
|
2952
|
+
reportFederationProgress(event);
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
if (!result && !cleanedPreviousOutput) logger.warning("No federation sources configured; nothing to do.", "federation");
|
|
2956
|
+
});
|
|
2357
2957
|
program.addHelpText(
|
|
2358
2958
|
"after",
|
|
2359
2959
|
`
|
|
@@ -2363,6 +2963,6 @@ program.addHelpText(
|
|
|
2363
2963
|
`
|
|
2364
2964
|
);
|
|
2365
2965
|
program.parseAsync().then(() => process.exit(0)).catch((err) => {
|
|
2366
|
-
console.error(err);
|
|
2966
|
+
if (!(err instanceof FederationConflictError)) console.error(err);
|
|
2367
2967
|
process.exit(1);
|
|
2368
2968
|
});
|