@eventcatalog/core 4.10.15 → 4.11.1

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 (165) 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/astro-output.cjs +60 -1
  6. package/dist/astro-output.js +1 -1
  7. package/dist/{chunk-U52FCZQ6.js → chunk-2YJRF4ZX.js} +1 -1
  8. package/dist/chunk-634NFV5U.js +75 -0
  9. package/dist/{chunk-I6HUOX4R.js → chunk-AXDNSGAN.js} +1 -1
  10. package/dist/chunk-DHH5U5PF.js +53 -0
  11. package/dist/{chunk-A4JRWRCV.js → chunk-DSCIWCX2.js} +1 -1
  12. package/dist/chunk-FWXEV4LF.js +19 -0
  13. package/dist/{chunk-Q2ATWE7N.js → chunk-GZVDXCYF.js} +1 -1
  14. package/dist/{chunk-7LWOCX4R.js → chunk-RYNPEIHQ.js} +1 -1
  15. package/dist/{chunk-HBVTU6ZP.js → chunk-YYVW4DCO.js} +1 -1
  16. package/dist/constants.cjs +1 -1
  17. package/dist/constants.js +1 -1
  18. package/dist/copy-core.cjs +88 -0
  19. package/dist/copy-core.d.cts +4 -0
  20. package/dist/copy-core.d.ts +4 -0
  21. package/dist/copy-core.js +8 -0
  22. package/dist/eventcatalog.cjs +234 -115
  23. package/dist/eventcatalog.js +36 -30
  24. package/dist/federation/federate.js +3 -3
  25. package/dist/federation/source-provider.js +2 -2
  26. package/dist/generate.cjs +1 -1
  27. package/dist/generate.js +3 -3
  28. package/dist/update-check.cjs +44 -0
  29. package/dist/update-check.d.cts +18 -0
  30. package/dist/update-check.d.ts +18 -0
  31. package/dist/update-check.js +8 -0
  32. package/dist/utils/cli-logger.cjs +1 -1
  33. package/dist/utils/cli-logger.js +2 -2
  34. package/eventcatalog/astro.config.mjs +8 -1
  35. package/eventcatalog/public/icons/languages/android.svg +1 -0
  36. package/eventcatalog/public/icons/languages/angular.svg +1 -0
  37. package/eventcatalog/public/icons/languages/apple.svg +1 -0
  38. package/eventcatalog/public/icons/languages/arduino.svg +1 -0
  39. package/eventcatalog/public/icons/languages/bash.svg +1 -0
  40. package/eventcatalog/public/icons/languages/bun.svg +1 -0
  41. package/eventcatalog/public/icons/languages/c.svg +1 -0
  42. package/eventcatalog/public/icons/languages/clojure.svg +1 -0
  43. package/eventcatalog/public/icons/languages/cpp.svg +1 -0
  44. package/eventcatalog/public/icons/languages/crystal.svg +1 -0
  45. package/eventcatalog/public/icons/languages/csharp.svg +1 -0
  46. package/eventcatalog/public/icons/languages/css.svg +1 -0
  47. package/eventcatalog/public/icons/languages/curl.svg +1 -0
  48. package/eventcatalog/public/icons/languages/dart.svg +1 -0
  49. package/eventcatalog/public/icons/languages/deno.svg +1 -0
  50. package/eventcatalog/public/icons/languages/docker.svg +1 -0
  51. package/eventcatalog/public/icons/languages/elm.svg +1 -0
  52. package/eventcatalog/public/icons/languages/erlang.svg +1 -0
  53. package/eventcatalog/public/icons/languages/flutter.svg +1 -0
  54. package/eventcatalog/public/icons/languages/fortran.svg +1 -0
  55. package/eventcatalog/public/icons/languages/fsharp.svg +1 -0
  56. package/eventcatalog/public/icons/languages/git.svg +1 -0
  57. package/eventcatalog/public/icons/languages/gradle.svg +1 -0
  58. package/eventcatalog/public/icons/languages/graphql.svg +1 -0
  59. package/eventcatalog/public/icons/languages/grpc.svg +1 -0
  60. package/eventcatalog/public/icons/languages/haskell.svg +1 -0
  61. package/eventcatalog/public/icons/languages/hasura.svg +1 -0
  62. package/eventcatalog/public/icons/languages/html.svg +1 -0
  63. package/eventcatalog/public/icons/languages/httpie.svg +1 -0
  64. package/eventcatalog/public/icons/languages/javascript.svg +1 -0
  65. package/eventcatalog/public/icons/languages/json-schema.svg +1 -0
  66. package/eventcatalog/public/icons/languages/json.svg +1 -0
  67. package/eventcatalog/public/icons/languages/julia.svg +1 -0
  68. package/eventcatalog/public/icons/languages/jupyter.svg +1 -0
  69. package/eventcatalog/public/icons/languages/kafka.svg +1 -0
  70. package/eventcatalog/public/icons/languages/kotlin.svg +1 -0
  71. package/eventcatalog/public/icons/languages/lua.svg +1 -0
  72. package/eventcatalog/public/icons/languages/markdown.svg +1 -0
  73. package/eventcatalog/public/icons/languages/maven.svg +1 -0
  74. package/eventcatalog/public/icons/languages/mongodb.svg +1 -0
  75. package/eventcatalog/public/icons/languages/mysql.svg +1 -0
  76. package/eventcatalog/public/icons/languages/nginx.svg +1 -0
  77. package/eventcatalog/public/icons/languages/nim.svg +1 -0
  78. package/eventcatalog/public/icons/languages/ocaml.svg +1 -0
  79. package/eventcatalog/public/icons/languages/perl.svg +1 -0
  80. package/eventcatalog/public/icons/languages/php.svg +1 -0
  81. package/eventcatalog/public/icons/languages/postgresql.svg +1 -0
  82. package/eventcatalog/public/icons/languages/prisma.svg +1 -0
  83. package/eventcatalog/public/icons/languages/purescript.svg +1 -0
  84. package/eventcatalog/public/icons/languages/python.svg +1 -0
  85. package/eventcatalog/public/icons/languages/r.svg +1 -0
  86. package/eventcatalog/public/icons/languages/react.svg +1 -0
  87. package/eventcatalog/public/icons/languages/reasonml.svg +1 -0
  88. package/eventcatalog/public/icons/languages/redis.svg +1 -0
  89. package/eventcatalog/public/icons/languages/ruby.svg +1 -0
  90. package/eventcatalog/public/icons/languages/rust.svg +1 -0
  91. package/eventcatalog/public/icons/languages/sass.svg +1 -0
  92. package/eventcatalog/public/icons/languages/scala.svg +1 -0
  93. package/eventcatalog/public/icons/languages/solidity.svg +1 -0
  94. package/eventcatalog/public/icons/languages/spring.svg +1 -0
  95. package/eventcatalog/public/icons/languages/sqlite.svg +1 -0
  96. package/eventcatalog/public/icons/languages/svelte.svg +1 -0
  97. package/eventcatalog/public/icons/languages/swagger.svg +1 -0
  98. package/eventcatalog/public/icons/languages/swift.svg +1 -0
  99. package/eventcatalog/public/icons/languages/tailwind.svg +1 -0
  100. package/eventcatalog/public/icons/languages/terraform.svg +1 -0
  101. package/eventcatalog/public/icons/languages/toml.svg +1 -0
  102. package/eventcatalog/public/icons/languages/typescript.svg +1 -0
  103. package/eventcatalog/public/icons/languages/unity.svg +1 -0
  104. package/eventcatalog/public/icons/languages/vim.svg +1 -0
  105. package/eventcatalog/public/icons/languages/vue.svg +1 -0
  106. package/eventcatalog/public/icons/languages/webassembly.svg +1 -0
  107. package/eventcatalog/public/icons/languages/yaml.svg +1 -0
  108. package/eventcatalog/public/icons/languages/zig.svg +1 -0
  109. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.astro +12 -0
  110. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +176 -0
  111. package/eventcatalog/src/components/MDX/CodeGroup/MarkdownCodeGroup.tsx +22 -0
  112. package/eventcatalog/src/components/MDX/CodeGroup/code-group-icons.ts +345 -0
  113. package/eventcatalog/src/components/MDX/CodeGroup/code-group.css +22 -0
  114. package/eventcatalog/src/components/MDX/CodeGroup/parse-panels.ts +11 -0
  115. package/eventcatalog/src/components/MDX/Columns/Column.astro +10 -0
  116. package/eventcatalog/src/components/MDX/Columns/Columns.astro +14 -0
  117. package/eventcatalog/src/components/MDX/Columns/MarkdownColumns.tsx +37 -0
  118. package/eventcatalog/src/components/MDX/Columns/layout.ts +16 -0
  119. package/eventcatalog/src/components/MDX/components.tsx +6 -0
  120. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +1 -1
  121. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +151 -114
  122. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +510 -248
  123. package/eventcatalog/src/components/SchemaExplorer/SchemaFlows.tsx +76 -0
  124. package/eventcatalog/src/components/SchemaExplorer/SchemaGraph.tsx +47 -0
  125. package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +11 -0
  126. package/eventcatalog/src/components/SchemaExplorer/SchemaUsage.tsx +17 -0
  127. package/eventcatalog/src/components/SchemaExplorer/types.ts +48 -3
  128. package/eventcatalog/src/components/SchemaExplorer/utils.ts +28 -1
  129. package/eventcatalog/src/content.config.ts +28 -0
  130. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +38 -7
  131. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +144 -37
  132. package/eventcatalog/src/pages/schemas/explorer/index.astro +5 -0
  133. package/eventcatalog/src/plugins/empty-collection-warning.d.ts +21 -0
  134. package/eventcatalog/src/plugins/empty-collection-warning.mjs +107 -0
  135. package/eventcatalog/src/plugins/quiet-empty-collections-logger.mjs +11 -0
  136. package/eventcatalog/src/remark-plugins/code-group.ts +39 -0
  137. package/eventcatalog/src/remark-plugins/markdown-code-group.ts +59 -0
  138. package/eventcatalog/src/remark-plugins/markdown-columns.ts +66 -0
  139. package/eventcatalog/src/utils/collections/channels.ts +12 -4
  140. package/eventcatalog/src/utils/collections/examples.ts +133 -41
  141. package/eventcatalog/src/utils/collections/flows.ts +43 -0
  142. package/eventcatalog/src/utils/collections/messages.ts +2 -2
  143. package/eventcatalog/src/utils/collections/schemas.ts +15 -0
  144. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +17 -15
  145. package/eventcatalog/src/utils/schema-explorer.ts +73 -7
  146. package/eventcatalog/src/utils/schema-usage-graph.ts +92 -0
  147. package/package.json +5 -4
  148. package/dist/chunk-RDYYZQYU.js +0 -18
  149. package/eventcatalog/src/components/EnvironmentDropdown.test.ts +0 -64
  150. package/eventcatalog/src/components/Grids/message-link.spec.ts +0 -59
  151. package/eventcatalog/src/components/MDX/CustomProperties/custom-properties.spec.ts +0 -38
  152. package/eventcatalog/src/components/MDX/SchemaViewer/schema-viewer-utils.spec.ts +0 -116
  153. package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.test.tsx +0 -114
  154. package/eventcatalog/src/components/SchemaExplorer/ProtobufSchemaViewer.test.tsx +0 -91
  155. package/eventcatalog/src/components/SchemaExplorer/SchemaViewerHover.test.tsx +0 -33
  156. package/eventcatalog/src/components/SchemaExplorer/protobuf-validation.test.ts +0 -84
  157. package/eventcatalog/src/components/Search/search-utils.spec.ts +0 -173
  158. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.spec.ts +0 -105
  159. package/eventcatalog/src/components/navigation-progress.spec.ts +0 -68
  160. package/eventcatalog/src/enterprise/directory/user-team-directory.spec.ts +0 -527
  161. package/eventcatalog/src/enterprise/fields/field-extractor.test.ts +0 -302
  162. package/eventcatalog/src/enterprise/fields/fields-db.test.ts +0 -185
  163. package/eventcatalog/src/stores/eventcatalog-store.spec.ts +0 -60
  164. package/eventcatalog/src/utils/collections/glob-loader.spec.ts +0 -87
  165. package/dist/{chunk-6K7XZAYI.js → chunk-A2RZR3U4.js} +3 -3
@@ -0,0 +1,176 @@
1
+ import { useEffect, useId, useRef, useState, type ReactNode } from 'react';
2
+ import { Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/react';
3
+ import { CheckIcon, ChevronDownIcon, ClipboardIcon } from '@heroicons/react/24/outline';
4
+ import { getCodeLanguageIconUrl, getCodeLanguageName } from './code-group-icons';
5
+ import './code-group.css';
6
+
7
+ export interface CodeGroupItem {
8
+ label: string;
9
+ language?: string;
10
+ content?: ReactNode;
11
+ html?: string;
12
+ }
13
+ const changeEvent = 'eventcatalog:code-group-change';
14
+ const focus = 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-[rgb(var(--ec-accent))]';
15
+ function LanguageIcon({ item }: { item: CodeGroupItem }) {
16
+ const url = getCodeLanguageIconUrl(item.language ?? '', item.label);
17
+ return url ? <img src={url} alt="" className="size-4 shrink-0" /> : null;
18
+ }
19
+ export default function CodeGroup({
20
+ items,
21
+ dropdown = false,
22
+ className = '',
23
+ }: {
24
+ items: CodeGroupItem[];
25
+ dropdown?: boolean;
26
+ className?: string;
27
+ }) {
28
+ const [selected, setSelected] = useState(0);
29
+ const [copied, setCopied] = useState(false);
30
+ const panels = useRef<HTMLDivElement>(null);
31
+ const tabs = useRef<(HTMLButtonElement | null)[]>([]);
32
+ const id = useId();
33
+ const active = Math.min(selected, Math.max(items.length - 1, 0));
34
+ useEffect(() => {
35
+ const sync = (event: Event) => {
36
+ const index = items.findIndex((item) => item.label === (event as CustomEvent<string>).detail);
37
+ if (index >= 0) setSelected(index);
38
+ };
39
+ document.addEventListener(changeEvent, sync);
40
+ return () => document.removeEventListener(changeEvent, sync);
41
+ }, [items]);
42
+ useEffect(() => {
43
+ setCopied(false);
44
+ }, [active]);
45
+ useEffect(() => {
46
+ if (!copied) return;
47
+ const timer = setTimeout(() => setCopied(false), 2000);
48
+ return () => clearTimeout(timer);
49
+ }, [copied]);
50
+ const select = (index: number) => {
51
+ setSelected(index);
52
+ document.dispatchEvent(new CustomEvent(changeEvent, { detail: items[index].label }));
53
+ };
54
+ const copy = async () => {
55
+ const panel = panels.current?.children[active];
56
+ const encoded = panel?.querySelector('[data-code]')?.getAttribute('data-code');
57
+ const code = encoded?.replace(/\u007f/g, '\n') ?? panel?.querySelector('pre')?.textContent ?? '';
58
+ try {
59
+ await navigator.clipboard.writeText(code);
60
+ setCopied(true);
61
+ } catch {
62
+ setCopied(false);
63
+ }
64
+ };
65
+ if (!items.length) return null;
66
+ return (
67
+ <div
68
+ className={`ec-code-group not-prose relative my-6 rounded-xl border border-[rgb(var(--ec-content-border))] bg-[rgb(var(--ec-code-bg,var(--ec-page-bg)))] ${className}`}
69
+ >
70
+ <div
71
+ data-code-controls
72
+ className="flex items-stretch justify-between gap-2 rounded-t-xl border-b border-[rgb(var(--ec-content-border))] bg-[rgb(var(--ec-content-hover))] pl-1 pr-2"
73
+ >
74
+ {dropdown ? (
75
+ <span className="min-w-0 truncate px-3 py-2.5 text-sm">{items[active].label}</span>
76
+ ) : (
77
+ <div role="tablist" aria-label="Code examples" className="flex min-w-0 overflow-x-auto">
78
+ {items.map((item, index) => (
79
+ <button
80
+ key={index}
81
+ ref={(el) => {
82
+ tabs.current[index] = el;
83
+ }}
84
+ type="button"
85
+ role="tab"
86
+ id={`${id}-tab-${index}`}
87
+ aria-controls={`${id}-panel-${index}`}
88
+ aria-selected={index === active}
89
+ tabIndex={index === active ? 0 : -1}
90
+ onClick={() => select(index)}
91
+ onKeyDown={(event) => {
92
+ const next =
93
+ event.key === 'ArrowRight'
94
+ ? (index + 1) % items.length
95
+ : event.key === 'ArrowLeft'
96
+ ? (index - 1 + items.length) % items.length
97
+ : event.key === 'Home'
98
+ ? 0
99
+ : event.key === 'End'
100
+ ? items.length - 1
101
+ : undefined;
102
+ if (next !== undefined) {
103
+ event.preventDefault();
104
+ select(next);
105
+ tabs.current[next]?.focus();
106
+ }
107
+ }}
108
+ className={`-mb-px inline-flex shrink-0 items-center gap-2 border-b-2 border-transparent px-3 py-2.5 text-[13px] font-medium text-[rgb(var(--ec-content-text-muted))] aria-selected:border-[rgb(var(--ec-accent))] aria-selected:text-[rgb(var(--ec-content-text))] ${focus}`}
109
+ >
110
+ <LanguageIcon item={item} />
111
+ {item.label}
112
+ </button>
113
+ ))}
114
+ </div>
115
+ )}
116
+ <div className="flex shrink-0 items-center gap-1 py-1">
117
+ {dropdown && (
118
+ <Listbox value={active} onChange={select}>
119
+ <div className="relative">
120
+ <ListboxButton
121
+ aria-label="Select code example"
122
+ className={`flex items-center gap-2 rounded-lg px-2 py-1 text-[13px] hover:bg-[rgb(var(--ec-page-bg))] ${focus}`}
123
+ >
124
+ <LanguageIcon item={items[active]} />
125
+ {getCodeLanguageName(items[active].language ?? '', items[active].label)}
126
+ <ChevronDownIcon className="size-3" />
127
+ </ListboxButton>
128
+ <ListboxOptions className="absolute right-0 top-full z-20 mt-2 min-w-48 rounded-xl border border-[rgb(var(--ec-content-border))] bg-[rgb(var(--ec-page-bg))] p-1 shadow-lg">
129
+ {items.map((item, index) => (
130
+ <ListboxOption
131
+ key={index}
132
+ value={index}
133
+ className="flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-[13px] data-[focus]:bg-[rgb(var(--ec-content-hover))] data-[selected]:text-[rgb(var(--ec-accent))]"
134
+ >
135
+ {({ selected }) => (
136
+ <>
137
+ <LanguageIcon item={item} />
138
+ <span className="flex-1">{getCodeLanguageName(item.language ?? '', item.label)}</span>
139
+ {selected && <CheckIcon className="size-4" />}
140
+ </>
141
+ )}
142
+ </ListboxOption>
143
+ ))}
144
+ </ListboxOptions>
145
+ </div>
146
+ </Listbox>
147
+ )}
148
+ <button
149
+ type="button"
150
+ aria-label={copied ? 'Copied!' : 'Copy code'}
151
+ onClick={copy}
152
+ className={`flex size-7 items-center justify-center rounded-md text-[rgb(var(--ec-content-text-muted))] hover:bg-[rgb(var(--ec-page-bg))] ${focus}`}
153
+ >
154
+ {copied ? <CheckIcon className="size-4 text-emerald-500" /> : <ClipboardIcon className="size-4" />}
155
+ </button>
156
+ </div>
157
+ </div>
158
+ <div ref={panels} data-code-panels className="overflow-hidden rounded-b-xl">
159
+ {items.map((item, index) => (
160
+ <div
161
+ key={index}
162
+ data-code-panel={item.label}
163
+ id={`${id}-panel-${index}`}
164
+ role={dropdown ? 'region' : 'tabpanel'}
165
+ aria-label={dropdown ? item.label : undefined}
166
+ aria-labelledby={dropdown ? undefined : `${id}-tab-${index}`}
167
+ hidden={index !== active}
168
+ tabIndex={0}
169
+ >
170
+ {item.html !== undefined ? <div dangerouslySetInnerHTML={{ __html: item.html }} /> : item.content}
171
+ </div>
172
+ ))}
173
+ </div>
174
+ </div>
175
+ );
176
+ }
@@ -0,0 +1,22 @@
1
+ import { Children, isValidElement, type ReactNode } from 'react';
2
+ import CodeGroup from './CodeGroup';
3
+
4
+ export function MarkdownCodeGroup({
5
+ children,
6
+ dropdown,
7
+ className,
8
+ }: {
9
+ children?: ReactNode;
10
+ dropdown?: boolean;
11
+ className?: string;
12
+ }) {
13
+ const items = Children.toArray(children).flatMap((child) => {
14
+ if (
15
+ !isValidElement<{ 'data-code-panel'?: string; 'data-code-lang'?: string }>(child) ||
16
+ child.props['data-code-panel'] === undefined
17
+ )
18
+ return [];
19
+ return [{ label: child.props['data-code-panel'], language: child.props['data-code-lang'], content: child }];
20
+ });
21
+ return <CodeGroup items={items} dropdown={dropdown} className={className} />;
22
+ }
@@ -0,0 +1,345 @@
1
+ import { buildUrl } from '@utils/url-builder';
2
+
3
+ /** Maps fence languages and file extensions to an icon in `public/icons/languages`. */
4
+ const LANGUAGE_ICONS: Record<string, string> = {
5
+ js: 'javascript',
6
+ javascript: 'javascript',
7
+ jsx: 'react',
8
+ mjs: 'javascript',
9
+ cjs: 'javascript',
10
+ ts: 'typescript',
11
+ typescript: 'typescript',
12
+ tsx: 'react',
13
+ node: 'nodejs',
14
+ nodejs: 'nodejs',
15
+ py: 'python',
16
+ python: 'python',
17
+ rb: 'ruby',
18
+ ruby: 'ruby',
19
+ php: 'php',
20
+ cs: 'csharp',
21
+ csharp: 'csharp',
22
+ 'c#': 'csharp',
23
+ dotnet: 'dotnet',
24
+ fsharp: 'fsharp',
25
+ vb: 'dotnet',
26
+ go: 'go',
27
+ golang: 'go',
28
+ java: 'java',
29
+ kt: 'kotlin',
30
+ kotlin: 'kotlin',
31
+ gradle: 'gradle',
32
+ swift: 'swift',
33
+ rs: 'rust',
34
+ rust: 'rust',
35
+ c: 'c',
36
+ h: 'c',
37
+ cpp: 'cpp',
38
+ cc: 'cpp',
39
+ cxx: 'cpp',
40
+ hpp: 'cpp',
41
+ 'c++': 'cpp',
42
+ dart: 'dart',
43
+ scala: 'scala',
44
+ r: 'r',
45
+ perl: 'perl',
46
+ pl: 'perl',
47
+ lua: 'lua',
48
+ hs: 'haskell',
49
+ haskell: 'haskell',
50
+ clj: 'clojure',
51
+ clojure: 'clojure',
52
+ erl: 'erlang',
53
+ erlang: 'erlang',
54
+ ml: 'ocaml',
55
+ ocaml: 'ocaml',
56
+ zig: 'zig',
57
+ deno: 'deno',
58
+ bun: 'bun',
59
+ sol: 'solidity',
60
+ solidity: 'solidity',
61
+ sh: 'bash',
62
+ bash: 'bash',
63
+ zsh: 'bash',
64
+ shell: 'bash',
65
+ console: 'bash',
66
+ curl: 'curl',
67
+ yaml: 'yaml',
68
+ yml: 'yaml',
69
+ json: 'json',
70
+ jsonc: 'json',
71
+ json5: 'json',
72
+ jsonschema: 'json-schema',
73
+ 'json-schema': 'json-schema',
74
+ toml: 'toml',
75
+ md: 'markdown',
76
+ mdx: 'markdown',
77
+ markdown: 'markdown',
78
+ html: 'html',
79
+ htm: 'html',
80
+ vue: 'vue',
81
+ svelte: 'svelte',
82
+ angular: 'angular',
83
+ css: 'css',
84
+ scss: 'sass',
85
+ less: 'css',
86
+ graphql: 'graphql',
87
+ gql: 'graphql',
88
+ sql: 'mysql',
89
+ mysql: 'mysql',
90
+ postgres: 'postgresql',
91
+ postgresql: 'postgresql',
92
+ psql: 'postgresql',
93
+ docker: 'docker',
94
+ dockerfile: 'docker',
95
+ git: 'git',
96
+ diff: 'git',
97
+ hcl: 'terraform',
98
+ tf: 'terraform',
99
+ terraform: 'terraform',
100
+ nginx: 'nginx',
101
+ kafka: 'kafka',
102
+ '.net': 'dotnet',
103
+ aspnet: 'dotnet',
104
+ 'asp.net': 'dotnet',
105
+ vbnet: 'dotnet',
106
+ 'vb.net': 'dotnet',
107
+ fs: 'fsharp',
108
+ 'f#': 'fsharp',
109
+ kts: 'kotlin',
110
+ groovy: 'gradle',
111
+ maven: 'maven',
112
+ pom: 'maven',
113
+ spring: 'spring',
114
+ jl: 'julia',
115
+ julia: 'julia',
116
+ f90: 'fortran',
117
+ fortran: 'fortran',
118
+ cr: 'crystal',
119
+ crystal: 'crystal',
120
+ nim: 'nim',
121
+ elm: 'elm',
122
+ purs: 'purescript',
123
+ purescript: 'purescript',
124
+ re: 'reasonml',
125
+ reason: 'reasonml',
126
+ reasonml: 'reasonml',
127
+ flutter: 'flutter',
128
+ grpc: 'grpc',
129
+ sqlite: 'sqlite',
130
+ mongodb: 'mongodb',
131
+ mongo: 'mongodb',
132
+ redis: 'redis',
133
+ prisma: 'prisma',
134
+ wasm: 'webassembly',
135
+ wat: 'webassembly',
136
+ webassembly: 'webassembly',
137
+ sass: 'sass',
138
+ tailwind: 'tailwind',
139
+ tailwindcss: 'tailwind',
140
+ http: 'httpie',
141
+ httpie: 'httpie',
142
+ swagger: 'swagger',
143
+ ipynb: 'jupyter',
144
+ jupyter: 'jupyter',
145
+ android: 'android',
146
+ ios: 'apple',
147
+ objc: 'apple',
148
+ objectivec: 'apple',
149
+ 'objective-c': 'apple',
150
+ ino: 'arduino',
151
+ arduino: 'arduino',
152
+ unity: 'unity',
153
+ vim: 'vim',
154
+ hasura: 'hasura',
155
+ };
156
+
157
+ /** Languages that have a display name but no icon. */
158
+ const NAME_ONLY_LANGUAGES: Record<string, string> = {
159
+ xml: 'XML',
160
+ xsd: 'XML Schema',
161
+ txt: 'Text',
162
+ text: 'Text',
163
+ plaintext: 'Text',
164
+ plain: 'Text',
165
+ ini: 'INI',
166
+ properties: 'Properties',
167
+ env: 'Env',
168
+ dotenv: 'Env',
169
+ makefile: 'Makefile',
170
+ make: 'Makefile',
171
+ cmake: 'CMake',
172
+ tex: 'LaTeX',
173
+ latex: 'LaTeX',
174
+ csv: 'CSV',
175
+ groovy: 'Groovy',
176
+ elixir: 'Elixir',
177
+ ex: 'Elixir',
178
+ exs: 'Elixir',
179
+ matlab: 'MATLAB',
180
+ ps1: 'PowerShell',
181
+ powershell: 'PowerShell',
182
+ pwsh: 'PowerShell',
183
+ bat: 'Batch',
184
+ cmd: 'Batch',
185
+ regex: 'Regex',
186
+ mermaid: 'Mermaid',
187
+ hbs: 'Handlebars',
188
+ handlebars: 'Handlebars',
189
+ ejs: 'EJS',
190
+ pug: 'Pug',
191
+ nix: 'Nix',
192
+ cobol: 'COBOL',
193
+ scheme: 'Scheme',
194
+ racket: 'Racket',
195
+ lisp: 'Lisp',
196
+ ada: 'Ada',
197
+ pascal: 'Pascal',
198
+ prolog: 'Prolog',
199
+ smalltalk: 'Smalltalk',
200
+ vhdl: 'VHDL',
201
+ verilog: 'Verilog',
202
+ asm: 'Assembly',
203
+ assembly: 'Assembly',
204
+ };
205
+
206
+ /** Formats that already have an icon elsewhere under `public/icons`. */
207
+ const SCHEMA_ICONS: Record<string, string> = {
208
+ avro: 'avro',
209
+ avsc: 'avro',
210
+ proto: 'proto',
211
+ protobuf: 'proto',
212
+ openapi: 'openapi',
213
+ asyncapi: 'asyncapi',
214
+ };
215
+
216
+ const LANGUAGE_NAMES: Record<string, string> = {
217
+ javascript: 'JavaScript',
218
+ typescript: 'TypeScript',
219
+ react: 'React',
220
+ nodejs: 'Node.js',
221
+ python: 'Python',
222
+ ruby: 'Ruby',
223
+ php: 'PHP',
224
+ csharp: 'C#',
225
+ dotnet: '.NET',
226
+ go: 'Go',
227
+ java: 'Java',
228
+ kotlin: 'Kotlin',
229
+ gradle: 'Gradle',
230
+ swift: 'Swift',
231
+ rust: 'Rust',
232
+ c: 'C',
233
+ cpp: 'C++',
234
+ dart: 'Dart',
235
+ scala: 'Scala',
236
+ r: 'R',
237
+ perl: 'Perl',
238
+ lua: 'Lua',
239
+ haskell: 'Haskell',
240
+ clojure: 'Clojure',
241
+ erlang: 'Erlang',
242
+ ocaml: 'OCaml',
243
+ zig: 'Zig',
244
+ deno: 'Deno',
245
+ bun: 'Bun',
246
+ solidity: 'Solidity',
247
+ bash: 'Shell',
248
+ curl: 'cURL',
249
+ yaml: 'YAML',
250
+ json: 'JSON',
251
+ 'json-schema': 'JSON Schema',
252
+ toml: 'TOML',
253
+ markdown: 'Markdown',
254
+ html: 'HTML',
255
+ vue: 'Vue',
256
+ svelte: 'Svelte',
257
+ angular: 'Angular',
258
+ css: 'CSS',
259
+ graphql: 'GraphQL',
260
+ mysql: 'SQL',
261
+ postgresql: 'PostgreSQL',
262
+ docker: 'Docker',
263
+ git: 'Git',
264
+ terraform: 'Terraform',
265
+ nginx: 'NGINX',
266
+ kafka: 'Kafka',
267
+ avro: 'Avro',
268
+ proto: 'Protobuf',
269
+ openapi: 'OpenAPI',
270
+ asyncapi: 'AsyncAPI',
271
+ fsharp: 'F#',
272
+ maven: 'Maven',
273
+ spring: 'Spring',
274
+ julia: 'Julia',
275
+ fortran: 'Fortran',
276
+ crystal: 'Crystal',
277
+ nim: 'Nim',
278
+ elm: 'Elm',
279
+ purescript: 'PureScript',
280
+ reasonml: 'ReasonML',
281
+ flutter: 'Flutter',
282
+ grpc: 'gRPC',
283
+ sqlite: 'SQLite',
284
+ mongodb: 'MongoDB',
285
+ redis: 'Redis',
286
+ prisma: 'Prisma',
287
+ webassembly: 'WebAssembly',
288
+ sass: 'Sass',
289
+ tailwind: 'Tailwind CSS',
290
+ httpie: 'HTTP',
291
+ swagger: 'Swagger',
292
+ jupyter: 'Jupyter',
293
+ android: 'Android',
294
+ apple: 'Apple',
295
+ arduino: 'Arduino',
296
+ unity: 'Unity',
297
+ vim: 'Vim',
298
+ hasura: 'Hasura',
299
+ };
300
+
301
+ const normalize = (value: string) => value.trim().toLowerCase();
302
+
303
+ const getExtension = (label?: string) => {
304
+ const match = label?.trim().match(/\.([a-z0-9+#]+)$/i);
305
+ return match?.[1];
306
+ };
307
+
308
+ const resolveIconKey = (language?: string, label?: string) => {
309
+ for (const candidate of [language, getExtension(label)]) {
310
+ if (!candidate) continue;
311
+ const key = normalize(candidate);
312
+ if (LANGUAGE_ICONS[key]) return LANGUAGE_ICONS[key];
313
+ if (SCHEMA_ICONS[key]) return SCHEMA_ICONS[key];
314
+ }
315
+ return undefined;
316
+ };
317
+
318
+ /**
319
+ * Human readable language name for a code panel, for example `Python` for `py`.
320
+ * Falls back to the fence language as written, or the label when there is no language.
321
+ */
322
+ export function getCodeLanguageName(language?: string, label?: string): string {
323
+ const key = resolveIconKey(language, label);
324
+ if (key && LANGUAGE_NAMES[key]) return LANGUAGE_NAMES[key];
325
+ for (const candidate of [language, getExtension(label)]) {
326
+ if (candidate && NAME_ONLY_LANGUAGES[normalize(candidate)]) return NAME_ONLY_LANGUAGES[normalize(candidate)];
327
+ }
328
+ const raw = language?.trim();
329
+ if (raw) return raw.charAt(0).toUpperCase() + raw.slice(1);
330
+ return label ?? '';
331
+ }
332
+
333
+ /**
334
+ * Resolves an icon URL for a code panel from its fence language, falling back to the file
335
+ * extension in the panel label. Returns `undefined` when there is no matching icon.
336
+ */
337
+ export function getCodeLanguageIconUrl(language?: string, label?: string): string | undefined {
338
+ for (const candidate of [language, getExtension(label)]) {
339
+ if (!candidate) continue;
340
+ const key = normalize(candidate);
341
+ if (LANGUAGE_ICONS[key]) return buildUrl(`/icons/languages/${LANGUAGE_ICONS[key]}.svg`, true);
342
+ if (SCHEMA_ICONS[key]) return buildUrl(`/icons/${SCHEMA_ICONS[key]}.svg`, true);
343
+ }
344
+ return undefined;
345
+ }
@@ -0,0 +1,22 @@
1
+ /* Overrides for markup generated by syntax highlighters, outside React's control. */
2
+ .ec-code-group [data-example-code-block] {
3
+ margin: 0;
4
+ border: 0;
5
+ border-radius: 0;
6
+ box-shadow: none;
7
+ }
8
+ .ec-code-group [data-code-panel] .expressive-code {
9
+ margin: 0;
10
+ }
11
+ .ec-code-group [data-code-panel] .frame {
12
+ border: 0;
13
+ border-radius: 0;
14
+ box-shadow: none;
15
+ }
16
+ .ec-code-group [data-code-panel] .frame > .header,
17
+ .ec-code-group [data-code-panel] .frame .copy {
18
+ display: none;
19
+ }
20
+ .ec-code-group [data-code-panel] pre {
21
+ border-radius: 0;
22
+ }
@@ -0,0 +1,11 @@
1
+ import { parseFragment, serialize } from 'parse5';
2
+
3
+ // Only parse the trusted HTML produced by Astro's rendered MDX slot.
4
+ export function parseCodePanels(html: string) {
5
+ return parseFragment(html).childNodes.flatMap((node) => {
6
+ if (!('attrs' in node)) return [];
7
+ const label = node.attrs.find((attr) => attr.name === 'data-code-panel')?.value;
8
+ if (label === undefined) return [];
9
+ return [{ label, language: node.attrs.find((attr) => attr.name === 'data-code-lang')?.value, html: serialize(node) }];
10
+ });
11
+ }
@@ -0,0 +1,10 @@
1
+ ---
2
+ import { columnClass, stickyClass } from './layout';
3
+ interface Props {
4
+ sticky?: boolean;
5
+ className?: string;
6
+ }
7
+ const { sticky = false, className } = Astro.props;
8
+ ---
9
+
10
+ <div class:list={[columnClass, sticky && stickyClass, className]} data-sticky={String(sticky)}><slot /></div>
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { getColumnsLayout, columnsClass, gridClass } from './layout';
3
+ interface Props {
4
+ cols?: number;
5
+ ratio?: string;
6
+ className?: string;
7
+ }
8
+ const { cols = 2, ratio, className } = Astro.props;
9
+ const { template } = getColumnsLayout(cols, ratio);
10
+ ---
11
+
12
+ <div class:list={[columnsClass, className]} style={{ '--ec-columns-template': template }}>
13
+ <div class={gridClass}><slot /></div>
14
+ </div>
@@ -0,0 +1,37 @@
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+ import { getColumnsLayout, columnsClass, gridClass, columnClass, stickyClass } from './layout';
3
+
4
+ export function MarkdownColumns({
5
+ children,
6
+ cols = 2,
7
+ ratio,
8
+ className = '',
9
+ }: {
10
+ children?: ReactNode;
11
+ cols?: number;
12
+ ratio?: string;
13
+ className?: string;
14
+ }) {
15
+ const { template } = getColumnsLayout(cols, ratio);
16
+ return (
17
+ <div className={`${columnsClass} ${className}`} style={{ '--ec-columns-template': template } as CSSProperties}>
18
+ <div className={gridClass}>{children}</div>
19
+ </div>
20
+ );
21
+ }
22
+
23
+ export function MarkdownColumn({
24
+ children,
25
+ sticky = false,
26
+ className = '',
27
+ }: {
28
+ children?: ReactNode;
29
+ sticky?: boolean;
30
+ className?: string;
31
+ }) {
32
+ return (
33
+ <div className={`${columnClass} ${sticky ? stickyClass : ''} ${className}`} data-sticky={String(sticky)}>
34
+ {children}
35
+ </div>
36
+ );
37
+ }
@@ -0,0 +1,16 @@
1
+ export function getColumnsLayout(cols: number = 2, ratio?: string) {
2
+ const count = Number.isInteger(cols) && cols >= 1 && cols <= 4 ? cols : 2;
3
+ const weights = ratio?.split(':').map(Number);
4
+ const validRatio = weights?.length === count && weights.every((weight) => Number.isFinite(weight) && weight > 0);
5
+ return {
6
+ count,
7
+ template: validRatio ? weights.map((weight) => `minmax(0, ${weight}fr)`).join(' ') : `repeat(${count}, minmax(0, 1fr))`,
8
+ };
9
+ }
10
+
11
+ export const columnsClass = 'ec-columns @container/ec-columns my-6';
12
+ export const gridClass =
13
+ 'ec-columns-grid grid grid-cols-1 items-start gap-8 [&>*]:min-w-0 @min-[640px]/ec-columns:grid-cols-[var(--ec-columns-template)]';
14
+ export const columnClass = 'ec-column min-w-0 [&>:first-child]:mt-0 [&>:last-child]:mb-0';
15
+ export const stickyClass =
16
+ '@min-[640px]/ec-columns:sticky @min-[640px]/ec-columns:top-[var(--ec-column-sticky-top,5rem)] print:static';
@@ -22,6 +22,9 @@ import ResourceGroupTable from '@components/MDX/ResourceGroupTable/ResourceGroup
22
22
  import EntityPropertiesTable from '@components/MDX/EntityPropertiesTable/EntityPropertiesTable.astro';
23
23
  import Tabs from '@components/MDX/Tabs/Tabs.astro';
24
24
  import TabItem from '@components/MDX/Tabs/TabItem.astro';
25
+ import CodeGroup from '@components/MDX/CodeGroup/CodeGroup.astro';
26
+ import Columns from '@components/MDX/Columns/Columns.astro';
27
+ import Column from '@components/MDX/Columns/Column.astro';
25
28
  import ResourceLink from '@components/MDX/ResourceLink/ResourceLink.astro';
26
29
  import ResourceRef from '@components/MDX/ResourceRef/ResourceRef.astro';
27
30
  import Link from '@components/MDX/Link/Link.astro';
@@ -85,6 +88,9 @@ const components = (props: any) => {
85
88
  Step,
86
89
  Steps,
87
90
  TabItem,
91
+ CodeGroup,
92
+ Columns,
93
+ Column,
88
94
  Tabs,
89
95
  Tile,
90
96
  Tiles,
@@ -134,7 +134,7 @@ export default function ApiContentViewer({ message, onCopy, copiedId, apiAccessE
134
134
  className="rounded-md border bg-[rgb(var(--ec-content-hover)/0.45)] px-2.5 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))]"
135
135
  style={{ borderColor: boxBorderColor }}
136
136
  >
137
- application/json
137
+ text/plain
138
138
  </code>
139
139
  </div>
140
140
  <div className="flex items-center justify-between gap-4 border-t px-4 py-3" style={{ borderColor: dividerColor }}>