@eventcatalog/core 5.0.0-beta.13 → 5.0.0-beta.14

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 (30) hide show
  1. package/LICENSE +13 -12
  2. package/dist/analytics/analytics.cjs +1 -1
  3. package/dist/analytics/analytics.js +2 -2
  4. package/dist/analytics/log-build.cjs +1 -1
  5. package/dist/analytics/log-build.js +4 -4
  6. package/dist/{chunk-T75DOVFP.js → chunk-4KE3EUDJ.js} +1 -1
  7. package/dist/{chunk-BO2HHVZ3.js → chunk-I3XDOICA.js} +1 -1
  8. package/dist/{chunk-XA5W6DXX.js → chunk-OFK7U7BX.js} +1 -1
  9. package/dist/{chunk-JPT3QC5V.js → chunk-PI5YNZZZ.js} +1 -1
  10. package/dist/{chunk-PLTLW45Q.js → chunk-WY6M3RPT.js} +5 -5
  11. package/dist/constants.cjs +1 -1
  12. package/dist/constants.js +1 -1
  13. package/dist/eventcatalog.cjs +10 -5
  14. package/dist/eventcatalog.js +23 -18
  15. package/dist/federation/federate.js +3 -3
  16. package/dist/generate.cjs +1 -1
  17. package/dist/generate.js +3 -3
  18. package/dist/utils/cli-logger.cjs +1 -1
  19. package/dist/utils/cli-logger.js +2 -2
  20. package/eventcatalog/astro.config.mjs +13 -1
  21. package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
  22. package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
  23. package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
  24. package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
  25. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +37 -76
  26. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
  27. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +122 -1
  28. package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
  29. package/package.json +3 -3
  30. package/dist/{chunk-ZEY2XUJN.js → chunk-KHACCTW2.js} +4 -4
package/LICENSE CHANGED
@@ -17,13 +17,13 @@ Licensed Work: EventCatalog version 5.0 or later, including pre-releases,
17
17
  and the examples directory. The Licensed Work is (c) 2026
18
18
  EventCatalog Ltd.
19
19
  Additional Use Grant: You may make Production use of the Licensed Work if You
20
- are an Eligible User and Your use is not a Hosted
20
+ qualify for Free Use and Your use is not a Hosted
21
21
  Offering. Sections 1 to 7 below form part of this grant.
22
22
  Capitalised terms are defined in section 6.
23
23
 
24
- 1. Eligible Users
24
+ 1. Free Use
25
25
 
26
- You are an "Eligible User" if You are:
26
+ You qualify for "Free Use" if You are:
27
27
 
28
28
  (a) an individual using the Licensed Work for Your own
29
29
  personal purposes, and not for or on behalf of any
@@ -47,9 +47,9 @@ Additional Use Grant: You may make Production use of the Licensed Work if You
47
47
 
48
48
  2. Growing past the thresholds
49
49
 
50
- An Organisation that is an Eligible User under 1(b)
51
- remains one until ninety (90) days after the first of the
52
- following to happen:
50
+ An Organisation that qualifies for Free Use under 1(b)
51
+ continues to qualify until ninety (90) days after the
52
+ first of the following to happen:
53
53
 
54
54
  (a) the end of the first financial year in which its
55
55
  Annual Revenue reaches USD 10,000,000; or
@@ -68,7 +68,7 @@ Additional Use Grant: You may make Production use of the Licensed Work if You
68
68
  vendor), including building, customising, hosting or
69
69
  distributing the Licensed Work or a catalog built with it
70
70
  for that client, Your use for that client is permitted if
71
- the client is an Eligible User or holds a Commercial
71
+ the client qualifies for Free Use or holds a Commercial
72
72
  License covering that use. You may rely on a client's
73
73
  written statement of its status unless You know it is
74
74
  wrong. Your own use of the Licensed Work for Your own
@@ -76,7 +76,7 @@ Additional Use Grant: You may make Production use of the Licensed Work if You
76
76
 
77
77
  4. Coming into compliance
78
78
 
79
- (a) If You stop being an Eligible User (for example
79
+ (a) If You stop qualifying for Free Use (for example
80
80
  because Your Evaluation Period ends), or You become
81
81
  aware (including by notice from the Licensor) that
82
82
  Your Production use is not permitted by this grant,
@@ -219,7 +219,8 @@ Additional Use Grant: You may make Production use of the Licensed Work if You
219
219
  Public Open Source Projects.
220
220
 
221
221
  "Commercial License" means a written license from the
222
- Licensor permitting Production use beyond this grant.
222
+ Licensor, including one accepted online, permitting
223
+ Production use beyond this grant.
223
224
 
224
225
  7. Everything else
225
226
 
@@ -230,9 +231,9 @@ Additional Use Grant: You may make Production use of the Licensed Work if You
230
231
  Work. The content of Your own catalog (the documentation,
231
232
  schemas, diagrams and configuration You create) is Yours
232
233
  and is not part of the Licensed Work.
233
- Change Date: Four years after the first public release, including any
234
- pre-release, of any release in the same release series as
235
- the Licensed Work.
234
+ Change Date: Four years after the first public release (including any
235
+ pre-release) in the same release series as the Licensed
236
+ Work.
236
237
 
237
238
  A "release series" is every release that shares the same
238
239
  major and minor release number, including its pre-releases
@@ -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 = "5.0.0-beta.13";
39
+ var version = "5.0.0-beta.14";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-JPT3QC5V.js";
3
+ } from "../chunk-PI5YNZZZ.js";
4
4
  import "../chunk-CPW6G62V.js";
5
- import "../chunk-BO2HHVZ3.js";
5
+ import "../chunk-I3XDOICA.js";
6
6
  export {
7
7
  raiseEvent
8
8
  };
@@ -176,7 +176,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
176
176
  var import_os = __toESM(require("os"), 1);
177
177
 
178
178
  // package.json
179
- var version = "5.0.0-beta.13";
179
+ var version = "5.0.0-beta.14";
180
180
 
181
181
  // src/constants.ts
182
182
  var VERSION = version;
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-PLTLW45Q.js";
4
- import "../chunk-JPT3QC5V.js";
3
+ } from "../chunk-WY6M3RPT.js";
4
+ import "../chunk-AWOO63ME.js";
5
5
  import "../chunk-ZAZHAVKB.js";
6
+ import "../chunk-PI5YNZZZ.js";
6
7
  import "../chunk-CPW6G62V.js";
7
- import "../chunk-AWOO63ME.js";
8
- import "../chunk-BO2HHVZ3.js";
8
+ import "../chunk-I3XDOICA.js";
9
9
  import "../chunk-RFIXKATC.js";
10
10
  export {
11
11
  log_build_default as default
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-XA5W6DXX.js";
3
+ } from "./chunk-OFK7U7BX.js";
4
4
  import {
5
5
  cleanup,
6
6
  getEventCatalogConfigFile
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "5.0.0-beta.13";
2
+ var version = "5.0.0-beta.14";
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-BO2HHVZ3.js";
3
+ } from "./chunk-I3XDOICA.js";
4
4
 
5
5
  // src/utils/cli-logger.ts
6
6
  import pc from "picocolors";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-CPW6G62V.js";
5
5
  import {
6
6
  VERSION
7
- } from "./chunk-BO2HHVZ3.js";
7
+ } from "./chunk-I3XDOICA.js";
8
8
 
9
9
  // src/analytics/analytics.js
10
10
  import os from "os";
@@ -1,18 +1,18 @@
1
1
  import {
2
- raiseEvent
3
- } from "./chunk-JPT3QC5V.js";
2
+ getLicenseAnalytics
3
+ } from "./chunk-AWOO63ME.js";
4
4
  import {
5
5
  countResources,
6
6
  hashCatalogContent,
7
7
  serializeCounts
8
8
  } from "./chunk-ZAZHAVKB.js";
9
+ import {
10
+ raiseEvent
11
+ } from "./chunk-PI5YNZZZ.js";
9
12
  import {
10
13
  TELEMETRY_REQUEST_TIMEOUT_MS,
11
14
  isTelemetryDisabled
12
15
  } from "./chunk-CPW6G62V.js";
13
- import {
14
- getLicenseAnalytics
15
- } from "./chunk-AWOO63ME.js";
16
16
  import {
17
17
  getEventCatalogConfigFile,
18
18
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "5.0.0-beta.13";
28
+ var version = "5.0.0-beta.14";
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-BO2HHVZ3.js";
3
+ } from "./chunk-I3XDOICA.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -181,7 +181,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
181
181
  var import_picocolors = __toESM(require("picocolors"), 1);
182
182
 
183
183
  // package.json
184
- var version = "5.0.0-beta.13";
184
+ var version = "5.0.0-beta.14";
185
185
 
186
186
  // src/constants.ts
187
187
  var VERSION = version;
@@ -2980,7 +2980,7 @@ var resolveDevPort = async ({ projectDir }) => {
2980
2980
  };
2981
2981
  var startDevPrewarm = ({
2982
2982
  port = 3e3,
2983
- paths = ["/ping", "/"],
2983
+ paths = ["/"],
2984
2984
  retries = 80,
2985
2985
  intervalMs = 250,
2986
2986
  initialDelayMs = 500
@@ -2989,10 +2989,13 @@ var startDevPrewarm = ({
2989
2989
  const hit = (requestPath) => new Promise((resolve2) => {
2990
2990
  const req = import_node_http.default.get(
2991
2991
  {
2992
- hostname: "127.0.0.1",
2992
+ // Not 127.0.0.1: the dev server can listen on IPv6 only ([::1]), which refused
2993
+ // every prewarm request
2994
+ hostname: "localhost",
2993
2995
  port,
2994
2996
  path: requestPath,
2995
- timeout: 1200
2997
+ // A refused connection fails at once; once connected, let the first render finish
2998
+ timeout: 3e4
2996
2999
  },
2997
3000
  (res) => {
2998
3001
  res.resume();
@@ -3251,7 +3254,9 @@ program.command("dev").description("Run development server of EventCatalog").opt
3251
3254
  });
3252
3255
  startDevPrewarm({
3253
3256
  port: prewarmPort,
3254
- paths: ["/ping", "/"]
3257
+ // Renders the homepage before anyone opens it. Requests stop at the first response,
3258
+ // so a probe path like /ping first would leave the homepage cold.
3259
+ paths: ["/"]
3255
3260
  });
3256
3261
  }
3257
3262
  await runCommandWithFilteredOutput({
@@ -1,29 +1,28 @@
1
- import {
2
- log_build_default
3
- } from "./chunk-PLTLW45Q.js";
4
- import "./chunk-JPT3QC5V.js";
5
- import "./chunk-ZAZHAVKB.js";
6
- import "./chunk-CPW6G62V.js";
7
1
  import {
8
2
  runMigrations
9
3
  } from "./chunk-XUAF2H54.js";
10
4
  import "./chunk-CA4U2JP7.js";
5
+ import {
6
+ log_build_default
7
+ } from "./chunk-WY6M3RPT.js";
11
8
  import {
12
9
  getLicenseStatus,
13
10
  printLicenseStatus
14
11
  } from "./chunk-AWOO63ME.js";
12
+ import "./chunk-ZAZHAVKB.js";
15
13
  import {
16
14
  FederationConflictError,
17
15
  FederationDiagnosticError,
18
16
  federateCatalog
19
- } from "./chunk-ZEY2XUJN.js";
17
+ } from "./chunk-KHACCTW2.js";
18
+ import "./chunk-WRNH5C3U.js";
19
+ import "./chunk-5EPNFDT5.js";
20
+ import "./chunk-A2RZR3U4.js";
21
+ import "./chunk-R7Z5ALYI.js";
20
22
  import {
21
23
  getFederationDiagnosticCounts,
22
24
  getVisibleFederationDiagnostics
23
25
  } from "./chunk-JJPB6EOZ.js";
24
- import "./chunk-WRNH5C3U.js";
25
- import "./chunk-5EPNFDT5.js";
26
- import "./chunk-A2RZR3U4.js";
27
26
  import "./chunk-SDZQJTJW.js";
28
27
  import "./chunk-352FGP6W.js";
29
28
  import {
@@ -34,10 +33,10 @@ import {
34
33
  } from "./chunk-MDWB5HLO.js";
35
34
  import {
36
35
  generate
37
- } from "./chunk-T75DOVFP.js";
36
+ } from "./chunk-4KE3EUDJ.js";
38
37
  import {
39
38
  logger
40
- } from "./chunk-XA5W6DXX.js";
39
+ } from "./chunk-OFK7U7BX.js";
41
40
  import {
42
41
  resolve_catalog_dependencies_default
43
42
  } from "./chunk-36RYTYT5.js";
@@ -51,7 +50,8 @@ import {
51
50
  import {
52
51
  watch
53
52
  } from "./chunk-U63GDPPY.js";
54
- import "./chunk-R7Z5ALYI.js";
53
+ import "./chunk-PI5YNZZZ.js";
54
+ import "./chunk-CPW6G62V.js";
55
55
  import {
56
56
  getAstroConfigPath
57
57
  } from "./chunk-VIJJ7JKH.js";
@@ -67,7 +67,7 @@ import {
67
67
  } from "./chunk-LFQJXLHG.js";
68
68
  import {
69
69
  VERSION
70
- } from "./chunk-BO2HHVZ3.js";
70
+ } from "./chunk-I3XDOICA.js";
71
71
  import {
72
72
  getEventCatalogConfigFile,
73
73
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -669,7 +669,7 @@ var resolveDevPort = async ({ projectDir }) => {
669
669
  };
670
670
  var startDevPrewarm = ({
671
671
  port = 3e3,
672
- paths = ["/ping", "/"],
672
+ paths = ["/"],
673
673
  retries = 80,
674
674
  intervalMs = 250,
675
675
  initialDelayMs = 500
@@ -678,10 +678,13 @@ var startDevPrewarm = ({
678
678
  const hit = (requestPath) => new Promise((resolve) => {
679
679
  const req = http.get(
680
680
  {
681
- hostname: "127.0.0.1",
681
+ // Not 127.0.0.1: the dev server can listen on IPv6 only ([::1]), which refused
682
+ // every prewarm request
683
+ hostname: "localhost",
682
684
  port,
683
685
  path: requestPath,
684
- timeout: 1200
686
+ // A refused connection fails at once; once connected, let the first render finish
687
+ timeout: 3e4
685
688
  },
686
689
  (res) => {
687
690
  res.resume();
@@ -940,7 +943,9 @@ program.command("dev").description("Run development server of EventCatalog").opt
940
943
  });
941
944
  startDevPrewarm({
942
945
  port: prewarmPort,
943
- paths: ["/ping", "/"]
946
+ // Renders the homepage before anyone opens it. Requests stop at the first response,
947
+ // so a probe path like /ping first would leave the homepage cold.
948
+ paths: ["/"]
944
949
  });
945
950
  }
946
951
  await runCommandWithFilteredOutput({
@@ -2,14 +2,14 @@ import {
2
2
  FederationConflictError,
3
3
  FederationDiagnosticError,
4
4
  federateCatalog
5
- } from "../chunk-ZEY2XUJN.js";
6
- import "../chunk-JJPB6EOZ.js";
5
+ } from "../chunk-KHACCTW2.js";
7
6
  import "../chunk-WRNH5C3U.js";
8
7
  import "../chunk-5EPNFDT5.js";
9
8
  import "../chunk-A2RZR3U4.js";
9
+ import "../chunk-R7Z5ALYI.js";
10
+ import "../chunk-JJPB6EOZ.js";
10
11
  import "../chunk-SDZQJTJW.js";
11
12
  import "../chunk-352FGP6W.js";
12
- import "../chunk-R7Z5ALYI.js";
13
13
  import "../chunk-RFIXKATC.js";
14
14
  export {
15
15
  FederationConflictError,
package/dist/generate.cjs CHANGED
@@ -112,7 +112,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
112
112
  var import_picocolors = __toESM(require("picocolors"), 1);
113
113
 
114
114
  // package.json
115
- var version = "5.0.0-beta.13";
115
+ var version = "5.0.0-beta.14";
116
116
 
117
117
  // src/constants.ts
118
118
  var VERSION = version;
package/dist/generate.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generate
3
- } from "./chunk-T75DOVFP.js";
4
- import "./chunk-XA5W6DXX.js";
5
- import "./chunk-BO2HHVZ3.js";
3
+ } from "./chunk-4KE3EUDJ.js";
4
+ import "./chunk-OFK7U7BX.js";
5
+ import "./chunk-I3XDOICA.js";
6
6
  import "./chunk-RFIXKATC.js";
7
7
  export {
8
8
  generate
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(cli_logger_exports);
36
36
  var import_picocolors = __toESM(require("picocolors"), 1);
37
37
 
38
38
  // package.json
39
- var version = "5.0.0-beta.13";
39
+ var version = "5.0.0-beta.14";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  logger
3
- } from "../chunk-XA5W6DXX.js";
4
- import "../chunk-BO2HHVZ3.js";
3
+ } from "../chunk-OFK7U7BX.js";
4
+ import "../chunk-I3XDOICA.js";
5
5
  export {
6
6
  logger
7
7
  };
@@ -35,6 +35,8 @@ import config from './src/utils/eventcatalog-config/source.ts';
35
35
  import preprocessExpressiveCodeConfig from './integrations/expressive-code-config.mjs';
36
36
  import catalogAssets from './integrations/catalog-assets.mjs';
37
37
  import runtimeDependencies from './integrations/runtime-dependencies.mjs';
38
+ import stableDependencyOrder from './integrations/stable-dependency-order.mjs';
39
+ import skipStartupReload from './integrations/skip-startup-reload.mjs';
38
40
  import { getRuntimePaths } from './integrations/runtime-paths.mjs';
39
41
  import { collectClientDependencies } from './integrations/client-dependencies.mjs';
40
42
 
@@ -166,6 +168,8 @@ export default defineConfig({
166
168
  },
167
169
  plugins: [
168
170
  runtimeDependencies(),
171
+ stableDependencyOrder(),
172
+ skipStartupReload(),
169
173
  tailwindcss(),
170
174
  ...(await eventCatalogLikeC4(projectDirectory)),
171
175
  ...(config.trailingSlash === true ? [astroTrailingSlashEndpointFix()] : []),
@@ -224,7 +228,15 @@ export default defineConfig({
224
228
  },
225
229
  optimizeDeps: {
226
230
  exclude: [],
227
- include: collectClientDependencies(),
231
+ include: [
232
+ ...collectClientDependencies(),
233
+ // <ClientRouter /> imports these from a virtual module the dependency scan can't follow,
234
+ // so without them every cold start re-bundles on the first page and reloads it
235
+ 'astro/virtual-modules/transitions-events.js',
236
+ 'astro/virtual-modules/transitions-router.js',
237
+ 'astro/virtual-modules/transitions-swap-functions.js',
238
+ 'astro/virtual-modules/transitions-types.js',
239
+ ],
228
240
  },
229
241
  },
230
242
  });
@@ -0,0 +1,31 @@
1
+ // Astro writes its content data store as the dev server starts, which sends the browser a
2
+ // full reload. With no browser connected yet, Vite holds that reload and hands it to the
3
+ // first page that connects, so the first page opened after every start loaded twice.
4
+ // A reload sent before any page has been served has nothing to refresh (a tab left open
5
+ // from the previous run reloads itself when it reconnects), so drop those. Once a page has
6
+ // been requested, reloads pass through as usual.
7
+
8
+ const isFullReload = (payload) => typeof payload === 'object' && payload?.type === 'full-reload';
9
+
10
+ export default function skipStartupReload() {
11
+ return {
12
+ name: 'eventcatalog:skip-startup-reload',
13
+ apply: 'serve',
14
+ configureServer(server) {
15
+ let pageRequested = false;
16
+ server.middlewares.use((req, _res, next) => {
17
+ // Browser navigations ask for HTML; the CLI's prewarm request doesn't
18
+ if (!pageRequested && req.headers.accept?.includes('text/html')) pageRequested = true;
19
+ next();
20
+ });
21
+
22
+ for (const channel of new Set([server.ws, server.environments?.client?.hot].filter(Boolean))) {
23
+ const send = channel.send.bind(channel);
24
+ channel.send = (...args) => {
25
+ if (!pageRequested && isFullReload(args[0])) return;
26
+ return send(...args);
27
+ };
28
+ }
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,29 @@
1
+ // Astro crawls framework packages with vitefu, which looks them up concurrently and lists
2
+ // each one in `resolve.external` / `resolve.noExternal` as its lookup finishes, so the
3
+ // order changes from one start to the next. Vite hashes these lists unsorted to decide
4
+ // whether its pre-bundled dependencies are still valid, so every `eventcatalog dev` saw a
5
+ // "changed" config and re-bundled all of them. Only membership matters in these lists, so
6
+ // sorting them keeps the hash (and the dependency cache) stable across starts.
7
+
8
+ const byName = (a, b) => {
9
+ const left = String(a);
10
+ const right = String(b);
11
+ return left < right ? -1 : left > right ? 1 : 0;
12
+ };
13
+
14
+ const sortResolveLists = (resolve) => {
15
+ if (Array.isArray(resolve?.external)) resolve.external.sort(byName);
16
+ if (Array.isArray(resolve?.noExternal)) resolve.noExternal.sort(byName);
17
+ };
18
+
19
+ export default function stableDependencyOrder() {
20
+ return {
21
+ name: 'eventcatalog:stable-dependency-order',
22
+ apply: 'serve',
23
+ configResolved(config) {
24
+ sortResolveLists(config.resolve);
25
+ sortResolveLists(config.ssr);
26
+ for (const environment of Object.values(config.environments ?? {})) sortResolveLists(environment.resolve);
27
+ },
28
+ };
29
+ }
@@ -170,6 +170,16 @@ const nodeRadius = (degree: number, isHub = false) =>
170
170
  /** Above this node count, expensive extras (collision force) are disabled. */
171
171
  const LARGE_GRAPH_NODE_COUNT = 1500;
172
172
 
173
+ /**
174
+ * The first layout settles before it paints instead of animating into place for ~5s: ticks
175
+ * run in short slices across frames (no long tasks) until it settles, ~40ms of work for 100
176
+ * nodes. Big catalogs stop once ticking has taken the work limit, paint past their largest
177
+ * moves and animate the rest. The limit counts ticking time, not elapsed time, so a page busy
178
+ * hydrating other islands doesn't cut the settle short.
179
+ */
180
+ const INITIAL_SETTLE_SLICE_MS = 12;
181
+ const INITIAL_SETTLE_WORK_LIMIT_MS = 400;
182
+
173
183
  /** Sprite resolution for node icons (drawn scaled-down, so keep it crisp) */
174
184
  const ICON_SPRITE_SIZE = 64;
175
185
 
@@ -617,6 +627,10 @@ const CatalogForceGraph = ({
617
627
  // normal-sized, whatever the lens's usual hubs are
618
628
  const isHub = (d: SimNode) => (focusKey ? d.key === focusKey : hubs.has(d.collection));
619
629
 
630
+ // Later layouts (lens, focus and filter changes) animate from the previous positions
631
+ const isFirstLayout = positionsRef.current.size === 0;
632
+ // Nothing paints until the first layout has settled (see INITIAL_SETTLE_SLICE_MS)
633
+ let settling = isFirstLayout;
620
634
  const visibleNodes: SimNode[] = viewNodes
621
635
  .filter((n) => !hiddenCollections.has(n.collection))
622
636
  .map((n) => ({ ...n, degree: 0, ...positionsRef.current.get(n.key) }));
@@ -734,6 +748,7 @@ const CatalogForceGraph = ({
734
748
  };
735
749
 
736
750
  const draw = () => {
751
+ if (settling) return;
737
752
  context.save();
738
753
  context.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
739
754
  context.clearRect(0, 0, width, height);
@@ -1014,6 +1029,29 @@ const CatalogForceGraph = ({
1014
1029
  );
1015
1030
  }
1016
1031
 
1032
+ // Settle the first layout before it paints rather than animating it into place
1033
+ let settleFrame: number | undefined;
1034
+ if (isFirstLayout) {
1035
+ simulation.stop();
1036
+ let workLeft = INITIAL_SETTLE_WORK_LIMIT_MS;
1037
+ const settle = () => {
1038
+ const sliceStart = performance.now();
1039
+ const sliceEnd = sliceStart + Math.min(INITIAL_SETTLE_SLICE_MS, workLeft);
1040
+ while (simulation.alpha() > simulation.alphaMin() && performance.now() < sliceEnd) simulation.tick();
1041
+ workLeft -= performance.now() - sliceStart;
1042
+ if (simulation.alpha() > simulation.alphaMin() && workLeft > 0) {
1043
+ settleFrame = requestAnimationFrame(settle);
1044
+ return;
1045
+ }
1046
+ settling = false;
1047
+ // Settled: paint it as is (restarting would run one tick more than the animated
1048
+ // layout). Otherwise paint and animate whatever is left.
1049
+ if (simulation.alpha() <= simulation.alphaMin()) fitToView();
1050
+ else simulation.restart();
1051
+ };
1052
+ settleFrame = requestAnimationFrame(settle);
1053
+ }
1054
+
1017
1055
  const findNode = (canvasX: number, canvasY: number): SimNode | null => {
1018
1056
  if (Number.isNaN(canvasX) || Number.isNaN(canvasY)) return null;
1019
1057
  const x = transform.invertX(canvasX);
@@ -1219,6 +1257,7 @@ const CatalogForceGraph = ({
1219
1257
  for (const node of visibleNodes) {
1220
1258
  if (node.x !== undefined && node.y !== undefined) positionsRef.current.set(node.key, { x: node.x, y: node.y });
1221
1259
  }
1260
+ if (settleFrame !== undefined) cancelAnimationFrame(settleFrame);
1222
1261
  simulation.stop();
1223
1262
  // The tooltip outlives this effect — never leave it showing stale content
1224
1263
  tooltip.style.opacity = '0';
@@ -16,34 +16,22 @@
16
16
  import { setSidebarData, sidebarStore } from '@stores/sidebar-store';
17
17
  import { buildUrl } from '@utils/url-builder';
18
18
 
19
- // Only fetch if we haven't already loaded the data
19
+ // Only fetch if we haven't already loaded the data. Fetched straight away rather than when
20
+ // the browser is idle: the sidebar's first paint comes from the server (see SideNav.astro),
21
+ // but drilling down and search wait on this data
20
22
  if (!sidebarStore.get()) {
21
- const apiUrl = buildUrl('/api/sidebar-data.json', true);
22
-
23
- const loadSidebarData = () => {
24
- // Double-check cache in case another component loaded data while waiting for idle
25
- if (sidebarStore.get()) return;
26
-
27
- fetch(apiUrl)
28
- .then((response) => {
29
- if (!response.ok) {
30
- throw new Error(`Failed to fetch sidebar data: ${response.status}`);
31
- }
32
- return response.json();
33
- })
34
- .then((data) => {
35
- setSidebarData(data);
36
- })
37
- .catch((error) => {
38
- console.error('Error loading sidebar data:', error);
39
- });
40
- };
41
-
42
- // Defer non-critical sidebar payload fetch until browser is idle
43
- if (typeof window.requestIdleCallback === 'function') {
44
- window.requestIdleCallback(loadSidebarData, { timeout: 1200 });
45
- } else {
46
- setTimeout(loadSidebarData, 300);
47
- }
23
+ fetch(buildUrl('/api/sidebar-data.json', true))
24
+ .then((response) => {
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch sidebar data: ${response.status}`);
27
+ }
28
+ return response.json();
29
+ })
30
+ .then((data) => {
31
+ setSidebarData(data);
32
+ })
33
+ .catch((error) => {
34
+ console.error('Error loading sidebar data:', error);
35
+ });
48
36
  }
49
37
  </script>
@@ -21,57 +21,56 @@ import {
21
21
  type FavoriteItem,
22
22
  } from '@stores/favorites-store';
23
23
  import {
24
+ buildNodeLookup,
24
25
  canCollapseGroup,
25
26
  findNodeKeyByUrl,
26
27
  getBadgeClasses,
27
28
  getDefaultCollapsedState,
28
29
  hidesDefaultItemIcon,
29
30
  isGroupCollapsed,
31
+ resolveNavRef,
32
+ rootLevel,
33
+ stripBasePath,
30
34
  toggleGroupCollapsed,
35
+ type NavigationLevel,
36
+ type SidebarSnapshot,
31
37
  } from './utils';
32
38
  import { resolveIconUrl } from '@utils/icon';
33
39
 
34
40
  const cn = (...classes: (string | false | undefined)[]) => classes.filter(Boolean).join(' ');
35
41
 
36
- /**
37
- * Strip the configured base path from a pathname so URL pattern matching works
38
- * regardless of the base path setting (e.g. `/eventcatalog/docs/...` → `/docs/...`).
39
- */
40
- const stripBasePath = (pathname: string): string => {
41
- const base = __EC_BASE__.replace(/\/$/, ''); // e.g. '/eventcatalog'
42
- if (base && pathname.startsWith(base)) {
43
- return pathname.slice(base.length) || '/';
44
- }
45
- return pathname;
46
- };
47
-
48
42
  // ============================================
49
43
  // Component
50
44
  // ============================================
51
45
 
52
- type NavigationLevel = {
53
- key: string | null; // The key of the node that was drilled into (null for root)
54
- entries: ChildRef[];
55
- title: string;
56
- badge?: string; // Category badge (e.g., "Domain", "Service")
46
+ type NestedSideBarProps = {
47
+ /**
48
+ * This page's level, built on the server. It renders the sidebar into the HTML and keeps
49
+ * the island showing it until the full navigation data (sidebar-data.json) is loaded.
50
+ */
51
+ snapshot?: SidebarSnapshot;
57
52
  };
58
53
 
59
- export default function NestedSideBar() {
60
- const data = useStore(sidebarStore);
54
+ export default function NestedSideBar({ snapshot }: NestedSideBarProps) {
55
+ const storeData = useStore(sidebarStore);
61
56
  const favorites = useStore(favoritesStore);
57
+ // Drill-down, restoring state and favorites need the whole catalog; until it loads the
58
+ // snapshot only shows this page's level
59
+ const hasFullData = storeData !== null;
60
+ const data = storeData ?? snapshot?.data ?? null;
62
61
 
63
62
  // Guard against undefined data (e.g., during hydration)
64
63
  // Use useMemo to ensure stable references for roots and nodes
65
64
  const roots = useMemo(() => data?.roots ?? [], [data?.roots]);
66
65
  const nodes = useMemo(() => data?.nodes ?? {}, [data?.nodes]);
67
66
 
68
- const [navigationStack, setNavigationStack] = useState<NavigationLevel[]>(() => [
69
- { key: null, entries: [], title: 'Documentation' },
70
- ]);
67
+ const [navigationStack, setNavigationStack] = useState<NavigationLevel[]>(
68
+ () => snapshot?.stack ?? [{ key: null, entries: [], title: 'Documentation' }]
69
+ );
71
70
  const [animationKey, setAnimationKey] = useState(0);
72
71
  const [slideDirection, setSlideDirection] = useState<'forward' | 'backward' | null>(null);
73
72
  const [isInitialized, setIsInitialized] = useState(false);
74
- const [currentPath, setCurrentPath] = useState<string>('');
73
+ const [currentPath, setCurrentPath] = useState<string>(snapshot?.url ?? '');
75
74
  const [sectionCollapsePreferences, setSectionCollapsePreferences] = useState(loadCollapsedSections);
76
75
  const [showPathPreview, setShowPathPreview] = useState(false);
77
76
  const [showFullPath, setShowFullPath] = useState(false);
@@ -79,33 +78,7 @@ export default function NestedSideBar() {
79
78
 
80
79
  // Build a lookup map for faster URL navigation
81
80
  // Map format: "type:id" -> "nodeKey"
82
- const nodeLookup = useMemo(() => {
83
- const lookup = new Map<string, string>();
84
-
85
- Object.entries(nodes).forEach(([key, value]) => {
86
- // Skip keys that are references (string values pointing to other keys)
87
- // These are unversioned aliases like "domain:E-Commerce" -> "domain:E-Commerce:1.0.0"
88
- if (typeof value === 'string') return;
89
-
90
- // Key formats:
91
- // - "type:id:version" (e.g., "service:OrdersService:0.0.3")
92
- // - "type:id" (e.g., "user:john", "team:backend") - non-versioned resources
93
- // - "list:name" (e.g., "list:domains") - skip these
94
- const parts = key.split(':');
95
-
96
- // Skip list items
97
- if (parts[0] === 'list') return;
98
-
99
- if (parts.length >= 2) {
100
- // Store as "type:id"
101
- const type = parts[0];
102
- const id = parts[1];
103
- lookup.set(`${type}:${id}`, key);
104
- }
105
- });
106
-
107
- return lookup;
108
- }, [nodes]);
81
+ const nodeLookup = useMemo(() => buildNodeLookup(nodes), [nodes]);
109
82
 
110
83
  /**
111
84
  * Toggle section collapse state
@@ -126,7 +99,7 @@ export default function NestedSideBar() {
126
99
  setNavigationStack((prevStack) => {
127
100
  // Only update if the current stack has no entries (initial state)
128
101
  if (prevStack.length === 1 && prevStack[0].entries.length === 0) {
129
- return [{ key: null, entries: roots, title: 'Documentation' }];
102
+ return [rootLevel(roots)];
130
103
  }
131
104
  return prevStack;
132
105
  });
@@ -146,21 +119,7 @@ export default function NestedSideBar() {
146
119
  * Resolve a child reference to a NavNode
147
120
  * Handles both direct keys and string references (unversioned aliases pointing to versioned keys)
148
121
  */
149
- const resolveRef = useCallback(
150
- (ref: ChildRef): NavNode | null => {
151
- if (typeof ref === 'string') {
152
- const node = nodes[ref];
153
- if (!node) return null;
154
- // If node is a string, it's a reference to another key (e.g., unversioned alias)
155
- if (typeof node === 'string') {
156
- return (nodes[node] as NavNode) ?? null;
157
- }
158
- return node;
159
- }
160
- return ref;
161
- },
162
- [nodes]
163
- );
122
+ const resolveRef = useCallback((ref: ChildRef): NavNode | null => resolveNavRef(nodes, ref), [nodes]);
164
123
 
165
124
  /**
166
125
  * Check if a node is visible (default: true)
@@ -175,7 +134,7 @@ export default function NestedSideBar() {
175
134
  */
176
135
  const buildStackFromPath = useCallback(
177
136
  (path: string[]): NavigationLevel[] => {
178
- const stack: NavigationLevel[] = [{ key: null, entries: roots, title: 'Documentation' }];
137
+ const stack: NavigationLevel[] = [rootLevel(roots)];
179
138
 
180
139
  for (const key of path) {
181
140
  const node = resolveRef(key);
@@ -281,7 +240,7 @@ export default function NestedSideBar() {
281
240
  if (foundNode && foundNode.pages && foundNode.pages.length > 0) {
282
241
  // Fallback: Flattened navigation
283
242
  return [
284
- { key: null, entries: roots, title: 'Documentation' },
243
+ rootLevel(roots),
285
244
  { key: foundNodeKey, entries: foundNode.pages, title: foundNode.title, badge: foundNode.badge },
286
245
  ];
287
246
  }
@@ -296,7 +255,7 @@ export default function NestedSideBar() {
296
255
  setSlideDirection('backward');
297
256
  setAnimationKey((prev) => prev + 1);
298
257
  }
299
- return [{ key: null, entries: roots, title: 'Documentation' }];
258
+ return [rootLevel(roots)];
300
259
  });
301
260
  return true;
302
261
  }
@@ -309,14 +268,14 @@ export default function NestedSideBar() {
309
268
  * Restore state from localStorage on mount, or navigate to URL
310
269
  */
311
270
  useEffect(() => {
312
- if (!data || roots.length === 0 || isInitialized) return;
271
+ if (!hasFullData || roots.length === 0 || isInitialized) return;
313
272
 
314
273
  const currentUrl = window.location.pathname;
315
274
  const normalizedCurrentUrl = stripBasePath(currentUrl);
316
275
 
317
276
  // Force root navigation on homepage
318
277
  if (normalizedCurrentUrl === '/' || normalizedCurrentUrl === '') {
319
- setNavigationStack([{ key: null, entries: roots, title: 'Documentation' }]);
278
+ setNavigationStack([rootLevel(roots)]);
320
279
  setIsInitialized(true);
321
280
  return;
322
281
  }
@@ -347,7 +306,7 @@ export default function NestedSideBar() {
347
306
  const targetNode = resolveRef(targetKey);
348
307
  if (targetNode && targetNode.pages && targetNode.pages.length > 0) {
349
308
  finalStack = [
350
- { key: null, entries: roots, title: 'Documentation' },
309
+ rootLevel(roots),
351
310
  { key: targetKey, entries: targetNode.pages, title: targetNode.title, badge: targetNode.badge },
352
311
  ];
353
312
  }
@@ -355,13 +314,13 @@ export default function NestedSideBar() {
355
314
 
356
315
  // 3. Fallback to root
357
316
  if (!finalStack) {
358
- setNavigationStack([{ key: null, entries: roots, title: 'Documentation' }]);
317
+ setNavigationStack([rootLevel(roots)]);
359
318
  } else {
360
319
  setNavigationStack(finalStack);
361
320
  }
362
321
 
363
322
  setIsInitialized(true);
364
- }, [data, roots, isInitialized, buildStackFromPath, findNodeKeyForUrl, tryConnectStack, resolveRef]);
323
+ }, [hasFullData, roots, isInitialized, buildStackFromPath, findNodeKeyForUrl, tryConnectStack, resolveRef]);
365
324
 
366
325
  /**
367
326
  * Save state whenever navigation changes
@@ -575,6 +534,7 @@ export default function NestedSideBar() {
575
534
  * Handle drilling down into an item with children
576
535
  */
577
536
  const handleDrillDown = (node: NavNode, nodeKey: string | null) => {
537
+ if (!hasFullData) return;
578
538
  if (node.pages && node.pages.length > 0) {
579
539
  setSlideDirection('forward');
580
540
  setAnimationKey((prev) => prev + 1);
@@ -590,7 +550,7 @@ export default function NestedSideBar() {
590
550
  * Navigate back one level
591
551
  */
592
552
  const navigateBack = () => {
593
- if (navigationStack.length > 1) {
553
+ if (hasFullData && navigationStack.length > 1) {
594
554
  setSlideDirection('backward');
595
555
  setAnimationKey((prev) => prev + 1);
596
556
  setNavigationStack(navigationStack.slice(0, -1));
@@ -604,7 +564,7 @@ export default function NestedSideBar() {
604
564
  * Navigate to a specific level in the stack
605
565
  */
606
566
  const navigateToLevel = (levelIndex: number) => {
607
- if (levelIndex < navigationStack.length - 1) {
567
+ if (hasFullData && levelIndex < navigationStack.length - 1) {
608
568
  setSlideDirection('backward');
609
569
  setAnimationKey((prev) => prev + 1);
610
570
  setNavigationStack(navigationStack.slice(0, levelIndex + 1));
@@ -634,6 +594,7 @@ export default function NestedSideBar() {
634
594
  * Navigate to a favorited item
635
595
  */
636
596
  const navigateToFavorite = (favorite: FavoriteItem) => {
597
+ if (!hasFullData) return;
637
598
  // If it has an href and no children, just navigate to the URL
638
599
  const node = resolveRef(favorite.nodeKey);
639
600
  if (favorite.href && (!node?.pages || node.pages.length === 0)) {
@@ -107,6 +107,8 @@ export const saveCollapsedSections = (preferences: SectionCollapsePreferences):
107
107
  };
108
108
 
109
109
  export const loadCollapsedSections = (): SectionCollapsePreferences => {
110
+ // Rendered on the server for the first paint, where there is nothing saved yet
111
+ if (typeof localStorage === 'undefined') return createSectionCollapsePreferences();
110
112
  try {
111
113
  const stored = localStorage.getItem(SECTION_PREFERENCES_KEY);
112
114
  if (stored) {
@@ -1,9 +1,130 @@
1
1
  // Shared utilities for NestedSideBar components
2
2
 
3
- import type { NavNode } from '@stores/sidebar-store/state';
3
+ import type { ChildRef, NavigationData, NavNode } from '@stores/sidebar-store/state';
4
4
 
5
5
  export const SIDEBAR_GROUP_COLLAPSE_THRESHOLD = 5;
6
6
 
7
+ /**
8
+ * Strip the configured base path from a pathname so URL pattern matching works
9
+ * regardless of the base path setting (e.g. `/eventcatalog/docs/...` → `/docs/...`).
10
+ */
11
+ export const stripBasePath = (pathname: string): string => {
12
+ const base = __EC_BASE__.replace(/\/$/, ''); // e.g. '/eventcatalog'
13
+ if (base && pathname.startsWith(base)) {
14
+ return pathname.slice(base.length) || '/';
15
+ }
16
+ return pathname;
17
+ };
18
+
19
+ export type NavigationLevel = {
20
+ key: string | null; // The key of the node that was drilled into (null for root)
21
+ entries: ChildRef[];
22
+ title: string;
23
+ badge?: string; // Category badge (e.g., "Domain", "Service")
24
+ };
25
+
26
+ export const rootLevel = (roots: ChildRef[]): NavigationLevel => ({ key: null, entries: roots, title: 'Documentation' });
27
+
28
+ /** Resolve a child reference to a node, following unversioned aliases (string → key). */
29
+ export const resolveNavRef = (nodes: NavigationData['nodes'], ref: ChildRef): NavNode | null => {
30
+ if (typeof ref !== 'string') return ref;
31
+ const node = nodes[ref];
32
+ if (!node) return null;
33
+ if (typeof node === 'string') return (nodes[node] as NavNode) ?? null;
34
+ return node;
35
+ };
36
+
37
+ /** Map of "type:id" → versioned node key, used to match URLs to nodes. */
38
+ export const buildNodeLookup = (nodes: NavigationData['nodes']): Map<string, string> => {
39
+ const lookup = new Map<string, string>();
40
+
41
+ Object.entries(nodes).forEach(([key, value]) => {
42
+ // Skip keys that are references (string values pointing to other keys)
43
+ // These are unversioned aliases like "domain:E-Commerce" -> "domain:E-Commerce:1.0.0"
44
+ if (typeof value === 'string') return;
45
+
46
+ // Key formats:
47
+ // - "type:id:version" (e.g., "service:OrdersService:0.0.3")
48
+ // - "type:id" (e.g., "user:john", "team:backend") - non-versioned resources
49
+ // - "list:name" (e.g., "list:domains") - skip these
50
+ const parts = key.split(':');
51
+
52
+ // Skip list items
53
+ if (parts[0] === 'list') return;
54
+
55
+ if (parts.length >= 2) {
56
+ // Store as "type:id"
57
+ lookup.set(`${parts[0]}:${parts[1]}`, key);
58
+ }
59
+ });
60
+
61
+ return lookup;
62
+ };
63
+
64
+ /**
65
+ * What the sidebar shows for a URL before it knows the reader's saved drill-down path:
66
+ * the root level on the homepage or for pages without children, otherwise the matching
67
+ * resource's own level (the same "flattened" stack the client falls back to).
68
+ */
69
+ export const getStackForUrl = (
70
+ url: string,
71
+ data: NavigationData,
72
+ nodeLookup: ReadonlyMap<string, string> = buildNodeLookup(data.nodes)
73
+ ): NavigationLevel[] => {
74
+ const root = rootLevel(data.roots);
75
+ if (url === '/' || url === '') return [root];
76
+
77
+ const targetKey = findNodeKeyByUrl(url, data.nodes, nodeLookup);
78
+ const target = targetKey ? resolveNavRef(data.nodes, targetKey) : null;
79
+ if (!targetKey || !target?.pages?.length) return [root];
80
+
81
+ return [root, { key: targetKey, entries: target.pages, title: target.title, badge: target.badge }];
82
+ };
83
+
84
+ /**
85
+ * Everything the sidebar needs to render one page's level, without the whole catalog: the
86
+ * stack for the URL and only the nodes that level shows (its items, and the contents of its
87
+ * groups). Rendered on the server so the sidebar appears with the HTML, and handed to the
88
+ * island so it keeps showing it until the full navigation data loads.
89
+ */
90
+ export type SidebarSnapshot = {
91
+ url: string;
92
+ stack: NavigationLevel[];
93
+ data: NavigationData;
94
+ };
95
+
96
+ const nodeLookupCache = new WeakMap<NavigationData, Map<string, string>>();
97
+
98
+ export const buildSidebarSnapshot = (data: NavigationData, url: string): SidebarSnapshot => {
99
+ let nodeLookup = nodeLookupCache.get(data);
100
+ if (!nodeLookup) {
101
+ nodeLookup = buildNodeLookup(data.nodes);
102
+ nodeLookupCache.set(data, nodeLookup);
103
+ }
104
+
105
+ const stack = getStackForUrl(url, data, nodeLookup);
106
+ const level = stack[stack.length - 1];
107
+ const nodes: NavigationData['nodes'] = {};
108
+
109
+ const include = (ref: ChildRef) => {
110
+ if (typeof ref === 'string') {
111
+ if (ref in nodes) return;
112
+ const value = data.nodes[ref];
113
+ if (value === undefined) return;
114
+ nodes[ref] = value;
115
+ if (typeof value === 'string' && data.nodes[value] !== undefined) nodes[value] = data.nodes[value];
116
+ }
117
+ const node = resolveNavRef(data.nodes, ref);
118
+ // Groups render their children inline; items with children only need their own node
119
+ if (node?.type === 'group') node.pages?.forEach(include);
120
+ };
121
+
122
+ if (level.key) include(level.key);
123
+ level.entries.forEach(include);
124
+
125
+ return { url, stack, data: { roots: data.roots, nodes } };
126
+ };
127
+
7
128
  /**
8
129
  * The user's overrides of each group's default collapse state, keyed by group id.
9
130
  * A group is in at most one set; absence means "use the group's default".
@@ -3,10 +3,18 @@ import type { HTMLAttributes } from 'astro/types';
3
3
 
4
4
  interface Props extends Omit<HTMLAttributes<'div'>, 'children'> {}
5
5
  import NestedSideBar from './NestedSideBar';
6
+ import { buildSidebarSnapshot, stripBasePath } from './NestedSideBar/utils';
7
+ import { getNestedSideBarData } from '@stores/sidebar-store/state';
6
8
  import { ClientRouter } from 'astro:transitions';
9
+
10
+ // Render this page's sidebar level into the HTML so it shows before any JavaScript runs;
11
+ // the full navigation still loads from /api/sidebar-data.json (see SearchDataLoader)
12
+ const snapshot = buildSidebarSnapshot(await getNestedSideBarData(), stripBasePath(Astro.url.pathname));
7
13
  ---
8
14
 
9
15
  <div {...Astro.props}>
10
16
  <ClientRouter />
11
- <NestedSideBar client:only="react" />
17
+ <NestedSideBar client:only="react" snapshot={snapshot}>
18
+ <NestedSideBar slot="fallback" snapshot={snapshot} />
19
+ </NestedSideBar>
12
20
  </div>
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "BUSL-1.1",
9
9
  "type": "module",
10
- "version": "5.0.0-beta.13",
10
+ "version": "5.0.0-beta.14",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -140,9 +140,9 @@
140
140
  "y-protocols": "^1.0.7",
141
141
  "yjs": "^13.6.33",
142
142
  "zod": "^4.3.6",
143
+ "@eventcatalog/sdk": "2.30.0-beta.1",
143
144
  "@eventcatalog/linter": "1.2.0-beta.2",
144
- "@eventcatalog/visualiser": "^5.0.0-beta.6",
145
- "@eventcatalog/sdk": "2.30.0-beta.1"
145
+ "@eventcatalog/visualiser": "^5.0.0-beta.6"
146
146
  },
147
147
  "devDependencies": {
148
148
  "@astrojs/check": "^0.9.10",
@@ -1,7 +1,3 @@
1
- import {
2
- getFederationDiagnostics,
3
- resolveFederationRules
4
- } from "./chunk-JJPB6EOZ.js";
5
1
  import {
6
2
  withFederationOutputTransaction
7
3
  } from "./chunk-WRNH5C3U.js";
@@ -14,6 +10,10 @@ import {
14
10
  import {
15
11
  createFederationContentCache
16
12
  } from "./chunk-R7Z5ALYI.js";
13
+ import {
14
+ getFederationDiagnostics,
15
+ resolveFederationRules
16
+ } from "./chunk-JJPB6EOZ.js";
17
17
  import {
18
18
  getEventCatalogConfigFile
19
19
  } from "./chunk-RFIXKATC.js";