@eventcatalog/core 4.6.2 → 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.
Files changed (44) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/chunk-352FGP6W.js +123 -0
  6. package/dist/{chunk-AT3AQ3PK.js → chunk-4BBXOL6K.js} +1 -1
  7. package/dist/{chunk-MDCIQJRX.js → chunk-54KFOI6Z.js} +3 -3
  8. package/dist/chunk-5EPNFDT5.js +133 -0
  9. package/dist/{chunk-U6E25IFA.js → chunk-FZJDCQZE.js} +1 -1
  10. package/dist/{chunk-LQLAEEOV.js → chunk-LB5VXS6X.js} +1 -1
  11. package/dist/{chunk-LZDDSWXC.js → chunk-PWY4SP6L.js} +1 -1
  12. package/dist/chunk-R7Z5ALYI.js +44 -0
  13. package/dist/chunk-ZR6AH5Z2.js +204 -0
  14. package/dist/constants.cjs +1 -1
  15. package/dist/constants.js +1 -1
  16. package/dist/eventcatalog.cjs +651 -51
  17. package/dist/eventcatalog.config.d.cts +18 -1
  18. package/dist/eventcatalog.config.d.ts +18 -1
  19. package/dist/eventcatalog.js +134 -7
  20. package/dist/federation/content-cache.cjs +78 -0
  21. package/dist/federation/content-cache.d.cts +9 -0
  22. package/dist/federation/content-cache.d.ts +9 -0
  23. package/dist/federation/content-cache.js +6 -0
  24. package/dist/federation/federate.cjs +588 -0
  25. package/dist/federation/federate.d.cts +81 -0
  26. package/dist/federation/federate.d.ts +81 -0
  27. package/dist/federation/federate.js +12 -0
  28. package/dist/federation/github-source-provider.cjs +157 -0
  29. package/dist/federation/github-source-provider.d.cts +24 -0
  30. package/dist/federation/github-source-provider.d.ts +24 -0
  31. package/dist/federation/github-source-provider.js +6 -0
  32. package/dist/federation/public-assets.cjs +167 -0
  33. package/dist/federation/public-assets.d.cts +24 -0
  34. package/dist/federation/public-assets.d.ts +24 -0
  35. package/dist/federation/public-assets.js +6 -0
  36. package/dist/federation/types.cjs +18 -0
  37. package/dist/federation/types.d.cts +19 -0
  38. package/dist/federation/types.d.ts +19 -0
  39. package/dist/federation/types.js +0 -0
  40. package/dist/generate.cjs +1 -1
  41. package/dist/generate.js +3 -3
  42. package/dist/utils/cli-logger.cjs +1 -1
  43. package/dist/utils/cli-logger.js +2 -2
  44. package/package.json +3 -3
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(analytics_exports);
36
36
  var import_os = __toESM(require("os"), 1);
37
37
 
38
38
  // package.json
39
- var version = "4.6.2";
39
+ var version = "4.6.3";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-LZDDSWXC.js";
4
- import "../chunk-LQLAEEOV.js";
3
+ } from "../chunk-PWY4SP6L.js";
4
+ import "../chunk-LB5VXS6X.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -140,7 +140,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
140
140
  var import_os = __toESM(require("os"), 1);
141
141
 
142
142
  // package.json
143
- var version = "4.6.2";
143
+ var version = "4.6.3";
144
144
 
145
145
  // src/constants.ts
146
146
  var VERSION = version;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-MDCIQJRX.js";
3
+ } from "../chunk-54KFOI6Z.js";
4
+ import "../chunk-PWY4SP6L.js";
4
5
  import "../chunk-K2XIENVT.js";
5
- import "../chunk-LZDDSWXC.js";
6
- import "../chunk-LQLAEEOV.js";
6
+ import "../chunk-LB5VXS6X.js";
7
7
  import "../chunk-6QENHZZP.js";
8
8
  export {
9
9
  log_build_default as default
@@ -0,0 +1,123 @@
1
+ // src/federation/github-source-provider.ts
2
+ import { execFile } from "child_process";
3
+ import fs from "fs/promises";
4
+ import os from "os";
5
+ import path from "path";
6
+ import { promisify } from "util";
7
+ import createSDK, { parseIndex } from "@eventcatalog/sdk";
8
+ var execFileAsync = promisify(execFile);
9
+ var parseGitHubSource = (source) => {
10
+ const match = /^github:([^/]+)\/(.+)$/.exec(source.source);
11
+ if (!match) throw new Error(`Unsupported federation source "${source.source}". Expected github:owner/repository.`);
12
+ return { owner: match[1], repository: match[2] };
13
+ };
14
+ var assertSafeCatalogPath = (source) => {
15
+ const catalogPath = source.path ?? ".";
16
+ const normalized = path.posix.normalize(catalogPath.replaceAll("\\", "/"));
17
+ if (catalogPath.includes("\\") || path.posix.isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
18
+ throw new Error(`Catalog path "${catalogPath}" escapes source "${source.id}"`);
19
+ }
20
+ };
21
+ var encodePath = (value) => value.split("/").filter(Boolean).map(encodeURIComponent).join("/");
22
+ var rawUrl = (source, ref, filePath) => {
23
+ const { owner, repository } = parseGitHubSource(source);
24
+ return `https://raw.githubusercontent.com/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/${encodeURIComponent(
25
+ ref
26
+ )}/${encodePath(filePath)}`;
27
+ };
28
+ var contentsApiUrl = (source, ref, filePath) => {
29
+ const { owner, repository } = parseGitHubSource(source);
30
+ return `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/contents/${encodePath(
31
+ filePath
32
+ )}?ref=${encodeURIComponent(ref)}`;
33
+ };
34
+ var fetchBytes = async (source, ref, filePath, fetcher, token) => {
35
+ const url = token ? contentsApiUrl(source, ref, filePath) : rawUrl(source, ref, filePath);
36
+ const response = token ? await fetcher(url, {
37
+ headers: {
38
+ Accept: "application/vnd.github.raw+json",
39
+ Authorization: `Bearer ${token}`,
40
+ "X-GitHub-Api-Version": "2026-03-10"
41
+ }
42
+ }) : await fetcher(url);
43
+ if (response.status === 404) return void 0;
44
+ if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
45
+ return Buffer.from(await response.arrayBuffer());
46
+ };
47
+ var getGitEnvironment = (token) => {
48
+ if (!token) return process.env;
49
+ const inheritedCount = Number.parseInt(process.env.GIT_CONFIG_COUNT ?? "0", 10);
50
+ const configIndex = Number.isInteger(inheritedCount) && inheritedCount >= 0 ? inheritedCount : 0;
51
+ return {
52
+ ...process.env,
53
+ GIT_CONFIG_COUNT: String(configIndex + 1),
54
+ [`GIT_CONFIG_KEY_${configIndex}`]: "http.https://github.com/.extraheader",
55
+ [`GIT_CONFIG_VALUE_${configIndex}`]: `AUTHORIZATION: basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`
56
+ };
57
+ };
58
+ var createCheckout = (executeFile, token) => async (source, ref, callback) => {
59
+ const { owner, repository } = parseGitHubSource(source);
60
+ const catalogPath = path.posix.normalize(source.path ?? ".");
61
+ const directory = await fs.mkdtemp(path.join(os.tmpdir(), "eventcatalog-federation-"));
62
+ const env = getGitEnvironment(token);
63
+ try {
64
+ const git = (args) => executeFile("git", args, { cwd: directory, env, encoding: "utf8" });
65
+ await git(["init", "--quiet"]);
66
+ await git(["remote", "add", "origin", `https://github.com/${owner}/${repository}.git`]);
67
+ if (catalogPath !== ".") {
68
+ await git(["sparse-checkout", "init", "--cone"]);
69
+ await git(["sparse-checkout", "set", catalogPath]);
70
+ }
71
+ await git(["fetch", "--quiet", "--depth", "1", "origin", ref]);
72
+ await git(["checkout", "--quiet", "--detach", "FETCH_HEAD"]);
73
+ return await callback(directory);
74
+ } finally {
75
+ await fs.rm(directory, { recursive: true, force: true });
76
+ }
77
+ };
78
+ var generateIndex = async (source, ref, checkout, executeFile) => checkout(source, ref, async (directory) => {
79
+ const { stdout } = await executeFile("git", ["rev-parse", "HEAD"], { cwd: directory, encoding: "utf8" });
80
+ const commit = stdout.trim();
81
+ const catalogDirectory = path.resolve(directory, source.path ?? ".");
82
+ const relativeCatalogDirectory = path.relative(directory, catalogDirectory);
83
+ if (relativeCatalogDirectory.startsWith("..") || path.isAbsolute(relativeCatalogDirectory)) {
84
+ throw new Error(`Catalog path "${source.path}" escapes source "${source.id}"`);
85
+ }
86
+ const index = await createSDK(catalogDirectory).buildIndex({ source: source.id, commit });
87
+ return { bytes: Buffer.from(JSON.stringify(index)), index, commit, generated: true };
88
+ });
89
+ var fetchPublishedIndex = async (source, ref, fetcher, token) => {
90
+ const indexPath = path.posix.join(source.path ?? ".", "catalog.index.json");
91
+ const bytes = await fetchBytes(source, ref, indexPath, fetcher, token);
92
+ if (!bytes) return void 0;
93
+ const index = parseIndex(JSON.parse(bytes.toString("utf8")));
94
+ if (index.source !== source.id) {
95
+ throw new Error(`Published index source "${index.source}" does not match configured id "${source.id}"`);
96
+ }
97
+ return { bytes, index, commit: index.commit, generated: false };
98
+ };
99
+ var createGitHubSourceProvider = (options = {}) => {
100
+ const fetcher = options.fetch ?? ((url, init) => fetch(url, init));
101
+ const executeFile = options.execFile ?? ((file, args, execOptions) => execFileAsync(file, args, execOptions));
102
+ const configuredToken = options.token ?? process.env.EVENTCATALOG_GITHUB_TOKEN ?? process.env.GITHUB_TOKEN;
103
+ const token = configuredToken?.trim() || void 0;
104
+ const checkout = options.checkout ?? createCheckout(executeFile, token);
105
+ return {
106
+ async resolve(source) {
107
+ assertSafeCatalogPath(source);
108
+ const ref = source.ref ?? "main";
109
+ return await fetchPublishedIndex(source, ref, fetcher, token) ?? generateIndex(source, ref, checkout, executeFile);
110
+ },
111
+ async fetchContent({ source, commit, path: artifactPath }) {
112
+ assertSafeCatalogPath(source);
113
+ const catalogPath = path.posix.join(source.path ?? ".", artifactPath);
114
+ const content = await fetchBytes(source, commit, catalogPath, fetcher, token);
115
+ if (!content) throw new Error(`Federated artifact not found for "${source.id}": ${artifactPath}`);
116
+ return content;
117
+ }
118
+ };
119
+ };
120
+
121
+ export {
122
+ createGitHubSourceProvider
123
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-U6E25IFA.js";
3
+ } from "./chunk-FZJDCQZE.js";
4
4
  import {
5
5
  cleanup,
6
6
  getEventCatalogConfigFile
@@ -1,10 +1,10 @@
1
+ import {
2
+ raiseEvent
3
+ } from "./chunk-PWY4SP6L.js";
1
4
  import {
2
5
  countResources,
3
6
  serializeCounts
4
7
  } from "./chunk-K2XIENVT.js";
5
- import {
6
- raiseEvent
7
- } from "./chunk-LZDDSWXC.js";
8
8
  import {
9
9
  getEventCatalogConfigFile,
10
10
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -0,0 +1,133 @@
1
+ // src/federation/public-assets.ts
2
+ import { createHash } from "crypto";
3
+ import fs from "fs/promises";
4
+ import path from "path";
5
+ var getContentHash = (content) => `sha256:${createHash("sha256").update(content).digest("hex")}`;
6
+ var getFileHash = async (filePath) => {
7
+ try {
8
+ const stat = await fs.lstat(filePath);
9
+ return stat.isFile() ? getContentHash(await fs.readFile(filePath)) : void 0;
10
+ } catch (error) {
11
+ if (error.code === "ENOENT") return void 0;
12
+ throw error;
13
+ }
14
+ };
15
+ var getSafePath = (directory, relativePath) => {
16
+ const normalizedPath = path.posix.normalize(relativePath);
17
+ const unsafe = relativePath.length === 0 || relativePath.includes("\0") || relativePath.includes("\\") || path.posix.isAbsolute(relativePath) || /^[a-zA-Z]:\//.test(relativePath) || normalizedPath !== relativePath || normalizedPath === ".." || normalizedPath.startsWith("../");
18
+ if (unsafe) return void 0;
19
+ const resolvedDirectory = path.resolve(directory);
20
+ const resolvedPath = path.resolve(resolvedDirectory, relativePath);
21
+ const relativeResolvedPath = path.relative(resolvedDirectory, resolvedPath);
22
+ if (relativeResolvedPath === "" || relativeResolvedPath === ".." || relativeResolvedPath.startsWith(`..${path.sep}`) || path.isAbsolute(relativeResolvedPath)) {
23
+ return void 0;
24
+ }
25
+ return resolvedPath;
26
+ };
27
+ var listFiles = async (directory, relativeDirectory = "") => {
28
+ let entries;
29
+ try {
30
+ entries = await fs.readdir(path.join(directory, relativeDirectory), { withFileTypes: true });
31
+ } catch (error) {
32
+ if (error.code === "ENOENT") return [];
33
+ throw error;
34
+ }
35
+ const files = await Promise.all(
36
+ entries.map(async (entry) => {
37
+ const relativePath = path.join(relativeDirectory, entry.name);
38
+ if (entry.isDirectory()) return listFiles(directory, relativePath);
39
+ return entry.isFile() ? [relativePath.split(path.sep).join("/")] : [];
40
+ })
41
+ );
42
+ return files.flat().sort();
43
+ };
44
+ var pathExists = async (filePath) => {
45
+ try {
46
+ await fs.lstat(filePath);
47
+ return true;
48
+ } catch (error) {
49
+ if (error.code === "ENOENT") return false;
50
+ throw error;
51
+ }
52
+ };
53
+ var hasBlockingParent = async (publicDirectory, destinationPath) => {
54
+ let currentPath = path.dirname(destinationPath);
55
+ while (currentPath !== publicDirectory) {
56
+ try {
57
+ if (!(await fs.lstat(currentPath)).isDirectory()) return true;
58
+ } catch (error) {
59
+ if (error.code !== "ENOENT") throw error;
60
+ }
61
+ currentPath = path.dirname(currentPath);
62
+ }
63
+ return false;
64
+ };
65
+ var pruneEmptyDirectories = async (publicDirectory, filePath) => {
66
+ let currentPath = path.dirname(filePath);
67
+ while (currentPath !== publicDirectory) {
68
+ try {
69
+ await fs.rmdir(currentPath);
70
+ } catch (error) {
71
+ if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
72
+ if (error.code === "ENOTEMPTY") return;
73
+ }
74
+ currentPath = path.dirname(currentPath);
75
+ }
76
+ };
77
+ var composePublicAssets = async ({
78
+ projectDirectory,
79
+ federatedDirectory,
80
+ assets,
81
+ previousFiles = {},
82
+ collisionPaths = /* @__PURE__ */ new Set()
83
+ }) => {
84
+ const publicDirectory = path.resolve(projectDirectory, "public");
85
+ const federatedPublicDirectory = path.resolve(federatedDirectory, "public");
86
+ const sourceFiles = await listFiles(federatedPublicDirectory);
87
+ const sourceFileSet = new Set(sourceFiles);
88
+ const managedFiles = /* @__PURE__ */ new Set();
89
+ for (const [relativePath, previousFile] of Object.entries(previousFiles)) {
90
+ const destinationPath = getSafePath(publicDirectory, relativePath);
91
+ if (destinationPath && await getFileHash(destinationPath) === previousFile.hash) managedFiles.add(relativePath);
92
+ }
93
+ let removed = 0;
94
+ for (const relativePath of managedFiles) {
95
+ if (sourceFileSet.has(relativePath)) continue;
96
+ const destinationPath = getSafePath(publicDirectory, relativePath);
97
+ if (!destinationPath) continue;
98
+ await fs.rm(destinationPath, { force: true });
99
+ await pruneEmptyDirectories(publicDirectory, destinationPath);
100
+ removed += 1;
101
+ }
102
+ const publicAssetsByPath = new Map(
103
+ assets.filter((asset) => asset.path.startsWith("public/")).map((asset) => [asset.path.slice("public/".length), asset])
104
+ );
105
+ const files = {};
106
+ let copied = 0;
107
+ let skipped = 0;
108
+ let overwritten = 0;
109
+ for (const relativePath of sourceFiles) {
110
+ const sourcePath = getSafePath(federatedPublicDirectory, relativePath);
111
+ const destinationPath = getSafePath(publicDirectory, relativePath);
112
+ if (!sourcePath || !destinationPath) throw new Error(`Unsafe federated public asset path "${relativePath}"`);
113
+ const mainCatalogOwnsPath = await pathExists(destinationPath) && !managedFiles.has(relativePath) || await hasBlockingParent(publicDirectory, destinationPath);
114
+ if (mainCatalogOwnsPath) {
115
+ skipped += 1;
116
+ continue;
117
+ }
118
+ const asset = publicAssetsByPath.get(relativePath);
119
+ if (!asset) throw new Error(`Cannot identify the source of federated public asset "${relativePath}"`);
120
+ const content = await fs.readFile(sourcePath);
121
+ await fs.mkdir(path.dirname(destinationPath), { recursive: true });
122
+ await fs.writeFile(destinationPath, content);
123
+ files[relativePath] = { source: asset.resolvedFrom.source, hash: getContentHash(content) };
124
+ copied += 1;
125
+ if (collisionPaths.has(`public/${relativePath}`)) overwritten += 1;
126
+ }
127
+ await fs.rm(federatedPublicDirectory, { recursive: true, force: true });
128
+ return { files, copied, skipped, overwritten, removed };
129
+ };
130
+
131
+ export {
132
+ composePublicAssets
133
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-LQLAEEOV.js";
3
+ } from "./chunk-LB5VXS6X.js";
4
4
 
5
5
  // src/utils/cli-logger.ts
6
6
  import pc from "picocolors";
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "4.6.2";
2
+ var version = "4.6.3";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-LQLAEEOV.js";
3
+ } from "./chunk-LB5VXS6X.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import os from "os";
@@ -0,0 +1,44 @@
1
+ // src/federation/content-cache.ts
2
+ import { createHash, randomUUID } from "crypto";
3
+ import fs from "fs/promises";
4
+ import path from "path";
5
+ var getContentHash = (content) => `sha256:${createHash("sha256").update(content).digest("hex")}`;
6
+ var isContentHash = (key) => /^sha256:[a-f0-9]{64}$/i.test(key);
7
+ var createFederationContentCache = (projectDirectory, options = {}) => {
8
+ const cacheDirectory = path.join(projectDirectory, ".eventcatalog-cache", "federation", "content");
9
+ const getCachePath = (key) => path.join(cacheDirectory, encodeURIComponent(key));
10
+ return {
11
+ async get(key) {
12
+ if (options.read === false || !isContentHash(key)) return void 0;
13
+ const cachePath = getCachePath(key);
14
+ try {
15
+ const content = await fs.readFile(cachePath);
16
+ if (getContentHash(content) !== key) {
17
+ await fs.rm(cachePath, { force: true });
18
+ return void 0;
19
+ }
20
+ options.onHit?.(key);
21
+ return content;
22
+ } catch (error) {
23
+ if (error.code === "ENOENT") return void 0;
24
+ throw error;
25
+ }
26
+ },
27
+ async set(key, content) {
28
+ if (!isContentHash(key) || getContentHash(content) !== key) return;
29
+ await fs.mkdir(cacheDirectory, { recursive: true });
30
+ const cachePath = getCachePath(key);
31
+ const temporaryPath = `${cachePath}.tmp-${process.pid}-${randomUUID()}`;
32
+ try {
33
+ await fs.writeFile(temporaryPath, content);
34
+ await fs.rename(temporaryPath, cachePath);
35
+ } finally {
36
+ await fs.rm(temporaryPath, { force: true });
37
+ }
38
+ }
39
+ };
40
+ };
41
+
42
+ export {
43
+ createFederationContentCache
44
+ };
@@ -0,0 +1,204 @@
1
+ import {
2
+ createFederationContentCache
3
+ } from "./chunk-R7Z5ALYI.js";
4
+ import {
5
+ createGitHubSourceProvider
6
+ } from "./chunk-352FGP6W.js";
7
+ import {
8
+ composePublicAssets
9
+ } from "./chunk-5EPNFDT5.js";
10
+ import {
11
+ getEventCatalogConfigFile
12
+ } from "./chunk-6QENHZZP.js";
13
+
14
+ // src/federation/federate.ts
15
+ import { createHash } from "crypto";
16
+ import fs from "fs/promises";
17
+ import path from "path";
18
+ import createSDK, { hydrate, resolve } from "@eventcatalog/sdk";
19
+ var FederationConflictError = class extends Error {
20
+ conflicts;
21
+ constructor(conflicts) {
22
+ const details = conflicts.map((conflict) => `${conflict.id}: ${conflict.sources.join(", ")}`).join("\n");
23
+ super(`Federation conflicts prevent hydration:
24
+ ${details}`);
25
+ this.name = "FederationConflictError";
26
+ this.conflicts = conflicts;
27
+ }
28
+ };
29
+ var validateSources = (sources) => {
30
+ const ids = /* @__PURE__ */ new Set();
31
+ for (const source of sources) {
32
+ if (!source.id?.trim()) throw new Error("Every federation source requires a stable id.");
33
+ if (ids.has(source.id)) throw new Error(`Federation source id "${source.id}" is configured more than once.`);
34
+ ids.add(source.id);
35
+ }
36
+ };
37
+ var writeLock = async (lockPath, lock) => {
38
+ const temporaryPath = `${lockPath}.tmp-${process.pid}`;
39
+ try {
40
+ await fs.writeFile(temporaryPath, `${JSON.stringify(lock, null, 2)}
41
+ `, "utf8");
42
+ await fs.rename(temporaryPath, lockPath);
43
+ } finally {
44
+ await fs.rm(temporaryPath, { force: true });
45
+ }
46
+ };
47
+ var readLock = async (lockPath) => {
48
+ try {
49
+ return JSON.parse(await fs.readFile(lockPath, "utf8"));
50
+ } catch (error) {
51
+ if (error.code === "ENOENT") return void 0;
52
+ throw new Error(`Cannot read federation lock at "${lockPath}"`, { cause: error });
53
+ }
54
+ };
55
+ var pathExists = async (filePath) => {
56
+ try {
57
+ await fs.access(filePath);
58
+ return true;
59
+ } catch (error) {
60
+ if (error.code === "ENOENT") return false;
61
+ throw error;
62
+ }
63
+ };
64
+ var cleanupPreviousFederation = async (projectDirectory, onProgress) => {
65
+ const outDir = path.join(projectDirectory, "federated");
66
+ const lockPath = path.join(projectDirectory, "eventcatalog.lock");
67
+ const previousLock = await readLock(lockPath);
68
+ const hadFederatedOutput = await pathExists(outDir);
69
+ const hadLock = previousLock !== void 0;
70
+ if (!hadFederatedOutput && !hadLock) return;
71
+ await fs.rm(outDir, { recursive: true, force: true });
72
+ const publicResult = await composePublicAssets({
73
+ projectDirectory,
74
+ federatedDirectory: outDir,
75
+ assets: [],
76
+ previousFiles: previousLock?.publicFiles
77
+ });
78
+ await fs.rm(lockPath, { force: true });
79
+ onProgress?.({
80
+ type: "cleanup:complete",
81
+ federated: hadFederatedOutput,
82
+ publicFiles: publicResult.removed,
83
+ lock: hadLock
84
+ });
85
+ };
86
+ var federateCatalog = async (projectDirectory, options = {}) => {
87
+ const config = await getEventCatalogConfigFile(projectDirectory);
88
+ const sources = config.federation?.sources ?? [];
89
+ options.onProgress?.({ type: "configured", sources: sources.length });
90
+ if (sources.length === 0) {
91
+ await cleanupPreviousFederation(projectDirectory, options.onProgress);
92
+ return null;
93
+ }
94
+ if (options.isFederationEnabled && !await options.isFederationEnabled()) {
95
+ throw new Error(
96
+ "Cannot federate catalogs: EventCatalog federation is an Enterprise feature. Visit https://www.eventcatalog.dev/pricing to enable federation."
97
+ );
98
+ }
99
+ validateSources(sources);
100
+ if (options.useCache === false) options.onProgress?.({ type: "cache:disabled" });
101
+ const provider = options.provider ?? createGitHubSourceProvider();
102
+ const resolvedSources = [];
103
+ for (const [index, source] of sources.entries()) {
104
+ const current = index + 1;
105
+ options.onProgress?.({ type: "source:start", source, current, total: sources.length });
106
+ try {
107
+ const resolved = await provider.resolve(source);
108
+ resolvedSources.push({ config: source, resolved });
109
+ options.onProgress?.({
110
+ type: "source:complete",
111
+ source,
112
+ current,
113
+ total: sources.length,
114
+ commit: resolved.commit,
115
+ resources: resolved.index.resources.length,
116
+ generated: resolved.generated
117
+ });
118
+ } catch (error) {
119
+ const message = error instanceof Error ? error.message : String(error);
120
+ throw new Error(`Failed to federate source "${source.id}": ${message}`, { cause: error });
121
+ }
122
+ }
123
+ const outDir = path.join(projectDirectory, "federated");
124
+ const lockPath = path.join(projectDirectory, "eventcatalog.lock");
125
+ const previousLock = await readLock(lockPath);
126
+ const resources = resolvedSources.reduce((total, source) => total + source.resolved.index.resources.length, 0);
127
+ const remoteIndexes = resolvedSources.map(({ resolved }) => resolved.index);
128
+ options.onProgress?.({ type: "local:start" });
129
+ const localIndex = await createSDK(projectDirectory).buildIndex({
130
+ source: config.cId,
131
+ commit: "local",
132
+ hashContent: false,
133
+ includeFederated: false
134
+ });
135
+ options.onProgress?.({ type: "local:complete", resources: localIndex.resources.length });
136
+ options.onProgress?.({ type: "resolving", resources, localResources: localIndex.resources.length });
137
+ const ownershipGraph = resolve([localIndex, ...remoteIndexes]);
138
+ if (ownershipGraph.conflicts.length > 0) {
139
+ options.onProgress?.({ type: "resolved", graph: ownershipGraph });
140
+ throw new FederationConflictError(ownershipGraph.conflicts);
141
+ }
142
+ const graph = resolve(remoteIndexes);
143
+ options.onProgress?.({ type: "resolved", graph });
144
+ const sourcesById = new Map(sources.map((source) => [source.id, source]));
145
+ options.onProgress?.({ type: "hydrating", outDir });
146
+ let hydratedFiles = 0;
147
+ let cachedFiles = 0;
148
+ const hydrateResult = await hydrate(graph, {
149
+ outDir,
150
+ cache: createFederationContentCache(projectDirectory, {
151
+ read: options.useCache !== false,
152
+ onHit: () => {
153
+ cachedFiles += 1;
154
+ options.onProgress?.({ type: "hydrate:cache", files: cachedFiles });
155
+ }
156
+ }),
157
+ modes: Object.fromEntries(sources.map((source) => [source.id, source.mode ?? "hydrate"])),
158
+ fetch: async ({ source: sourceId, commit, path: artifactPath }) => {
159
+ const source = sourcesById.get(sourceId);
160
+ if (!source) throw new Error(`Cannot fetch content for unconfigured source "${sourceId}"`);
161
+ const content = await provider.fetchContent({ source, commit, path: artifactPath });
162
+ hydratedFiles += 1;
163
+ options.onProgress?.({ type: "hydrate:file", files: hydratedFiles, source: sourceId, path: artifactPath });
164
+ return content;
165
+ }
166
+ });
167
+ const publicResult = await composePublicAssets({
168
+ projectDirectory,
169
+ federatedDirectory: outDir,
170
+ assets: graph.assets,
171
+ previousFiles: previousLock?.publicFiles,
172
+ collisionPaths: new Set(
173
+ graph.warnings.filter((warning) => warning.kind === "asset-collision").map((warning) => warning.path)
174
+ )
175
+ });
176
+ options.onProgress?.({ type: "public:complete", result: publicResult });
177
+ const resolvedAt = (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
178
+ await writeLock(lockPath, {
179
+ lockVersion: 1,
180
+ sources: resolvedSources.map(({ config: source, resolved }) => ({
181
+ id: source.id,
182
+ digest: `sha256:${createHash("sha256").update(resolved.bytes).digest("hex")}`,
183
+ commit: resolved.commit,
184
+ resolvedAt
185
+ })).sort((left, right) => left.id.localeCompare(right.id)),
186
+ publicFiles: publicResult.files
187
+ });
188
+ const result = {
189
+ sources: sources.length,
190
+ resources,
191
+ graph,
192
+ hydrate: hydrateResult,
193
+ public: publicResult,
194
+ outDir,
195
+ lockPath
196
+ };
197
+ options.onProgress?.({ type: "complete", result });
198
+ return result;
199
+ };
200
+
201
+ export {
202
+ FederationConflictError,
203
+ federateCatalog
204
+ };
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "4.6.2";
28
+ var version = "4.6.3";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-LQLAEEOV.js";
3
+ } from "./chunk-LB5VXS6X.js";
4
4
  export {
5
5
  VERSION
6
6
  };