diff --git a/pages/404.tsx b/pages/404.tsx index ee6cac141..bd4835800 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -13,17 +13,24 @@ const REPLACE_PATHNAME = "%%PATHNAME%%"; export async function getStaticProps() { // Matches mdx/rehyppe.mjs const highlighter = await shiki.getHighlighter({ theme: "css-variables" }); - const shikiTokens = await highlighter.codeToThemedTokens( - getCode({ pathname: REPLACE_PATHNAME }), - "typescript" - ); - const shikiCodeHtml = shiki.renderToHtml(shikiTokens, { - elements: { - pre: ({ children }) => children, - code: ({ children }) => children, - line: ({ children }) => `${children}`, - }, - }); + // This can fail locally + let shikiCodeHtml = ""; + try { + const shikiTokens = await highlighter.codeToThemedTokens( + getCode({ pathname: REPLACE_PATHNAME }), + "typescript" + ); + shikiCodeHtml = shiki.renderToHtml(shikiTokens, { + elements: { + pre: ({ children }) => children, + code: ({ children }) => children, + line: ({ children }) => `${children}`, + }, + }); + } catch (e) { + // If it fails, just leave it blank + } + return { props: { designVersion: "2", @@ -52,6 +59,9 @@ export default function Custom404({ shikiCodeHtml }) { const pathname = router.asPath; const isDocs = pathname.match(/^\/docs/); + // For debugging + // console.log("404 - Page not found: ", pathname); + const title = `404 - Page not found`; const lede = `We've triggered an event and a serverless function is forwarding it to the team as you read this.`; diff --git a/pages/docs/guides/index.mdx b/pages/docs/guides/index.mdx index 170437977..4075c8ca9 100644 --- a/pages/docs/guides/index.mdx +++ b/pages/docs/guides/index.mdx @@ -1,5 +1,59 @@ +import { ResourceGrid, Resource } from 'src/shared/Docs/Resources' + # Guides -Dive into advanced uses for Inngest with our guides: +Learn how to build with Inngest: + +## Patterns + + + + + + + + + + + + + + -- [Trigger your code directly from Retool](/docs/guides/trigger-your-code-from-retool) \ No newline at end of file + \ No newline at end of file diff --git a/pages/docs/index.mdx b/pages/docs/index.mdx index 5eb131caa..05ffc454d 100644 --- a/pages/docs/index.mdx +++ b/pages/docs/index.mdx @@ -1,99 +1,94 @@ +import { CodeBracketSquareIcon } from "@heroicons/react/20/solid" +import { Home } from 'src/shared/Docs/Home' import { Guides } from 'src/shared/Docs/Guides' -import { Resources } from 'src/shared/Docs/Resources' +import { ResourceGrid, Resource } from 'src/shared/Docs/Resources' +import { GuideGrid, Guide } from 'src/shared/Docs/Guides' import { HeroPattern } from 'src/shared/Docs/HeroPattern' +import EventIcon from 'src/shared/Icons/Event' +import StepsIcon from 'src/shared/Icons/Steps' export const pageTitle = 'Inngest Documentation & Guides' export const description = 'Learn how to use Inngest to deploy reliable background functions to any platform.' -{/*export const sections = [ - { title: 'Guides', id: 'guides' }, - { title: 'Resources', id: 'resources' }, -]*/} +{/* TODO - Add cool header graphic*/} -# Introduction to Inngest + -Inngest is an open source platform that adds superpowers to serverless functions. {{ className: 'lead' }} +## Getting Started -Using our SDK, a single line of code adds retries, queues, sleeps, cron schedules, fan-out jobs, and reliable steps to serverless functions in your existing projects. It's deployable to any platform, without any infrastructure or configuration. And, everything is locally testable via our UI. - -Learn how to get started: +Discover how to get set up with Inngest in just a few minutes:
- - {/* */} +
-## A small but powerful example {{ anchor: false }} - -Adding sleeps, retries, and reliable steps to a function: - - -```ts -import { EventSchemas, Inngest } from "inngest"; - -type Events = { - "user/new.signup": { - data: { - email: string; - name: string; - }; - }; -}; - -const inngest = new Inngest({ - id: "nextjs-shop", - schemas: new EventSchemas().fromRecord(), -}); - -export default inngest.createFunction( - { id: "signup-flow" }, // Function options - { event: "user/new.signup" }, // One or more events that trigger this function - async ({ event, step }) => { - // ⚡ If this step fails it will retry automatically. It will only run once - // if it succeeds - const promo = await step.run("generate-promo-code", async () => { - const promoCode = await generatePromoCode(); - return promoCode.code; - }); - - // ⚡ Again, if the email provider is down this will retry - but we will - // only generate one promo code - await step.run("send-a-welcome-promo", () => - sendEmail({ email: event.data, promo }) - ); - - // 😴 You can sleep on any platform! - await step.sleep("wait-before-followup", "1 day"); - - // ⏰ This runs exactly 1 day after the user signs up - await step.run("send-drip-campaign", () => sendDripCampaign()); - } -); -``` - - -In this example, you can reliably run serverless functions even if external APIs are down. You can also sleep or delay work without configuring queues. Plus, all events, jobs, and functions are strictly typed via TypeScript for maximum correctness. -Here's how things look when you run locally: - - - - -### Comparisons - -Without Inngest, you would have to configure several jobs amongst several different queues, then handle retries yourself. There's also a chance that many promo codes are generated depending on the reliability of that API. With Inngest, you can push this function live and everything happens automatically. - -{/* - - */} - - -### Resources and help - -If you have any questions we're always around in our [Discord community](https://www.inngest.com/discord) or on [GitHub](https://github.com/inngest/inngest). +## Introduction to Inngest + +Inngest as a durable workflow engine that enables you to run reliable code on any platform, including serverless. + +With any Inngest SDK, you write **Functions** in your codebase and make them available to Inngest using an HTTP endpoint. Functions are triggered by **Events** which can be sent with our SDKs, via a regular POST request, or can come from an external source such as webhooks. + +Functions are composed of **Steps**, which decouple parts of your functions into individually retriable blocks. Steps enable yuo to write complex, durable, long-lived workflows, _event on serverless_, in a single function. + +## Guides + + + + + + + + + + + + + + + + + +## Support + +If you need help with Inngest, you can reach out to us in the following ways: + +* [Open a ticket in our support center](https://app.inngest.com/support) +* [Ask your questions in our Discord community](/discord) +* [Contact our sales engineering team](/contact?ref=docs) diff --git a/pages/docs/platform/index.mdx b/pages/docs/platform/index.mdx new file mode 100644 index 000000000..2a40dd9e6 --- /dev/null +++ b/pages/docs/platform/index.mdx @@ -0,0 +1,27 @@ +import { GuideGrid, Guide } from 'src/shared/Docs/Guides' + +# Platform Guides + +Learn how to use the Inngest platform + + + + + + + + + + \ No newline at end of file diff --git a/pages/docs/reference/python/index.mdx b/pages/docs/reference/python/index.mdx new file mode 100644 index 000000000..d426afaf3 --- /dev/null +++ b/pages/docs/reference/python/index.mdx @@ -0,0 +1,7 @@ +import GithubIcon from "shared/Icons/Github" + +# Python SDK + +_Docs coming soon!_ For now, check out the Github repo README for basic info: [ inngest/inngest-py](https://github.com/inngest/inngest-py). + +During the beta phase, please share feedback or report issues on [Discord](https://www.inngest.com/discord) or [Github](https://github.com/inngest/inngest-py/issues) \ No newline at end of file diff --git a/pages/docs/reference/rest-api/index.mdx b/pages/docs/reference/rest-api/index.mdx new file mode 100644 index 000000000..da650f35c --- /dev/null +++ b/pages/docs/reference/rest-api/index.mdx @@ -0,0 +1,5 @@ +# REST API + +Our API is currently in development. We will be releasing new endpoints as they are ready. + +If you have any feedback or suggestions, please send it via [the form on our roadmap here](https://roadmap.inngest.com/). \ No newline at end of file diff --git a/pages/docs/reference/typescript/index.mdx b/pages/docs/reference/typescript/index.mdx new file mode 100644 index 000000000..a15d05e1a --- /dev/null +++ b/pages/docs/reference/typescript/index.mdx @@ -0,0 +1,24 @@ +import GithubIcon from "shared/Icons/Github" + +# TypeScript SDK + +## Installing + + +```shell {{ title: "npm" }} +npm install inngest +``` +```shell {{ title: "pnpm" }} +pnpm add inngest +``` +```shell {{ title: "yarn" }} +yarn add inngest +``` + + +## Source code + +Our TypeScript SDK is open source and available on Github: [ inngest/inngest-js](https://github.com/inngest/inngest-js). + + +{/* example repos */} \ No newline at end of file diff --git a/pages/docs/sdk/overview.mdx b/pages/docs/sdk/overview.mdx index 6e5c65728..54af57949 100644 --- a/pages/docs/sdk/overview.mdx +++ b/pages/docs/sdk/overview.mdx @@ -1,6 +1,6 @@ -# SDK Overview +# Installing the SDK -The Inngest SDK allows you to write reliable, durable functions in your existing projects incrementally. Functions can be automatically triggered by events or run on a schedule without infrastructure, and can be fully serverless or added to your existing HTTP server. +The Inngest SDK allows you to write reliable, durable functions in your existing projects incrementally. Functions can be automatically triggered by events or run on a schedule without infrastructure, and can be fully serverless or added to your existing HTTP server. - It works with any framework and platform by using HTTP to call your functions - It supports serverless providers, without any additional infrastructure @@ -9,23 +9,22 @@ The Inngest SDK allows you to write reliable, durable functions in your existing ## Getting started -To get started, install the SDK via npm or Yarn: +To get started, install the SDK via your favorite package manager: -```bash -npm install inngest # or yarn add inngest + +```shell {{ title: "npm" }} +npm install inngest ``` +```shell {{ title: "pnpm" }} +pnpm add inngest +``` +```shell {{ title: "yarn" }} +yarn add inngest +``` + -You’ll need to do a few things to get set up, which will only take a few minutes. +You'll need to do a few things to get set up, which will only take a few minutes. -1. [Set up and serve the Inngest API](/docs/sdk/serve) +1. [Set up and serve the Inngest API for your framework](/docs/sdk/serve) 2. [Define and write your functions](/docs/functions) -3. [Trigger functions with events](/docs/events) - -<> -{/* -TODO: -You can skip straight to serving the Inngest API, or read on to learn how the SDK works. - -## How the SDK works -*/} - +3. [Trigger functions with events](/docs/events) \ No newline at end of file diff --git a/public/assets/textures/wave-gray.svg b/public/assets/textures/wave-gray.svg new file mode 100644 index 000000000..88f46221f --- /dev/null +++ b/public/assets/textures/wave-gray.svg @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/shared/Docs/Footer.tsx b/shared/Docs/Footer.tsx index 530de3a63..fd703534e 100644 --- a/shared/Docs/Footer.tsx +++ b/shared/Docs/Footer.tsx @@ -4,7 +4,7 @@ import { useRouter } from "next/router"; import { Transition } from "@headlessui/react"; import { Button } from "../Button"; -import { navigation } from "./Navigation"; +import { topLevelNav } from "./navigationStructure"; import SocialBadges from "./SocialBadges"; function CheckIcon(props) { @@ -131,9 +131,19 @@ function PageLink({ label, page, previous = false }) { ); } +function flattenNav(nav) { + return nav.flatMap((group) => { + return group.sectionLinks + ? flattenNav(group.sectionLinks) + : group.links + ? flattenNav(group.links) + : group; + }); +} + function PageNavigation() { let router = useRouter(); - let allPages = navigation.flatMap((group) => group.links); + let allPages = flattenNav(topLevelNav); let currentPageIndex = allPages.findIndex( (page) => page.href === router.pathname ); @@ -145,6 +155,11 @@ function PageNavigation() { let previousPage = allPages[currentPageIndex - 1]; let nextPage = allPages[currentPageIndex + 1]; + // Skip next page if it's an external link + if (!nextPage?.href.match(/^\//)) { + nextPage = null; + } + if (!previousPage && !nextPage) { return null; } diff --git a/shared/Docs/Guides.tsx b/shared/Docs/Guides.tsx index 43cb887e1..5092a9fab 100644 --- a/shared/Docs/Guides.tsx +++ b/shared/Docs/Guides.tsx @@ -26,29 +26,50 @@ const guides = [ }, ]; +export function Guide(guide: { + href: string; + name: string; + description: string; +}) { + return ( +
+

+ {guide.name} +

+

+ {guide.description} +

+

+ +

+
+ ); +} + +export function GuideGrid({ cols = 4, children }) { + return ( +
+ {children} +
+ ); +} + export function Guides() { return (
Guides -
+ {guides.map((guide) => ( -
-

- {guide.name} -

-

- {guide.description} -

-

- -

-
+ ))} -
+
); } diff --git a/shared/Docs/Home.tsx b/shared/Docs/Home.tsx new file mode 100644 index 000000000..bc2974a74 --- /dev/null +++ b/shared/Docs/Home.tsx @@ -0,0 +1,14 @@ +import Link from "next/link"; +import { BoltIcon } from "@heroicons/react/24/outline"; +import Logo from "../Icons/Logo"; + +export function Home() { + return ( +
+

Inngest Documentation

+

+ Learn how to build with Inngest's durable workflow engine. +

+
+ ); +} diff --git a/shared/Docs/Layout.tsx b/shared/Docs/Layout.tsx index 4e69f0e24..9e2251964 100644 --- a/shared/Docs/Layout.tsx +++ b/shared/Docs/Layout.tsx @@ -6,6 +6,7 @@ import { motion } from "framer-motion"; import * as mdxComponents from "src/shared/Docs/mdx"; import { Footer } from "./Footer"; +import { Home } from "./Home"; import { Header } from "./Header"; import Logo from "../Icons/Logo"; import { Navigation } from "./Navigation"; @@ -48,6 +49,7 @@ export function Layout({ const metaDescription = description || `Inngest documentation for ${preferredTitle}`; const metaImage = getOpenGraphImageURL({ title: preferredTitle }); + return (
diff --git a/shared/Docs/Navigation.tsx b/shared/Docs/Navigation.tsx index d1e7cb083..ce59a1f76 100644 --- a/shared/Docs/Navigation.tsx +++ b/shared/Docs/Navigation.tsx @@ -9,7 +9,9 @@ import { useIsInsideMobileNavigation } from "./MobileNavigation"; import { useSectionStore } from "./SectionProvider"; import { Tag } from "./Tag"; import { remToPx } from "../../utils/remToPx"; -import { IconDocs, IconGuide } from "../Icons/duotone"; +import { topLevelNav } from "./navigationStructure"; + +const BASE_DIR = "/docs"; function useInitialValue(value, condition = true) { let initialValue = useRef(value).current; @@ -34,12 +36,16 @@ function NavLink({ tag, active, isAnchorLink = false, + isTopLevel = false, + className = "", children, }: { href: string; tag?: any; active?: boolean; isAnchorLink?: boolean; + isTopLevel?: boolean; + className?: string; children: React.ReactNode; }) { return ( @@ -47,11 +53,12 @@ function NavLink({ href={href} aria-current={active ? "page" : undefined} className={clsx( - "flex justify-between gap-2 py-1 pr-3 text-sm transition", - isAnchorLink ? "pl-7" : "pl-4", + "flex justify-between gap-2 py-1 pr-3 text-sm font-medium transition group", // group for nested hovers + isTopLevel ? "pl-0" : isAnchorLink ? "pl-7" : "pl-4", active ? "text-slate-900 dark:text-white" - : "text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white" + : "text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white", + className )} > {children} @@ -118,7 +125,8 @@ function ActivePageMarker({ group, pathname }) { ); } -function NavigationGroup({ group, className }) { +// A nested navigation group of links that expand and follow +function NavigationGroup({ group, className = "" }) { // If this is the mobile navigation then we always render the initial // state, so that the state does not change during the close animation. // The state will still update when we re-open (re-render) the navigation. @@ -135,7 +143,7 @@ function NavigationGroup({ group, className }) {
  • {group.title} @@ -157,7 +165,11 @@ function NavigationGroup({ group, className }) {
      {group.links.map((link) => ( - + {link.title} @@ -196,254 +208,10 @@ function NavigationGroup({ group, className }) { ); } -const baseDir = "/docs"; -export const navigation = [ - { - title: "Introduction", - links: [ - { title: "Overview", href: `${baseDir}` }, - { title: "Quick Start Tutorial", href: `${baseDir}/quick-start` }, - ], - }, - { - title: "Getting Started", - links: [ - { title: "SDK Overview", href: `${baseDir}/sdk/overview` }, - { title: "Serving the API & Frameworks", href: `${baseDir}/sdk/serve` }, - { title: "Writing Functions", href: `${baseDir}/functions` }, - { title: "Sending Events", href: `${baseDir}/events` }, - { - title: "Multi-step Functions", - href: `${baseDir}/functions/multi-step`, - }, - { - title: "Local Development", - href: `${baseDir}/local-development`, - }, - ], - }, - { - title: "Use Cases", - links: [ - { - title: "Background jobs", - href: `${baseDir}/guides/background-jobs`, - }, - { - title: "Enqueueing future jobs", - href: `${baseDir}/guides/enqueueing-future-jobs`, - }, - { - title: "Scheduled functions", - href: `${baseDir}/guides/scheduled-functions`, - }, - { - title: "Step parallelism", - href: `${baseDir}/guides/step-parallelism`, - }, - { - title: "Fan-out jobs", - href: `${baseDir}/guides/fan-out-jobs`, - }, - { - title: "User-defined Workflows", - href: `${baseDir}/guides/user-defined-workflows`, - }, - { - title: "Logging", - href: `${baseDir}/guides/logging`, - }, - { - title: "Batching events", - href: `${baseDir}/guides/batching`, - }, - { - title: "Trigger code from Retool", - href: `${baseDir}/guides/trigger-your-code-from-retool`, - }, - { - title: "Instrumenting GraphQL", - href: `${baseDir}/guides/instrumenting-graphql`, - }, - ], - }, - { - title: "Platform", - links: [ - { - title: "Working With Environments", - href: `${baseDir}/platform/environments`, - }, - { - title: "Creating an Event Key", - href: `${baseDir}/events/creating-an-event-key`, - }, - { title: "How to Deploy", href: `${baseDir}/deploy` }, - { title: "Deploy: Vercel", href: `${baseDir}/deploy/vercel` }, - { title: "Deploy: Netlify", href: `${baseDir}/deploy/netlify` }, - { - title: "Deploy: Cloudflare Pages", - href: `${baseDir}/deploy/cloudflare`, - }, - ], - }, -]; - -const referenceNavigation = [ - { - title: "Inngest Client", - links: [ - { - title: "Create the client", - href: `${baseDir}/reference/client/create`, - }, - ], - }, - { - title: "Functions", - links: [ - { - title: "Create function", - href: `${baseDir}/reference/functions/create`, - }, - { - title: "Define steps (step.run)", - href: `${baseDir}/reference/functions/step-run`, - }, - { - title: "Sleep", - href: `${baseDir}/reference/functions/step-sleep`, - }, - { - title: "Sleep until a time", - href: `${baseDir}/reference/functions/step-sleep-until`, - }, - { - title: "Wait for additional events", - href: `${baseDir}/reference/functions/step-wait-for-event`, - }, - { - title: "Sending events from functions", - href: `${baseDir}/reference/functions/step-send-event`, - }, - { - title: "Error handling & retries", - href: `${baseDir}/functions/retries`, - // href: `${baseDir}/reference/functions/error-handling`, - }, - { - title: "Handling failures", - href: `${baseDir}/reference/functions/handling-failures`, - }, - { - title: "Cancel running functions", - href: `${baseDir}/functions/cancellation`, - // href: `${baseDir}/reference/functions/cancel-running-functions`, - }, - { - title: "Concurrency", - href: `${baseDir}/functions/concurrency`, - // href: `${baseDir}/reference/functions/concurrency`, - }, - { - title: "Rate limit", - href: `${baseDir}/reference/functions/rate-limit`, - }, - { - title: "Debounce", - href: `${baseDir}/reference/functions/debounce`, - }, - { - title: "Function run priority", - href: `${baseDir}/reference/functions/run-priority`, - }, - // { - // title: "Logging", - // href: `${baseDir}/reference/functions/logging`, - // }, - ], - }, - { - title: "Events", - links: [ - { - title: "Send", - href: `${baseDir}/reference/events/send`, - }, - ], - }, - { - title: "Serve", - links: [ - // { - // title: "Framework handlers", - // href: `${baseDir}/sdk/serve`, - // }, - { - title: "Configuration", - href: `${baseDir}/reference/serve`, - }, - { title: "Streaming", href: `${baseDir}/streaming` }, - ], - }, - { - title: "Middleware", - links: [ - { - title: "Overview", - href: `${baseDir}/reference/middleware/overview`, - }, - { - title: "Creating middleware", - href: `${baseDir}/reference/middleware/create`, - }, - { - title: "Lifecycle", - href: `${baseDir}/reference/middleware/lifecycle`, - }, - { - title: "Examples", - href: `${baseDir}/reference/middleware/examples`, - }, - { - title: "TypeScript", - href: `${baseDir}/reference/middleware/typescript`, - }, - ], - }, - { - title: "Using the SDK", - links: [ - { - title: "Environment variables", - href: `${baseDir}/sdk/environment-variables`, - }, - { - title: "Using TypeScript", - href: `${baseDir}/typescript`, - }, - { title: "Upgrading to v3", href: `${baseDir}/sdk/migration` }, - ], - }, - { - title: "Usage Limits", - links: [ - { - title: "Inngest Cloud", - href: `${baseDir}/usage-limits/inngest`, - }, - { - title: "Serverless Providers", - href: `${baseDir}/usage-limits/providers`, - }, - ], - }, -]; - export const headerLinks = [ { title: "Docs", - href: baseDir, + href: BASE_DIR, }, { title: "Patterns", @@ -451,41 +219,101 @@ export const headerLinks = [ }, ]; +// Flatten the nested nav and get all nav sections w/ sectionLinks +function getAllSections(nav) { + return nav.reduce((acc, item) => { + if (item.sectionLinks) { + acc.push(item); + } + if (item.links) { + acc.push(...getAllSections(item.links)); + } + return acc; + }, []); +} + +function findRecursiveSectionLinkMatch(nav, pathname) { + const sections = getAllSections(nav); + return sections.find(({ matcher, sectionLinks }) => { + if (matcher?.test(pathname)) { + return true; + } + return !!sectionLinks?.find((item) => { + return item.links?.find((link) => link.href === pathname); + }); + }); +} + export function Navigation(props) { + const router = useRouter(); + const pathname = router.asPath; + + const nestedSection = findRecursiveSectionLinkMatch(topLevelNav, pathname); + const isNested = !!nestedSection; + const nestedNavigation = nestedSection; + return (
  • ); } @@ -88,7 +136,7 @@ function ResourcePattern({ mouseX, mouseY, ...gridProps }) { /> JSX.Element); + }; +}) { let mouseX = useMotionValue(0); let mouseY = useMotionValue(0); @@ -116,6 +174,17 @@ function Resource({ resource }) { mouseX.set(clientX - left); mouseY.set(clientY - top); } + const pattern = + resource.pattern === null + ? patterns[0] + : typeof resource.pattern === "number" + ? patterns[resource.pattern] + : resource.pattern; + + const icon = + typeof resource.icon === "string" && icons.hasOwnProperty(resource.icon) + ? icons[resource.icon] + : resource.icon; return (
    - +
    - + {!!icon && }

    @@ -155,3 +224,14 @@ export function Resources() {

    ); } + +export function ResourceGrid({ cols = 4, children }) { + return ( +
    = 2 ? 2 : cols} xl:grid-cols-${cols}`} + > + {children} +
    + ); +} diff --git a/shared/Docs/navigationStructure.ts b/shared/Docs/navigationStructure.ts new file mode 100644 index 000000000..07bf87242 --- /dev/null +++ b/shared/Docs/navigationStructure.ts @@ -0,0 +1,405 @@ +import TypeScriptIcon from "src/shared/Icons/TypeScript"; +import PythonIcon from "src/shared/Icons/Python"; +import GuideIcon from "src/shared/Icons/Guide"; +import { StatusIcon } from "src/shared/StatusWidget"; +import { + HomeIcon, + CogIcon, + PlayIcon, + CommandLineIcon, + LifebuoyIcon, +} from "@heroicons/react/24/outline"; + +// A basic link in the nav +type NavLink = { + title: string; + href: string; +}; +// A group nested of nav links with a header +type NavGroup = { + title: string; + icon?: React.FC>; + links: (NavLink | NavSection)[]; +}; +// A nav section with a nested navigation section +type NavSection = { + title: string; + icon?: React.FC>; + href: string; + matcher?: RegExp; + sectionLinks: { + title: string; + links: NavLink[]; + }[]; +}; + +const sectionGettingStarted = [ + { + title: "Quick start tutorials", + links: [ + { + title: "Next.js", + href: "/docs/quick-start", + }, + ], + }, + { + title: "Learn the basics", + links: [ + { title: "Installing the SDK", href: `/docs/sdk/overview` }, + { title: "Serving the API & Frameworks", href: `/docs/sdk/serve` }, + { title: "Writing Functions", href: `/docs/functions` }, + { title: "Sending Events", href: `/docs/events` }, + { + title: "Multi-step Functions", + href: `/docs/functions/multi-step`, + }, + { + title: "Local Development", + href: `/docs/local-development`, + }, + ], + }, +]; +const sectionGuides = [ + { + title: "Patterns", + links: [ + { + title: "Background jobs", + href: `/docs/guides/background-jobs`, + }, + { + title: "Enqueueing future jobs", + href: `/docs/guides/enqueueing-future-jobs`, + }, + { + title: "Parallelize steps", + href: `/docs/guides/step-parallelism`, + }, + { + title: "Fan-out jobs", + href: `/docs/guides/fan-out-jobs`, + }, + { + title: "Batching events", + href: `/docs/guides/batching`, + }, + { + title: "Scheduled functions", + href: `/docs/guides/scheduled-functions`, + }, + ], + }, + { + title: "How to", + links: [ + { + title: "Logging", + href: `/docs/guides/logging`, + }, + ], + }, + { + title: "Use cases", + links: [ + { + title: "User-defined Workflows", + href: `/docs/guides/user-defined-workflows`, + }, + { + title: "Trigger code from Retool", + href: `/docs/guides/trigger-your-code-from-retool`, + }, + { + title: "Instrumenting GraphQL", + href: `/docs/guides/instrumenting-graphql`, + }, + ], + }, +]; + +const sectionPlatform = [ + { + title: "Deploying", + links: [ + { title: "How to Deploy", href: `/docs/deploy` }, + { title: "Deploy: Vercel", href: `/docs/deploy/vercel` }, + { title: "Deploy: Netlify", href: `/docs/deploy/netlify` }, + { + title: "Deploy: Cloudflare Pages", + href: `/docs/deploy/cloudflare`, + }, + ], + }, + { + title: "Inngest Cloud", + links: [ + { + title: "Working With Environments", + href: `/docs/platform/environments`, + }, + { + title: "Creating an Event Key", + href: `/docs/events/creating-an-event-key`, + }, + ], + }, + { + title: "Usage Limits", + links: [ + { + title: "Inngest Cloud", + href: `/docs/usage-limits/inngest`, + }, + { + title: "Serverless Providers", + href: `/docs/usage-limits/providers`, + }, + ], + }, +]; + +const sectionTypeScriptReference = [ + { + title: "Overview", + // TODO - Allow this to be flattened w/ NavGroup + links: [ + { + title: "Introduction", + href: `/docs/reference/typescript`, + }, + ], + }, + { + title: "Inngest Client", + links: [ + { + title: "Create the client", + href: `/docs/reference/client/create`, + }, + ], + }, + { + title: "Functions", + links: [ + { + title: "Create function", + href: `/docs/reference/functions/create`, + }, + { + title: "Error handling & retries", + href: `/docs/functions/retries`, + // href: `/docs/reference/functions/error-handling`, + }, + { + title: "Handling failures", + href: `/docs/reference/functions/handling-failures`, + }, + { + title: "Cancel running functions", + href: `/docs/functions/cancellation`, + // href: `/docs/reference/functions/cancel-running-functions`, + }, + { + title: "Concurrency", + href: `/docs/functions/concurrency`, + // href: `/docs/reference/functions/concurrency`, + }, + { + title: "Rate limit", + href: `/docs/reference/functions/rate-limit`, + }, + { + title: "Debounce", + href: `/docs/reference/functions/debounce`, + }, + { + title: "Function run priority", + href: `/docs/reference/functions/run-priority`, + }, + // { + // title: "Logging", + // href: `/docs/reference/functions/logging`, + // }, + ], + }, + { + title: "Steps", + links: [ + { + title: "step.run()", + href: `/docs/reference/functions/step-run`, + className: "font-mono", + }, + { + title: "step.sleep()", + href: `/docs/reference/functions/step-sleep`, + className: "font-mono", + }, + { + title: "step.sleepUntil()", + href: `/docs/reference/functions/step-sleep-until`, + className: "font-mono", + }, + { + title: "step.waitForEvent()", + href: `/docs/reference/functions/step-wait-for-event`, + className: "font-mono", + }, + { + title: "step.sendEvent()", + href: `/docs/reference/functions/step-send-event`, + className: "font-mono", + }, + ], + }, + { + title: "Events", + links: [ + { + title: "Send", + href: `/docs/reference/events/send`, + }, + ], + }, + { + title: "Serve", + links: [ + // { + // title: "Framework handlers", + // href: `/docs/sdk/serve`, + // }, + { + title: "Configuration", + href: `/docs/reference/serve`, + }, + { title: "Streaming", href: `/docs/streaming` }, + ], + }, + { + title: "Middleware", + links: [ + { + title: "Overview", + href: `/docs/reference/middleware/overview`, + }, + { + title: "Creating middleware", + href: `/docs/reference/middleware/create`, + }, + { + title: "Lifecycle", + href: `/docs/reference/middleware/lifecycle`, + }, + { + title: "Examples", + href: `/docs/reference/middleware/examples`, + }, + { + title: "TypeScript", + href: `/docs/reference/middleware/typescript`, + }, + ], + }, + { + title: "Using the SDK", + links: [ + { + title: "Environment variables", + href: `/docs/sdk/environment-variables`, + }, + { + title: "Using TypeScript", + href: `/docs/typescript`, + }, + { title: "Upgrading to v3", href: `/docs/sdk/migration` }, + ], + }, +]; + +const sectionPythonReference = [ + { + title: "Overview", + // TODO - Allow this to be flattened w/ NavGroup + links: [ + { + title: "Introduction", + href: `/docs/reference/python`, + }, + ], + }, +]; + +export const topLevelNav = [ + { + title: "Home", + icon: HomeIcon, + href: `/docs/`, + }, + { + title: "Getting started", + icon: PlayIcon, + href: "/docs/quick-start", + matcher: /\/(getting-started|quick-start)/, + sectionLinks: sectionGettingStarted, + }, + { + title: "Guides", + icon: GuideIcon, + href: "/docs/guides", + matcher: /\/guides/, + sectionLinks: sectionGuides, + }, + { + title: "Platform", + icon: CogIcon, + href: "/docs/platform", + matcher: /\/platform/, + sectionLinks: sectionPlatform, + }, + { + title: "SDK Reference", + links: [ + { + title: "TypeScript", + icon: TypeScriptIcon, + href: `/docs/reference/typescript`, + sectionLinks: sectionTypeScriptReference, + }, + { + title: "Python", + icon: PythonIcon, + href: `/docs/reference/python`, + tag: "Beta", + sectionLinks: sectionPythonReference, + }, + ], + }, + { + title: "API", + links: [ + { + title: "REST API", + icon: CommandLineIcon, + href: `/docs/reference/rest-api`, + tag: "Coming soon", + }, + ], + }, + { + title: "Help", + links: [ + { + title: "Status Page", + icon: StatusIcon, + href: "https://status.inngest.com", + }, + { + title: "Support Center", + icon: LifebuoyIcon, + href: "https://app.inngest.com/support", + }, + ], + }, +]; diff --git a/shared/Icons/Event.tsx b/shared/Icons/Event.tsx index 2f9fb967c..ef869835b 100644 --- a/shared/Icons/Event.tsx +++ b/shared/Icons/Event.tsx @@ -1,16 +1,31 @@ import React from "react"; +import type { IconProps } from "./props"; -export default ({ fill = "#222631", size = "20" }) => ( - - - -); +const Event = ({ + size = "1em", + fill = "currentColor", + className, + style, +}: IconProps) => { + return ( + + + + + + + + ); +}; + +export default Event; diff --git a/shared/Icons/Functions.tsx b/shared/Icons/Functions.tsx deleted file mode 100644 index d5efde51f..000000000 --- a/shared/Icons/Functions.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from "react" - -const Functions: React.FC<{}> = (props) => { - return ( - - - - - ) -} - -export default Functions - diff --git a/shared/Icons/Github.tsx b/shared/Icons/Github.tsx index aa2ebface..af2faf33a 100644 --- a/shared/Icons/Github.tsx +++ b/shared/Icons/Github.tsx @@ -1,12 +1,17 @@ import React from "react"; import type { IconProps } from "./props"; -const Github = ({ size = "1em", fill = "currentColor" }: IconProps) => ( +const Github = ({ + size = "1em", + fill = "currentColor", + className = "", +}: IconProps) => ( + + + + + + + + + + + + + + + + + ); +} diff --git a/shared/Icons/Parallel.tsx b/shared/Icons/Parallel.tsx new file mode 100644 index 000000000..40a52e7f8 --- /dev/null +++ b/shared/Icons/Parallel.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import type { IconProps } from "./props"; + +const Parallel = ({ + size = "1em", + fill = "currentColor", + className, +}: IconProps) => { + return ( + + + + + ); +}; + +export default Parallel; diff --git a/shared/Icons/Python.tsx b/shared/Icons/Python.tsx new file mode 100644 index 000000000..a150e7db2 --- /dev/null +++ b/shared/Icons/Python.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import type { IconProps } from "./props"; + +const Python = ({ + size = "1em", + fill = "currentColor", + className, +}: IconProps) => { + return ( + + + + ); +}; + +export default Python; diff --git a/shared/Icons/Steps.tsx b/shared/Icons/Steps.tsx new file mode 100644 index 000000000..76542e9d8 --- /dev/null +++ b/shared/Icons/Steps.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import type { IconProps } from "./props"; + +const Steps = ({ + size = "1em", + fill = "currentColor", + className, + style, +}: IconProps) => { + return ( + + + + + + ); +}; + +export default Steps; diff --git a/shared/Icons/TypeScript.tsx b/shared/Icons/TypeScript.tsx new file mode 100644 index 000000000..b45f7b2e1 --- /dev/null +++ b/shared/Icons/TypeScript.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import type { IconProps } from "./props"; + +const TypeScript = ({ + size = "1em", + fill = "currentColor", + className, +}: IconProps) => { + return ( + + + + + + + + + + + ); +}; + +export default TypeScript; diff --git a/shared/Icons/props.ts b/shared/Icons/props.ts index fe90bf504..cde8ca2fc 100644 --- a/shared/Icons/props.ts +++ b/shared/Icons/props.ts @@ -7,4 +7,5 @@ export type IconProps = { /** Size used for height and width */ size?: string | number; className?: string; + style?: object; }; diff --git a/shared/StatusWidget.tsx b/shared/StatusWidget.tsx index f26198249..e0a3bbb5b 100644 --- a/shared/StatusWidget.tsx +++ b/shared/StatusWidget.tsx @@ -27,11 +27,7 @@ const fetchStatus = async (): Promise => { ); }; -export default function StatusWidget({ - className = "", -}: { - className?: string; -}) { +const useStatus = (): Status => { const [status, setStatus] = useState({ url: "http://status.inngest.com", // Not https description: "Fetching status...", @@ -48,13 +44,36 @@ export default function StatusWidget({ }); })(); }, []); - // We use hex colors b/c tailwind only includes what is initially rendered - const statusColor: { [K in Indicator]: string } = { - none: "#22c55e", // green-500 - minor: "#fde047", // yellow-300 - major: "#f97316", // orange-500 - critical: "#dc2626", // red-600 - }; + return status; +}; + +// We use hex colors b/c tailwind only includes what is initially rendered +const statusColor: { [K in Indicator]: string } = { + none: "#22c55e", // green-500 + minor: "#fde047", // yellow-300 + major: "#f97316", // orange-500 + critical: "#dc2626", // red-600 +}; + +export function StatusIcon({ className = "" }: { className?: string }) { + const status = useStatus(); + return ( + + + + ); +} + +export default function StatusWidget({ + className = "", +}: { + className?: string; +}) { + const status = useStatus(); return ( - }, - "sending & managing events": { - order: 1, - icon: - }, - "managing workflows": { - order: 1, - icon: - }, -}