diff options
| author | Samrat Malisetti <[email protected]> | 2024-07-19 11:19:50 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-07-19 11:19:50 -0700 |
| commit | 581ce2780ea8b8b931539adcbddacf1d322fd027 (patch) | |
| tree | 4813158cd7aa3abb44c7e3884cfd4415d8b5148c /apps/web/lib | |
| parent | fixed mbile layout (diff) | |
| parent | Disabled sentry for now because of unreasonably large bundle size (diff) | |
| download | supermemory-581ce2780ea8b8b931539adcbddacf1d322fd027.tar.xz supermemory-581ce2780ea8b8b931539adcbddacf1d322fd027.zip | |
Merge branch 'main' into fix/mobile-layout
Diffstat (limited to 'apps/web/lib')
| -rw-r--r-- | apps/web/lib/constants.ts | 76 | ||||
| -rw-r--r-- | apps/web/lib/context.ts | 14 | ||||
| -rw-r--r-- | apps/web/lib/createAssetUrl.ts | 158 | ||||
| -rw-r--r-- | apps/web/lib/createEmbeds.ts | 418 | ||||
| -rw-r--r-- | apps/web/lib/get-metadata.ts | 68 | ||||
| -rw-r--r-- | apps/web/lib/get-theme-button.tsx | 10 | ||||
| -rw-r--r-- | apps/web/lib/handle-errors.ts | 30 | ||||
| -rw-r--r-- | apps/web/lib/loadSnap.ts | 12 | ||||
| -rw-r--r-- | apps/web/lib/searchParams.ts | 48 |
9 files changed, 417 insertions, 417 deletions
diff --git a/apps/web/lib/constants.ts b/apps/web/lib/constants.ts index 7a9485cf..241a6a1d 100644 --- a/apps/web/lib/constants.ts +++ b/apps/web/lib/constants.ts @@ -1,43 +1,43 @@ export const LIMITS = { - page: 100, - tweet: 1000, - note: 1000, + page: 100, + tweet: 1000, + note: 1000, }; export const codeLanguageSubset = [ - "python", - "javascript", - "java", - "go", - "bash", - "c", - "cpp", - "csharp", - "css", - "diff", - "graphql", - "json", - "kotlin", - "less", - "lua", - "makefile", - "markdown", - "objectivec", - "perl", - "php", - "php-template", - "plaintext", - "python-repl", - "r", - "ruby", - "rust", - "scss", - "shell", - "sql", - "swift", - "typescript", - "vbnet", - "wasm", - "xml", - "yaml", + "python", + "javascript", + "java", + "go", + "bash", + "c", + "cpp", + "csharp", + "css", + "diff", + "graphql", + "json", + "kotlin", + "less", + "lua", + "makefile", + "markdown", + "objectivec", + "perl", + "php", + "php-template", + "plaintext", + "python-repl", + "r", + "ruby", + "rust", + "scss", + "shell", + "sql", + "swift", + "typescript", + "vbnet", + "wasm", + "xml", + "yaml", ]; diff --git a/apps/web/lib/context.ts b/apps/web/lib/context.ts index 840c0d31..6c6bfa1b 100644 --- a/apps/web/lib/context.ts +++ b/apps/web/lib/context.ts @@ -1,18 +1,18 @@ import { createContext, useContext } from "react"; export interface DragContextType { - isDraggingOver: boolean; - setIsDraggingOver: React.Dispatch<React.SetStateAction<boolean>>; + isDraggingOver: boolean; + setIsDraggingOver: React.Dispatch<React.SetStateAction<boolean>>; } const DragContext = createContext<DragContextType | undefined>(undefined); export const useDragContext = () => { - const context = useContext(DragContext); - if (context === undefined) { - throw new Error("useAppContext must be used within an AppProvider"); - } - return context; + const context = useContext(DragContext); + if (context === undefined) { + throw new Error("useAppContext must be used within an AppProvider"); + } + return context; }; export default DragContext; diff --git a/apps/web/lib/createAssetUrl.ts b/apps/web/lib/createAssetUrl.ts index 05c2baea..cdebb919 100644 --- a/apps/web/lib/createAssetUrl.ts +++ b/apps/web/lib/createAssetUrl.ts @@ -1,94 +1,94 @@ import { - AssetRecordType, - TLAsset, - getHashForString, - truncateStringWithEllipsis, + AssetRecordType, + TLAsset, + getHashForString, + truncateStringWithEllipsis, } from "tldraw"; // import { BOOKMARK_ENDPOINT } from './config' interface ResponseBody { - title?: string; - description?: string; - image?: string; + title?: string; + description?: string; + image?: string; } export async function createAssetFromUrl({ - url, + url, }: { - type: "url"; - url: string; + type: "url"; + url: string; }): Promise<TLAsset> { - // try { - // // First, try to get the meta data from our endpoint - // const meta = (await ( - // await fetch(BOOKMARK_ENDPOINT, { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ - // url, - // }), - // }) - // ).json()) as ResponseBody + // try { + // // First, try to get the meta data from our endpoint + // const meta = (await ( + // await fetch(BOOKMARK_ENDPOINT, { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // url, + // }), + // }) + // ).json()) as ResponseBody - // return { - // id: AssetRecordType.createId(getHashForString(url)), - // typeName: 'asset', - // type: 'bookmark', - // props: { - // src: url, - // description: meta.description ?? '', - // image: meta.image ?? '', - // title: meta.title ?? truncateStringWithEllipsis(url, 32), - // }, - // meta: {}, - // } - // } catch (error) { - // Otherwise, fallback to fetching data from the url + // return { + // id: AssetRecordType.createId(getHashForString(url)), + // typeName: 'asset', + // type: 'bookmark', + // props: { + // src: url, + // description: meta.description ?? '', + // image: meta.image ?? '', + // title: meta.title ?? truncateStringWithEllipsis(url, 32), + // }, + // meta: {}, + // } + // } catch (error) { + // Otherwise, fallback to fetching data from the url - let meta: { image: string; title: string; description: string }; + let meta: { image: string; title: string; description: string }; - try { - const resp = await fetch(url, { method: "GET", mode: "no-cors" }); - const html = await resp.text(); - const doc = new DOMParser().parseFromString(html, "text/html"); - meta = { - image: - doc.head - .querySelector('meta[property="og:image"]') - ?.getAttribute("content") ?? "", - title: - doc.head - .querySelector('meta[property="og:title"]') - ?.getAttribute("content") ?? truncateStringWithEllipsis(url, 32), - description: - doc.head - .querySelector('meta[property="og:description"]') - ?.getAttribute("content") ?? "", - }; - } catch (error) { - console.error(error); - meta = { - image: "", - title: truncateStringWithEllipsis(url, 32), - description: "", - }; - } + try { + const resp = await fetch(url, { method: "GET", mode: "no-cors" }); + const html = await resp.text(); + const doc = new DOMParser().parseFromString(html, "text/html"); + meta = { + image: + doc.head + .querySelector('meta[property="og:image"]') + ?.getAttribute("content") ?? "", + title: + doc.head + .querySelector('meta[property="og:title"]') + ?.getAttribute("content") ?? truncateStringWithEllipsis(url, 32), + description: + doc.head + .querySelector('meta[property="og:description"]') + ?.getAttribute("content") ?? "", + }; + } catch (error) { + console.error(error); + meta = { + image: "", + title: truncateStringWithEllipsis(url, 32), + description: "", + }; + } - // Create the bookmark asset from the meta - return { - id: AssetRecordType.createId(getHashForString(url)), - typeName: "asset", - type: "bookmark", - props: { - src: url, - image: meta.image, - title: meta.title, - description: meta.description, - favicon: meta.image, - }, - meta: {}, - }; - // } + // Create the bookmark asset from the meta + return { + id: AssetRecordType.createId(getHashForString(url)), + typeName: "asset", + type: "bookmark", + props: { + src: url, + image: meta.image, + title: meta.title, + description: meta.description, + favicon: meta.image, + }, + meta: {}, + }; + // } } diff --git a/apps/web/lib/createEmbeds.ts b/apps/web/lib/createEmbeds.ts index b3a7fb52..75347d31 100644 --- a/apps/web/lib/createEmbeds.ts +++ b/apps/web/lib/createEmbeds.ts @@ -1,236 +1,236 @@ // @ts-nocheck TODO: A LOT OF TS ERRORS HERE import { - AssetRecordType, - Editor, - TLAsset, - TLAssetId, - TLBookmarkShape, - TLExternalContentSource, - TLShapePartial, - Vec, - VecLike, - createShapeId, - getEmbedInfo, - getHashForString, + AssetRecordType, + Editor, + TLAsset, + TLAssetId, + TLBookmarkShape, + TLExternalContentSource, + TLShapePartial, + Vec, + VecLike, + createShapeId, + getEmbedInfo, + getHashForString, } from "tldraw"; export default async function createEmbedsFromUrl({ - url, - point, - sources, - editor, + url, + point, + sources, + editor, }: { - url: string; - point?: VecLike | undefined; - sources?: TLExternalContentSource[] | undefined; - editor: Editor; + url: string; + point?: VecLike | undefined; + sources?: TLExternalContentSource[] | undefined; + editor: Editor; }) { - const position = - point ?? - (editor.inputs.shiftKey - ? editor.inputs.currentPagePoint - : editor.getViewportPageBounds().center); - - if (url?.includes("x.com") || url?.includes("twitter.com")) { - return editor.createShape({ - type: "Twittercard", - x: position.x - 250, - y: position.y - 150, - props: { url: url }, - }); - } - - // try to paste as an embed first - const embedInfo = getEmbedInfo(url); - - if (embedInfo) { - return editor.putExternalContent({ - type: "embed", - url: embedInfo.url, - point, - embed: embedInfo.definition, - }); - } - - const assetId: TLAssetId = AssetRecordType.createId(getHashForString(url)); - const shape = createEmptyBookmarkShape(editor, url, position); - - // Use an existing asset if we have one, or else else create a new one - let asset = editor.getAsset(assetId) as TLAsset; - let shouldAlsoCreateAsset = false; - if (!asset) { - shouldAlsoCreateAsset = true; - try { - const bookmarkAsset = await editor.getAssetForExternalContent({ - type: "url", - url, - }); - const fetchWebsite: { - title?: string; - image?: string; - description?: string; - } = await ( - await fetch(`/api/unfirlsite?website=${url}`, { - method: "POST", - }) - ).json(); - if (bookmarkAsset) { - if (fetchWebsite.title) bookmarkAsset.props.title = fetchWebsite.title; - if (fetchWebsite.image) bookmarkAsset.props.image = fetchWebsite.image; - if (fetchWebsite.description) - bookmarkAsset.props.description = fetchWebsite.description; - } - if (!bookmarkAsset) throw Error("Could not create an asset"); - asset = bookmarkAsset; - } catch (e) { - console.log(e); - return; - } - } - - editor.batch(() => { - if (shouldAlsoCreateAsset) { - editor.createAssets([asset]); - } - - editor.updateShapes([ - { - id: shape.id, - type: shape.type, - props: { - assetId: asset.id, - }, - }, - ]); - }); + const position = + point ?? + (editor.inputs.shiftKey + ? editor.inputs.currentPagePoint + : editor.getViewportPageBounds().center); + + if (url?.includes("x.com") || url?.includes("twitter.com")) { + return editor.createShape({ + type: "Twittercard", + x: position.x - 250, + y: position.y - 150, + props: { url: url }, + }); + } + + // try to paste as an embed first + const embedInfo = getEmbedInfo(url); + + if (embedInfo) { + return editor.putExternalContent({ + type: "embed", + url: embedInfo.url, + point, + embed: embedInfo.definition, + }); + } + + const assetId: TLAssetId = AssetRecordType.createId(getHashForString(url)); + const shape = createEmptyBookmarkShape(editor, url, position); + + // Use an existing asset if we have one, or else else create a new one + let asset = editor.getAsset(assetId) as TLAsset; + let shouldAlsoCreateAsset = false; + if (!asset) { + shouldAlsoCreateAsset = true; + try { + const bookmarkAsset = await editor.getAssetForExternalContent({ + type: "url", + url, + }); + const fetchWebsite: { + title?: string; + image?: string; + description?: string; + } = await ( + await fetch(`/api/unfirlsite?website=${url}`, { + method: "POST", + }) + ).json(); + if (bookmarkAsset) { + if (fetchWebsite.title) bookmarkAsset.props.title = fetchWebsite.title; + if (fetchWebsite.image) bookmarkAsset.props.image = fetchWebsite.image; + if (fetchWebsite.description) + bookmarkAsset.props.description = fetchWebsite.description; + } + if (!bookmarkAsset) throw Error("Could not create an asset"); + asset = bookmarkAsset; + } catch (e) { + console.log(e); + return; + } + } + + editor.batch(() => { + if (shouldAlsoCreateAsset) { + editor.createAssets([asset]); + } + + editor.updateShapes([ + { + id: shape.id, + type: shape.type, + props: { + assetId: asset.id, + }, + }, + ]); + }); } function isURL(str: string) { - try { - new URL(str); - return true; - } catch { - return false; - } + try { + new URL(str); + return true; + } catch { + return false; + } } function formatTextToRatio(text: string) { - const totalWidth = text.length; - const maxLineWidth = Math.floor(totalWidth / 10); - - const words = text.split(" "); - let lines = []; - let currentLine = ""; - - words.forEach((word) => { - if ((currentLine + word).length <= maxLineWidth) { - currentLine += (currentLine ? " " : "") + word; - } else { - lines.push(currentLine); - currentLine = word; - } - }); - if (currentLine) { - lines.push(currentLine); - } - return { height: (lines.length + 1) * 18, width: maxLineWidth * 10 }; + const totalWidth = text.length; + const maxLineWidth = Math.floor(totalWidth / 10); + + const words = text.split(" "); + let lines = []; + let currentLine = ""; + + words.forEach((word) => { + if ((currentLine + word).length <= maxLineWidth) { + currentLine += (currentLine ? " " : "") + word; + } else { + lines.push(currentLine); + currentLine = word; + } + }); + if (currentLine) { + lines.push(currentLine); + } + return { height: (lines.length + 1) * 18, width: maxLineWidth * 10 }; } export function handleExternalDroppedContent({ - text, - editor, + text, + editor, }: { - text: string; - editor: Editor; + text: string; + editor: Editor; }) { - const position = editor.inputs.shiftKey - ? editor.inputs.currentPagePoint - : editor.getViewportPageBounds().center; - - if (isURL(text)) { - createEmbedsFromUrl({ editor, url: text }); - } else { - // editor.createShape({ - // type: "text", - // x: position.x - 75, - // y: position.y - 75, - // props: { - // text: text, - // size: "s", - // textAlign: "start", - // }, - // }); - const { height, width } = formatTextToRatio(text); - editor.createShape({ - type: "Textcard", - x: position.x - width / 2, - y: position.y - height / 2, - props: { - content: text, - extrainfo: "https://chatgpt.com/c/762cd44e-1752-495b-967a-aa3c23c6024a", - w: width, - h: height, - }, - }); - } + const position = editor.inputs.shiftKey + ? editor.inputs.currentPagePoint + : editor.getViewportPageBounds().center; + + if (isURL(text)) { + createEmbedsFromUrl({ editor, url: text }); + } else { + // editor.createShape({ + // type: "text", + // x: position.x - 75, + // y: position.y - 75, + // props: { + // text: text, + // size: "s", + // textAlign: "start", + // }, + // }); + const { height, width } = formatTextToRatio(text); + editor.createShape({ + type: "Textcard", + x: position.x - width / 2, + y: position.y - height / 2, + props: { + content: text, + extrainfo: "https://chatgpt.com/c/762cd44e-1752-495b-967a-aa3c23c6024a", + w: width, + h: height, + }, + }); + } } function centerSelectionAroundPoint(editor: Editor, position: VecLike) { - // Re-position shapes so that the center of the group is at the provided point - const viewportPageBounds = editor.getViewportPageBounds(); - let selectionPageBounds = editor.getSelectionPageBounds(); - - if (selectionPageBounds) { - const offset = selectionPageBounds!.center.sub(position); - - editor.updateShapes( - editor.getSelectedShapes().map((shape) => { - const localRotation = editor - .getShapeParentTransform(shape) - .decompose().rotation; - const localDelta = Vec.Rot(offset, -localRotation); - return { - id: shape.id, - type: shape.type, - x: shape.x! - localDelta.x, - y: shape.y! - localDelta.y, - }; - }), - ); - } - - // Zoom out to fit the shapes, if necessary - selectionPageBounds = editor.getSelectionPageBounds(); - if ( - selectionPageBounds && - !viewportPageBounds.contains(selectionPageBounds) - ) { - editor.zoomToSelection(); - } + // Re-position shapes so that the center of the group is at the provided point + const viewportPageBounds = editor.getViewportPageBounds(); + let selectionPageBounds = editor.getSelectionPageBounds(); + + if (selectionPageBounds) { + const offset = selectionPageBounds!.center.sub(position); + + editor.updateShapes( + editor.getSelectedShapes().map((shape) => { + const localRotation = editor + .getShapeParentTransform(shape) + .decompose().rotation; + const localDelta = Vec.Rot(offset, -localRotation); + return { + id: shape.id, + type: shape.type, + x: shape.x! - localDelta.x, + y: shape.y! - localDelta.y, + }; + }), + ); + } + + // Zoom out to fit the shapes, if necessary + selectionPageBounds = editor.getSelectionPageBounds(); + if ( + selectionPageBounds && + !viewportPageBounds.contains(selectionPageBounds) + ) { + editor.zoomToSelection(); + } } export function createEmptyBookmarkShape( - editor: Editor, - url: string, - position: VecLike, + editor: Editor, + url: string, + position: VecLike, ): TLBookmarkShape { - const partial: TLShapePartial = { - id: createShapeId(), - type: "bookmark", - x: position.x - 150, - y: position.y - 160, - opacity: 1, - props: { - assetId: null, - url, - }, - }; - - editor.batch(() => { - editor.createShapes([partial]).select(partial.id); - centerSelectionAroundPoint(editor, position); - }); - - return editor.getShape(partial.id) as TLBookmarkShape; + const partial: TLShapePartial = { + id: createShapeId(), + type: "bookmark", + x: position.x - 150, + y: position.y - 160, + opacity: 1, + props: { + assetId: null, + url, + }, + }; + + editor.batch(() => { + editor.createShapes([partial]).select(partial.id); + centerSelectionAroundPoint(editor, position); + }); + + return editor.getShape(partial.id) as TLBookmarkShape; } diff --git a/apps/web/lib/get-metadata.ts b/apps/web/lib/get-metadata.ts index 81dab2ba..c81397ff 100644 --- a/apps/web/lib/get-metadata.ts +++ b/apps/web/lib/get-metadata.ts @@ -3,38 +3,38 @@ import * as cheerio from "cheerio"; // TODO: THIS SHOULD PROBABLY ALSO FETCH THE OG-IMAGE export async function getMetaData(url: string) { - const response = await fetch(url); - const html = await response.text(); - - const $ = cheerio.load(html); - - // Extract the base URL - const baseUrl = url - - // Extract title - const title = $("title").text().trim(); - - const description = $("meta[name=description]").attr("content") ?? ""; - - const _favicon = - $("link[rel=icon]").attr("href") ?? "https://supermemory.dhr.wtf/web.svg"; - - let favicon = - _favicon.trim().length > 0 - ? _favicon.trim() - : "https://supermemory.dhr.wtf/web.svg"; - if (favicon.startsWith("/")) { - favicon = baseUrl + favicon; - } else if (favicon.startsWith("./")) { - favicon = baseUrl + favicon.slice(1); - } - - // Prepare the metadata object - const metadata = { - title, - description, - image: favicon, - baseUrl, - }; - return metadata; + const response = await fetch(url); + const html = await response.text(); + + const $ = cheerio.load(html); + + // Extract the base URL + const baseUrl = url; + + // Extract title + const title = $("title").text().trim(); + + const description = $("meta[name=description]").attr("content") ?? ""; + + const _favicon = + $("link[rel=icon]").attr("href") ?? "https://supermemory.dhr.wtf/web.svg"; + + let favicon = + _favicon.trim().length > 0 + ? _favicon.trim() + : "https://supermemory.dhr.wtf/web.svg"; + if (favicon.startsWith("/")) { + favicon = baseUrl + favicon; + } else if (favicon.startsWith("./")) { + favicon = baseUrl + favicon.slice(1); + } + + // Prepare the metadata object + const metadata = { + title, + description, + image: favicon, + baseUrl, + }; + return metadata; } diff --git a/apps/web/lib/get-theme-button.tsx b/apps/web/lib/get-theme-button.tsx index 020cc976..e504e996 100644 --- a/apps/web/lib/get-theme-button.tsx +++ b/apps/web/lib/get-theme-button.tsx @@ -4,8 +4,8 @@ import dynamic from "next/dynamic"; // Don't SSR the toggle since the value on the server will be different than the client export const getThemeToggler = () => - dynamic(() => import("@repo/ui/shadcn/theme-toggle"), { - ssr: false, - // Make sure to code a placeholder so the UI doesn't jump when the component loads - loading: () => <div className="w-6 h-6" />, - }); + dynamic(() => import("@repo/ui/shadcn/theme-toggle"), { + ssr: false, + // Make sure to code a placeholder so the UI doesn't jump when the component loads + loading: () => <div className="w-6 h-6" />, + }); diff --git a/apps/web/lib/handle-errors.ts b/apps/web/lib/handle-errors.ts index 42cae589..2e207178 100644 --- a/apps/web/lib/handle-errors.ts +++ b/apps/web/lib/handle-errors.ts @@ -3,23 +3,23 @@ import { toast } from "sonner"; import { z } from "zod"; export function getErrorMessage(err: unknown) { - const unknownError = "Something went wrong, please try again later."; + const unknownError = "Something went wrong, please try again later."; - if (err instanceof z.ZodError) { - const errors = err.issues.map((issue) => { - return issue.message; - }); - return errors.join("\n"); - } else if (err instanceof Error) { - return err.message; - } else if (isRedirectError(err)) { - throw err; - } else { - return unknownError; - } + if (err instanceof z.ZodError) { + const errors = err.issues.map((issue) => { + return issue.message; + }); + return errors.join("\n"); + } else if (err instanceof Error) { + return err.message; + } else if (isRedirectError(err)) { + throw err; + } else { + return unknownError; + } } export function showErrorToast(err: unknown) { - const errorMessage = getErrorMessage(err); - return toast.error(errorMessage); + const errorMessage = getErrorMessage(err); + return toast.error(errorMessage); } diff --git a/apps/web/lib/loadSnap.ts b/apps/web/lib/loadSnap.ts index 083603eb..bcf81eca 100644 --- a/apps/web/lib/loadSnap.ts +++ b/apps/web/lib/loadSnap.ts @@ -4,11 +4,11 @@ import { twitterCardUtil } from "../components/canvas/twitterCard"; import { textCardUtil } from "../components/canvas/textCard"; export async function loadRemoteSnapshot(id: string) { - const snapshot = await getCanvasData(id); + const snapshot = await getCanvasData(id); - const newStore = createTLStore({ - shapeUtils: [...defaultShapeUtils, twitterCardUtil, textCardUtil], - }); - loadSnapshot(newStore, snapshot.snapshot); - return newStore; + const newStore = createTLStore({ + shapeUtils: [...defaultShapeUtils, twitterCardUtil, textCardUtil], + }); + loadSnapshot(newStore, snapshot.snapshot); + return newStore; } diff --git a/apps/web/lib/searchParams.ts b/apps/web/lib/searchParams.ts index f3188a6f..2e8b1633 100644 --- a/apps/web/lib/searchParams.ts +++ b/apps/web/lib/searchParams.ts @@ -1,35 +1,35 @@ import { - createSearchParamsCache, - parseAsInteger, - parseAsString, - parseAsBoolean, - parseAsArrayOf, - parseAsJson, + createSearchParamsCache, + parseAsInteger, + parseAsString, + parseAsBoolean, + parseAsArrayOf, + parseAsJson, } from "nuqs/server"; import { z } from "zod"; export const homeSearchParamsCache = createSearchParamsCache({ - firstTime: parseAsBoolean.withDefault(false), + firstTime: parseAsBoolean.withDefault(false), }); export const chatSearchParamsCache = createSearchParamsCache({ - firstTime: parseAsBoolean.withDefault(false), - q: parseAsString.withDefault(""), - spaces: parseAsJson((c) => { - const valid = z - .array( - z.object({ - id: z.number(), - name: z.string(), - }), - ) - .safeParse(c); + firstTime: parseAsBoolean.withDefault(false), + q: parseAsString.withDefault(""), + spaces: parseAsJson((c) => { + const valid = z + .array( + z.object({ + id: z.number(), + name: z.string(), + }), + ) + .safeParse(c); - if (!valid.success) { - console.log("invalid spaces", valid.error); - return null; - } + if (!valid.success) { + console.log("invalid spaces", valid.error); + return null; + } - return valid.data; - }), + return valid.data; + }), }); |