@lukoweb/apitogo 0.2.31 → 0.2.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/cli.js +440 -153
- package/dist/declarations/app/entry.server.d.ts +2 -2
- package/dist/declarations/app/main.d.ts +3 -1
- package/dist/declarations/cli/platform/client/platform-api.d.ts +0 -4
- package/dist/declarations/cli/platform/helpers/deploy-artifact-hash.d.ts +1 -1
- package/dist/declarations/cli/platform/helpers/package-files.d.ts +1 -1
- package/dist/declarations/cli/platform/helpers/zip.d.ts +1 -1
- package/dist/declarations/cli/platform/tools/infrastructure.d.ts +0 -4
- package/dist/declarations/config/effective-redirects.d.ts +6 -0
- package/dist/declarations/config/header-navigation-guard.d.ts +2 -0
- package/dist/declarations/config/resolve-modules.d.ts +1 -4
- package/dist/declarations/config/validators/HeaderNavigationSchema.d.ts +1 -2
- package/docs/cli/commands.md +19 -0
- package/docs/configuration/gateway.md +52 -0
- package/docs/guides/customer-account.md +11 -1
- package/docs/mcp/tools.md +101 -102
- package/package.json +2 -1
- package/src/app/demo.tsx +78 -77
- package/src/app/entry.client.tsx +115 -107
- package/src/app/entry.server.tsx +164 -160
- package/src/app/main.tsx +175 -167
- package/src/app/standalone.tsx +61 -60
- package/src/config/effective-redirects.ts +31 -0
- package/src/config/header-navigation-guard.ts +13 -0
- package/src/config/resolve-modules.ts +331 -354
- package/src/config/v2/home-redirect.ts +104 -98
- package/src/config/v2/index.ts +127 -126
- package/src/config/v2/ledger.ts +242 -235
- package/src/config/v2/resolve.ts +12 -4
- package/src/config/v2/rules.ts +716 -714
- package/src/config/v2/services.ts +799 -663
- package/src/config/v2/workspace.ts +17 -1
- package/src/config/validators/HeaderNavigationSchema.ts +42 -44
- package/src/lib/assets/powered-by-icon.png +0 -0
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +667 -602
- package/src/lib/authentication/providers/dev-portal.tsx +476 -448
- package/src/lib/components/HeaderNavigation.tsx +176 -176
- package/src/lib/components/MobileTopNavigation.tsx +283 -283
- package/src/lib/components/navigation/PoweredByZudoku.tsx +42 -39
- package/src/vite/plugin-offroot-resolve.ts +68 -67
- package/src/vite/prerender/prerender.ts +242 -242
package/src/app/main.tsx
CHANGED
|
@@ -1,167 +1,175 @@
|
|
|
1
|
-
import { Apitogo } from "@lukoweb/apitogo/components";
|
|
2
|
-
import { Outlet } from "@lukoweb/apitogo/router";
|
|
3
|
-
import type { RouteObject } from "react-router";
|
|
4
|
-
import type { HighlighterCore } from "shiki";
|
|
5
|
-
import { configuredApiKeysPlugin } from "virtual:zudoku-api-keys-plugin";
|
|
6
|
-
import {
|
|
7
|
-
configuredApiCatalogPlugins,
|
|
8
|
-
configuredApiPlugins,
|
|
9
|
-
} from "virtual:zudoku-api-plugins";
|
|
10
|
-
import { configuredAuthProvider } from "virtual:zudoku-auth";
|
|
11
|
-
import { configuredCustomPagesPlugin } from "virtual:zudoku-custom-pages-plugin";
|
|
12
|
-
import { configuredDocsPlugin } from "virtual:zudoku-docs-plugin";
|
|
13
|
-
import {
|
|
14
|
-
configuredModulesPlugins,
|
|
15
|
-
configuredModulesSlots,
|
|
16
|
-
} from "virtual:zudoku-modules-plugin";
|
|
17
|
-
import "virtual:zudoku-theme.css";
|
|
18
|
-
import {
|
|
19
|
-
configuredHeaderNavigation,
|
|
20
|
-
configuredNavigation,
|
|
21
|
-
configuredNavigationRules,
|
|
22
|
-
} from "virtual:zudoku-navigation";
|
|
23
|
-
import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
|
|
24
|
-
import type { ApitogoConfig } from "../config/config.js";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
import {
|
|
30
|
-
import "./
|
|
31
|
-
import {
|
|
32
|
-
import "
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
...
|
|
82
|
-
...(
|
|
83
|
-
...
|
|
84
|
-
...(
|
|
85
|
-
...
|
|
86
|
-
...
|
|
87
|
-
...
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
export const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
1
|
+
import { Apitogo } from "@lukoweb/apitogo/components";
|
|
2
|
+
import { Outlet } from "@lukoweb/apitogo/router";
|
|
3
|
+
import type { RouteObject } from "react-router";
|
|
4
|
+
import type { HighlighterCore } from "shiki";
|
|
5
|
+
import { configuredApiKeysPlugin } from "virtual:zudoku-api-keys-plugin";
|
|
6
|
+
import {
|
|
7
|
+
configuredApiCatalogPlugins,
|
|
8
|
+
configuredApiPlugins,
|
|
9
|
+
} from "virtual:zudoku-api-plugins";
|
|
10
|
+
import { configuredAuthProvider } from "virtual:zudoku-auth";
|
|
11
|
+
import { configuredCustomPagesPlugin } from "virtual:zudoku-custom-pages-plugin";
|
|
12
|
+
import { configuredDocsPlugin } from "virtual:zudoku-docs-plugin";
|
|
13
|
+
import {
|
|
14
|
+
configuredModulesPlugins,
|
|
15
|
+
configuredModulesSlots,
|
|
16
|
+
} from "virtual:zudoku-modules-plugin";
|
|
17
|
+
import "virtual:zudoku-theme.css";
|
|
18
|
+
import {
|
|
19
|
+
configuredHeaderNavigation,
|
|
20
|
+
configuredNavigation,
|
|
21
|
+
configuredNavigationRules,
|
|
22
|
+
} from "virtual:zudoku-navigation";
|
|
23
|
+
import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
|
|
24
|
+
import type { ApitogoConfig } from "../config/config.js";
|
|
25
|
+
import { getEffectiveRedirects } from "../config/effective-redirects.js";
|
|
26
|
+
import type { ResolvedModulesConfig } from "../config/validators/ModulesSchema.js";
|
|
27
|
+
import { BuildCheck } from "../lib/components/BuildCheck.js";
|
|
28
|
+
import "./main.css";
|
|
29
|
+
import { Meta } from "../lib/components/Meta.js";
|
|
30
|
+
import "./polyfills.js";
|
|
31
|
+
import { StatusPage } from "../lib/components/StatusPage.js";
|
|
32
|
+
import { isNavigationPlugin } from "../lib/core/plugins.js";
|
|
33
|
+
import { RouteGuard } from "../lib/core/RouteGuard.js";
|
|
34
|
+
import type { ZudokuContextOptions } from "../lib/core/ZudokuContext.js";
|
|
35
|
+
import { RouterError } from "../lib/errors/RouterError.js";
|
|
36
|
+
import { ZuploEnv } from "./env.js";
|
|
37
|
+
import { processRoutes } from "./processRoutes.js";
|
|
38
|
+
import { createRedirectRoutes } from "./utils/createRedirectRoutes.js";
|
|
39
|
+
|
|
40
|
+
export const shikiReady: Promise<HighlighterCore> =
|
|
41
|
+
import("../lib/shiki.js").then(async ({ highlighterPromise }) => {
|
|
42
|
+
const highlighter = await highlighterPromise;
|
|
43
|
+
const { registerShiki } = await import("virtual:zudoku-shiki-register");
|
|
44
|
+
await registerShiki(highlighter);
|
|
45
|
+
return highlighter;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const convertApitogoConfigToOptions = (
|
|
49
|
+
config: ApitogoConfig,
|
|
50
|
+
): ZudokuContextOptions => {
|
|
51
|
+
const siteTitle =
|
|
52
|
+
config.metadata?.applicationName ?? config.site?.title ?? "API ToGo";
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
basePath: config.basePath,
|
|
56
|
+
canonicalUrlOrigin: config.canonicalUrlOrigin,
|
|
57
|
+
aiAssistants: config.aiAssistants,
|
|
58
|
+
protectedRoutes: config.protectedRoutes,
|
|
59
|
+
site: {
|
|
60
|
+
...config.site,
|
|
61
|
+
showPoweredBy: ZuploEnv.buildConfig?.entitlements.devPortalZuploBranding,
|
|
62
|
+
logo: config.site?.logo,
|
|
63
|
+
},
|
|
64
|
+
slots: { ...configuredModulesSlots, ...config.slots },
|
|
65
|
+
metadata: {
|
|
66
|
+
applicationName: siteTitle,
|
|
67
|
+
defaultTitle: siteTitle,
|
|
68
|
+
title: `%s - ${siteTitle}`,
|
|
69
|
+
...config.metadata,
|
|
70
|
+
},
|
|
71
|
+
header: {
|
|
72
|
+
navigation: configuredHeaderNavigation,
|
|
73
|
+
placements: config.header?.placements,
|
|
74
|
+
},
|
|
75
|
+
navigation: configuredNavigation,
|
|
76
|
+
navigationRules: configuredNavigationRules,
|
|
77
|
+
mdx: config.mdx,
|
|
78
|
+
plugins: [
|
|
79
|
+
...(configuredAuthProvider ? [configuredAuthProvider] : []),
|
|
80
|
+
...(configuredDocsPlugin ? [configuredDocsPlugin] : []),
|
|
81
|
+
...configuredApiPlugins,
|
|
82
|
+
...(configuredSearchPlugin ? [configuredSearchPlugin] : []),
|
|
83
|
+
...(configuredApiKeysPlugin ? [configuredApiKeysPlugin] : []),
|
|
84
|
+
...(configuredCustomPagesPlugin ? [configuredCustomPagesPlugin] : []),
|
|
85
|
+
...configuredModulesPlugins,
|
|
86
|
+
...configuredApiCatalogPlugins,
|
|
87
|
+
...(config.plugins ?? []),
|
|
88
|
+
],
|
|
89
|
+
syntaxHighlighting: {
|
|
90
|
+
highlighterPromise: shikiReady,
|
|
91
|
+
themes: config.syntaxHighlighting?.themes,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @deprecated Use {@link convertApitogoConfigToOptions} instead.
|
|
98
|
+
*/
|
|
99
|
+
export const convertZudokuConfigToOptions = convertApitogoConfigToOptions;
|
|
100
|
+
|
|
101
|
+
export const getRoutesByOptions = (
|
|
102
|
+
options: ZudokuContextOptions,
|
|
103
|
+
enableStatusPages = true,
|
|
104
|
+
) => {
|
|
105
|
+
const allPlugins = [
|
|
106
|
+
...(options.plugins ?? []),
|
|
107
|
+
...(options.authentication ? [options.authentication] : []),
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
const routes = allPlugins
|
|
111
|
+
.flatMap((plugin) => (isNavigationPlugin(plugin) ? plugin.getRoutes() : []))
|
|
112
|
+
.concat(
|
|
113
|
+
enableStatusPages
|
|
114
|
+
? [400, 404, 500].map((statusCode) => ({
|
|
115
|
+
path: `/${statusCode}`,
|
|
116
|
+
element: <StatusPage statusCode={statusCode} />,
|
|
117
|
+
}))
|
|
118
|
+
: [],
|
|
119
|
+
)
|
|
120
|
+
.concat([{ path: "*", element: <StatusPage statusCode={404} /> }])
|
|
121
|
+
.map((route) => ({
|
|
122
|
+
...route,
|
|
123
|
+
errorElement: <RouterError className="w-full m-0" />,
|
|
124
|
+
}));
|
|
125
|
+
|
|
126
|
+
// @TODO Detect conflicts in routes and log warning
|
|
127
|
+
|
|
128
|
+
return routes;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* How the entry gets the resolved modules of a config. The server and the demo parse them from the config
|
|
133
|
+
* (`resolveModulesConfig`, which needs zod); the browser takes what the build already resolved, so the schema
|
|
134
|
+
* is not in the browser bundle.
|
|
135
|
+
*/
|
|
136
|
+
export type ModulesResolver = (config: ApitogoConfig) => ResolvedModulesConfig;
|
|
137
|
+
|
|
138
|
+
export const getRoutesByConfig = (
|
|
139
|
+
config: ApitogoConfig,
|
|
140
|
+
resolveModules: ModulesResolver,
|
|
141
|
+
): RouteObject[] => {
|
|
142
|
+
const resolvedModules = resolveModules(config);
|
|
143
|
+
const options = convertApitogoConfigToOptions(config);
|
|
144
|
+
const routes = getRoutesByOptions(
|
|
145
|
+
options,
|
|
146
|
+
import.meta.env.IS_ZUPLO || config.enableStatusPages,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return [
|
|
150
|
+
...createRedirectRoutes(getEffectiveRedirects(config, resolvedModules)),
|
|
151
|
+
{
|
|
152
|
+
element: (
|
|
153
|
+
<Apitogo {...options} env={import.meta.env}>
|
|
154
|
+
<BuildCheck
|
|
155
|
+
buildId={import.meta.env.ZUPLO_BUILD_ID}
|
|
156
|
+
environmentType={import.meta.env.ZUPLO_ENVIRONMENT_TYPE}
|
|
157
|
+
/>
|
|
158
|
+
<Outlet />
|
|
159
|
+
</Apitogo>
|
|
160
|
+
),
|
|
161
|
+
hydrateFallbackElement: <div>Loading...</div>,
|
|
162
|
+
children: [
|
|
163
|
+
{
|
|
164
|
+
element: (
|
|
165
|
+
<Meta>
|
|
166
|
+
<RouteGuard />
|
|
167
|
+
</Meta>
|
|
168
|
+
),
|
|
169
|
+
errorElement: <RouterError />,
|
|
170
|
+
children: processRoutes(routes),
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
};
|
package/src/app/standalone.tsx
CHANGED
|
@@ -1,60 +1,61 @@
|
|
|
1
|
-
import { createRoot } from "react-dom/client";
|
|
2
|
-
import { createBrowserRouter } from "react-router";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import "../lib/util/logInit.js";
|
|
7
|
-
import "./main.css";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// This
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
1
|
+
import { createRoot } from "react-dom/client";
|
|
2
|
+
import { createBrowserRouter } from "react-router";
|
|
3
|
+
import { resolveModulesConfig } from "../config/resolve-modules.js";
|
|
4
|
+
import type { ZudokuConfig } from "../config/validators/ZudokuConfig.js";
|
|
5
|
+
import { Bootstrap } from "../lib/components/Bootstrap.js";
|
|
6
|
+
import "../lib/util/logInit.js";
|
|
7
|
+
import "./main.css";
|
|
8
|
+
import { openApiPlugin } from "../lib/plugins/openapi/index.js";
|
|
9
|
+
import { getRoutesByConfig } from "./main.js";
|
|
10
|
+
|
|
11
|
+
const root = document.querySelector("div[data-api-url]");
|
|
12
|
+
if (!root) {
|
|
13
|
+
throw new Error("No div found with attribute data-api-url");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const apiUrl = root.getAttribute("data-api-url");
|
|
17
|
+
|
|
18
|
+
if (!apiUrl) {
|
|
19
|
+
throw new Error("No api url found");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const title = document.getElementsByTagName("title")[0]?.innerText;
|
|
23
|
+
const logoUrl = root.getAttribute("data-logo-url");
|
|
24
|
+
|
|
25
|
+
// IMPORTANT: This component must not contain tailwind classes
|
|
26
|
+
// This directory is not processed by the tailwind plugin
|
|
27
|
+
|
|
28
|
+
const config = {
|
|
29
|
+
site: {
|
|
30
|
+
logo: logoUrl
|
|
31
|
+
? {
|
|
32
|
+
src: {
|
|
33
|
+
light: logoUrl,
|
|
34
|
+
dark: logoUrl,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
: undefined,
|
|
38
|
+
title,
|
|
39
|
+
},
|
|
40
|
+
navigation: [
|
|
41
|
+
{
|
|
42
|
+
type: "link",
|
|
43
|
+
label: "API Reference",
|
|
44
|
+
to: "/",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
plugins: [
|
|
48
|
+
// Using the plugin directly because there's no config file to load in the virtual plugins
|
|
49
|
+
openApiPlugin({
|
|
50
|
+
type: "url",
|
|
51
|
+
input: apiUrl,
|
|
52
|
+
path: "/",
|
|
53
|
+
}),
|
|
54
|
+
],
|
|
55
|
+
} satisfies ZudokuConfig;
|
|
56
|
+
|
|
57
|
+
const routes = getRoutesByConfig(config, resolveModulesConfig);
|
|
58
|
+
const router = createBrowserRouter(routes, {
|
|
59
|
+
basename: window.location.pathname,
|
|
60
|
+
});
|
|
61
|
+
createRoot(root).render(<Bootstrap router={router} />);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ResolvedModulesConfig } from "./validators/ModulesSchema.js";
|
|
2
|
+
import type { ZudokuConfig } from "./validators/ZudokuConfig.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The redirects of the config without the one that would shadow the landing page. Lives apart from
|
|
6
|
+
* `resolve-modules.ts` because the browser entry needs it and must not pull in the module schema (zod).
|
|
7
|
+
*/
|
|
8
|
+
export const getEffectiveRedirects = (
|
|
9
|
+
config: ZudokuConfig,
|
|
10
|
+
resolved: ResolvedModulesConfig,
|
|
11
|
+
) => {
|
|
12
|
+
const redirects = config.redirects ?? [];
|
|
13
|
+
|
|
14
|
+
if (!resolved.landing.enabled) {
|
|
15
|
+
return redirects;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const landingPath = normalizePath(resolved.landing.path);
|
|
19
|
+
|
|
20
|
+
return redirects.filter(
|
|
21
|
+
(redirect) => normalizePath(redirect.from) !== landingPath,
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const normalizePath = (path: string) => {
|
|
26
|
+
if (path === "/") {
|
|
27
|
+
return "/";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return path.replace(/\/+$/, "") || "/";
|
|
31
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HeaderNavGroup,
|
|
3
|
+
HeaderNavItem,
|
|
4
|
+
HeaderNavLinkItem,
|
|
5
|
+
} from "./validators/HeaderNavigationSchema.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Lives apart from `HeaderNavigationSchema.ts` because the header components need it in the browser, and that file
|
|
9
|
+
* holds the schema (zod) and the list of every icon name.
|
|
10
|
+
*/
|
|
11
|
+
export const isHeaderNavGroup = (
|
|
12
|
+
item: HeaderNavItem | HeaderNavLinkItem | HeaderNavGroup,
|
|
13
|
+
): item is HeaderNavGroup => "items" in item;
|