diff --git a/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts b/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts
new file mode 100644
index 00000000000..935bd6d388e
--- /dev/null
+++ b/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPrompts.utils.ts
@@ -0,0 +1,96 @@
+import type { Code, Heading, Root } from 'mdast'
+import { fromMarkdown } from 'mdast-util-from-markdown'
+import { toMarkdown } from 'mdast-util-to-markdown'
+import { readdir, readFile, stat } from 'node:fs/promises'
+import { basename, extname, join } from 'node:path'
+import { cache } from 'react'
+import { visit, EXIT } from 'unist-util-visit'
+
+import { EXAMPLES_DIRECTORY } from '~/lib/docs'
+
+const PROMPTS_DIRECTORY = join(EXAMPLES_DIRECTORY, 'prompts')
+
+function parseMarkdown(markdown: string) {
+ const mdast = fromMarkdown(markdown)
+
+ let heading = ''
+ visit(mdast, 'heading', (node: Heading) => {
+ if (node.depth === 1) {
+ if ('value' in node.children[0]) {
+ heading = node.children[0].value
+ }
+ return EXIT
+ }
+ })
+
+ const codeBlock: Code = {
+ type: 'code',
+ lang: 'markdown',
+ value: markdown,
+ }
+ const root: Root = {
+ type: 'root',
+ children: [codeBlock],
+ }
+ const content = toMarkdown(root)
+
+ return { heading, content }
+}
+
+async function getAiPromptsImpl() {
+ const directoryContents = await readdir(PROMPTS_DIRECTORY)
+
+ const prompts = directoryContents
+ .filter(async (file) => {
+ if (extname(file) !== '.md') {
+ return false
+ }
+
+ const fileStats = await stat(join(PROMPTS_DIRECTORY, file))
+ const isFile = fileStats.isFile()
+ return isFile
+ })
+ .map(async (filename) => {
+ const rawContent = await readFile(join(PROMPTS_DIRECTORY, filename), 'utf-8')
+ const { heading, content } = parseMarkdown(rawContent)
+
+ return {
+ filename: basename(filename, '.md'),
+ heading,
+ content,
+ }
+ })
+
+ return (await Promise.all(prompts)).sort((a, b) => b.filename.localeCompare(a.filename))
+}
+export const getAiPrompts = cache(getAiPromptsImpl)
+
+async function getAiPromptImpl(prompt: string) {
+ const filePath = join(PROMPTS_DIRECTORY, `${prompt}.md`)
+ try {
+ const rawContent = await readFile(filePath, 'utf-8')
+ const { heading, content } = parseMarkdown(rawContent)
+ return { heading, content }
+ } catch (err) {
+ console.error('Failed to fetch prompt from repo: %o', err)
+ }
+}
+export const getAiPrompt = cache(getAiPromptImpl)
+
+export async function generateAiPromptMetadata({ params: { slug } }: { params: { slug: string } }) {
+ const prompt = await getAiPrompt(slug)
+
+ return {
+ title: `AI Prompt: ${prompt.heading} | Supabase Docs`,
+ }
+}
+
+export async function generateAiPromptsStaticParams() {
+ const prompts = await getAiPrompts()
+
+ return prompts.map((prompt) => {
+ return {
+ slug: prompt.filename,
+ }
+ })
+}
diff --git a/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx b/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx
new file mode 100644
index 00000000000..ed32afb8c67
--- /dev/null
+++ b/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx
@@ -0,0 +1,26 @@
+import Link from 'next/link'
+
+import { GlassPanel } from 'ui-patterns'
+
+import { getAiPrompts } from './AiPrompts.utils'
+
+export async function AiPromptsIndex() {
+ const prompts = await getAiPrompts()
+
+ return (
+
+ {prompts.map((prompt) => (
+
+
+
+ ))}
+
+ )
+}
diff --git a/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/page.tsx b/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/page.tsx
new file mode 100644
index 00000000000..c2b7c84e67e
--- /dev/null
+++ b/apps/docs/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/page.tsx
@@ -0,0 +1,34 @@
+import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
+import {
+ generateAiPromptMetadata,
+ generateAiPromptsStaticParams,
+ getAiPrompt,
+} from './AiPrompts.utils'
+
+export const dynamicParams = false
+
+export default async function AiPromptsPage({ params: { slug } }: { params: { slug: string } }) {
+ let { heading, content } = await getAiPrompt(slug)
+ content = `
+## How to use
+
+Copy the prompt to a file in your repo.
+
+Use the "include file" feature from your AI tool to include the prompt when chatting with your AI assistant. For example, with GitHub Copilot, use \`#\`, in Cursor, use \`@Files\`, and in Zed, use \`/file\`.
+
+## Prompt
+
+${content}
+`.trim()
+
+ return (
+
+ )
+}
+
+export const generateMetadata = generateAiPromptMetadata
+export const generateStaticParams = generateAiPromptsStaticParams
diff --git a/apps/docs/app/guides/(with-sidebar)/layout.tsx b/apps/docs/app/guides/(with-sidebar)/layout.tsx
index 7c57707cdee..532645a070d 100644
--- a/apps/docs/app/guides/(with-sidebar)/layout.tsx
+++ b/apps/docs/app/guides/(with-sidebar)/layout.tsx
@@ -2,25 +2,41 @@ import { cache, type PropsWithChildren } from 'react'
import { IS_PLATFORM } from 'common'
-import { supabaseMisc } from '~/lib/supabaseMisc'
+import { NavMenuSection } from '~/components/Navigation/Navigation.types'
import Layout from '~/layouts/guides'
+import { supabaseMisc } from '~/lib/supabaseMisc'
+import { getAiPrompts } from './getting-started/ai-prompts/[slug]/AiPrompts.utils'
// Revalidate occasionally to pick up changes to partners
// 60 seconds/minute * 60 minutes/hour * 24 hours/day
export const revalidate = 86_400
const GuidesLayout = async ({ children }: PropsWithChildren) => {
- const partners = IS_PLATFORM ? await getPartners() : []
- const partnerNavItems = partners.map((partner) => ({
- name: partner.title,
- url: `https://supabase.com/partners/integrations/${partner.slug}` as `https://${string}`,
- }))
+ const [partnerNavItems, promptNavItems] = await Promise.all([getPartners(), getPrompts()])
- return {children}
+ const additionalNavItems =
+ partnerNavItems.length > 0 || promptNavItems.length > 0
+ ? { integrations: partnerNavItems, prompts: promptNavItems }
+ : undefined
+
+ return {children}
+}
+
+async function getPrompts() {
+ const prompts = await getAiPrompts()
+ return prompts.map(
+ (prompt) =>
+ ({
+ name: prompt.heading,
+ url: `/guides/getting-started/ai-prompts/${prompt.filename}`,
+ }) as Partial
+ )
}
const getPartners = cache(getPartnersImpl)
async function getPartnersImpl() {
+ if (!IS_PLATFORM) return []
+
const { data, error } = await supabaseMisc()
.from('partners')
.select('slug, title')
@@ -31,7 +47,15 @@ async function getPartnersImpl() {
console.error(new Error('Error fetching partners', { cause: error }))
}
- return data ?? []
+ const partnerNavItems = (data ?? []).map(
+ (partner) =>
+ ({
+ name: partner.title,
+ url: `https://supabase.com/partners/integrations/${partner.slug}` as `https://${string}`,
+ }) as Partial
+ )
+
+ return partnerNavItems
}
export default GuidesLayout
diff --git a/apps/docs/components/Breadcrumbs.tsx b/apps/docs/components/Breadcrumbs.tsx
index 398a5112c2a..d8d2e4856fb 100644
--- a/apps/docs/components/Breadcrumbs.tsx
+++ b/apps/docs/components/Breadcrumbs.tsx
@@ -166,6 +166,15 @@ function useBreadcrumbs() {
return breadcrumbs
}
+ const isAiPromptsPage = pathname.startsWith('/guides/getting-started/ai-prompts')
+ if (isAiPromptsPage) {
+ const breadcrumbs = [
+ { name: 'Getting started', url: '/guides/getting-started' },
+ { name: 'AI Prompts', url: '/guides/getting-started/ai-prompts' },
+ ]
+ return breadcrumbs
+ }
+
const menuId = getMenuId(pathname)
const menu = NavItems[menuId]
return findMenuItemByUrl(menu, pathname, [])
diff --git a/apps/docs/components/HomePageCover.tsx b/apps/docs/components/HomePageCover.tsx
index 4e8bff25734..dd67ee4e641 100644
--- a/apps/docs/components/HomePageCover.tsx
+++ b/apps/docs/components/HomePageCover.tsx
@@ -1,11 +1,32 @@
'use client'
-import { useBreakpoint } from 'common'
+import { ChevronRight, Play, Sparkles } from 'lucide-react'
import Link from 'next/link'
-import { IconBackground } from 'ui'
+
+import { useBreakpoint } from 'common'
+import { cn, IconBackground } from 'ui'
import { IconPanel } from 'ui-patterns/IconPanel'
+
import DocsCoverLogo from './DocsCoverLogo'
-import { Play } from 'lucide-react'
+
+function AiPrompt({ className }: { className?: string }) {
+ return (
+
+
+ Start with Supabase AI prompts
+
+
+ )
+}
const HomePageCover = (props) => {
const isXs = useBreakpoint(639)
@@ -74,7 +95,7 @@ const HomePageCover = (props) => {
p-5 md:p-8
"
>
-
+
@@ -83,20 +104,23 @@ const HomePageCover = (props) => {
Getting Started
- Discover how to set up a database to an app making queries in just a few minutes.
+ Set up and connect a database in just a few minutes.
-
- {frameworks.map((framework, i) => (
-
-
-
- ))}
+
+
+ {frameworks.map((framework, i) => (
+
+
+
+ ))}
+
+
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 719c333152d..c0e952b351b 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -349,6 +349,16 @@ export const gettingstarted: NavMenuConstant = {
},
],
},
+ {
+ name: 'AI Prompts',
+ url: undefined,
+ items: [
+ {
+ name: 'Overview',
+ url: '/guides/getting-started/ai-prompts',
+ },
+ ],
+ },
],
}
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx
index d47562c9229..ffb21177637 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx
@@ -256,7 +256,7 @@ const NavigationMenu = ({
additionalNavItems,
}: {
menuId: MenuId
- additionalNavItems?: Partial
[]
+ additionalNavItems?: Record[]>
}) => {
const level = menuId
const menu = getMenuById(level)
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx
index af40ab7e9e8..e9e211a8265 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx
@@ -6,20 +6,23 @@ import { type NavMenuSection } from '../Navigation.types'
import * as NavItems from './NavigationMenu.constants'
import NavigationMenuGuideListItems from './NavigationMenuGuideListItems'
import { usePathname } from 'next/navigation'
+import { PropsWithChildren } from 'react'
+import { MenuId } from './NavigationMenu'
const NavigationMenuGuideList = ({
id,
additionalNavItems,
}: {
id: string
- additionalNavItems?: Partial[]
+ additionalNavItems?: Record[]>
}) => {
const pathname = usePathname()
const firstLevelRoute = pathname?.split('/')?.slice(0, 4)?.join('/')
+ // eslint-disable-next-line import/namespace -- dynamic access, can't lint properly
let menu = NavItems[id]
- if (id === 'integrations' && additionalNavItems) {
+ if (id === MenuId.Integrations && additionalNavItems?.integrations) {
const integrationsListIndex = menu.items.findIndex((item) => item.name === 'Integrations')
if (integrationsListIndex !== -1) {
menu = {
@@ -28,7 +31,7 @@ const NavigationMenuGuideList = ({
...menu.items.slice(0, integrationsListIndex),
{
...menu.items[integrationsListIndex],
- items: [...menu.items[integrationsListIndex].items, ...additionalNavItems],
+ items: [...menu.items[integrationsListIndex].items, ...additionalNavItems.integrations],
},
...menu.items.slice(integrationsListIndex + 1),
],
@@ -36,6 +39,38 @@ const NavigationMenuGuideList = ({
}
}
+ if (id === MenuId.GettingStarted && additionalNavItems?.prompts) {
+ const promptsSectionIndex = menu.items.findIndex((item) => item.name === 'AI Prompts')
+ if (promptsSectionIndex !== -1) {
+ menu = {
+ ...menu,
+ items: [
+ ...menu.items.slice(0, promptsSectionIndex),
+ {
+ ...menu.items[promptsSectionIndex],
+ items: [...menu.items[promptsSectionIndex].items, ...additionalNavItems.prompts],
+ },
+ ...menu.items.slice(promptsSectionIndex + 1),
+ ],
+ }
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+
+export function NavigationMenuGuideListWrapper({
+ id,
+ firstLevelRoute,
+ children,
+}: PropsWithChildren<{
+ id: string
+ firstLevelRoute?: string
+}>) {
return (
-
+ {children}
)
}
diff --git a/apps/docs/content/guides/getting-started/ai-prompts.mdx b/apps/docs/content/guides/getting-started/ai-prompts.mdx
new file mode 100644
index 00000000000..412364c2bfb
--- /dev/null
+++ b/apps/docs/content/guides/getting-started/ai-prompts.mdx
@@ -0,0 +1,16 @@
+---
+title: AI Prompts
+subtitle: Prompts for working with Supabase using AI-powered IDE tools
+---
+
+We've curated a selection of prompts to help you work with Supabase using your favorite AI-powered IDE tools, such as GitHub Copilot or Cursor.
+
+## How to use
+
+Copy the prompt to a file in your repo.
+
+Use the "include file" feature from your AI tool to include the prompt when chatting with your AI assistant. For example, with GitHub Copilot, use `#`, in Cursor, use `@Files`, and in Zed, use `/file`.
+
+## Prompts
+
+
diff --git a/apps/docs/features/docs/MdxBase.tsx b/apps/docs/features/docs/MdxBase.tsx
index 1bda6f1f0b1..20413a1cc7d 100644
--- a/apps/docs/features/docs/MdxBase.tsx
+++ b/apps/docs/features/docs/MdxBase.tsx
@@ -7,9 +7,14 @@ import remarkGfm from 'remark-gfm'
import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'
+import { AiPromptsIndex } from '~/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPromptsIndex'
import { preprocessMdxWithDefaults } from '~/features/directives/utils'
import { components } from '~/features/docs/MdxBase.shared'
+const serverOnlyComponents = {
+ AiPromptsIndex,
+}
+
const codeHikeOptions: CodeHikeConfig = {
theme: codeHikeTheme,
lineNumbers: true,
@@ -66,7 +71,7 @@ const MDXRemoteBase = async ({
return (
diff --git a/apps/docs/layouts/MainSkeleton.tsx b/apps/docs/layouts/MainSkeleton.tsx
index 62bf27160f8..9e116e883c9 100644
--- a/apps/docs/layouts/MainSkeleton.tsx
+++ b/apps/docs/layouts/MainSkeleton.tsx
@@ -336,7 +336,7 @@ interface SkeletonProps extends PropsWithChildren {
NavigationMenu?: ReactNode
hideFooter?: boolean
className?: string
- additionalNavItems?: Partial[]
+ additionalNavItems?: Record[]>
}
function TopNavSkeleton({ children }) {
diff --git a/apps/docs/layouts/guides/index.tsx b/apps/docs/layouts/guides/index.tsx
index 0a2695833c9..c6ba38d293a 100644
--- a/apps/docs/layouts/guides/index.tsx
+++ b/apps/docs/layouts/guides/index.tsx
@@ -1,5 +1,5 @@
import 'katex/dist/katex.min.css'
-import { type PropsWithChildren } from 'react'
+import type { ReactNode, PropsWithChildren } from 'react'
import { type NavMenuSection } from '~/components/Navigation/Navigation.types'
import { LayoutMainContent } from '~/layouts/DefaultLayout'
@@ -8,9 +8,13 @@ import { SidebarSkeleton } from '~/layouts/MainSkeleton'
const Layout = ({
children,
additionalNavItems,
-}: PropsWithChildren<{ additionalNavItems?: Partial[] }>) => {
+ NavigationMenu,
+}: PropsWithChildren<{
+ additionalNavItems?: Record[]>
+ NavigationMenu?: ReactNode
+}>) => {
return (
-
+
{children}
)
diff --git a/apps/docs/next-env.d.ts b/apps/docs/next-env.d.ts
index 725dd6f2451..40c3d68096c 100644
--- a/apps/docs/next-env.d.ts
+++ b/apps/docs/next-env.d.ts
@@ -1,6 +1,5 @@
///
///
-///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 9cf97308475..857ab7ba745 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -101,7 +101,6 @@
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"unist-util-visit": "^4.1.2",
- "unist-util-visit-parents": "^5.0.0",
"uuid": "^9.0.1",
"valtio": "^1.12.0",
"yaml": "^2.4.5",
diff --git a/examples/prompts/code-format-sql.md b/examples/prompts/code-format-sql.md
index ed3cb254a30..45e55811dac 100644
--- a/examples/prompts/code-format-sql.md
+++ b/examples/prompts/code-format-sql.md
@@ -1,4 +1,4 @@
-# PostgreSQL SQL Style Guide
+# Postgres SQL Style Guide
## General
@@ -12,7 +12,7 @@
- Avoid SQL reserved words and ensure names are unique and under 63 characters.
- Use snake_case for tables and columns.
-- Prefer plurals for table names
+- Prefer plurals for table names
- Prefer singular names for columns.
## Tables
@@ -62,12 +62,12 @@ where employee_id = 1001;
Larger queries:
```sql
-select
- first_name,
+select
+ first_name,
last_name
-from
+from
employees
-where
+where
start_date between '2021-01-01' and '2021-12-31'
and
status = 'employed';
@@ -80,14 +80,14 @@ and
- Prefer full table names when referencing tables. This helps for readability.
```sql
-select
- employees.employee_name,
+select
+ employees.employee_name,
departments.department_name
-from
+from
employees
-join
+join
departments on employees.department_id = departments.department_id
-where
+where
employees.start_date > '2022-01-01';
```
@@ -104,7 +104,7 @@ where end_date is null;
## Complex queries and CTEs
-- If a query is extremely complex, prefer a CTE.
+- If a query is extremely complex, prefer a CTE.
- Make sure the CTE is clear and linear. Prefer readability over performance.
- Add comments to each block.
@@ -138,4 +138,4 @@ from
employee_counts
order by
department_name;
-```
\ No newline at end of file
+```
diff --git a/examples/prompts/nextjs-supabase-auth.md b/examples/prompts/nextjs-supabase-auth.md
new file mode 100644
index 00000000000..b10a4d63c3d
--- /dev/null
+++ b/examples/prompts/nextjs-supabase-auth.md
@@ -0,0 +1,108 @@
+# Bootstrap Next.js app with Supabase Auth
+
+Create a Next.js app that uses App Router with Supabase Auth.
+
+Follow Supabase's guidelines for using the `@supabase/ssr` package and Server-Side Auth. Specifically, there should be:
+
+- A utility function to create a client on the client side
+- A utility function create a client on the server side, using the Next.js `cookies` API to access the cookies. Use the latest version of the API, where `cookies` must be awaited.
+- A utility function to handle refreshing the user session in middleware.
+
+## Working with cookies
+
+Use the latest version of `@supabase/ssr`, where cookie options are defined with the `getAll` and `setAll` functions, like so:
+
+```
+const supabase = createServerClient(
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
+ {
+ cookies: {
+ getAll() {
+ return request.cookies.getAll()
+ },
+ setAll(cookiesToSet) {
+ cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
+ supabaseResponse = NextResponse.next({
+ request,
+ })
+ cookiesToSet.forEach(({ name, value, options }) =>
+ supabaseResponse.cookies.set(name, value, options)
+ )
+ },
+ },
+ }
+ )
+```
+
+No other cookie options should be provided.
+
+## Middleware
+
+The middleware should use the following `updateSession` function:
+
+```
+import { createServerClient } from '@supabase/ssr'
+import { NextResponse, type NextRequest } from 'next/server'
+
+export async function updateSession(request: NextRequest) {
+ let supabaseResponse = NextResponse.next({
+ request,
+ })
+
+ const supabase = createServerClient(
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
+ {
+ cookies: {
+ getAll() {
+ return request.cookies.getAll()
+ },
+ setAll(cookiesToSet) {
+ cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
+ supabaseResponse = NextResponse.next({
+ request,
+ })
+ cookiesToSet.forEach(({ name, value, options }) =>
+ supabaseResponse.cookies.set(name, value, options)
+ )
+ },
+ },
+ }
+ )
+
+ // IMPORTANT: Avoid writing any logic between createServerClient and
+ // supabase.auth.getUser(). A simple mistake could make it very hard to debug
+ // issues with users being randomly logged out.
+
+ const {
+ data: { user },
+ } = await supabase.auth.getUser()
+
+ if (
+ !user &&
+ !request.nextUrl.pathname.startsWith('/login') &&
+ !request.nextUrl.pathname.startsWith('/auth')
+ ) {
+ // no user, potentially respond by redirecting the user to the login page
+ const url = request.nextUrl.clone()
+ url.pathname = '/login'
+ return NextResponse.redirect(url)
+ }
+
+ // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
+ // creating a new response object with NextResponse.next() make sure to:
+ // 1. Pass the request in it, like so:
+ // const myNewResponse = NextResponse.next({ request })
+ // 2. Copy over the cookies, like so:
+ // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
+ // 3. Change the myNewResponse object to fit your needs, but avoid changing
+ // the cookies!
+ // 4. Finally:
+ // return myNewResponse
+ // If this is not done, you may be causing the browser and server to go out
+ // of sync and terminate the user's session prematurely!
+
+ return supabaseResponse
+}
+```
diff --git a/package-lock.json b/package-lock.json
index 8d9b9e74f34..2bdf6f2e7ea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -931,7 +931,6 @@
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"unist-util-visit": "^4.1.2",
- "unist-util-visit-parents": "^5.0.0",
"uuid": "^9.0.1",
"valtio": "^1.12.0",
"yaml": "^2.4.5",