Skip to content

Commit

Permalink
proposal: new edge config schema
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity committed Oct 10, 2024
1 parent 9cc4e78 commit cb603f4
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 38 deletions.
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

0 comments on commit cb603f4

Please sign in to comment.