@lukoweb/apitogo 0.2.54 → 0.2.57
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 +2196 -1352
- package/dist/declarations/cli/platform/client/platform-api.d.ts +29 -0
- package/dist/declarations/cli/platform/helpers/platform-plan-catalog.d.ts +6 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +2 -0
- package/dist/declarations/lib/authentication/providers/openid.d.ts +2 -1
- package/dist/declarations/lib/plugins/markdown/index.d.ts +2 -1
- package/dist/flat-config.d.ts +1 -0
- package/docs/cli/commands.md +50 -32
- package/docs/cli/go-live.md +66 -93
- package/docs/cli/overview.md +18 -17
- package/docs/cli/setup.md +130 -141
- package/docs/cli/shape.md +94 -11
- package/docs/cli/walkthrough.md +1 -1
- package/docs/components/slot.mdx +116 -119
- package/docs/configuration/api-reference.md +98 -397
- package/docs/configuration/authentication.md +118 -159
- package/docs/configuration/footer.mdx +89 -118
- package/docs/configuration/make-config.mdx +2 -2
- package/docs/configuration/overview.md +97 -166
- package/docs/configuration/plans.md +100 -110
- package/docs/configuration/portal.md +116 -0
- package/docs/customization/colors-theme.mdx +255 -278
- package/docs/customization/fonts.md +105 -110
- package/docs/customization/landing-page.md +189 -188
- package/docs/deploy/apitogo.md +13 -17
- package/docs/deployment.md +15 -9
- package/docs/guides/stripe-setup.mdx +123 -0
- package/docs/markdown/frontmatter.md +172 -173
- package/docs/mcp/go-live.md +100 -100
- package/docs/mcp/overview.md +76 -76
- package/docs/quickstart.md +88 -72
- package/docs/writing.mdx +68 -72
- package/package.json +1 -1
- package/src/config/v2/docs-navigation.ts +7 -2
- package/src/config/v2/index.ts +1 -0
- package/src/config/v2/resolve.ts +28 -0
- package/src/config/v2/rules.ts +32 -1
- package/src/config/v2/schema.ts +9 -0
- package/src/config/v2/services.ts +90 -15
- package/src/config/v2/starter-landing.ts +41 -0
- package/src/config/v2/wrapper.ts +141 -7
- package/src/config/validators/ZudokuConfig.ts +794 -792
- package/src/lib/authentication/providers/openid.tsx +6 -1
- package/src/lib/plugins/markdown/index.tsx +86 -82
- package/src/vite/dev-server.ts +9 -1
- package/src/vite/dev-ssr-config.ts +32 -0
- package/src/vite/plugin-config.ts +4 -2
- package/src/vite/plugin-docs.ts +213 -205
- package/docs/concepts/auth-provider-api-identities.md +0 -105
- package/docs/configuration/ai-assistants.md +0 -65
- package/docs/configuration/api-catalog.md +0 -109
- package/docs/configuration/authentication-auth0.md +0 -181
- package/docs/configuration/authentication-azure-ad.md +0 -245
- package/docs/configuration/authentication-clerk.md +0 -117
- package/docs/configuration/authentication-firebase.md +0 -69
- package/docs/configuration/authentication-pingfederate.md +0 -143
- package/docs/configuration/authentication-supabase.md +0 -233
- package/docs/configuration/build-configuration.mdx +0 -147
- package/docs/configuration/docs.md +0 -282
- package/docs/configuration/llms.md +0 -90
- package/docs/configuration/manifest.md +0 -129
- package/docs/configuration/navigation.mdx +0 -415
- package/docs/configuration/protected-routes.md +0 -150
- package/docs/configuration/search.md +0 -164
- package/docs/configuration/sentry.mdx +0 -44
- package/docs/configuration/site.md +0 -124
- package/docs/configuration/slots.mdx +0 -125
- package/docs/configuration/vite-config.md +0 -18
- package/docs/custom-plugins.md +0 -285
- package/docs/deploy/apache-nginx.md +0 -41
- package/docs/deploy/cloudflare-pages.md +0 -74
- package/docs/deploy/direct-upload.md +0 -17
- package/docs/deploy/github-pages.md +0 -49
- package/docs/deploy/vercel.md +0 -101
- package/docs/extending/events.md +0 -124
- package/docs/guides/environment-variables.md +0 -94
- package/docs/guides/managing-api-keys-and-identities.md +0 -89
- package/docs/guides/navigation-rules.mdx +0 -203
- package/docs/guides/processors.mdx +0 -234
- package/docs/guides/transforming-examples.md +0 -156
- package/docs/guides/using-multiple-apis.md +0 -87
- package/docs/plugins.md +0 -24
|
@@ -1,792 +1,794 @@
|
|
|
1
|
-
import type { Options } from "@mdx-js/rollup";
|
|
2
|
-
import colors from "picocolors";
|
|
3
|
-
import type { ComponentType, ReactNode } from "react";
|
|
4
|
-
import { isValidElement } from "react";
|
|
5
|
-
import type { BundledLanguage, BundledTheme } from "shiki";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import type { UseAuthReturn } from "../../lib/authentication/hook.js";
|
|
8
|
-
import type { AuthState } from "../../lib/authentication/state.js";
|
|
9
|
-
import type { SlotType } from "../../lib/components/context/SlotProvider.js";
|
|
10
|
-
import type { ApitogoPlugin } from "../../lib/core/plugins.js";
|
|
11
|
-
import type { ZudokuContext } from "../../lib/core/ZudokuContext.js";
|
|
12
|
-
import type { FilterCatalogItemsFn } from "../../lib/plugins/api-catalog/index.js";
|
|
13
|
-
import type { ApiConsumer } from "../../lib/plugins/api-keys/index.js";
|
|
14
|
-
import type {
|
|
15
|
-
GenerateCodeSnippetFn,
|
|
16
|
-
TransformExamplesFn,
|
|
17
|
-
} from "../../lib/plugins/openapi/interfaces.js";
|
|
18
|
-
import type { PagefindSearchFragment } from "../../lib/plugins/search-pagefind/types.js";
|
|
19
|
-
import type { MdxComponentsType } from "../../lib/util/MdxComponents.js";
|
|
20
|
-
import type { ExposedComponentProps } from "../../lib/util/useExposedProps.js";
|
|
21
|
-
import { GOOGLE_FONTS } from "../../vite/plugin-theme.js";
|
|
22
|
-
import { HeaderNavigationSchema } from "./HeaderNavigationSchema.js";
|
|
23
|
-
import {
|
|
24
|
-
InputNavigationSchema,
|
|
25
|
-
NavigationRulesSchema,
|
|
26
|
-
} from "./InputNavigationSchema.js";
|
|
27
|
-
import { ModulesSchema } from "./ModulesSchema.js";
|
|
28
|
-
import { ProtectedRoutesSchema } from "./ProtectedRoutesSchema.js";
|
|
29
|
-
|
|
30
|
-
const ThemeSchema = z
|
|
31
|
-
.object({
|
|
32
|
-
background: z.string(),
|
|
33
|
-
foreground: z.string(),
|
|
34
|
-
card: z.string(),
|
|
35
|
-
cardForeground: z.string(),
|
|
36
|
-
popover: z.string(),
|
|
37
|
-
popoverForeground: z.string(),
|
|
38
|
-
primary: z.string(),
|
|
39
|
-
primaryForeground: z.string(),
|
|
40
|
-
secondary: z.string(),
|
|
41
|
-
secondaryForeground: z.string(),
|
|
42
|
-
muted: z.string(),
|
|
43
|
-
mutedForeground: z.string(),
|
|
44
|
-
accent: z.string(),
|
|
45
|
-
accentForeground: z.string(),
|
|
46
|
-
destructive: z.string(),
|
|
47
|
-
destructiveForeground: z.string(),
|
|
48
|
-
border: z.string(),
|
|
49
|
-
input: z.string(),
|
|
50
|
-
ring: z.string(),
|
|
51
|
-
radius: z.string(),
|
|
52
|
-
})
|
|
53
|
-
.partial();
|
|
54
|
-
|
|
55
|
-
const ApiCatalogCategorySchema = z.object({
|
|
56
|
-
label: z.string(),
|
|
57
|
-
tags: z.array(z.string()),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const LanguageOption = z.object({
|
|
61
|
-
value: z.string().min(1),
|
|
62
|
-
label: z.string().min(1),
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const AiAssistantCustomSchema = z.object({
|
|
66
|
-
label: z.string(),
|
|
67
|
-
icon: z.custom<ReactNode>().optional(),
|
|
68
|
-
url: z.union([
|
|
69
|
-
z.string(),
|
|
70
|
-
z.custom<
|
|
71
|
-
(context: { pageUrl: string; type: "docs" | "openapi" }) => string
|
|
72
|
-
>((val) => typeof val === "function"),
|
|
73
|
-
]),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
export type AiAssistantCustom = z.infer<typeof AiAssistantCustomSchema>;
|
|
77
|
-
|
|
78
|
-
const AiAssistantPresets = ["claude", "chatgpt"] as const;
|
|
79
|
-
|
|
80
|
-
const AiAssistantsSchema = z
|
|
81
|
-
.union([
|
|
82
|
-
z.literal(false),
|
|
83
|
-
z.array(z.union([z.enum(AiAssistantPresets), AiAssistantCustomSchema])),
|
|
84
|
-
])
|
|
85
|
-
.optional();
|
|
86
|
-
|
|
87
|
-
export type AiAssistantsConfig = z.infer<typeof AiAssistantsSchema>;
|
|
88
|
-
|
|
89
|
-
const ApiOptionsSchema = z
|
|
90
|
-
.object({
|
|
91
|
-
examplesLanguage: z.string(),
|
|
92
|
-
supportedLanguages: z.array(LanguageOption),
|
|
93
|
-
disablePlayground: z.boolean(),
|
|
94
|
-
disableSidecar: z.boolean(),
|
|
95
|
-
showVersionSelect: z.enum(["always", "if-available", "hide"]),
|
|
96
|
-
expandAllTags: z.boolean(),
|
|
97
|
-
showInfoPage: z.boolean(),
|
|
98
|
-
schemaDownload: z.object({ enabled: z.boolean() }).partial(),
|
|
99
|
-
transformExamples: z.custom<TransformExamplesFn>(
|
|
100
|
-
(val) => typeof val === "function",
|
|
101
|
-
),
|
|
102
|
-
generateCodeSnippet: z.custom<GenerateCodeSnippetFn>(
|
|
103
|
-
(val) => typeof val === "function",
|
|
104
|
-
),
|
|
105
|
-
})
|
|
106
|
-
.partial();
|
|
107
|
-
|
|
108
|
-
const ApiConfigSchema = z
|
|
109
|
-
.object({
|
|
110
|
-
server: z.string(),
|
|
111
|
-
path: z.string(),
|
|
112
|
-
categories: z.array(ApiCatalogCategorySchema),
|
|
113
|
-
options: ApiOptionsSchema,
|
|
114
|
-
})
|
|
115
|
-
.partial();
|
|
116
|
-
|
|
117
|
-
const VersionConfigSchema = z.object({
|
|
118
|
-
path: z.string(),
|
|
119
|
-
input: z.string(),
|
|
120
|
-
label: z.string().optional(),
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const ApiSchema = z.discriminatedUnion("type", [
|
|
124
|
-
z.object({
|
|
125
|
-
type: z.literal("url"),
|
|
126
|
-
input: z.union([z.string(), z.array(VersionConfigSchema)]),
|
|
127
|
-
...ApiConfigSchema.shape,
|
|
128
|
-
}),
|
|
129
|
-
z.object({
|
|
130
|
-
type: z.literal("file"),
|
|
131
|
-
input: z.union([
|
|
132
|
-
z.string(),
|
|
133
|
-
z.array(z.union([z.string(), VersionConfigSchema])),
|
|
134
|
-
]),
|
|
135
|
-
...ApiConfigSchema.shape,
|
|
136
|
-
}),
|
|
137
|
-
z.object({
|
|
138
|
-
type: z.literal("raw"),
|
|
139
|
-
input: z.string(),
|
|
140
|
-
...ApiConfigSchema.shape,
|
|
141
|
-
}),
|
|
142
|
-
]);
|
|
143
|
-
|
|
144
|
-
const ApiKeysSchema = z.object({
|
|
145
|
-
enabled: z.boolean(),
|
|
146
|
-
getConsumers: z
|
|
147
|
-
.custom<(context: ZudokuContext) => Promise<ApiConsumer[]>>(
|
|
148
|
-
(val) => typeof val === "function",
|
|
149
|
-
)
|
|
150
|
-
.optional(),
|
|
151
|
-
rollKey: z
|
|
152
|
-
.custom<(id: string, context: ZudokuContext) => Promise<void>>(
|
|
153
|
-
(val) => typeof val === "function",
|
|
154
|
-
)
|
|
155
|
-
.optional(),
|
|
156
|
-
deleteKey: z
|
|
157
|
-
.custom<
|
|
158
|
-
(
|
|
159
|
-
consumerId: string,
|
|
160
|
-
keyId: string,
|
|
161
|
-
context: ZudokuContext,
|
|
162
|
-
) => Promise<void>
|
|
163
|
-
>((val) => typeof val === "function")
|
|
164
|
-
.optional(),
|
|
165
|
-
updateKeyDescription: z
|
|
166
|
-
.custom<
|
|
167
|
-
(
|
|
168
|
-
apiKey: { id: string; description: string },
|
|
169
|
-
context: ZudokuContext,
|
|
170
|
-
) => Promise<void>
|
|
171
|
-
>((val) => typeof val === "function")
|
|
172
|
-
.optional(),
|
|
173
|
-
createKey: z
|
|
174
|
-
.custom<
|
|
175
|
-
({
|
|
176
|
-
apiKey,
|
|
177
|
-
context,
|
|
178
|
-
auth,
|
|
179
|
-
}: {
|
|
180
|
-
apiKey: { description: string; expiresOn?: string };
|
|
181
|
-
context: ZudokuContext;
|
|
182
|
-
auth: UseAuthReturn;
|
|
183
|
-
}) => Promise<void>
|
|
184
|
-
>((val) => typeof val === "function")
|
|
185
|
-
.optional(),
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
export type ApiKeysOptions = z.infer<typeof ApiKeysSchema>;
|
|
189
|
-
|
|
190
|
-
const LogoSchema = z.object({
|
|
191
|
-
src: z.object({ light: z.string(), dark: z.string() }),
|
|
192
|
-
alt: z.string().optional(),
|
|
193
|
-
width: z.string().or(z.number()).optional(),
|
|
194
|
-
href: z.string().optional(),
|
|
195
|
-
reloadDocument: z.boolean().optional(),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
export const FooterSocialIcons = [
|
|
199
|
-
"reddit",
|
|
200
|
-
"discord",
|
|
201
|
-
"github",
|
|
202
|
-
"x",
|
|
203
|
-
"linkedin",
|
|
204
|
-
"facebook",
|
|
205
|
-
"instagram",
|
|
206
|
-
"youtube",
|
|
207
|
-
"tiktok",
|
|
208
|
-
"twitch",
|
|
209
|
-
"pinterest",
|
|
210
|
-
"snapchat",
|
|
211
|
-
"whatsapp",
|
|
212
|
-
"telegram",
|
|
213
|
-
] as const;
|
|
214
|
-
|
|
215
|
-
export const FooterSocialSchema = z.object({
|
|
216
|
-
label: z.string().optional(),
|
|
217
|
-
href: z.string(),
|
|
218
|
-
icon: z
|
|
219
|
-
.union([
|
|
220
|
-
z.enum(FooterSocialIcons),
|
|
221
|
-
z.custom<ReactNode>((val) => isValidElement(val)),
|
|
222
|
-
])
|
|
223
|
-
.optional(),
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
export const FooterSchema = z
|
|
227
|
-
.object({
|
|
228
|
-
columns: z
|
|
229
|
-
.array(
|
|
230
|
-
z.object({
|
|
231
|
-
position: z.enum(["start", "center", "end"]).optional(),
|
|
232
|
-
title: z.string(),
|
|
233
|
-
links: z.array(z.object({ label: z.string(), href: z.string() })),
|
|
234
|
-
}),
|
|
235
|
-
)
|
|
236
|
-
.optional(),
|
|
237
|
-
social: z.array(FooterSocialSchema).optional(),
|
|
238
|
-
copyright: z.string().optional(),
|
|
239
|
-
logo: LogoSchema.optional(),
|
|
240
|
-
position: z.enum(["start", "center", "end"]).optional(),
|
|
241
|
-
})
|
|
242
|
-
.optional();
|
|
243
|
-
|
|
244
|
-
const SiteMapSchema = z
|
|
245
|
-
.object({
|
|
246
|
-
/**
|
|
247
|
-
* Base url of your website
|
|
248
|
-
*/
|
|
249
|
-
siteUrl: z.string(),
|
|
250
|
-
/**
|
|
251
|
-
* Change frequency.
|
|
252
|
-
* @default 'daily'
|
|
253
|
-
*/
|
|
254
|
-
changefreq: z.optional(
|
|
255
|
-
z.enum([
|
|
256
|
-
"always",
|
|
257
|
-
"hourly",
|
|
258
|
-
"daily",
|
|
259
|
-
"weekly",
|
|
260
|
-
"monthly",
|
|
261
|
-
"yearly",
|
|
262
|
-
"never",
|
|
263
|
-
]),
|
|
264
|
-
),
|
|
265
|
-
/**
|
|
266
|
-
* Priority
|
|
267
|
-
* @default 0.7
|
|
268
|
-
*/
|
|
269
|
-
priority: z.optional(z.number()),
|
|
270
|
-
outDir: z.string().optional(),
|
|
271
|
-
/**
|
|
272
|
-
* Add <lastmod/> property.
|
|
273
|
-
* @default true
|
|
274
|
-
*/
|
|
275
|
-
autoLastmod: z.boolean().optional(),
|
|
276
|
-
/**
|
|
277
|
-
* Array of relative paths to exclude from listing on sitemap.xml or sitemap-*.xml.
|
|
278
|
-
* @example ['/page-0', '/page/example']
|
|
279
|
-
*/
|
|
280
|
-
exclude: z
|
|
281
|
-
.union([
|
|
282
|
-
z.custom<() => Promise<string[]>>((val) => typeof val === "function"),
|
|
283
|
-
z.array(z.string()),
|
|
284
|
-
])
|
|
285
|
-
.optional(),
|
|
286
|
-
})
|
|
287
|
-
.optional();
|
|
288
|
-
|
|
289
|
-
const DEFAULT_DOCS_FILES = "/pages/**/*.{md,mdx}";
|
|
290
|
-
|
|
291
|
-
const LlmsConfigSchema = z
|
|
292
|
-
.object({
|
|
293
|
-
llmsTxt: z
|
|
294
|
-
.boolean()
|
|
295
|
-
.default(false)
|
|
296
|
-
.describe(
|
|
297
|
-
"When enabled, generates an llms.txt file following the spec at https://llmstxt.org/. This file provides a summary with links to all documentation.",
|
|
298
|
-
),
|
|
299
|
-
llmsTxtFull: z
|
|
300
|
-
.boolean()
|
|
301
|
-
.default(false)
|
|
302
|
-
.describe(
|
|
303
|
-
"When enabled, generates an llms-full.txt file with the complete content of all markdown documents for LLM consumption.",
|
|
304
|
-
),
|
|
305
|
-
includeProtected: z
|
|
306
|
-
.boolean()
|
|
307
|
-
.default(false)
|
|
308
|
-
.describe(
|
|
309
|
-
"When enabled, includes content from protected routes in the generated .md files and llms.txt files. By default, protected routes are excluded.",
|
|
310
|
-
),
|
|
311
|
-
})
|
|
312
|
-
.partial();
|
|
313
|
-
|
|
314
|
-
export const DocsConfigSchema = z.object({
|
|
315
|
-
files: z
|
|
316
|
-
.union([z.string(), z.array(z.string())])
|
|
317
|
-
.transform((val) => (typeof val === "string" ? [val] : val))
|
|
318
|
-
.default([DEFAULT_DOCS_FILES]),
|
|
319
|
-
publishMarkdown: z
|
|
320
|
-
.boolean()
|
|
321
|
-
.default(false)
|
|
322
|
-
.describe(
|
|
323
|
-
"When enabled, generates .md files for each document during build. Access documents at their URL path with .md extension (e.g., /foo/hello.md). Markdown files are generated without frontmatter.",
|
|
324
|
-
),
|
|
325
|
-
defaultOptions: z
|
|
326
|
-
.object({
|
|
327
|
-
toc: z.boolean(),
|
|
328
|
-
copyPage: z.boolean().optional(),
|
|
329
|
-
disablePager: z.boolean(),
|
|
330
|
-
showLastModified: z.boolean(),
|
|
331
|
-
suggestEdit: z
|
|
332
|
-
.object({
|
|
333
|
-
url: z.string(),
|
|
334
|
-
text: z.string().optional(),
|
|
335
|
-
})
|
|
336
|
-
.optional(),
|
|
337
|
-
})
|
|
338
|
-
.partial()
|
|
339
|
-
.optional(),
|
|
340
|
-
llms: LlmsConfigSchema.optional(),
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
const Redirect = z.object({
|
|
344
|
-
from: z.string(),
|
|
345
|
-
to: z.string(),
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
const SearchSchema = z
|
|
349
|
-
.discriminatedUnion("type", [
|
|
350
|
-
// looseObject to allow additional properties so the
|
|
351
|
-
// user can set other inkeep settings
|
|
352
|
-
z.looseObject({
|
|
353
|
-
type: z.literal("inkeep"),
|
|
354
|
-
apiKey: z.string(),
|
|
355
|
-
integrationId: z.string(),
|
|
356
|
-
organizationId: z.string(),
|
|
357
|
-
primaryBrandColor: z.string(),
|
|
358
|
-
organizationDisplayName: z.string(),
|
|
359
|
-
}),
|
|
360
|
-
z.object({
|
|
361
|
-
type: z.literal("pagefind"),
|
|
362
|
-
|
|
363
|
-
ranking: z
|
|
364
|
-
.object({
|
|
365
|
-
termFrequency: z.number(),
|
|
366
|
-
pageLength: z.number(),
|
|
367
|
-
termSimilarity: z.number(),
|
|
368
|
-
termSaturation: z.number(),
|
|
369
|
-
})
|
|
370
|
-
.optional(),
|
|
371
|
-
maxResults: z.number().optional(),
|
|
372
|
-
maxSubResults: z.number().optional(),
|
|
373
|
-
transformResults: z
|
|
374
|
-
.custom<
|
|
375
|
-
(data: {
|
|
376
|
-
result: PagefindSearchFragment;
|
|
377
|
-
auth: AuthState;
|
|
378
|
-
context: ZudokuContext;
|
|
379
|
-
}) => PagefindSearchFragment | boolean | undefined | void
|
|
380
|
-
>((val) => typeof val === "function")
|
|
381
|
-
.optional(),
|
|
382
|
-
}),
|
|
383
|
-
])
|
|
384
|
-
.optional();
|
|
385
|
-
|
|
386
|
-
const AuthenticationSchema = z.discriminatedUnion("type", [
|
|
387
|
-
z.object({
|
|
388
|
-
type: z.literal("clerk"),
|
|
389
|
-
clerkPubKey: z
|
|
390
|
-
.custom<`pk_test_${string}` | `pk_live_${string}`>()
|
|
391
|
-
.refine((val) => /^pk_(test|live)_\w+$/.test(val), {
|
|
392
|
-
message: "Clerk public key invalid, must start with pk_test or pk_live",
|
|
393
|
-
}),
|
|
394
|
-
jwtTemplateName: z.string().optional().default("dev-portal"),
|
|
395
|
-
redirectToAfterSignUp: z.string().optional(),
|
|
396
|
-
redirectToAfterSignIn: z.string().optional(),
|
|
397
|
-
redirectToAfterSignOut: z.string().optional(),
|
|
398
|
-
}),
|
|
399
|
-
z.object({
|
|
400
|
-
type: z.literal("firebase"),
|
|
401
|
-
apiKey: z.string(),
|
|
402
|
-
authDomain: z.string(),
|
|
403
|
-
projectId: z.string(),
|
|
404
|
-
storageBucket: z.string().optional(),
|
|
405
|
-
messagingSenderId: z.string().optional(),
|
|
406
|
-
appId: z.string(),
|
|
407
|
-
measurementId: z.string().optional(),
|
|
408
|
-
providers: z
|
|
409
|
-
.array(
|
|
410
|
-
z.enum([
|
|
411
|
-
"google",
|
|
412
|
-
"facebook",
|
|
413
|
-
"twitter",
|
|
414
|
-
"github",
|
|
415
|
-
"microsoft",
|
|
416
|
-
"apple",
|
|
417
|
-
"yahoo",
|
|
418
|
-
"password",
|
|
419
|
-
"phone",
|
|
420
|
-
"emailLink",
|
|
421
|
-
]),
|
|
422
|
-
)
|
|
423
|
-
.optional(),
|
|
424
|
-
redirectToAfterSignUp: z.string().optional(),
|
|
425
|
-
redirectToAfterSignIn: z.string().optional(),
|
|
426
|
-
redirectToAfterSignOut: z.string().optional(),
|
|
427
|
-
}),
|
|
428
|
-
z.object({
|
|
429
|
-
type: z.literal("openid"),
|
|
430
|
-
basePath: z.string().optional(),
|
|
431
|
-
clientId: z.string(),
|
|
432
|
-
issuer: z.string(),
|
|
433
|
-
audience: z.string().optional(),
|
|
434
|
-
scopes: z.array(z.string()).optional(),
|
|
435
|
-
redirectToAfterSignUp: z.string().optional(),
|
|
436
|
-
redirectToAfterSignIn: z.string().optional(),
|
|
437
|
-
redirectToAfterSignOut: z.string().optional(),
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
.
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
.
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
)
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
export
|
|
721
|
-
|
|
722
|
-
export type
|
|
723
|
-
export type
|
|
724
|
-
export type
|
|
725
|
-
export type
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
export type
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
header
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
// biome-ignore lint/suspicious/noConsole: Logging allowed here
|
|
790
|
-
console.log(colors.yellow(
|
|
791
|
-
|
|
792
|
-
|
|
1
|
+
import type { Options } from "@mdx-js/rollup";
|
|
2
|
+
import colors from "picocolors";
|
|
3
|
+
import type { ComponentType, ReactNode } from "react";
|
|
4
|
+
import { isValidElement } from "react";
|
|
5
|
+
import type { BundledLanguage, BundledTheme } from "shiki";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import type { UseAuthReturn } from "../../lib/authentication/hook.js";
|
|
8
|
+
import type { AuthState } from "../../lib/authentication/state.js";
|
|
9
|
+
import type { SlotType } from "../../lib/components/context/SlotProvider.js";
|
|
10
|
+
import type { ApitogoPlugin } from "../../lib/core/plugins.js";
|
|
11
|
+
import type { ZudokuContext } from "../../lib/core/ZudokuContext.js";
|
|
12
|
+
import type { FilterCatalogItemsFn } from "../../lib/plugins/api-catalog/index.js";
|
|
13
|
+
import type { ApiConsumer } from "../../lib/plugins/api-keys/index.js";
|
|
14
|
+
import type {
|
|
15
|
+
GenerateCodeSnippetFn,
|
|
16
|
+
TransformExamplesFn,
|
|
17
|
+
} from "../../lib/plugins/openapi/interfaces.js";
|
|
18
|
+
import type { PagefindSearchFragment } from "../../lib/plugins/search-pagefind/types.js";
|
|
19
|
+
import type { MdxComponentsType } from "../../lib/util/MdxComponents.js";
|
|
20
|
+
import type { ExposedComponentProps } from "../../lib/util/useExposedProps.js";
|
|
21
|
+
import { GOOGLE_FONTS } from "../../vite/plugin-theme.js";
|
|
22
|
+
import { HeaderNavigationSchema } from "./HeaderNavigationSchema.js";
|
|
23
|
+
import {
|
|
24
|
+
InputNavigationSchema,
|
|
25
|
+
NavigationRulesSchema,
|
|
26
|
+
} from "./InputNavigationSchema.js";
|
|
27
|
+
import { ModulesSchema } from "./ModulesSchema.js";
|
|
28
|
+
import { ProtectedRoutesSchema } from "./ProtectedRoutesSchema.js";
|
|
29
|
+
|
|
30
|
+
const ThemeSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
background: z.string(),
|
|
33
|
+
foreground: z.string(),
|
|
34
|
+
card: z.string(),
|
|
35
|
+
cardForeground: z.string(),
|
|
36
|
+
popover: z.string(),
|
|
37
|
+
popoverForeground: z.string(),
|
|
38
|
+
primary: z.string(),
|
|
39
|
+
primaryForeground: z.string(),
|
|
40
|
+
secondary: z.string(),
|
|
41
|
+
secondaryForeground: z.string(),
|
|
42
|
+
muted: z.string(),
|
|
43
|
+
mutedForeground: z.string(),
|
|
44
|
+
accent: z.string(),
|
|
45
|
+
accentForeground: z.string(),
|
|
46
|
+
destructive: z.string(),
|
|
47
|
+
destructiveForeground: z.string(),
|
|
48
|
+
border: z.string(),
|
|
49
|
+
input: z.string(),
|
|
50
|
+
ring: z.string(),
|
|
51
|
+
radius: z.string(),
|
|
52
|
+
})
|
|
53
|
+
.partial();
|
|
54
|
+
|
|
55
|
+
const ApiCatalogCategorySchema = z.object({
|
|
56
|
+
label: z.string(),
|
|
57
|
+
tags: z.array(z.string()),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const LanguageOption = z.object({
|
|
61
|
+
value: z.string().min(1),
|
|
62
|
+
label: z.string().min(1),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const AiAssistantCustomSchema = z.object({
|
|
66
|
+
label: z.string(),
|
|
67
|
+
icon: z.custom<ReactNode>().optional(),
|
|
68
|
+
url: z.union([
|
|
69
|
+
z.string(),
|
|
70
|
+
z.custom<
|
|
71
|
+
(context: { pageUrl: string; type: "docs" | "openapi" }) => string
|
|
72
|
+
>((val) => typeof val === "function"),
|
|
73
|
+
]),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export type AiAssistantCustom = z.infer<typeof AiAssistantCustomSchema>;
|
|
77
|
+
|
|
78
|
+
const AiAssistantPresets = ["claude", "chatgpt"] as const;
|
|
79
|
+
|
|
80
|
+
const AiAssistantsSchema = z
|
|
81
|
+
.union([
|
|
82
|
+
z.literal(false),
|
|
83
|
+
z.array(z.union([z.enum(AiAssistantPresets), AiAssistantCustomSchema])),
|
|
84
|
+
])
|
|
85
|
+
.optional();
|
|
86
|
+
|
|
87
|
+
export type AiAssistantsConfig = z.infer<typeof AiAssistantsSchema>;
|
|
88
|
+
|
|
89
|
+
const ApiOptionsSchema = z
|
|
90
|
+
.object({
|
|
91
|
+
examplesLanguage: z.string(),
|
|
92
|
+
supportedLanguages: z.array(LanguageOption),
|
|
93
|
+
disablePlayground: z.boolean(),
|
|
94
|
+
disableSidecar: z.boolean(),
|
|
95
|
+
showVersionSelect: z.enum(["always", "if-available", "hide"]),
|
|
96
|
+
expandAllTags: z.boolean(),
|
|
97
|
+
showInfoPage: z.boolean(),
|
|
98
|
+
schemaDownload: z.object({ enabled: z.boolean() }).partial(),
|
|
99
|
+
transformExamples: z.custom<TransformExamplesFn>(
|
|
100
|
+
(val) => typeof val === "function",
|
|
101
|
+
),
|
|
102
|
+
generateCodeSnippet: z.custom<GenerateCodeSnippetFn>(
|
|
103
|
+
(val) => typeof val === "function",
|
|
104
|
+
),
|
|
105
|
+
})
|
|
106
|
+
.partial();
|
|
107
|
+
|
|
108
|
+
const ApiConfigSchema = z
|
|
109
|
+
.object({
|
|
110
|
+
server: z.string(),
|
|
111
|
+
path: z.string(),
|
|
112
|
+
categories: z.array(ApiCatalogCategorySchema),
|
|
113
|
+
options: ApiOptionsSchema,
|
|
114
|
+
})
|
|
115
|
+
.partial();
|
|
116
|
+
|
|
117
|
+
const VersionConfigSchema = z.object({
|
|
118
|
+
path: z.string(),
|
|
119
|
+
input: z.string(),
|
|
120
|
+
label: z.string().optional(),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const ApiSchema = z.discriminatedUnion("type", [
|
|
124
|
+
z.object({
|
|
125
|
+
type: z.literal("url"),
|
|
126
|
+
input: z.union([z.string(), z.array(VersionConfigSchema)]),
|
|
127
|
+
...ApiConfigSchema.shape,
|
|
128
|
+
}),
|
|
129
|
+
z.object({
|
|
130
|
+
type: z.literal("file"),
|
|
131
|
+
input: z.union([
|
|
132
|
+
z.string(),
|
|
133
|
+
z.array(z.union([z.string(), VersionConfigSchema])),
|
|
134
|
+
]),
|
|
135
|
+
...ApiConfigSchema.shape,
|
|
136
|
+
}),
|
|
137
|
+
z.object({
|
|
138
|
+
type: z.literal("raw"),
|
|
139
|
+
input: z.string(),
|
|
140
|
+
...ApiConfigSchema.shape,
|
|
141
|
+
}),
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const ApiKeysSchema = z.object({
|
|
145
|
+
enabled: z.boolean(),
|
|
146
|
+
getConsumers: z
|
|
147
|
+
.custom<(context: ZudokuContext) => Promise<ApiConsumer[]>>(
|
|
148
|
+
(val) => typeof val === "function",
|
|
149
|
+
)
|
|
150
|
+
.optional(),
|
|
151
|
+
rollKey: z
|
|
152
|
+
.custom<(id: string, context: ZudokuContext) => Promise<void>>(
|
|
153
|
+
(val) => typeof val === "function",
|
|
154
|
+
)
|
|
155
|
+
.optional(),
|
|
156
|
+
deleteKey: z
|
|
157
|
+
.custom<
|
|
158
|
+
(
|
|
159
|
+
consumerId: string,
|
|
160
|
+
keyId: string,
|
|
161
|
+
context: ZudokuContext,
|
|
162
|
+
) => Promise<void>
|
|
163
|
+
>((val) => typeof val === "function")
|
|
164
|
+
.optional(),
|
|
165
|
+
updateKeyDescription: z
|
|
166
|
+
.custom<
|
|
167
|
+
(
|
|
168
|
+
apiKey: { id: string; description: string },
|
|
169
|
+
context: ZudokuContext,
|
|
170
|
+
) => Promise<void>
|
|
171
|
+
>((val) => typeof val === "function")
|
|
172
|
+
.optional(),
|
|
173
|
+
createKey: z
|
|
174
|
+
.custom<
|
|
175
|
+
({
|
|
176
|
+
apiKey,
|
|
177
|
+
context,
|
|
178
|
+
auth,
|
|
179
|
+
}: {
|
|
180
|
+
apiKey: { description: string; expiresOn?: string };
|
|
181
|
+
context: ZudokuContext;
|
|
182
|
+
auth: UseAuthReturn;
|
|
183
|
+
}) => Promise<void>
|
|
184
|
+
>((val) => typeof val === "function")
|
|
185
|
+
.optional(),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
export type ApiKeysOptions = z.infer<typeof ApiKeysSchema>;
|
|
189
|
+
|
|
190
|
+
const LogoSchema = z.object({
|
|
191
|
+
src: z.object({ light: z.string(), dark: z.string() }),
|
|
192
|
+
alt: z.string().optional(),
|
|
193
|
+
width: z.string().or(z.number()).optional(),
|
|
194
|
+
href: z.string().optional(),
|
|
195
|
+
reloadDocument: z.boolean().optional(),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
export const FooterSocialIcons = [
|
|
199
|
+
"reddit",
|
|
200
|
+
"discord",
|
|
201
|
+
"github",
|
|
202
|
+
"x",
|
|
203
|
+
"linkedin",
|
|
204
|
+
"facebook",
|
|
205
|
+
"instagram",
|
|
206
|
+
"youtube",
|
|
207
|
+
"tiktok",
|
|
208
|
+
"twitch",
|
|
209
|
+
"pinterest",
|
|
210
|
+
"snapchat",
|
|
211
|
+
"whatsapp",
|
|
212
|
+
"telegram",
|
|
213
|
+
] as const;
|
|
214
|
+
|
|
215
|
+
export const FooterSocialSchema = z.object({
|
|
216
|
+
label: z.string().optional(),
|
|
217
|
+
href: z.string(),
|
|
218
|
+
icon: z
|
|
219
|
+
.union([
|
|
220
|
+
z.enum(FooterSocialIcons),
|
|
221
|
+
z.custom<ReactNode>((val) => isValidElement(val)),
|
|
222
|
+
])
|
|
223
|
+
.optional(),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
export const FooterSchema = z
|
|
227
|
+
.object({
|
|
228
|
+
columns: z
|
|
229
|
+
.array(
|
|
230
|
+
z.object({
|
|
231
|
+
position: z.enum(["start", "center", "end"]).optional(),
|
|
232
|
+
title: z.string(),
|
|
233
|
+
links: z.array(z.object({ label: z.string(), href: z.string() })),
|
|
234
|
+
}),
|
|
235
|
+
)
|
|
236
|
+
.optional(),
|
|
237
|
+
social: z.array(FooterSocialSchema).optional(),
|
|
238
|
+
copyright: z.string().optional(),
|
|
239
|
+
logo: LogoSchema.optional(),
|
|
240
|
+
position: z.enum(["start", "center", "end"]).optional(),
|
|
241
|
+
})
|
|
242
|
+
.optional();
|
|
243
|
+
|
|
244
|
+
const SiteMapSchema = z
|
|
245
|
+
.object({
|
|
246
|
+
/**
|
|
247
|
+
* Base url of your website
|
|
248
|
+
*/
|
|
249
|
+
siteUrl: z.string(),
|
|
250
|
+
/**
|
|
251
|
+
* Change frequency.
|
|
252
|
+
* @default 'daily'
|
|
253
|
+
*/
|
|
254
|
+
changefreq: z.optional(
|
|
255
|
+
z.enum([
|
|
256
|
+
"always",
|
|
257
|
+
"hourly",
|
|
258
|
+
"daily",
|
|
259
|
+
"weekly",
|
|
260
|
+
"monthly",
|
|
261
|
+
"yearly",
|
|
262
|
+
"never",
|
|
263
|
+
]),
|
|
264
|
+
),
|
|
265
|
+
/**
|
|
266
|
+
* Priority
|
|
267
|
+
* @default 0.7
|
|
268
|
+
*/
|
|
269
|
+
priority: z.optional(z.number()),
|
|
270
|
+
outDir: z.string().optional(),
|
|
271
|
+
/**
|
|
272
|
+
* Add <lastmod/> property.
|
|
273
|
+
* @default true
|
|
274
|
+
*/
|
|
275
|
+
autoLastmod: z.boolean().optional(),
|
|
276
|
+
/**
|
|
277
|
+
* Array of relative paths to exclude from listing on sitemap.xml or sitemap-*.xml.
|
|
278
|
+
* @example ['/page-0', '/page/example']
|
|
279
|
+
*/
|
|
280
|
+
exclude: z
|
|
281
|
+
.union([
|
|
282
|
+
z.custom<() => Promise<string[]>>((val) => typeof val === "function"),
|
|
283
|
+
z.array(z.string()),
|
|
284
|
+
])
|
|
285
|
+
.optional(),
|
|
286
|
+
})
|
|
287
|
+
.optional();
|
|
288
|
+
|
|
289
|
+
const DEFAULT_DOCS_FILES = "/pages/**/*.{md,mdx}";
|
|
290
|
+
|
|
291
|
+
const LlmsConfigSchema = z
|
|
292
|
+
.object({
|
|
293
|
+
llmsTxt: z
|
|
294
|
+
.boolean()
|
|
295
|
+
.default(false)
|
|
296
|
+
.describe(
|
|
297
|
+
"When enabled, generates an llms.txt file following the spec at https://llmstxt.org/. This file provides a summary with links to all documentation.",
|
|
298
|
+
),
|
|
299
|
+
llmsTxtFull: z
|
|
300
|
+
.boolean()
|
|
301
|
+
.default(false)
|
|
302
|
+
.describe(
|
|
303
|
+
"When enabled, generates an llms-full.txt file with the complete content of all markdown documents for LLM consumption.",
|
|
304
|
+
),
|
|
305
|
+
includeProtected: z
|
|
306
|
+
.boolean()
|
|
307
|
+
.default(false)
|
|
308
|
+
.describe(
|
|
309
|
+
"When enabled, includes content from protected routes in the generated .md files and llms.txt files. By default, protected routes are excluded.",
|
|
310
|
+
),
|
|
311
|
+
})
|
|
312
|
+
.partial();
|
|
313
|
+
|
|
314
|
+
export const DocsConfigSchema = z.object({
|
|
315
|
+
files: z
|
|
316
|
+
.union([z.string(), z.array(z.string())])
|
|
317
|
+
.transform((val) => (typeof val === "string" ? [val] : val))
|
|
318
|
+
.default([DEFAULT_DOCS_FILES]),
|
|
319
|
+
publishMarkdown: z
|
|
320
|
+
.boolean()
|
|
321
|
+
.default(false)
|
|
322
|
+
.describe(
|
|
323
|
+
"When enabled, generates .md files for each document during build. Access documents at their URL path with .md extension (e.g., /foo/hello.md). Markdown files are generated without frontmatter.",
|
|
324
|
+
),
|
|
325
|
+
defaultOptions: z
|
|
326
|
+
.object({
|
|
327
|
+
toc: z.boolean(),
|
|
328
|
+
copyPage: z.boolean().optional(),
|
|
329
|
+
disablePager: z.boolean(),
|
|
330
|
+
showLastModified: z.boolean(),
|
|
331
|
+
suggestEdit: z
|
|
332
|
+
.object({
|
|
333
|
+
url: z.string(),
|
|
334
|
+
text: z.string().optional(),
|
|
335
|
+
})
|
|
336
|
+
.optional(),
|
|
337
|
+
})
|
|
338
|
+
.partial()
|
|
339
|
+
.optional(),
|
|
340
|
+
llms: LlmsConfigSchema.optional(),
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const Redirect = z.object({
|
|
344
|
+
from: z.string(),
|
|
345
|
+
to: z.string(),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const SearchSchema = z
|
|
349
|
+
.discriminatedUnion("type", [
|
|
350
|
+
// looseObject to allow additional properties so the
|
|
351
|
+
// user can set other inkeep settings
|
|
352
|
+
z.looseObject({
|
|
353
|
+
type: z.literal("inkeep"),
|
|
354
|
+
apiKey: z.string(),
|
|
355
|
+
integrationId: z.string(),
|
|
356
|
+
organizationId: z.string(),
|
|
357
|
+
primaryBrandColor: z.string(),
|
|
358
|
+
organizationDisplayName: z.string(),
|
|
359
|
+
}),
|
|
360
|
+
z.object({
|
|
361
|
+
type: z.literal("pagefind"),
|
|
362
|
+
|
|
363
|
+
ranking: z
|
|
364
|
+
.object({
|
|
365
|
+
termFrequency: z.number(),
|
|
366
|
+
pageLength: z.number(),
|
|
367
|
+
termSimilarity: z.number(),
|
|
368
|
+
termSaturation: z.number(),
|
|
369
|
+
})
|
|
370
|
+
.optional(),
|
|
371
|
+
maxResults: z.number().optional(),
|
|
372
|
+
maxSubResults: z.number().optional(),
|
|
373
|
+
transformResults: z
|
|
374
|
+
.custom<
|
|
375
|
+
(data: {
|
|
376
|
+
result: PagefindSearchFragment;
|
|
377
|
+
auth: AuthState;
|
|
378
|
+
context: ZudokuContext;
|
|
379
|
+
}) => PagefindSearchFragment | boolean | undefined | void
|
|
380
|
+
>((val) => typeof val === "function")
|
|
381
|
+
.optional(),
|
|
382
|
+
}),
|
|
383
|
+
])
|
|
384
|
+
.optional();
|
|
385
|
+
|
|
386
|
+
const AuthenticationSchema = z.discriminatedUnion("type", [
|
|
387
|
+
z.object({
|
|
388
|
+
type: z.literal("clerk"),
|
|
389
|
+
clerkPubKey: z
|
|
390
|
+
.custom<`pk_test_${string}` | `pk_live_${string}`>()
|
|
391
|
+
.refine((val) => /^pk_(test|live)_\w+$/.test(val), {
|
|
392
|
+
message: "Clerk public key invalid, must start with pk_test or pk_live",
|
|
393
|
+
}),
|
|
394
|
+
jwtTemplateName: z.string().optional().default("dev-portal"),
|
|
395
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
396
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
397
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
398
|
+
}),
|
|
399
|
+
z.object({
|
|
400
|
+
type: z.literal("firebase"),
|
|
401
|
+
apiKey: z.string(),
|
|
402
|
+
authDomain: z.string(),
|
|
403
|
+
projectId: z.string(),
|
|
404
|
+
storageBucket: z.string().optional(),
|
|
405
|
+
messagingSenderId: z.string().optional(),
|
|
406
|
+
appId: z.string(),
|
|
407
|
+
measurementId: z.string().optional(),
|
|
408
|
+
providers: z
|
|
409
|
+
.array(
|
|
410
|
+
z.enum([
|
|
411
|
+
"google",
|
|
412
|
+
"facebook",
|
|
413
|
+
"twitter",
|
|
414
|
+
"github",
|
|
415
|
+
"microsoft",
|
|
416
|
+
"apple",
|
|
417
|
+
"yahoo",
|
|
418
|
+
"password",
|
|
419
|
+
"phone",
|
|
420
|
+
"emailLink",
|
|
421
|
+
]),
|
|
422
|
+
)
|
|
423
|
+
.optional(),
|
|
424
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
425
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
426
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
427
|
+
}),
|
|
428
|
+
z.object({
|
|
429
|
+
type: z.literal("openid"),
|
|
430
|
+
basePath: z.string().optional(),
|
|
431
|
+
clientId: z.string(),
|
|
432
|
+
issuer: z.string(),
|
|
433
|
+
audience: z.string().optional(),
|
|
434
|
+
scopes: z.array(z.string()).optional(),
|
|
435
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
436
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
437
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
438
|
+
/** Try it offers "Signed-in user" (the visitor's token); false when the gateway checks keys, not tokens (API-129). */
|
|
439
|
+
tokenIdentity: z.boolean().optional(),
|
|
440
|
+
}),
|
|
441
|
+
z.object({
|
|
442
|
+
type: z.literal("azureb2c"),
|
|
443
|
+
basePath: z.string().optional(),
|
|
444
|
+
clientId: z.string(),
|
|
445
|
+
tenantName: z.string(),
|
|
446
|
+
policyName: z.string(),
|
|
447
|
+
scopes: z.array(z.string()).optional(),
|
|
448
|
+
issuer: z.string(),
|
|
449
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
450
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
451
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
452
|
+
}),
|
|
453
|
+
|
|
454
|
+
z.object({
|
|
455
|
+
type: z.literal("auth0"),
|
|
456
|
+
clientId: z.string(),
|
|
457
|
+
domain: z.string().refine(
|
|
458
|
+
(val) => {
|
|
459
|
+
if (val.startsWith("http://") || val.startsWith("https://")) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
if (val.includes("/")) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
return val.includes(".") && val.length > 0;
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
message:
|
|
469
|
+
"Domain must be a host only (e.g., 'example.com') without protocol or slashes",
|
|
470
|
+
},
|
|
471
|
+
),
|
|
472
|
+
audience: z.string().optional(),
|
|
473
|
+
scopes: z.array(z.string()).optional(),
|
|
474
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
475
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
476
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
477
|
+
options: z
|
|
478
|
+
.object({
|
|
479
|
+
alwaysPromptLogin: z.boolean().optional(),
|
|
480
|
+
prompt: z.string().optional(),
|
|
481
|
+
})
|
|
482
|
+
.optional(),
|
|
483
|
+
}),
|
|
484
|
+
z.object({
|
|
485
|
+
type: z.literal("supabase"),
|
|
486
|
+
basePath: z.string().optional(),
|
|
487
|
+
supabaseUrl: z.string(),
|
|
488
|
+
supabaseKey: z.string(),
|
|
489
|
+
provider: z.string().optional(),
|
|
490
|
+
providers: z.array(z.string()).optional(),
|
|
491
|
+
onlyThirdPartyProviders: z.boolean().optional(),
|
|
492
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
493
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
494
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
495
|
+
}),
|
|
496
|
+
z.object({
|
|
497
|
+
type: z.literal("dev-portal").optional().default("dev-portal"),
|
|
498
|
+
apiBaseUrl: z.string().optional(),
|
|
499
|
+
demoMode: z.boolean().optional(),
|
|
500
|
+
native: z
|
|
501
|
+
.object({
|
|
502
|
+
enabled: z.boolean().optional(),
|
|
503
|
+
})
|
|
504
|
+
.optional(),
|
|
505
|
+
email: z
|
|
506
|
+
.object({
|
|
507
|
+
provider: z.string().optional(),
|
|
508
|
+
from: z.string().optional(),
|
|
509
|
+
connectionString: z.string().optional(),
|
|
510
|
+
})
|
|
511
|
+
.optional(),
|
|
512
|
+
providers: z
|
|
513
|
+
.array(
|
|
514
|
+
z.object({
|
|
515
|
+
id: z.string().min(1),
|
|
516
|
+
displayName: z.string().optional(),
|
|
517
|
+
authority: z.string().optional(),
|
|
518
|
+
clientId: z.string().optional(),
|
|
519
|
+
clientSecret: z.string().optional(),
|
|
520
|
+
scopes: z.string().optional(),
|
|
521
|
+
}),
|
|
522
|
+
)
|
|
523
|
+
.optional(),
|
|
524
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
525
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
526
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
527
|
+
}),
|
|
528
|
+
]);
|
|
529
|
+
|
|
530
|
+
const MetadataSchema = z
|
|
531
|
+
.object({
|
|
532
|
+
title: z.string(),
|
|
533
|
+
defaultTitle: z.string().optional(),
|
|
534
|
+
description: z.string(),
|
|
535
|
+
logo: z.string(),
|
|
536
|
+
favicon: z.string(),
|
|
537
|
+
generator: z.string(),
|
|
538
|
+
applicationName: z.string(),
|
|
539
|
+
referrer: z.string(),
|
|
540
|
+
keywords: z.array(z.string()),
|
|
541
|
+
authors: z.array(z.string()),
|
|
542
|
+
creator: z.string(),
|
|
543
|
+
publisher: z.string(),
|
|
544
|
+
})
|
|
545
|
+
.partial();
|
|
546
|
+
|
|
547
|
+
const FontConfigSchema = z.union([
|
|
548
|
+
z.enum(GOOGLE_FONTS),
|
|
549
|
+
z.object({
|
|
550
|
+
url: z.string(),
|
|
551
|
+
fontFamily: z.string().optional(),
|
|
552
|
+
}),
|
|
553
|
+
]);
|
|
554
|
+
|
|
555
|
+
export type FontConfig = z.infer<typeof FontConfigSchema>;
|
|
556
|
+
|
|
557
|
+
const FontsConfigSchema = z.object({
|
|
558
|
+
sans: FontConfigSchema.optional(),
|
|
559
|
+
serif: FontConfigSchema.optional(),
|
|
560
|
+
mono: FontConfigSchema.optional(),
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const CssObject = z.record(
|
|
564
|
+
z.string(),
|
|
565
|
+
z.lazy(() =>
|
|
566
|
+
z.union([
|
|
567
|
+
z.string(),
|
|
568
|
+
z.record(
|
|
569
|
+
z.string(),
|
|
570
|
+
z.union([z.string(), z.record(z.string(), z.string())]),
|
|
571
|
+
),
|
|
572
|
+
]),
|
|
573
|
+
),
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
const ThemeConfigSchema = z.object({
|
|
577
|
+
registryUrl: z.string().url().optional(),
|
|
578
|
+
customCss: z.union([z.string(), CssObject]).optional(),
|
|
579
|
+
light: ThemeSchema.optional(),
|
|
580
|
+
dark: ThemeSchema.optional(),
|
|
581
|
+
fonts: FontsConfigSchema.optional(),
|
|
582
|
+
noDefaultTheme: z.boolean().optional(),
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
const SiteSchema = z
|
|
586
|
+
.object({
|
|
587
|
+
title: z.string(),
|
|
588
|
+
showTitleInHeader: z.boolean().optional(),
|
|
589
|
+
logoUrl: z.string(),
|
|
590
|
+
dir: z.enum(["ltr", "rtl"]).optional(),
|
|
591
|
+
logo: LogoSchema,
|
|
592
|
+
banner: z.object({
|
|
593
|
+
message: z.custom<NonNullable<ReactNode>>(),
|
|
594
|
+
color: z
|
|
595
|
+
.custom<"note" | "tip" | "info" | "caution" | "danger" | (string & {})>(
|
|
596
|
+
(val) => typeof val === "string",
|
|
597
|
+
)
|
|
598
|
+
.optional(),
|
|
599
|
+
dismissible: z.boolean().optional(),
|
|
600
|
+
}),
|
|
601
|
+
footer: FooterSchema,
|
|
602
|
+
})
|
|
603
|
+
.partial();
|
|
604
|
+
|
|
605
|
+
const PlacementPosition = z.enum(["start", "center", "end"]);
|
|
606
|
+
|
|
607
|
+
const HeaderConfigSchema = z
|
|
608
|
+
.object({
|
|
609
|
+
navigation: HeaderNavigationSchema,
|
|
610
|
+
placements: z
|
|
611
|
+
.object({
|
|
612
|
+
navigation: PlacementPosition,
|
|
613
|
+
search: PlacementPosition,
|
|
614
|
+
auth: z.enum(["start", "center", "end", "navigation"]),
|
|
615
|
+
})
|
|
616
|
+
.partial()
|
|
617
|
+
.optional(),
|
|
618
|
+
})
|
|
619
|
+
.partial();
|
|
620
|
+
|
|
621
|
+
const ApiCatalogSchema = z.object({
|
|
622
|
+
path: z.string(),
|
|
623
|
+
label: z.string(),
|
|
624
|
+
items: z.array(z.string()).optional(),
|
|
625
|
+
filterItems: z
|
|
626
|
+
.custom<FilterCatalogItemsFn>((val) => typeof val === "function")
|
|
627
|
+
.optional(),
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
export const CdnUrlSchema = z
|
|
631
|
+
.union([
|
|
632
|
+
z.string(),
|
|
633
|
+
z.object({
|
|
634
|
+
base: z.string().optional(),
|
|
635
|
+
media: z.string().optional(),
|
|
636
|
+
}),
|
|
637
|
+
])
|
|
638
|
+
.transform((val) => {
|
|
639
|
+
if (typeof val === "string") {
|
|
640
|
+
return { base: val, media: val };
|
|
641
|
+
}
|
|
642
|
+
return { base: val.base, media: val.media };
|
|
643
|
+
})
|
|
644
|
+
.optional();
|
|
645
|
+
|
|
646
|
+
const BaseConfigSchema = z.object({
|
|
647
|
+
slots: z.record(z.string(), z.custom<SlotType>()),
|
|
648
|
+
/**
|
|
649
|
+
* @deprecated Use `slots` instead
|
|
650
|
+
*/
|
|
651
|
+
UNSAFE_slotlets: z.record(z.string(), z.custom<SlotType>()),
|
|
652
|
+
mdx: z
|
|
653
|
+
.object({
|
|
654
|
+
components: z.custom<MdxComponentsType>(),
|
|
655
|
+
})
|
|
656
|
+
.partial(),
|
|
657
|
+
customPages: z.array(
|
|
658
|
+
z.object({
|
|
659
|
+
path: z.string(),
|
|
660
|
+
element: z.custom<NonNullable<ReactNode>>().optional(),
|
|
661
|
+
render: z.custom<ComponentType<ExposedComponentProps>>().optional(),
|
|
662
|
+
prose: z.boolean().optional(),
|
|
663
|
+
}),
|
|
664
|
+
),
|
|
665
|
+
plugins: z.array(z.custom<ApitogoPlugin>()),
|
|
666
|
+
build: z.custom<{
|
|
667
|
+
remarkPlugins?: Options["remarkPlugins"];
|
|
668
|
+
rehypePlugins?: Options["rehypePlugins"];
|
|
669
|
+
}>(),
|
|
670
|
+
protectedRoutes: ProtectedRoutesSchema,
|
|
671
|
+
basePath: z.string().optional(),
|
|
672
|
+
canonicalUrlOrigin: z.string().optional(),
|
|
673
|
+
cdnUrl: CdnUrlSchema.optional(),
|
|
674
|
+
port: z.number().optional(),
|
|
675
|
+
https: z
|
|
676
|
+
.object({
|
|
677
|
+
key: z.string(),
|
|
678
|
+
cert: z.string(),
|
|
679
|
+
ca: z.string().optional(),
|
|
680
|
+
})
|
|
681
|
+
.optional(),
|
|
682
|
+
site: SiteSchema,
|
|
683
|
+
header: HeaderConfigSchema.optional(),
|
|
684
|
+
navigation: InputNavigationSchema,
|
|
685
|
+
navigationRules: NavigationRulesSchema.optional(),
|
|
686
|
+
theme: ThemeConfigSchema,
|
|
687
|
+
syntaxHighlighting: z
|
|
688
|
+
.object({
|
|
689
|
+
languages: z.array(z.custom<BundledLanguage>()),
|
|
690
|
+
themes: z.object({
|
|
691
|
+
light: z.custom<BundledTheme>(),
|
|
692
|
+
dark: z.custom<BundledTheme>(),
|
|
693
|
+
}),
|
|
694
|
+
})
|
|
695
|
+
.partial()
|
|
696
|
+
.optional(),
|
|
697
|
+
metadata: MetadataSchema,
|
|
698
|
+
authentication: AuthenticationSchema,
|
|
699
|
+
search: SearchSchema,
|
|
700
|
+
docs: DocsConfigSchema.optional(),
|
|
701
|
+
modules: ModulesSchema,
|
|
702
|
+
apis: z.union([ApiSchema, z.array(ApiSchema)]),
|
|
703
|
+
catalogs: z.union([ApiCatalogSchema, z.array(ApiCatalogSchema)]),
|
|
704
|
+
apiKeys: ApiKeysSchema,
|
|
705
|
+
aiAssistants: AiAssistantsSchema,
|
|
706
|
+
redirects: z.array(Redirect),
|
|
707
|
+
sitemap: SiteMapSchema,
|
|
708
|
+
enableStatusPages: z.boolean().optional(),
|
|
709
|
+
defaults: z.object({
|
|
710
|
+
apis: ApiOptionsSchema,
|
|
711
|
+
/**
|
|
712
|
+
* @deprecated Use `apis.examplesLanguage` or `defaults.apis.examplesLanguage` instead
|
|
713
|
+
*/
|
|
714
|
+
examplesLanguage: z.string().optional(),
|
|
715
|
+
}),
|
|
716
|
+
// Internal: populated by plugins via `transformConfig` to track plugin directories
|
|
717
|
+
__pluginDirs: z.array(z.string()),
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
export const ZudokuConfig = BaseConfigSchema.partial();
|
|
721
|
+
|
|
722
|
+
export type ZudokuApiConfig = z.infer<typeof ApiSchema>;
|
|
723
|
+
export type VersionConfig = z.infer<typeof VersionConfigSchema>;
|
|
724
|
+
export type ZudokuSiteMapConfig = z.infer<typeof SiteMapSchema>;
|
|
725
|
+
export type ZudokuDocsConfig = z.infer<typeof DocsConfigSchema>;
|
|
726
|
+
export type { ModulesConfig } from "./ModulesSchema.js";
|
|
727
|
+
export type {
|
|
728
|
+
ResolvedModulesConfig,
|
|
729
|
+
DocsModuleConfig,
|
|
730
|
+
LandingModuleConfig,
|
|
731
|
+
LandingContentConfig,
|
|
732
|
+
LandingFeatureConfig,
|
|
733
|
+
LandingLinkConfig,
|
|
734
|
+
ResolvedLandingContent,
|
|
735
|
+
ResolvedDashboardContent,
|
|
736
|
+
ResolvedProfileContent,
|
|
737
|
+
ResolvedPlansContent,
|
|
738
|
+
ResolvedLandingModule,
|
|
739
|
+
ResolvedUserPanelModule,
|
|
740
|
+
ResolvedDashboardModule,
|
|
741
|
+
ResolvedUserManagementModule,
|
|
742
|
+
ResolvedApiKeysModule,
|
|
743
|
+
ResolvedPlansModule,
|
|
744
|
+
UserPanelModuleConfig,
|
|
745
|
+
DashboardModuleConfig,
|
|
746
|
+
UserManagementModuleConfig,
|
|
747
|
+
ApiKeysModuleConfig,
|
|
748
|
+
PlansModuleConfig,
|
|
749
|
+
DashboardContentConfig,
|
|
750
|
+
ProfileContentConfig,
|
|
751
|
+
PlansContentConfig,
|
|
752
|
+
PlanTierConfig,
|
|
753
|
+
} from "./ModulesSchema.js";
|
|
754
|
+
export type ZudokuLlmsConfig = z.infer<typeof LlmsConfigSchema>;
|
|
755
|
+
export type ZudokuRedirect = z.infer<typeof Redirect>;
|
|
756
|
+
export type AuthenticationConfig = z.infer<typeof AuthenticationSchema>;
|
|
757
|
+
|
|
758
|
+
// Use `z.input` type for flexibility with transforms,
|
|
759
|
+
// but override navigation with `z.infer` for strict validation
|
|
760
|
+
type BaseZudokuConfig = z.input<typeof ZudokuConfig>;
|
|
761
|
+
export type ZudokuConfig = Omit<
|
|
762
|
+
BaseZudokuConfig,
|
|
763
|
+
"header" | "navigation" | "navigationRules"
|
|
764
|
+
> & {
|
|
765
|
+
header?: {
|
|
766
|
+
navigation?: z.infer<typeof HeaderNavigationSchema>;
|
|
767
|
+
placements?: {
|
|
768
|
+
navigation?: "start" | "center" | "end";
|
|
769
|
+
search?: "start" | "center" | "end";
|
|
770
|
+
auth?: "start" | "center" | "end" | "navigation";
|
|
771
|
+
};
|
|
772
|
+
};
|
|
773
|
+
navigation?: z.infer<typeof InputNavigationSchema>;
|
|
774
|
+
navigationRules?: z.infer<typeof NavigationRulesSchema>;
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
export function validateConfig(config: unknown, configPath?: string) {
|
|
778
|
+
const validationResult = ZudokuConfig.safeParse(config);
|
|
779
|
+
|
|
780
|
+
if (!validationResult.success) {
|
|
781
|
+
// In production (build mode), throw an error to fail the build
|
|
782
|
+
if (process.env.NODE_ENV === "production") {
|
|
783
|
+
throw new Error(
|
|
784
|
+
`Whoops, looks like there's an issue with your ${configPath ?? "config"}:\n${z.prettifyError(validationResult.error)}`,
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// In development mode, log warnings but don't fail
|
|
789
|
+
// biome-ignore lint/suspicious/noConsole: Logging allowed here
|
|
790
|
+
console.log(colors.yellow("Validation errors:"));
|
|
791
|
+
// biome-ignore lint/suspicious/noConsole: Logging allowed here
|
|
792
|
+
console.log(colors.yellow(z.prettifyError(validationResult.error)));
|
|
793
|
+
}
|
|
794
|
+
}
|