Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: new edge config schema #1640

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ui/fern-docs-edge-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@fern-ui/fern-docs-auth": "workspace:*",
"@fern-ui/fern-docs-utils": "workspace:*",
"@vercel/edge-config": "^1.1.0",
"ts-essentials": "^10.0.1",
"url-join": "5.0.0",
"zod": "^3.23.8"
},
Expand Down
36 changes: 36 additions & 0 deletions packages/ui/fern-docs-edge-config/src/getEdgeConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { withoutStaging } from "@fern-ui/fern-docs-utils";
import { get } from "@vercel/edge-config";
import { DeepReadonly } from "ts-essentials";
import { z } from "zod";
import { FernDocsEdgeConfigV2 } from "./types";

const FernDocsEdgeConfigUnion = z.union([FernDocsEdgeConfigV2, z.string().describe("Alias to another config (host)")]);
type FernDocsEdgeConfigUnion = z.infer<typeof FernDocsEdgeConfigUnion>;

async function internalGetEdgeConfig(host: string): Promise<DeepReadonly<FernDocsEdgeConfigUnion> | undefined> {
const config = await get<FernDocsEdgeConfigV2 | string>(host);
const parse = FernDocsEdgeConfigUnion.safeParse(config);
if (!parse.success) {
// TODO: log error to sentry (and alert the team)
// eslint-disable-next-line no-console
console.error("Failed to parse FernDocsEdgeConfig", parse.error);
}
return parse.data; // returns undefined if parsing failed (swallows error)
}

export async function getEdgeConfig(host: string): Promise<DeepReadonly<FernDocsEdgeConfigV2> | undefined> {
let config = await internalGetEdgeConfig(host);

const hostWithoutStaging = withoutStaging(host);

if (config === undefined && hostWithoutStaging !== host) {
config = await internalGetEdgeConfig(hostWithoutStaging);
}

// if config is a string, it's an alias to another config
if (typeof config === "string") {
return getEdgeConfig(config);
}

return config;
}
145 changes: 145 additions & 0 deletions packages/ui/fern-docs-edge-config/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { AuthEdgeConfigSchema } from "@fern-ui/fern-docs-auth";
import { z } from "zod";

export const FeatureFlagsConfig = z.object({
"api-playground-enabled": z.boolean().optional().default(false).describe("Enables the API playground"),
"api-reference-paginated": z
.boolean()
.optional()
.default(false)
.describe(
"By default, all API references are rendered in a single page. This flag forces all API references to be rendered as individual pages.",
),
whitelabeled: z.boolean().optional().default(false).describe("Enables whitelabeling for the customer."),
"seo-disabled": z.boolean().optional().default(false).describe("Sets noindex, nofollow on all pages."),
"additional-toc-default-enabled": z
.boolean()
.optional()
.default(false)
.describe(
"By default, the table of contents does not include accordions, steps, or tabs by default, though they can be enabled individually. Turning this flag on will enable all of them by default.",
),
"snippet-template-enabled": z
.boolean()
.optional()
.default(false)
.describe("Enables evaluating snippet templates in the API playground."),
"http-snippets-enabled": z
.boolean()
.optional()
.default(false)
.describe("Enables generating code examples in the api reference using http-snippets-lite."),
"inline-feedback-enabled": z
.boolean()
.optional()
.default(false)
.describe("Enables the inline feedback widget, which adds a toolbar next to highlighted text."),
"dark-code-in-light-mode": z
.boolean()
.optional()
.default(false)
.describe("Enables dark code blocks in light mode."),
"proxy-uses-app-buildwithfern": z
.boolean()
.optional()
.default(false)
.describe("Enables the API Playground proxy to use app.buildwithfern.com instead of the current URL"),
"image-zoom-disabled": z
.boolean()
.optional()
.default(false)
.describe("Disables the image zoom feature in the docs."),
"use-javascript-as-typescript": z.boolean().optional().default(false).describe("Renames TypeScript to JavaScript"),
"always-enable-javascript-fetch": z
.boolean()
.optional()
.default(false)
.describe(
"An additional flag that will always generate a JavaScript fetch example via the http-snippets-lite plugin.",
),
"use-mdx-bundler": z
.boolean()
.optional()
.default(false)
.describe("Enables the use of mdx-bundler instead of next-mdx-remote for rendering MDX content."),
"batch-stream-toggle-disabled": z
.boolean()
.optional()
.default(false)
.describe("Disables the batch/stream toggle and renders batch and stream examples separately."),
"enabled-auth-in-generated-docs": z
.boolean()
.optional()
.default(false)
.describe("Renders the authentication scheme in the generated docs."),
"ai-chat-preview": z
.boolean()
.optional()
.default(false)
.describe("[Preview] Enables the conversational search plugin."),
"audio-file-download-span-summary": z
.boolean()
.optional()
.default(false)
.describe("[Hack] Renders `audio/mpeg` in the response summary for file downloads."),
"docs-logo-text-enabled": z
.boolean()
.optional()
.default(false)
.describe("[Hack] Renders `Docs` text next to the logo in the header."),
"audio-example-internal": z
.boolean()
.optional()
.default(false)
.describe("[Hack] Enables rendering a hard-coded audio example in the API reference."),
"uses-application-json-in-form-data-value": z
.boolean()
.optional()
.default(false)
.describe(
"Most APIs assume string values in form data are application/json. This flag will actually send application/json as the content type. This affects both code snippet generation, and the actual request sent via the playground proxy",
),
"binary-octet-stream-audio-player": z
.boolean()
.optional()
.default(false)
.describe("[Hack] Enables an audio player for binary/octet-stream responses."),
"voice-id-playground-form": z
.boolean()
.optional()
.default(false)
.describe("[Hack] Enables a voice ID form in the API playground for Elevenlabs."),
"cohere-theme": z.boolean().optional().default(false).describe("Enables the Cohere theme for the customer."),
"file-forge-hack-enabled": z
.boolean()
.optional()
.default(false)
.describe("[Hack] Enables the file forge hack for the customer."),
"hide-404-page": z
.boolean()
.optional()
.default(false)
.describe("Hides the 404 page and redirects to the root page."),
"new-search-experience": z.boolean().optional().default(false).describe("Enables the new search experience."),
});

export const InkeepSettings = z.object({
replaceSearch: z.boolean().optional().default(false),
baseSettings: z.record(z.any()).optional(),
aiChatSettings: z.record(z.any()).optional(),
searchSettings: z.record(z.any()).optional(),
modalSettings: z.record(z.any()).optional(),
});

export const LaunchDarklySettings = z.object({
"client-side-id": z.string().optional(),
});

export const FernDocsEdgeConfigV2 = z.object({
"feature-flags": FeatureFlagsConfig.optional(),
authentication: AuthEdgeConfigSchema.optional(),
inkeep: InkeepSettings.optional(),
"launch-darkly": LaunchDarklySettings.optional(),
});

export type FernDocsEdgeConfigV2 = z.infer<typeof FernDocsEdgeConfigV2>;
Loading
Loading