From dc757d10dd3568baf6ec3f31a80648531e349107 Mon Sep 17 00:00:00 2001 From: Daniel Lehr Date: Thu, 17 Oct 2024 15:56:03 +0200 Subject: [PATCH] fixes --- docs/articles/step-3-add-api-key-auth.md | 2 +- docs/handlers/url-rewrite.md | 2 +- package-lock.json | 9 +- package.json | 5 +- scripts/build.mjs | 13 - scripts/fix-markdown.ts | 12 +- scripts/update-policies.main.mjs | 5 - scripts/update-policies.tsx | 419 ----------------------- sidebar.ts | 4 +- vercel.json | 6 +- 10 files changed, 21 insertions(+), 456 deletions(-) delete mode 100644 scripts/build.mjs delete mode 100644 scripts/update-policies.main.mjs delete mode 100644 scripts/update-policies.tsx diff --git a/docs/articles/step-3-add-api-key-auth.md b/docs/articles/step-3-add-api-key-auth.md index bc63e22d..2654e4fb 100644 --- a/docs/articles/step-3-add-api-key-auth.md +++ b/docs/articles/step-3-add-api-key-auth.md @@ -12,7 +12,7 @@ this authentication method is considered one of the easiest to use by developers but hard for API developers to get right. We also support JWT tokens and other authentication methods. -:::info{title="What's a Policy?} +:::info{title="What's a Policy?"} [Policies](./policies.md) are modules that can intercept and transform an incoming request or outgoing response. Zuplo offers a wide range of policies diff --git a/docs/handlers/url-rewrite.md b/docs/handlers/url-rewrite.md index 0faa2c65..9c33471e 100644 --- a/docs/handlers/url-rewrite.md +++ b/docs/handlers/url-rewrite.md @@ -17,7 +17,7 @@ The Rewrite Handler can be added to any route using the Route Designer. Open the **routes.oas.json**. Inside any route, select **URL Rewrite** from the **Request Handlers** drop-down. -![Url Rewrite Handler selection](../../public/media/url-rewrite-handler-selection.png) +![Url Rewrite Handler selection](/media/url-rewrite-handler-selection.png) In the text box enter the URL to rewrite the request. Values can be mixed into the URL string using Javascript string interpolation syntax. For example: diff --git a/package-lock.json b/package-lock.json index c99c05de..42dbcb6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "typescript": "5.6.3", - "zudoku": "0.0.0-d99ad45" + "zudoku": "0.0.0-740d272" }, "devDependencies": { "@types/json-schema": "7.0.15", @@ -1347,7 +1347,6 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", - "extraneous": true, "inBundle": true, "license": "MIT", "engines": { @@ -25243,9 +25242,9 @@ } }, "node_modules/zudoku": { - "version": "0.0.0-d99ad45", - "resolved": "https://registry.npmjs.org/zudoku/-/zudoku-0.0.0-d99ad45.tgz", - "integrity": "sha512-R6SdBlqxZa7w7Z/n+jTyu/2ryHNqB2jwWGEIAWXEZSRiiNN9u/DIJKByhSZnmRVlJqBV89M7BAxGOmQBiuSODA==", + "version": "0.0.0-740d272", + "resolved": "https://registry.npmjs.org/zudoku/-/zudoku-0.0.0-740d272.tgz", + "integrity": "sha512-f3FmPIIP9jyYt7vrl884k2MmAI7T5s2Q3je+L+ZlwbfVKgA6MBGG269RghhPVAXhVgGlwRcXOH3lTUbftuxFCQ==", "dependencies": { "@envelop/core": "5.0.2", "@graphql-typed-document-node/core": "3.2.0", diff --git a/package.json b/package.json index 48dd5574..a14b2e57 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,9 @@ "scripts": { "dev": "zudoku dev", "build": "zudoku build", - "build:scripts": "node ./scripts/build.mjs", "format": "prettier --write .", "typecheck": "tsc", - "postinstall": "pnpm policies:get && pnpm policies:generate", + "postinstall": "npm run policies:get && npm run policies:generate", "policies:get": "sh ./scripts/get-policies.sh", "policies:generate": "tsx scripts/generate-policies.ts", "policies:ci": "node ./scripts/build.mjs && node --enable-source-maps ./scripts/update-policies.main.mjs" @@ -24,7 +23,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "typescript": "5.6.3", - "zudoku": "0.0.0-d99ad45" + "zudoku": "0.0.0-740d272" }, "devDependencies": { "@types/json-schema": "7.0.15", diff --git a/scripts/build.mjs b/scripts/build.mjs deleted file mode 100644 index 7f8a936c..00000000 --- a/scripts/build.mjs +++ /dev/null @@ -1,13 +0,0 @@ -import esbuild from "esbuild"; -import path from "path"; - -esbuild.buildSync({ - entryPoints: [path.resolve(process.cwd(), "./scripts/update-policies.tsx")], - bundle: true, - platform: "node", - packages: "external", - format: "esm", - sourcemap: true, - - outfile: path.resolve(process.cwd(), "./scripts/update-policies.mjs"), -}); diff --git a/scripts/fix-markdown.ts b/scripts/fix-markdown.ts index 1370a6f4..2cc6dc10 100644 --- a/scripts/fix-markdown.ts +++ b/scripts/fix-markdown.ts @@ -1,13 +1,15 @@ export const fixMarkdown = (content: string) => content - .replaceAll(/\.mdx?(#.*?)?\)/gm, "$1)") // replace paths like `../../public` without the `public` prefix + .replaceAll(/\.mdx?(#.*?)?\)/gm, "$1)") + // replace paths like `../../public` without the `public` prefix .replaceAll(/\((\.\.\/)+public/gm, "(") - // replace local gifs and pngs with webm and webp - .replaceAll(/(]\(\.*\/.+?)(\.gif\))/gm, "$1.webm)") - .replaceAll(/(]\(\.*\/.+?)(\.png\))/gm, "$1.webp)") // replace outdated directives with correct syntax .replaceAll(/:::\s([^\n]+)/gm, ":::$1") + // /^(.*?)\s*{#([\w-]+)}$ + // replace custom slug ids: + .replaceAll(/^(#+.*?)\s*{#([\w-]+)}/gm, "$1") .replaceAll( /:::(tip|info|note|caution|warning|danger)\s([^\n]+)/gm, ':::$1{title="$2"}', - ); + ) + .replaceAll(/:::\w+{title="[^"]*"}/g, (m) => m.replaceAll(/\n/g, "")); diff --git a/scripts/update-policies.main.mjs b/scripts/update-policies.main.mjs deleted file mode 100644 index d28bdf4d..00000000 --- a/scripts/update-policies.main.mjs +++ /dev/null @@ -1,5 +0,0 @@ -import { run } from "./update-policies.mjs"; -run().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/scripts/update-policies.tsx b/scripts/update-policies.tsx deleted file mode 100644 index e8829b21..00000000 --- a/scripts/update-policies.tsx +++ /dev/null @@ -1,419 +0,0 @@ -import { dereference } from "@apidevtools/json-schema-ref-parser"; -import chalk from "chalk"; -import { existsSync } from "fs"; -import { mkdir, readFile, writeFile } from "fs/promises"; -import { glob } from "glob"; -import { JSONSchema7 } from "json-schema"; -import type { Heading as AstHeading } from "mdast"; -import { toString } from "mdast-util-to-string"; -import path from "path"; -import prettier from "prettier"; -import React from "react"; -import { renderToStaticMarkup } from "react-dom/server"; -import { Node } from "unist"; -import { visit } from "unist-util-visit"; - -const isNext = !!process.env.IS_NEXT; - -// here just to keep the react import -const version = React.version; - -type SchemaRecord = { - [key: string]: JSONSchema7; -}; - -type PolicySchema = JSONSchema7 & { - isBeta?: boolean; - isDeprecated?: boolean; - isInternal?: boolean; - isPaidAddOn?: boolean; - isEnterprise?: boolean; - isHidden?: boolean; - isCustom?: boolean; - deprecatedMessage?: string; -}; - -type Heading = { - depth: number; - value: string; - data?: any; -}; - -type RenderResult = { - html: string | Buffer; - headings: Array; -}; - -const OptionProperty = ({ schema }: { schema: JSONSchema7 }) => { - isObjectSchema(schema); - - if (schema.type === "object" && schema.properties) { - return ; - } else if (schema.type === "array" && schema.items) { - return ; - } -}; - -function ObjectSchema({ schema }: { schema: JSONSchema7 }) { - return ( -
    - {Object.entries(schema.properties as SchemaRecord).map(([key, value]) => ( -
  • - {key} - - {schema.required?.includes(key) && ( - {" (Required)"} - )} - {" - "} - {value.description && ( -
    - )} - {value.type === "string" && value.enum && ( - - {" "} - Allowed values are{" "} - {value.enum.map((v, i) => { - const comma = i < value.enum!.length - 1 ? ", " : null; - const and = i === value.enum!.length - 1 ? "and " : null; - return ( - - {and} - {v!.toString()} - {comma} - - ); - })} - . - - )} - {value.default && ( - - {" "} - Defaults to {value.default.toString()}. - - )} - -
  • - ))} -
- ); -} - -function ArraySchema({ schema }: { schema: JSONSchema7 }) { - const items = schema.items as JSONSchema7; - if (items.type === "object" && items.properties) { - return ; - } - return null; -} - -function OptionType({ value }: { value: JSONSchema7 }) { - let typeString: string | undefined; - if (value.type === "array") { - const arrayType = (value.items as JSONSchema7 | undefined)?.type; - typeString = arrayType ? `${arrayType}[]` : "array"; - } else if (value.type) { - typeString = value.type.toString(); - } else if (value.oneOf) { - typeString = value.oneOf - .map((o: any) => { - if (o.items && o.items.type) { - return `${o.items.type.toString()}[]`; - } - return o.type?.toString(); - }) - .filter((t) => t !== undefined) - .join(" | "); - } - if (typeString) { - return {` <${typeString}>`}; - } -} - -function isObjectSchema(val: unknown): asserts val is object { - if (typeof val === "boolean") { - throw new Error("Invalid schema"); - } -} - -const docsDir = path.resolve( - process.cwd(), - isNext ? "./src/app/policies" : "./docs/policies", -); -const policiesDir = path.resolve(process.cwd(), "./policies"); -const policyManifestOutputPath = path.resolve( - process.cwd(), - "policies.v3.json", -); - -const headings = (root: Node): Array => { - const headingList: Array = []; - - visit(root, "heading", (node: AstHeading) => { - const heading: Heading = { - depth: node.depth, - value: toString(node, { includeImageAlt: false }), - }; - - // Other remark plugins can store arbitrary data - // inside a node's `data` property, such as a - // custom heading id. - const data = node?.data; - if (data) { - heading.data = data; - } - - headingList.push(heading); - }); - - return headingList; -}; - -function remarkHeadings(): (node: Node, file: any) => void { - return (node, file) => { - file.data.headings = headings(node); - }; -} - -function stringify(obj: any) { - if (process.env.NODE_ENV === "production") { - return JSON.stringify(obj); - } - return prettier.format(JSON.stringify(obj), { - parser: "json", - }); -} - -async function render(markdown: string): Promise { - const unified = (await import("unified")).unified; - const rehypeSlug = (await import("rehype-slug")).default; - const rehypeStringify = (await import("rehype-stringify")).default; - const remarkGfm = (await import("remark-gfm")).default; - const remarkParse = (await import("remark-parse")).default; - const remarkRehype = (await import("remark-rehype")).default; - const rehypeAutolinkHeadings = (await import("rehype-autolink-headings")) - .default; - const rehypePrettyCode = (await import("rehype-pretty-code")).default; - - const result = await unified() - .use(remarkParse) - .use(remarkGfm) - .use(remarkHeadings) - .use(remarkRehype) - .use(rehypePrettyCode as any) - .use(rehypeSlug) - .use(rehypeAutolinkHeadings) - .use(rehypeStringify) - .process(markdown); - - return { - html: result.value, - headings: result.data.headings as Array, - }; -} - -async function processProperties(properties) { - const tasks = Object.keys(properties).map(async (key) => { - if (properties[key].description) { - const { html } = await render(properties[key].description); - properties[key].description = html; - } - if (properties[key].properties) { - await processProperties(properties[key].properties); - } - }); - return Promise.all(tasks); -} - -function getPolicyFilePaths(policyDir) { - return { - iconSvg: path.join(policyDir, "icon.svg"), - introMd: path.join(policyDir, "intro.md"), - docMd: path.join(policyDir, "doc.md"), - policyTs: path.join(policyDir, "policy.ts"), - }; -} - -export async function run() { - console.log("Generating policies"); - // await rm(docsDir, { recursive: true, force: true }); - if (!existsSync(docsDir)) { - await mkdir(docsDir, { recursive: true }); - } - const policyConfigJson = await readFile( - path.join(policiesDir, "config.json"), - "utf-8", - ); - const policyConfig = JSON.parse(policyConfigJson); - - const matches: string[] = await glob("./{policies,temp}/**/schema.json"); - - const policies: any[] = []; - const tasks = matches.map(async (match) => { - const policyDir = path.join( - process.cwd(), - match.replace(/[\\/]schema.json$/, ""), - ); - const policyId = match.split(/[\\/]/)[1]; - const schemaPath = path.join(policyDir, "schema.json"); - const schemaJson = await readFile(schemaPath, "utf-8"); - const rawSchema = JSON.parse(schemaJson); - // RefParser uses cwd to resolve refs - process.chdir(policyDir); - const schema = (await dereference(rawSchema)) as PolicySchema; - - // Hidden policies don't even get added to the list, portal never sees them. - if (schema.isHidden) { - return; - } - - await processProperties(schema.properties); - - const policyFilePaths = getPolicyFilePaths(policyDir); - - // Build the meta format for use in the portal - const meta: Record = {}; - meta.name = schema.title; - meta.isBeta = !!schema.isBeta; - meta.isPaidAddOn = !!schema.isPaidAddOn; - meta.isEnterprise = !!schema.isEnterprise; - meta.isCustom = !!schema.isCustom; - meta.isDeprecated = !!schema.isDeprecated; - meta.isInternal = !!schema.isInternal; - meta.deprecationMessage = schema.deprecatedMessage; - meta.documentationUrl = `https://zuplo.com/docs/policies/${policyId}/`; - meta.id = policyId; - - if (policyId.endsWith("-policy")) { - console.error( - chalk.red( - `ERROR: Policy ID '${policyId}' should not end with '-policy'.`, - ), - ); - process.exit(1); - } - - const { examples } = schema.properties?.handler as any; - if (schema.isCustom) { - meta.defaultHandler = { - export: "default", - module: `$import(./modules/${policyId})`, - }; - } else if (examples && examples.length > 0) { - const example = { ...examples[0] }; - delete example._name; - meta.defaultHandler = example; - } else { - console.warn( - chalk.yellow( - `WARN: Policy ${policyId} does not have any examples in the schema.json`, - schemaJson, - ), - ); - const handler = schema.properties!.handler as JSONSchema7; - meta.defaultHandler = { - export: (handler.properties!.export as JSONSchema7).const, - module: (handler.properties!.module as JSONSchema7).const, - options: {}, - }; - } - - meta.exampleHtml = await getExampleHtml( - policyId, - schema, - policyFilePaths.introMd, - ); - - if (existsSync(policyFilePaths.iconSvg)) { - const svg = await readFile(policyFilePaths.iconSvg, "utf-8"); - const src = `data:image/svg+xml;base64,${btoa(svg)}`; - meta.icon = src; - } - - if (schema.isCustom && existsSync(policyFilePaths.policyTs)) { - const policyTs = await readFile(policyFilePaths.policyTs, "utf-8"); - meta.customPolicyTemplate = policyTs; - } - - policies.push(meta); - }); - - await Promise.all(tasks); - - const policyDataV3 = { - config: policyConfig, - policies, - }; - - const policiesV3Json = await stringify(policyDataV3); - await writeFile(policyManifestOutputPath, policiesV3Json, "utf-8"); - - console.info("Policies updated"); -} - -async function getExampleHtml( - policyId: string, - schema: PolicySchema, - introMdPath: string, -) { - let introOrDescription: string; - if (existsSync(introMdPath)) { - introOrDescription = await readFile(introMdPath, "utf-8"); - } else { - introOrDescription = schema.description!; - } - - const { html: introHtml } = await render(introOrDescription); - - isObjectSchema(schema); - const { handler } = schema.properties!; - isObjectSchema(handler); - const { properties } = handler; - const { options } = properties!; - - let deprecatedHtml; - if (schema.isDeprecated) { - let deprecatedInnerHtml = "This policy is deprecated."; - if (schema.deprecatedMessage) { - const { html } = await render(schema.deprecatedMessage); - deprecatedInnerHtml = - "

" + deprecatedInnerHtml + " " + html.toString().substring(3); - } - deprecatedHtml = ( -

- ); - } - - if (properties!.options && Object.keys(properties!.options).length === 0) { - console.warn( - chalk.yellow( - `WARN: The policy ${policyId} does not have any options set in the schema.`, - ), - ); - } - - // TODO: Move the deprecated message into custom UI in the portal - // and remove from here. - const html = renderToStaticMarkup( - <> - {deprecatedHtml} -
- {options && Object.keys(options).length > 0 ? ( - <> -

Options

-

- The options for this policy are specified below. All properties are - optional unless specifically marked as required. -

- - - ) : null} - , - ); - - return html; -} diff --git a/sidebar.ts b/sidebar.ts index 9308efa0..9f19314a 100644 --- a/sidebar.ts +++ b/sidebar.ts @@ -1,6 +1,4 @@ -import type { Sidebar } from "zudoku"; - -type SidebarEntry = Sidebar[keyof Sidebar]; +import type { SidebarEntry } from "zudoku"; export const sidebar: SidebarEntry = [ { diff --git a/vercel.json b/vercel.json index 2f6c5ad3..2b0bdf6f 100644 --- a/vercel.json +++ b/vercel.json @@ -1,8 +1,12 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", - "framework": "nextjs", + "framework": null, "cleanUrls": true, "trailingSlash": false, + "devCommand": "npm run dev", + "buildCommand": "npm run build", + "installCommand": "npm install", + "outputDirectory": "dist", "redirects": [ { "source": "/docs/articles/local-development-api-keys",