diff --git a/.gitignore b/.gitignore index 6b5f96f5e..ff2137ac6 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ yarn-error.log* # Local development files local/* .cursor/debug.log +apps/tldraw-sync-worker/tsconfig.tsbuildinfo +apps/tldraw-sync-worker/.wrangler/* diff --git a/apps/roam/package.json b/apps/roam/package.json index a3dd53321..18b7acdd9 100644 --- a/apps/roam/package.json +++ b/apps/roam/package.json @@ -47,6 +47,7 @@ "@tldraw/state": "2.4.6", "@tldraw/state-react": "2.4.6", "@tldraw/store": "2.4.6", + "@tldraw/sync": "2.4.6", "@tldraw/tlschema": "2.4.6", "@tldraw/utils": "2.4.6", "@tldraw/validate": "2.4.6", diff --git a/apps/roam/src/components/canvas/Tldraw.tsx b/apps/roam/src/components/canvas/Tldraw.tsx index de96db5a1..a8e70a425 100644 --- a/apps/roam/src/components/canvas/Tldraw.tsx +++ b/apps/roam/src/components/canvas/Tldraw.tsx @@ -1,5 +1,12 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import React, { useState, useRef, useMemo, useEffect, useCallback } from "react"; +import React, { + useState, + useRef, + useMemo, + useEffect, + useCallback, +} from "react"; +import { Icon } from "@blueprintjs/core"; import ExtensionApiContextProvider, { useExtensionAPI, } from "roamjs-components/components/ExtensionApiContext"; @@ -32,6 +39,8 @@ import { getHashForString, TLShapeId, TLShape, + TLStore, + TLStoreWithStatus, useToasts, useTranslation, DEFAULT_SUPPORTED_IMAGE_TYPES, @@ -43,6 +52,9 @@ import { StateNode, DefaultSpinner, Box, + MigrationSequence, + TLAnyBindingUtilConstructor, + TLAnyShapeUtilConstructor, } from "tldraw"; import "tldraw/tldraw.css"; import tldrawStyles from "./tldrawStyles"; @@ -62,6 +74,11 @@ import { createNodeShapeUtils, } from "./DiscourseNodeUtil"; import { useRoamStore } from "./useRoamStore"; +import { + TLDRAW_CLOUDFLARE_SYNC_ENABLED, + TLDRAW_CLOUDFLARE_SYNC_WS_BASE_URL, + useCloudflareSyncStore, +} from "./TldrawCanvasCloudflareSync"; import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle"; import getTextByBlockUid from "roamjs-components/queries/getTextByBlockUid"; import getUids from "roamjs-components/dom/getUids"; @@ -98,6 +115,12 @@ import { TLRecord } from "@tldraw/tlschema"; import { WHITE_LOGO_SVG } from "~/icons"; import { BLOCK_REF_REGEX } from "roamjs-components/dom"; import { defaultHandleExternalTextContent } from "./defaultHandleExternalTextContent"; +import { + CanvasSyncMode, + ensureCanvasSyncMode, + getEffectiveCanvasSyncMode, + setCanvasSyncMode, +} from "./canvasSyncMode"; declare global { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -127,12 +150,219 @@ export const MAX_WIDTH = "400px"; const ICON_URL = `data:image/svg+xml;utf8,${encodeURIComponent(WHITE_LOGO_SVG)}`; +// hack for "cannot change atoms during reaction cycle" bug +const isAtomReactionCycleError = (error: unknown): error is Error => + error instanceof Error && + /cannot change atoms during reaction cycle/i.test(error.message); +const installSafeHintingSetter = ({ app }: { app: Editor }): void => { + const originalSetHintingShapes = app.setHintingShapes.bind(app); + + app.setHintingShapes = ((shapeIds: TLShapeId[]) => { + try { + return originalSetHintingShapes(shapeIds); + } catch (error) { + if (!isAtomReactionCycleError(error)) { + throw error; + } + + app.timers.setTimeout(() => { + try { + originalSetHintingShapes(shapeIds); + } catch { + // Ignore deferred hinting updates if the editor is gone or still reacting. + } + }, 0); + + return app; + } + }) as Editor["setHintingShapes"]; +}; + export const isPageUid = (uid: string) => !!window.roamAlphaAPI.pull("[:node/title]", [":block/uid", uid])?.[ ":node/title" ]; const TldrawCanvas = ({ title }: { title: string }) => { + // In Roam, canvas identity is currently keyed by the page UID. + // Room sync is graph/page encoded as an opaque base64url token. + const pageUid = useMemo(() => getPageUidByPageTitle(title), [title]); + const [canvasSyncMode, setCanvasSyncModeState] = useState( + () => getEffectiveCanvasSyncMode({ pageUid }), + ); + const isCloudflareSyncAvailable = + TLDRAW_CLOUDFLARE_SYNC_ENABLED && !!TLDRAW_CLOUDFLARE_SYNC_WS_BASE_URL; + const useCloudflareSync = + canvasSyncMode === "sync" && isCloudflareSyncAvailable; + + useEffect(() => { + setCanvasSyncModeState(ensureCanvasSyncMode({ pageUid })); + }, [pageUid]); + + const onCanvasSyncModeChange = useCallback( + (mode: CanvasSyncMode) => { + setCanvasSyncMode({ pageUid, mode }); + setCanvasSyncModeState(mode); + }, + [pageUid], + ); + + if (useCloudflareSync) { + return ( + + ); + } + + return ( + + ); +}; + +type CanvasStoreAdapterArgs = { + pageUid: string; + migrations: MigrationSequence[]; + customShapeUtils: readonly TLAnyShapeUtilConstructor[]; + customBindingUtils: readonly TLAnyBindingUtilConstructor[]; + customShapeTypes: string[]; + customBindingTypes: string[]; +}; + +type CanvasStoreAdapterResult = { + store: TLStore | TLStoreWithStatus | null; + needsUpgrade: boolean; + performUpgrade: () => void; + error: Error | null; + isLoading: boolean; +}; + +const useRoamCanvasStore = ({ + pageUid, + migrations, + customShapeUtils, + customBindingUtils, +}: CanvasStoreAdapterArgs): CanvasStoreAdapterResult => { + const { store, needsUpgrade, performUpgrade, error } = useRoamStore({ + migrations, + customShapeUtils, + customBindingUtils, + pageUid, + }); + + return { + store, + needsUpgrade, + performUpgrade, + error, + isLoading: false, + }; +}; + +const useCloudflareCanvasStore = ({ + pageUid, + migrations, + customShapeUtils, + customBindingUtils, + customShapeTypes, + customBindingTypes, +}: CanvasStoreAdapterArgs): CanvasStoreAdapterResult => { + const { store, error, isLoading } = useCloudflareSyncStore({ + pageUid, + migrations, + customShapeUtils, + customBindingUtils, + customShapeTypes, + customBindingTypes, + }); + return { + store, + error, + isLoading, + needsUpgrade: false, + performUpgrade: () => {}, + }; +}; + +const TldrawCanvasRoam = ({ + title, + pageUid, + canvasSyncMode, + isCloudflareSyncAvailable, + onCanvasSyncModeChange, +}: { + title: string; + pageUid: string; + canvasSyncMode: CanvasSyncMode; + isCloudflareSyncAvailable: boolean; + onCanvasSyncModeChange: (mode: CanvasSyncMode) => void; +}) => { + return ( + + ); +}; + +const TldrawCanvasCloudflare = ({ + title, + pageUid, + canvasSyncMode, + isCloudflareSyncAvailable, + onCanvasSyncModeChange, +}: { + title: string; + pageUid: string; + canvasSyncMode: CanvasSyncMode; + isCloudflareSyncAvailable: boolean; + onCanvasSyncModeChange: (mode: CanvasSyncMode) => void; +}) => { + return ( + + ); +}; + +const TldrawCanvasShared = ({ + title, + pageUid, + useStoreAdapter, + isCloudflareSync, + canvasSyncMode, + isCloudflareSyncAvailable, + onCanvasSyncModeChange, +}: { + title: string; + pageUid: string; + useStoreAdapter: (args: CanvasStoreAdapterArgs) => CanvasStoreAdapterResult; + isCloudflareSync: boolean; + canvasSyncMode: CanvasSyncMode; + isCloudflareSyncAvailable: boolean; + onCanvasSyncModeChange: (mode: CanvasSyncMode) => void; +}) => { const appRef = useRef(null); const lastInsertRef = useRef(); const containerRef = useRef(null); @@ -141,8 +371,10 @@ const TldrawCanvas = ({ title }: { title: string }) => { ); const [isConvertToDialogOpen, setConvertToDialogOpen] = useState(false); + // hack for "cannot change atoms during reaction cycle" bug + const [hasMountedEditor, setHasMountedEditor] = useState(false); - const updateViewportScreenBounds = (el: HTMLDivElement) => { + const updateViewportScreenBounds = useCallback((el: HTMLDivElement) => { // Use tldraw's built-in viewport bounds update with centering requestAnimationFrame(() => { const rect = el.getBoundingClientRect(); @@ -151,8 +383,8 @@ const TldrawCanvas = ({ title }: { title: string }) => { true, ); }); - }; - const handleMaximizedChange = () => { + }, []); + const handleMaximizedChange = useCallback(() => { // Direct DOM manipulation to avoid React re-renders if (!containerRef.current) return; const tldrawEl = containerRef.current; @@ -171,7 +403,7 @@ const TldrawCanvas = ({ title }: { title: string }) => { tldrawEl.classList.remove("absolute", "inset-0"); updateViewportScreenBounds(tldrawEl); } - }; + }, [updateViewportScreenBounds]); // Workaround to avoid a race condition when loading a canvas page directly // Start false to avoid noisy warnings on first render if timer isn't initialized yet @@ -427,71 +659,150 @@ const TldrawCanvas = ({ title }: { title: string }) => { }; // COMPONENTS - const defaultEditorComponents: TLEditorComponents = { - Scribble: TldrawScribble, - CollaboratorScribble: TldrawScribble, - SelectionForeground: TldrawSelectionForeground, - SelectionBackground: TldrawSelectionBackground, - Handles: TldrawHandles, - }; - const editorComponents: TLEditorComponents = { - ...defaultEditorComponents, - OnTheCanvas: ToastListener, - }; - const customUiComponents: TLUiComponents = createUiComponents({ - allNodes, - allRelationNames, - allAddReferencedNodeActions, - }); + const defaultEditorComponents: TLEditorComponents = useMemo( + () => ({ + Scribble: TldrawScribble, + CollaboratorScribble: TldrawScribble, + SelectionForeground: TldrawSelectionForeground, + SelectionBackground: TldrawSelectionBackground, + Handles: TldrawHandles, + }), + [], + ); + const editorComponents: TLEditorComponents = useMemo( + () => ({ + ...defaultEditorComponents, + OnTheCanvas: ToastListener, + }), + [defaultEditorComponents], + ); + const customUiComponents: TLUiComponents = useMemo( + () => + createUiComponents({ + allNodes, + allRelationNames, + allAddReferencedNodeActions, + canvasSyncMode, + isCloudflareSyncAvailable, + onCanvasSyncModeChange, + }), + [ + allNodes, + allRelationNames, + allAddReferencedNodeActions, + canvasSyncMode, + isCloudflareSyncAvailable, + onCanvasSyncModeChange, + ], + ); // UTILS - const discourseNodeUtils = createNodeShapeUtils(allNodes); - const discourseRelationUtils = createAllRelationShapeUtils(allRelationIds); - const referencedNodeUtils = createAllReferencedNodeUtils( - allAddReferencedNodeByAction, + const discourseNodeUtils = useMemo( + () => createNodeShapeUtils(allNodes), + [allNodes], + ); + const discourseRelationUtils = useMemo( + () => createAllRelationShapeUtils(allRelationIds), + [allRelationIds], + ); + const referencedNodeUtils = useMemo( + () => createAllReferencedNodeUtils(allAddReferencedNodeByAction), + [allAddReferencedNodeByAction], + ); + const customShapeUtils = useMemo( + () => [ + ...discourseNodeUtils, + ...discourseRelationUtils, + ...referencedNodeUtils, + ], + [discourseNodeUtils, discourseRelationUtils, referencedNodeUtils], ); - const customShapeUtils = [ - ...discourseNodeUtils, - ...discourseRelationUtils, - ...referencedNodeUtils, - ]; // TOOLS - const discourseGraphTool = class DiscourseGraphTool extends StateNode { - static override id = "discourse-tool"; - static override initial = "idle"; - }; - const discourseNodeTools = createNodeShapeTools(allNodes); - const discourseRelationTools = createAllRelationShapeTools(allRelationNames); - const referencedNodeTools = createAllReferencedNodeTools( - allAddReferencedNodeByAction, + const discourseGraphTool = useMemo( + () => + class DiscourseGraphTool extends StateNode { + static override id = "discourse-tool"; + static override initial = "idle"; + }, + [], + ); + const discourseNodeTools = useMemo( + () => createNodeShapeTools(allNodes), + [allNodes], + ); + const discourseRelationTools = useMemo( + () => createAllRelationShapeTools(allRelationNames), + [allRelationNames], + ); + const referencedNodeTools = useMemo( + () => createAllReferencedNodeTools(allAddReferencedNodeByAction), + [allAddReferencedNodeByAction], + ); + const customTools = useMemo( + () => [ + discourseGraphTool, + ...discourseNodeTools, + ...discourseRelationTools, + ...referencedNodeTools, + ], + [ + discourseGraphTool, + discourseNodeTools, + discourseRelationTools, + referencedNodeTools, + ], ); - const customTools = [ - discourseGraphTool, - ...discourseNodeTools, - ...discourseRelationTools, - ...referencedNodeTools, - ]; // BINDINGS - const relationBindings = createAllRelationBindings(allRelationIds); - const referencedNodeBindings = createAllReferencedNodeBindings( - allAddReferencedNodeByAction, + const relationBindings = useMemo( + () => createAllRelationBindings(allRelationIds), + [allRelationIds], + ); + const referencedNodeBindings = useMemo( + () => createAllReferencedNodeBindings(allAddReferencedNodeByAction), + [allAddReferencedNodeByAction], + ); + const customBindingUtils = useMemo( + () => [...relationBindings, ...referencedNodeBindings], + [relationBindings, referencedNodeBindings], + ); + const customShapeTypes = useMemo( + () => + customShapeUtils + .map((s) => (s as unknown as { type?: string }).type) + .filter((t): t is string => !!t), + [customShapeUtils], + ); + const customBindingTypes = useMemo( + () => + customBindingUtils + .map((b) => (b as unknown as { type?: string }).type) + .filter((t): t is string => !!t), + [customBindingUtils], ); - const customBindingUtils = [...relationBindings, ...referencedNodeBindings]; // UI OVERRIDES - const uiOverrides = createUiOverrides({ - allNodes, - allRelationNames, - allAddReferencedNodeByAction, - toggleMaximized: handleMaximizedChange, - setConvertToDialogOpen, - discourseContext, - }); + const uiOverrides = useMemo( + () => + createUiOverrides({ + allNodes, + allRelationNames, + allAddReferencedNodeByAction, + toggleMaximized: handleMaximizedChange, + setConvertToDialogOpen, + discourseContext, + }), + [ + allNodes, + allRelationNames, + allAddReferencedNodeByAction, + handleMaximizedChange, + setConvertToDialogOpen, + ], + ); // STORE - const pageUid = useMemo(() => getPageUidByPageTitle(title), [title]); const arrowShapeMigrations = useMemo( () => createMigrations({ @@ -502,13 +813,19 @@ const TldrawCanvas = ({ title }: { title: string }) => { [allRelationIds, allAddReferencedNodeActions, allNodes], ); - const migrations = [arrowShapeMigrations]; - const { store, needsUpgrade, performUpgrade, error } = useRoamStore({ - migrations, - customShapeUtils, - customBindingUtils, - pageUid, - }); + const migrations = useMemo( + () => [arrowShapeMigrations], + [arrowShapeMigrations], + ); + const { store, needsUpgrade, performUpgrade, error, isLoading } = + useStoreAdapter({ + migrations, + customShapeUtils, + customBindingUtils, + customShapeTypes, + customBindingTypes, + pageUid, + }); // ASSETS const assetLoading = usePreloadAssets(defaultEditorAssetUrls); @@ -597,6 +914,53 @@ const TldrawCanvas = ({ title }: { title: string }) => { }; }, [title]); + const lastReportedStoreErrorRef = useRef(""); + useEffect(() => { + if (!error) return; + const errorKey = `${pageUid}:${error.message}`; + if (lastReportedStoreErrorRef.current === errorKey) return; + lastReportedStoreErrorRef.current = errorKey; + + const isInvalidRecord = /invalidRecord/i.test(error.message); + console.error("[DG Canvas] Store error", { + title, + pageUid, + message: error.message, + stack: error.stack, + ...(isInvalidRecord + ? { + hint: "Cloudflare sync worker schema is rejecting one or more custom records.", + customShapeTypes, + customBindingTypes, + } + : {}), + }); + internalError({ + error, + type: "Canvas Store Error", + context: { + title, + pageUid, + ...(isInvalidRecord + ? { + customShapeTypes, + customBindingTypes, + } + : {}), + }, + }); + }, [error, pageUid, title, customShapeTypes, customBindingTypes]); + + // Keep the mounted editor alive through transient reconnect errors, + // but still surface errors when the store is unavailable. + const blockingStoreError = error && (!hasMountedEditor || !store) ? error : null; + const isCanvasBlocked = + !store || + !assetLoading.done || + !extensionAPI || + !isPluginReady || + (!hasMountedEditor && (isLoading || !!error)); + return (
{ onDragOver={handleDragOver} onDrop={handleDrop} > + {isCloudflareSync && ( +
+ +
+ )} {needsUpgrade ? ( @@ -632,17 +1004,24 @@ const TldrawCanvas = ({ title }: { title: string }) => {
- ) : !store || !assetLoading.done || !extensionAPI || !isPluginReady ? ( + ) : isCanvasBlocked ? (

- {error || assetLoading.error + {blockingStoreError || assetLoading.error ? "Error Loading Canvas" : "Loading Canvas"}

- {error || assetLoading.error ? ( - "There was a problem loading the Tldraw canvas. Please try again later." + {blockingStoreError || assetLoading.error ? ( + + {blockingStoreError?.message?.includes("invalidRecord") + ? "Cloudflare sync rejected a custom Discourse Graph record (invalidRecord). The sync worker schema must include DG custom shapes and bindings." + : "There was a problem loading the Tldraw canvas."}{" "} + {blockingStoreError?.message + ? `Details: ${blockingStoreError.message}` + : ""} + ) : ( )} @@ -676,6 +1055,10 @@ const TldrawCanvas = ({ title }: { title: string }) => { appRef.current = app; + // hack for "cannot change atoms during reaction cycle" bug + installSafeHintingSetter({ app }); + setHasMountedEditor(true); + app.on("change", (entry) => { lastActionsRef.current.push(entry); if (lastActionsRef.current.length > 5) @@ -761,7 +1144,7 @@ const TldrawCanvas = ({ title }: { title: string }) => { // apps\examples\src\examples\exploded\ExplodedExample.tsx // We put these hooks into a component here so that they can run inside of the context provided by TldrawEditor and TldrawUi -const InsideEditorAndUiContext = ({ +export const InsideEditorAndUiContext = ({ extensionAPI, allNodes, // allRelationIds, diff --git a/apps/roam/src/components/canvas/TldrawCanvasCloudflareSync.tsx b/apps/roam/src/components/canvas/TldrawCanvasCloudflareSync.tsx new file mode 100644 index 000000000..53a6b7ef9 --- /dev/null +++ b/apps/roam/src/components/canvas/TldrawCanvasCloudflareSync.tsx @@ -0,0 +1,105 @@ +import { useSync } from "@tldraw/sync"; +import { + TLAnyBindingUtilConstructor, + TLAnyShapeUtilConstructor, + TLAssetStore, + TLStoreWithStatus, + defaultBindingUtils, + defaultShapeUtils, + MigrationSequence, +} from "tldraw"; +import { useMemo } from "react"; + +/** When true, newly created canvases (no Roam-persisted state) use tldraw sync via Cloudflare. PoC only. */ +export const TLDRAW_CLOUDFLARE_SYNC_ENABLED = true; +/** Base URL for tldraw-sync-cloudflare worker. Use https (not wss) - useSync upgrades to WebSocket. */ +export const TLDRAW_CLOUDFLARE_SYNC_WS_BASE_URL = + "https://multiplayer-dg-sync.discoursegraphs.workers.dev"; + +export type CloudflareCanvasStoreAdapterResult = { + store: TLStoreWithStatus; + error: Error | null; + isLoading: boolean; +}; + +export const getSyncRoomId = ({ pageUid }: { pageUid: string }): string => { + const graphName = window.roamAlphaAPI.graph.name; + const payload = JSON.stringify({ graphName, pageUid }); + const bytes = new TextEncoder().encode(payload); + let binary = ""; + for (const byte of bytes) { + binary += String.fromCharCode(byte); + } + + return btoa(binary) + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/g, ""); +}; + +const parseRoamUploadResponse = (value: string): string => { + return value.replace(/^!\[\]\(/, "").replace(/\)$/, ""); +}; + +const createRoamAssetStore = (): TLAssetStore => { + return { + // eslint-disable-next-line @typescript-eslint/naming-convention + upload: async (_asset, file) => { + const response = await window.roamAlphaAPI.file.upload({ file }); + return parseRoamUploadResponse(response); + }, + resolve: (asset) => asset.props.src, + }; +}; + +export const useCloudflareSyncStore = ({ + pageUid, + migrations, + customShapeUtils, + customBindingUtils, + customShapeTypes, + customBindingTypes, +}: { + pageUid: string; + migrations: MigrationSequence[]; + customShapeUtils: readonly TLAnyShapeUtilConstructor[]; + customBindingUtils: readonly TLAnyBindingUtilConstructor[]; + customShapeTypes: string[]; + customBindingTypes: string[]; +}): CloudflareCanvasStoreAdapterResult => { + const assets = useMemo(() => createRoamAssetStore(), []); + const shapeUtils = useMemo( + () => [...defaultShapeUtils, ...customShapeUtils], + [customShapeUtils], + ); + const bindingUtils = useMemo( + () => [...defaultBindingUtils, ...customBindingUtils], + [customBindingUtils], + ); + + const uri = useMemo(() => { + const roomId = getSyncRoomId({ pageUid }); + const query = new URLSearchParams(); + for (const shapeType of customShapeTypes) { + query.append("shapeType", shapeType); + } + for (const bindingType of customBindingTypes) { + query.append("bindingType", bindingType); + } + return `${TLDRAW_CLOUDFLARE_SYNC_WS_BASE_URL}/connect/${roomId}?${query.toString()}`; + }, [customShapeTypes, customBindingTypes, pageUid]); + + const store = useSync({ + uri, + assets, + migrations, + shapeUtils, + bindingUtils, + }); + + return { + store, + error: store.status === "error" ? store.error : null, + isLoading: store.status === "loading", + }; +}; diff --git a/apps/roam/src/components/canvas/canvasSyncMode.ts b/apps/roam/src/components/canvas/canvasSyncMode.ts new file mode 100644 index 000000000..5543e201a --- /dev/null +++ b/apps/roam/src/components/canvas/canvasSyncMode.ts @@ -0,0 +1,67 @@ +import getBlockProps, { json } from "~/utils/getBlockProps"; +import setBlockProps from "~/utils/setBlockProps"; + +export type CanvasSyncMode = "local" | "sync"; + +const QUERY_BUILDER_PROP_KEY = "roamjs-query-builder"; +const CANVAS_SYNC_MODE_KEY = "canvasSyncMode"; +const DEFAULT_CANVAS_SYNC_MODE: CanvasSyncMode = "local"; + +const isCanvasSyncMode = (value: unknown): value is CanvasSyncMode => + value === "local" || value === "sync"; + +const getRoamJsQueryBuilderProps = ( + pageUid: string, +): Record => { + const props = getBlockProps(pageUid); + const value = props[QUERY_BUILDER_PROP_KEY]; + if (value && typeof value === "object" && !Array.isArray(value)) { + return value; + } + return {}; +}; + +export const getPersistedCanvasSyncMode = ({ + pageUid, +}: { + pageUid: string; +}): CanvasSyncMode | null => { + const rjsqb = getRoamJsQueryBuilderProps(pageUid); + const mode = rjsqb[CANVAS_SYNC_MODE_KEY]; + return isCanvasSyncMode(mode) ? mode : null; +}; + +export const getEffectiveCanvasSyncMode = ({ + pageUid, +}: { + pageUid: string; +}): CanvasSyncMode => { + return getPersistedCanvasSyncMode({ pageUid }) ?? DEFAULT_CANVAS_SYNC_MODE; +}; + +export const setCanvasSyncMode = ({ + pageUid, + mode, +}: { + pageUid: string; + mode: CanvasSyncMode; +}): void => { + const rjsqb = getRoamJsQueryBuilderProps(pageUid); + setBlockProps(pageUid, { + [QUERY_BUILDER_PROP_KEY]: { + ...rjsqb, + [CANVAS_SYNC_MODE_KEY]: mode, + }, + }); +}; + +export const ensureCanvasSyncMode = ({ + pageUid, +}: { + pageUid: string; +}): CanvasSyncMode => { + const mode = getPersistedCanvasSyncMode({ pageUid }); + if (mode) return mode; + setCanvasSyncMode({ pageUid, mode: DEFAULT_CANVAS_SYNC_MODE }); + return DEFAULT_CANVAS_SYNC_MODE; +}; diff --git a/apps/roam/src/components/canvas/uiOverrides.tsx b/apps/roam/src/components/canvas/uiOverrides.tsx index c3aafa80e..f69866065 100644 --- a/apps/roam/src/components/canvas/uiOverrides.tsx +++ b/apps/roam/src/components/canvas/uiOverrides.tsx @@ -15,19 +15,17 @@ import { DefaultToolbarContent, TldrawUiMenuItem, DefaultMainMenu, + DefaultMainMenuContent, TldrawUiMenuGroup, + TldrawUiDropdownMenuItem, + TldrawUiButton, + TldrawUiButtonLabel, + TldrawUiIcon, useActions, DefaultContextMenu, DefaultContextMenuContent, TLUiComponents, - EditSubmenu, - ExportFileContentSubMenu, - ExtrasGroup, - PreferencesGroup, TldrawUiMenuSubmenu, - ZoomTo100MenuItem, - ZoomToFitMenuItem, - ZoomToSelectionMenuItem, useEditor, useValue, useToasts, @@ -46,6 +44,41 @@ import { DISCOURSE_TOOL_SHORTCUT_KEY } from "~/data/userSettings"; import { getSetting } from "~/utils/extensionSettings"; import { CustomDefaultToolbar } from "./CustomDefaultToolbar"; import { renderModifyNodeDialog } from "~/components/ModifyNodeDialog"; +import { CanvasSyncMode } from "./canvasSyncMode"; + +const SyncModeMenuSwitchItem = ({ + checked, + disabled, + label, + onToggle, +}: { + checked: boolean; + disabled: boolean; + label: string; + onToggle: () => void; +}): React.ReactElement => { + return ( + + + {label} + + + + + + ); +}; export const getOnSelectForShape = ({ shape, @@ -185,10 +218,16 @@ export const createUiComponents = ({ allNodes, allAddReferencedNodeActions, allRelationNames, + canvasSyncMode, + isCloudflareSyncAvailable, + onCanvasSyncModeChange, }: { allNodes: DiscourseNode[]; allRelationNames: string[]; allAddReferencedNodeActions: string[]; + canvasSyncMode: CanvasSyncMode; + isCloudflareSyncAvailable: boolean; + onCanvasSyncModeChange: (mode: CanvasSyncMode) => void; }): TLUiComponents => { return { Toolbar: (props) => { @@ -219,29 +258,26 @@ export const createUiComponents = ({ ); }, MainMenu: () => { - const CustomViewMenu = () => { - const actions = useActions(); - return ( - - - - - - - - - - - ); + const onToggleSyncMode = (): void => { + const nextMode: CanvasSyncMode = + canvasSyncMode === "sync" ? "local" : "sync"; + onCanvasSyncModeChange(nextMode); }; + const syncModeLabel = isCloudflareSyncAvailable + ? "Use cloud canvas" + : "Cloud canvas unavailable"; return ( - - {/* Replaced */} - - - + + + + ); }, diff --git a/apps/tldraw-sync-worker/package.json b/apps/tldraw-sync-worker/package.json new file mode 100644 index 000000000..e7c9a0d64 --- /dev/null +++ b/apps/tldraw-sync-worker/package.json @@ -0,0 +1,25 @@ +{ + "name": "@repo/tldraw-sync-worker", + "private": true, + "version": "0.1.0", + "license": "Apache-2.0", + "type": "module", + "scripts": { + "dev": "wrangler dev", + "deploy": "wrangler deploy", + "check-types": "tsc --noEmit -p tsconfig.json" + }, + "dependencies": { + "@tldraw/sync-core": "2.4.6", + "@tldraw/tlschema": "2.4.6", + "cloudflare-workers-unfurl": "^0.0.7", + "itty-router": "^5.0.17", + "lodash.throttle": "^4.1.1" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20240208.0", + "@types/lodash.throttle": "^4", + "typescript": "^5.0.2", + "wrangler": "^3.64.0" + } +} diff --git a/apps/tldraw-sync-worker/tsconfig.json b/apps/tldraw-sync-worker/tsconfig.json new file mode 100644 index 000000000..5b14916fe --- /dev/null +++ b/apps/tldraw-sync-worker/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "lib": ["ES2020"], + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "types": ["@cloudflare/workers-types"], + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["worker"] +} diff --git a/apps/tldraw-sync-worker/worker/TldrawDurableObject.ts b/apps/tldraw-sync-worker/worker/TldrawDurableObject.ts new file mode 100644 index 000000000..a80c240e7 --- /dev/null +++ b/apps/tldraw-sync-worker/worker/TldrawDurableObject.ts @@ -0,0 +1,202 @@ +import { RoomSnapshot, TLSocketRoom } from '@tldraw/sync-core' +import { + TLRecord, + createTLSchema, + defaultBindingSchemas, + defaultShapeSchemas, +} from '@tldraw/tlschema' +import { AutoRouter, IRequest, error } from 'itty-router' +import throttle from 'lodash.throttle' +import { Environment } from './types' + +type RoomSchemaConfig = { + shapeTypes: string[] + bindingTypes: string[] +} + +const STORAGE_SCHEMA_CONFIG_KEY = 'schemaConfig' + +const createRoomSchema = ({ shapeTypes, bindingTypes }: RoomSchemaConfig) => { + const customShapeSchemas = Object.fromEntries(shapeTypes.map((type) => [type, {}])) + const customBindingSchemas = Object.fromEntries(bindingTypes.map((type) => [type, {}])) + + return createTLSchema({ + shapes: { + ...defaultShapeSchemas, + ...customShapeSchemas, + }, + bindings: { + ...defaultBindingSchemas, + ...customBindingSchemas, + }, + }) +} + +const dedupeAndSort = (values: string[]): string[] => { + return Array.from(new Set(values.filter(Boolean))).sort((a, b) => + a.localeCompare(b) + ) +} + +const mergeSchemaConfig = ( + baseConfig: RoomSchemaConfig, + incomingConfig: RoomSchemaConfig +): RoomSchemaConfig => ({ + shapeTypes: dedupeAndSort(baseConfig.shapeTypes.concat(incomingConfig.shapeTypes)), + bindingTypes: dedupeAndSort(baseConfig.bindingTypes.concat(incomingConfig.bindingTypes)), +}) + +const isSameSchemaConfig = (a: RoomSchemaConfig, b: RoomSchemaConfig): boolean => { + return ( + a.shapeTypes.length === b.shapeTypes.length && + a.bindingTypes.length === b.bindingTypes.length && + a.shapeTypes.every((value, index) => value === b.shapeTypes[index]) && + a.bindingTypes.every((value, index) => value === b.bindingTypes[index]) + ) +} + +// each whiteboard room is hosted in a DurableObject: +// https://developers.cloudflare.com/durable-objects/ + +// there's only ever one durable object instance per room. it keeps all the room state in memory and +// handles websocket connections. periodically, it persists the room state to the R2 bucket. +export class TldrawDurableObject { + private r2: R2Bucket + // the room ID will be missing whilst the room is being initialized + private roomId: string | null = null + private roomSchemaConfig: RoomSchemaConfig = { shapeTypes: [], bindingTypes: [] } + // when we load the room from the R2 bucket, we keep it here. it's a promise so we only ever + // load it once. + private roomPromise: Promise> | null = null + + constructor( + private readonly ctx: DurableObjectState, + env: Environment + ) { + this.r2 = env.TLDRAW_BUCKET + + ctx.blockConcurrencyWhile(async () => { + this.roomId = ((await this.ctx.storage.get('roomId')) ?? null) as string | null + const schemaConfig = + ((await this.ctx.storage.get(STORAGE_SCHEMA_CONFIG_KEY)) as RoomSchemaConfig) ?? + null + if (schemaConfig) { + this.roomSchemaConfig = { + shapeTypes: dedupeAndSort(schemaConfig.shapeTypes ?? []), + bindingTypes: dedupeAndSort(schemaConfig.bindingTypes ?? []), + } + } + }) + } + + private readonly router = AutoRouter({ + catch: (e) => { + console.log(e) + return error(e) + }, + }) + // when we get a connection request, we stash the room id if needed and handle the connection + .get('/connect/:roomId', async (request) => { + if (!this.roomId) { + await this.ctx.blockConcurrencyWhile(async () => { + await this.ctx.storage.put('roomId', request.params.roomId) + this.roomId = request.params.roomId + }) + } + return this.handleConnect(request) + }) + + // `fetch` is the entry point for all requests to the Durable Object + fetch(request: Request): Response | Promise { + return this.router.fetch(request) + } + + // what happens when someone tries to connect to this room? + async handleConnect(request: IRequest): Promise { + // extract query params from request + const sessionId = request.query.sessionId as string + if (!sessionId) return error(400, 'Missing sessionId') + const incomingSchemaConfig = this.getIncomingSchemaConfig(request) + await this.ensureSchemaConfig(incomingSchemaConfig) + + // Create the websocket pair for the client + const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair() + serverWebSocket.accept() + + // load the room, or retrieve it if it's already loaded + const room = await this.getRoom() + + // connect the client to the room + room.handleSocketConnect({ sessionId, socket: serverWebSocket }) + + // return the websocket connection to the client + return new Response(null, { status: 101, webSocket: clientWebSocket }) + } + + private getIncomingSchemaConfig(request: IRequest): RoomSchemaConfig { + const url = new URL(request.url) + return { + shapeTypes: dedupeAndSort(url.searchParams.getAll('shapeType')), + bindingTypes: dedupeAndSort(url.searchParams.getAll('bindingType')), + } + } + + private async ensureSchemaConfig(incomingSchemaConfig: RoomSchemaConfig): Promise { + const nextConfig = mergeSchemaConfig(this.roomSchemaConfig, incomingSchemaConfig) + if (isSameSchemaConfig(this.roomSchemaConfig, nextConfig)) return + + this.roomSchemaConfig = nextConfig + await this.ctx.storage.put(STORAGE_SCHEMA_CONFIG_KEY, this.roomSchemaConfig) + + if (this.roomPromise) { + const previousRoom = await this.roomPromise + const snapshot = previousRoom.getCurrentSnapshot() + previousRoom.close() + this.roomPromise = Promise.resolve(this.createRoom(snapshot)) + } + } + + private createRoom(initialSnapshot?: RoomSnapshot): TLSocketRoom { + return new TLSocketRoom({ + schema: createRoomSchema(this.roomSchemaConfig), + initialSnapshot, + onDataChange: () => { + // and persist whenever the data in the room changes + this.schedulePersistToR2() + }, + }) + } + + getRoom() { + const roomId = this.roomId + if (!roomId) throw new Error('Missing roomId') + + if (!this.roomPromise) { + this.roomPromise = (async () => { + // fetch the room from R2 + const roomFromBucket = await this.r2.get(`rooms/${roomId}`) + + // if it doesn't exist, we'll just create a new empty room + const initialSnapshot = roomFromBucket + ? ((await roomFromBucket.json()) as RoomSnapshot) + : undefined + + // create a new TLSocketRoom. This handles all the sync protocol & websocket connections. + // it's up to us to persist the room state to R2 when needed though. + return this.createRoom(initialSnapshot) + })() + } + + return this.roomPromise + } + + // we throttle persistance so it only happens every 10 seconds + schedulePersistToR2: () => void = throttle(async () => { + if (!this.roomPromise || !this.roomId) return + const room = await this.getRoom() + + // convert the room to JSON and upload it to R2 + const snapshot = JSON.stringify(room.getCurrentSnapshot()) + await this.r2.put(`rooms/${this.roomId}`, snapshot) + }, 10_000) +} diff --git a/apps/tldraw-sync-worker/worker/types.ts b/apps/tldraw-sync-worker/worker/types.ts new file mode 100644 index 000000000..fb063e660 --- /dev/null +++ b/apps/tldraw-sync-worker/worker/types.ts @@ -0,0 +1,6 @@ +// the contents of the environment should mostly be determined by wrangler.toml. These entries match +// the bindings defined there. +export interface Environment { + TLDRAW_BUCKET: R2Bucket + TLDRAW_DURABLE_OBJECT: DurableObjectNamespace +} diff --git a/apps/tldraw-sync-worker/worker/worker.ts b/apps/tldraw-sync-worker/worker/worker.ts new file mode 100644 index 000000000..c51bdb043 --- /dev/null +++ b/apps/tldraw-sync-worker/worker/worker.ts @@ -0,0 +1,95 @@ +import { handleUnfurlRequest } from "cloudflare-workers-unfurl"; +import { AutoRouter, error, IRequest } from "itty-router"; +import { Environment } from "./types"; + +// make sure our sync durable object is made available to cloudflare +export { TldrawDurableObject } from "./TldrawDurableObject"; + +const ALLOWED_ORIGINS = [ + "https://roamresearch.com", + "http://localhost:3000", + "app://obsidian.md", +]; + +const isVercelPreviewUrl = (origin: string): boolean => + /^https:\/\/.*-discourse-graph-[a-z0-9]+\.vercel\.app$/.test(origin); + +const isAllowedOrigin = (origin: string): boolean => + ALLOWED_ORIGINS.includes(origin) || + ALLOWED_ORIGINS.some((allowedOrigin) => origin.startsWith(allowedOrigin)) || + isVercelPreviewUrl(origin); + +const setCorsHeaders = ({ + request, + response, +}: { + request: IRequest; + response: Response; +}): Response => { + if (response.status === 101) return response; // WebSocket upgrade response; headers are immutable here, so skip CORS header mutation. + + const origin = request.headers.get("origin"); + if (origin && isAllowedOrigin(origin)) { + response.headers.set("Access-Control-Allow-Origin", origin); + response.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + response.headers.set( + "Access-Control-Allow-Headers", + "Content-Type, Authorization, x-vercel-protection-bypass", + ); + } + return response; +}; + +const handlePreflight = (request: IRequest): Response => { + const origin = request.headers.get("origin"); + if (!origin || !isAllowedOrigin(origin)) { + return error(403, "Origin not allowed"); + } + + return new Response(null, { + status: 204, + headers: { + "Access-Control-Allow-Origin": origin, + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": + "Content-Type, Authorization, x-vercel-protection-bypass", + "Access-Control-Max-Age": "86400", + }, + }); +}; + +const enforceAllowedOrigin = (request: IRequest): Response | void => { + if (request.method === "OPTIONS") return; + const origin = request.headers.get("origin"); + if (origin && !isAllowedOrigin(origin)) { + return error(403, "Origin not allowed"); + } +}; + +const router = AutoRouter({ + before: [enforceAllowedOrigin], + catch: (e) => { + console.error(e); + return error(e); + }, +}) + .options("*", handlePreflight) + // requests to /connect are routed to the Durable Object, and handle realtime websocket syncing + .get("/connect/:roomId", async (request, env) => { + const id = env.TLDRAW_DURABLE_OBJECT.idFromName(request.params.roomId); + const room = env.TLDRAW_DURABLE_OBJECT.get(id); + const response = await room.fetch(request.url, { + headers: request.headers, + body: request.body, + }); + return setCorsHeaders({ request, response }); + }) + + // bookmarks need to extract metadata from pasted URLs: + .get("/unfurl", async (request) => { + const response = await handleUnfurlRequest(request); + return setCorsHeaders({ request, response }); + }); + +// export our router for cloudflare +export default router; diff --git a/apps/tldraw-sync-worker/wrangler.toml b/apps/tldraw-sync-worker/wrangler.toml new file mode 100644 index 000000000..a7dd25c0a --- /dev/null +++ b/apps/tldraw-sync-worker/wrangler.toml @@ -0,0 +1,24 @@ +name = "multiplayer-dg-sync" +main = "worker/worker.ts" +compatibility_date = "2024-07-01" + +[dev] +port = 5172 +ip = "0.0.0.0" + +# Set up the durable object used for each tldraw room +[durable_objects] +bindings = [ + { name = "TLDRAW_DURABLE_OBJECT", class_name = "TldrawDurableObject" }, +] + +# Durable objects require migrations to create/modify/delete them +[[migrations]] +tag = "v1" +new_sqlite_classes = ["TldrawDurableObject"] + +# We store room snapshots (assest are stored in native apps) in an R2 bucket +[[r2_buckets]] +binding = 'TLDRAW_BUCKET' +bucket_name = 'tldraw-content' +preview_bucket_name = 'tldraw-content-preview' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a715ce10d..c946e7237 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,6 +236,9 @@ importers: '@tldraw/store': specifier: 2.4.6 version: 2.4.6(react@18.2.0) + '@tldraw/sync': + specifier: 2.4.6 + version: 2.4.6(@types/react-dom@18.2.17)(@types/react@18.2.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@tldraw/tlschema': specifier: 2.4.6 version: 2.4.6(react@18.2.0) @@ -367,6 +370,37 @@ importers: specifier: ^4.19.2 version: 4.20.5 + apps/tldraw-sync-worker: + dependencies: + '@tldraw/sync-core': + specifier: 2.4.6 + version: 2.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tldraw/tlschema': + specifier: 2.4.6 + version: 2.4.6(react@19.1.1) + cloudflare-workers-unfurl: + specifier: ^0.0.7 + version: 0.0.7 + itty-router: + specifier: ^5.0.17 + version: 5.0.22 + lodash.throttle: + specifier: ^4.1.1 + version: 4.1.1 + devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20240208.0 + version: 4.20260207.0 + '@types/lodash.throttle': + specifier: ^4 + version: 4.1.9 + typescript: + specifier: ^5.0.2 + version: 5.9.2 + wrangler: + specifier: ^3.64.0 + version: 3.114.17(@cloudflare/workers-types@4.20260207.0) + apps/website: dependencies: '@repo/database': @@ -1032,6 +1066,52 @@ packages: '@bundled-es-modules/statuses@1.0.1': resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + '@cloudflare/kv-asset-handler@0.3.4': + resolution: {integrity: sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==} + engines: {node: '>=16.13'} + + '@cloudflare/unenv-preset@2.0.2': + resolution: {integrity: sha512-nyzYnlZjjV5xT3LizahG1Iu6mnrCaxglJ04rZLpDwlDVDZ7v46lNsfxhV3A/xtfgQuSHmLnc6SVI+KwBpc3Lwg==} + peerDependencies: + unenv: 2.0.0-rc.14 + workerd: ^1.20250124.0 + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/workerd-darwin-64@1.20250718.0': + resolution: {integrity: sha512-FHf4t7zbVN8yyXgQ/r/GqLPaYZSGUVzeR7RnL28Mwj2djyw2ZergvytVc7fdGcczl6PQh+VKGfZCfUqpJlbi9g==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20250718.0': + resolution: {integrity: sha512-fUiyUJYyqqp4NqJ0YgGtp4WJh/II/YZsUnEb6vVy5Oeas8lUOxnN+ZOJ8N/6/5LQCVAtYCChRiIrBbfhTn5Z8Q==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20250718.0': + resolution: {integrity: sha512-5+eb3rtJMiEwp08Kryqzzu8d1rUcK+gdE442auo5eniMpT170Dz0QxBrqkg2Z48SFUPYbj+6uknuA5tzdRSUSg==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20250718.0': + resolution: {integrity: sha512-Aa2M/DVBEBQDdATMbn217zCSFKE+ud/teS+fFS+OQqKABLn0azO2qq6ANAHYOIE6Q3Sq4CxDIQr8lGdaJHwUog==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20250718.0': + resolution: {integrity: sha512-dY16RXKffmugnc67LTbyjdDHZn5NoTF1yHEf2fN4+OaOnoGSp3N1x77QubTDwqZ9zECWxgQfDLjddcH8dWeFhg==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workers-types@4.20260207.0': + resolution: {integrity: sha512-PSxgnAOK0EtTytlY7/+gJcsQJYg0Qo7KlOMSC/wiBE+pBqKjuKdd1ZgM+NvpPNqZAjWV5jqAMTTNYEmgk27gYw==} + '@codemirror/state@6.5.2': resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} @@ -1148,15 +1228,22 @@ packages: '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - '@emnapi/runtime@1.7.1': resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@esbuild-plugins/node-globals-polyfill@0.2.3': + resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} + peerDependencies: + esbuild: '*' + + '@esbuild-plugins/node-modules-polyfill@0.2.2': + resolution: {integrity: sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==} + peerDependencies: + esbuild: '*' + '@esbuild/aix-ppc64@0.25.9': resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} @@ -1175,6 +1262,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.17.19': + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.17.3': resolution: {integrity: sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==} engines: {node: '>=12'} @@ -1199,6 +1292,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.17.19': + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.17.3': resolution: {integrity: sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==} engines: {node: '>=12'} @@ -1223,6 +1322,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.17.19': + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.17.3': resolution: {integrity: sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==} engines: {node: '>=12'} @@ -1247,6 +1352,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.17.19': + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.17.3': resolution: {integrity: sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==} engines: {node: '>=12'} @@ -1271,6 +1382,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.17.19': + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.17.3': resolution: {integrity: sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==} engines: {node: '>=12'} @@ -1295,6 +1412,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.17.19': + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.17.3': resolution: {integrity: sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==} engines: {node: '>=12'} @@ -1319,6 +1442,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.17.19': + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.17.3': resolution: {integrity: sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==} engines: {node: '>=12'} @@ -1343,6 +1472,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.17.19': + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.17.3': resolution: {integrity: sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==} engines: {node: '>=12'} @@ -1367,6 +1502,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.17.19': + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.17.3': resolution: {integrity: sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==} engines: {node: '>=12'} @@ -1391,6 +1532,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.17.19': + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.17.3': resolution: {integrity: sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==} engines: {node: '>=12'} @@ -1415,6 +1562,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.17.19': + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.17.3': resolution: {integrity: sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==} engines: {node: '>=12'} @@ -1439,6 +1592,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.17.19': + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.17.3': resolution: {integrity: sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==} engines: {node: '>=12'} @@ -1463,6 +1622,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.17.19': + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.17.3': resolution: {integrity: sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==} engines: {node: '>=12'} @@ -1487,6 +1652,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.17.19': + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.17.3': resolution: {integrity: sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==} engines: {node: '>=12'} @@ -1511,6 +1682,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.17.19': + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.17.3': resolution: {integrity: sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==} engines: {node: '>=12'} @@ -1535,6 +1712,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.17.19': + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.17.3': resolution: {integrity: sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==} engines: {node: '>=12'} @@ -1571,6 +1754,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.17.19': + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.17.3': resolution: {integrity: sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==} engines: {node: '>=12'} @@ -1607,6 +1796,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.17.19': + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.17.3': resolution: {integrity: sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==} engines: {node: '>=12'} @@ -1643,6 +1838,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.17.19': + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.17.3': resolution: {integrity: sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==} engines: {node: '>=12'} @@ -1667,6 +1868,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.17.19': + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.17.3': resolution: {integrity: sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==} engines: {node: '>=12'} @@ -1691,6 +1898,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.17.19': + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.17.3': resolution: {integrity: sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==} engines: {node: '>=12'} @@ -1715,6 +1928,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.17.19': + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.17.3': resolution: {integrity: sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==} engines: {node: '>=12'} @@ -1802,33 +2021,65 @@ packages: '@iarna/toml@2.2.5': resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-arm64@0.34.3': resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + '@img/sharp-darwin-x64@0.34.3': resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.2.0': resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.2.0': resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} cpu: [x64] os: [darwin] + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linux-arm64@1.2.0': resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + '@img/sharp-libvips-linux-arm@1.2.0': resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} cpu: [arm] @@ -1839,32 +2090,64 @@ packages: cpu: [ppc64] os: [linux] + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + '@img/sharp-libvips-linux-s390x@1.2.0': resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} cpu: [s390x] os: [linux] + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + '@img/sharp-libvips-linux-x64@1.2.0': resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} cpu: [x64] os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} cpu: [arm64] os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + '@img/sharp-libvips-linuxmusl-x64@1.2.0': resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} cpu: [x64] os: [linux] + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linux-arm64@0.34.3': resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + '@img/sharp-linux-arm@0.34.3': resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1877,30 +2160,59 @@ packages: cpu: [ppc64] os: [linux] + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + '@img/sharp-linux-s390x@0.34.3': resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-linux-x64@0.34.3': resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + '@img/sharp-linuxmusl-arm64@0.34.3': resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + '@img/sharp-linuxmusl-x64@0.34.3': resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + '@img/sharp-wasm32@0.34.3': resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1912,12 +2224,24 @@ packages: cpu: [arm64] os: [win32] + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-ia32@0.34.3': resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@img/sharp-win32-x64@0.34.3': resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3970,6 +4294,18 @@ packages: peerDependencies: react: ^18.2.0 || ^19.0.0 + '@tldraw/sync-core@2.4.6': + resolution: {integrity: sha512-D5X/tDoT/dOE6NGRo1FnY0kUYrAQLIO2/TpBs83BfdTx22rWUF2djBTzaz3PCZPC5BM+Famlo3hq+TPKrtcs6g==} + peerDependencies: + react: ^18 + react-dom: ^18 + + '@tldraw/sync@2.4.6': + resolution: {integrity: sha512-OgAKoC1MO/NNZFdc6kwPCIb31x7m+qoIiRhoMWVh70OSueQwLH6XGoLPxiodfB/hy+1YnsIXHcJi5N05gkHhXA==} + peerDependencies: + react: ^18 + react-dom: ^18 + '@tldraw/tlschema@2.4.6': resolution: {integrity: sha512-wsV+w86jn0sT2XB4i/E/XW4L4u5UJyyQHWgwr5fmMA38LYe1n/akvGpcrc99Tayk5PvArkT6HRSX1oy6iaMsXw==} peerDependencies: @@ -4110,6 +4446,12 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash.throttle@4.1.9': + resolution: {integrity: sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==} + + '@types/lodash@4.17.23': + resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} + '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} @@ -4576,10 +4918,19 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + acorn-walk@8.3.4: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -4737,6 +5088,9 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + as-table@1.0.55: + resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} + assertion-error-formatter@3.0.0: resolution: {integrity: sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==} @@ -4837,6 +5191,9 @@ packages: bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + body-parser@2.2.0: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} engines: {node: '>=18'} @@ -5060,6 +5417,9 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + cloudflare-workers-unfurl@0.0.7: + resolution: {integrity: sha512-34yY66OBWtX4nSq8cYvOG4pZ4ufaPEnBfm00Z6sfLcXT0JdUFcCROt9LRD2cWRd1euQ3cdhFXw782WEui9rHxw==} + clsx@1.2.1: resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} engines: {node: '>=6'} @@ -5299,6 +5659,9 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + data-uri-to-buffer@2.0.2: + resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -5394,6 +5757,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + degenerator@5.0.1: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} @@ -5598,6 +5964,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.17.3: resolution: {integrity: sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==} engines: {node: '>=12'} @@ -5852,6 +6223,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@0.6.1: + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -5897,6 +6271,10 @@ packages: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + exit-hook@2.2.1: + resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} + engines: {node: '>=6'} + express-rate-limit@7.5.1: resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} engines: {node: '>= 16'} @@ -5907,6 +6285,9 @@ packages: resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} engines: {node: '>= 18'} + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -6137,6 +6518,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-source@2.0.12: + resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} + get-stream@4.1.0: resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} engines: {node: '>=6'} @@ -6171,6 +6555,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -6744,6 +7131,9 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + itty-router@5.0.22: + resolution: {integrity: sha512-9hmdGErWdYDOurGYxSbqLhy4EFReIwk71hMZTJ5b+zfa2zjMNV1ftFno2b8VjAQvX615gNB8Qxbl9JMRqHnIVA==} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -7031,6 +7421,9 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -7194,6 +7587,11 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + miniflare@3.20250718.3: + resolution: {integrity: sha512-JuPrDJhwLrNLEJiNLWO7ZzJrv/Vv9kZuwMYCfv0LskQDM6Eonw4OvywO3CH/wCGjgHzha/qyjUh8JQ068TjDgQ==} + engines: {node: '>=16.13'} + hasBin: true + minimatch@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -7285,6 +7683,10 @@ packages: typescript: optional: true + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -7295,6 +7697,10 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoevents@7.0.1: + resolution: {integrity: sha512-o6lpKiCxLeijK4hgsqfR6CNToPyRU3keKyyI6uwuHRvpRTbZ0wXw51WRgyldVugZqoJfkGFrjrIenYH3bfEO3Q==} + engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} + nanoid@2.0.4: resolution: {integrity: sha512-sOJnBmY3TJQBVIBqKHoifuwygrocXg3NjS9rZSMnVl05XWSHK7Qxb177AIZQyMDjP86bz+yneozj/h9qsPLcCA==} @@ -7496,6 +7902,9 @@ packages: '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -7712,6 +8121,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -7908,6 +8320,9 @@ packages: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} engines: {node: '>=10'} + printable-characters@1.0.42: + resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + prismjs@1.27.0: resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} engines: {node: '>=6'} @@ -8410,6 +8825,16 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup-plugin-inject@3.0.2: + resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. + + rollup-plugin-node-polyfills@0.2.1: + resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} + + rollup-pluginutils@2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} @@ -8534,6 +8959,10 @@ packages: resolution: {integrity: sha512-/zxjmHGbaYVFtI6bUridFVV7VFStIv3vU/w1h7xenhz7KRzc9pqHsyFvcExZprG7dlA5kW9knRgv8+Cl/H7w9w==} hasBin: true + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.34.3: resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -8629,6 +9058,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + space-separated-tokens@1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} @@ -8661,6 +9094,9 @@ packages: stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + stacktracey@2.1.8: + resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} + stat-mode@0.3.0: resolution: {integrity: sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng==} @@ -8684,6 +9120,10 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stoppable@1.1.0: + resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} + engines: {node: '>=4', npm: '>=6'} + stream-to-array@2.3.0: resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} @@ -9218,6 +9658,9 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -9244,6 +9687,9 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} + unenv@2.0.0-rc.14: + resolution: {integrity: sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q==} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -9486,6 +9932,21 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + workerd@1.20250718.0: + resolution: {integrity: sha512-kqkIJP/eOfDlUyBzU7joBg+tl8aB25gEAGqDap+nFWb+WHhnooxjGHgxPBy3ipw2hnShPFNOQt5lFRxbwALirg==} + engines: {node: '>=16'} + hasBin: true + + wrangler@3.114.17: + resolution: {integrity: sha512-tAvf7ly+tB+zwwrmjsCyJ2pJnnc7SZhbnNwXbH+OIdVas3zTSmjcZOjmLKcGGptssAA3RyTKhcF9BvKZzMUycA==} + engines: {node: '>=16.17.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20250408.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -9505,6 +9966,18 @@ packages: resolution: {integrity: sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==} engines: {node: ^20.17.0 || >=22.9.0} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -9609,6 +10082,9 @@ packages: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} + youch@3.3.4: + resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==} + yup@1.7.0: resolution: {integrity: sha512-VJce62dBd+JQvoc+fCVq+KZfPHr+hXaxCcVgotfwWvlR0Ja3ffYKaJBT8rptPOSKOGJDCUnW2C2JWpud7aRP6Q==} @@ -9626,6 +10102,9 @@ packages: peerDependencies: zod: ^3.25 || ^4 + zod@3.22.3: + resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} + zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} @@ -10413,6 +10892,33 @@ snapshots: dependencies: statuses: 2.0.2 + '@cloudflare/kv-asset-handler@0.3.4': + dependencies: + mime: 3.0.0 + + '@cloudflare/unenv-preset@2.0.2(unenv@2.0.0-rc.14)(workerd@1.20250718.0)': + dependencies: + unenv: 2.0.0-rc.14 + optionalDependencies: + workerd: 1.20250718.0 + + '@cloudflare/workerd-darwin-64@1.20250718.0': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20250718.0': + optional: true + + '@cloudflare/workerd-linux-64@1.20250718.0': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20250718.0': + optional: true + + '@cloudflare/workerd-windows-64@1.20250718.0': + optional: true + + '@cloudflare/workers-types@4.20260207.0': {} + '@codemirror/state@6.5.2': dependencies: '@marijn/find-cluster-break': 1.0.2 @@ -10600,11 +11106,6 @@ snapshots: tslib: 2.5.1 optional: true - '@emnapi/runtime@1.5.0': - dependencies: - tslib: 2.5.1 - optional: true - '@emnapi/runtime@1.7.1': dependencies: tslib: 2.5.1 @@ -10615,6 +11116,16 @@ snapshots: tslib: 2.5.1 optional: true + '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19)': + dependencies: + esbuild: 0.17.19 + + '@esbuild-plugins/node-modules-polyfill@0.2.2(esbuild@0.17.19)': + dependencies: + esbuild: 0.17.19 + escape-string-regexp: 4.0.0 + rollup-plugin-node-polyfills: 0.2.1 + '@esbuild/aix-ppc64@0.25.9': optional: true @@ -10624,6 +11135,9 @@ snapshots: '@esbuild/android-arm64@0.17.14': optional: true + '@esbuild/android-arm64@0.17.19': + optional: true + '@esbuild/android-arm64@0.17.3': optional: true @@ -10636,6 +11150,9 @@ snapshots: '@esbuild/android-arm@0.17.14': optional: true + '@esbuild/android-arm@0.17.19': + optional: true + '@esbuild/android-arm@0.17.3': optional: true @@ -10648,6 +11165,9 @@ snapshots: '@esbuild/android-x64@0.17.14': optional: true + '@esbuild/android-x64@0.17.19': + optional: true + '@esbuild/android-x64@0.17.3': optional: true @@ -10660,6 +11180,9 @@ snapshots: '@esbuild/darwin-arm64@0.17.14': optional: true + '@esbuild/darwin-arm64@0.17.19': + optional: true + '@esbuild/darwin-arm64@0.17.3': optional: true @@ -10672,6 +11195,9 @@ snapshots: '@esbuild/darwin-x64@0.17.14': optional: true + '@esbuild/darwin-x64@0.17.19': + optional: true + '@esbuild/darwin-x64@0.17.3': optional: true @@ -10684,6 +11210,9 @@ snapshots: '@esbuild/freebsd-arm64@0.17.14': optional: true + '@esbuild/freebsd-arm64@0.17.19': + optional: true + '@esbuild/freebsd-arm64@0.17.3': optional: true @@ -10696,6 +11225,9 @@ snapshots: '@esbuild/freebsd-x64@0.17.14': optional: true + '@esbuild/freebsd-x64@0.17.19': + optional: true + '@esbuild/freebsd-x64@0.17.3': optional: true @@ -10708,6 +11240,9 @@ snapshots: '@esbuild/linux-arm64@0.17.14': optional: true + '@esbuild/linux-arm64@0.17.19': + optional: true + '@esbuild/linux-arm64@0.17.3': optional: true @@ -10720,6 +11255,9 @@ snapshots: '@esbuild/linux-arm@0.17.14': optional: true + '@esbuild/linux-arm@0.17.19': + optional: true + '@esbuild/linux-arm@0.17.3': optional: true @@ -10732,6 +11270,9 @@ snapshots: '@esbuild/linux-ia32@0.17.14': optional: true + '@esbuild/linux-ia32@0.17.19': + optional: true + '@esbuild/linux-ia32@0.17.3': optional: true @@ -10744,6 +11285,9 @@ snapshots: '@esbuild/linux-loong64@0.17.14': optional: true + '@esbuild/linux-loong64@0.17.19': + optional: true + '@esbuild/linux-loong64@0.17.3': optional: true @@ -10756,6 +11300,9 @@ snapshots: '@esbuild/linux-mips64el@0.17.14': optional: true + '@esbuild/linux-mips64el@0.17.19': + optional: true + '@esbuild/linux-mips64el@0.17.3': optional: true @@ -10768,6 +11315,9 @@ snapshots: '@esbuild/linux-ppc64@0.17.14': optional: true + '@esbuild/linux-ppc64@0.17.19': + optional: true + '@esbuild/linux-ppc64@0.17.3': optional: true @@ -10780,6 +11330,9 @@ snapshots: '@esbuild/linux-riscv64@0.17.14': optional: true + '@esbuild/linux-riscv64@0.17.19': + optional: true + '@esbuild/linux-riscv64@0.17.3': optional: true @@ -10792,6 +11345,9 @@ snapshots: '@esbuild/linux-s390x@0.17.14': optional: true + '@esbuild/linux-s390x@0.17.19': + optional: true + '@esbuild/linux-s390x@0.17.3': optional: true @@ -10804,6 +11360,9 @@ snapshots: '@esbuild/linux-x64@0.17.14': optional: true + '@esbuild/linux-x64@0.17.19': + optional: true + '@esbuild/linux-x64@0.17.3': optional: true @@ -10822,6 +11381,9 @@ snapshots: '@esbuild/netbsd-x64@0.17.14': optional: true + '@esbuild/netbsd-x64@0.17.19': + optional: true + '@esbuild/netbsd-x64@0.17.3': optional: true @@ -10840,6 +11402,9 @@ snapshots: '@esbuild/openbsd-x64@0.17.14': optional: true + '@esbuild/openbsd-x64@0.17.19': + optional: true + '@esbuild/openbsd-x64@0.17.3': optional: true @@ -10858,6 +11423,9 @@ snapshots: '@esbuild/sunos-x64@0.17.14': optional: true + '@esbuild/sunos-x64@0.17.19': + optional: true + '@esbuild/sunos-x64@0.17.3': optional: true @@ -10870,6 +11438,9 @@ snapshots: '@esbuild/win32-arm64@0.17.14': optional: true + '@esbuild/win32-arm64@0.17.19': + optional: true + '@esbuild/win32-arm64@0.17.3': optional: true @@ -10882,6 +11453,9 @@ snapshots: '@esbuild/win32-ia32@0.17.14': optional: true + '@esbuild/win32-ia32@0.17.19': + optional: true + '@esbuild/win32-ia32@0.17.3': optional: true @@ -10894,6 +11468,9 @@ snapshots: '@esbuild/win32-x64@0.17.14': optional: true + '@esbuild/win32-x64@0.17.19': + optional: true + '@esbuild/win32-x64@0.17.3': optional: true @@ -10978,48 +11555,92 @@ snapshots: '@iarna/toml@2.2.5': {} + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + '@img/sharp-darwin-arm64@0.34.3': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.2.0 optional: true + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + '@img/sharp-darwin-x64@0.34.3': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.2.0 optional: true + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + '@img/sharp-libvips-darwin-arm64@1.2.0': optional: true + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + '@img/sharp-libvips-darwin-x64@1.2.0': optional: true + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + '@img/sharp-libvips-linux-arm64@1.2.0': optional: true + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + '@img/sharp-libvips-linux-arm@1.2.0': optional: true '@img/sharp-libvips-linux-ppc64@1.2.0': optional: true + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + '@img/sharp-libvips-linux-s390x@1.2.0': optional: true + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + '@img/sharp-libvips-linux-x64@1.2.0': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.2.0': optional: true + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + '@img/sharp-linux-arm64@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.2.0 optional: true + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + '@img/sharp-linux-arm@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.2.0 @@ -11030,37 +11651,68 @@ snapshots: '@img/sharp-libvips-linux-ppc64': 1.2.0 optional: true + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + '@img/sharp-linux-s390x@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.2.0 optional: true + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + '@img/sharp-linux-x64@0.34.3': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.2.0 optional: true + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + '@img/sharp-linuxmusl-arm64@0.34.3': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 optional: true + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + '@img/sharp-linuxmusl-x64@0.34.3': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.2.0 optional: true + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.7.1 + optional: true + '@img/sharp-wasm32@0.34.3': dependencies: - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.7.1 optional: true '@img/sharp-win32-arm64@0.34.3': optional: true + '@img/sharp-win32-ia32@0.33.5': + optional: true + '@img/sharp-win32-ia32@0.34.3': optional: true + '@img/sharp-win32-x64@0.33.5': + optional: true + '@img/sharp-win32-x64@0.34.3': optional: true @@ -11216,7 +11868,7 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.0 optional: true @@ -13650,12 +14302,71 @@ snapshots: nanoid: 4.0.2 react: 18.2.0 + '@tldraw/store@2.4.6(react@19.1.1)': + dependencies: + '@tldraw/state': 2.4.6 + '@tldraw/utils': 2.4.6 + lodash.isequal: 4.5.0 + nanoid: 4.0.2 + react: 19.1.1 + '@tldraw/store@3.14.2(react@19.0.0)': dependencies: '@tldraw/state': 3.14.2 '@tldraw/utils': 3.14.2 react: 19.0.0 + '@tldraw/sync-core@2.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@tldraw/state': 2.4.6 + '@tldraw/store': 2.4.6(react@18.2.0) + '@tldraw/tlschema': 2.4.6(react@18.2.0) + '@tldraw/utils': 2.4.6 + lodash.isequal: 4.5.0 + nanoevents: 7.0.1 + nanoid: 4.0.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tldraw/sync-core@2.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@tldraw/state': 2.4.6 + '@tldraw/store': 2.4.6(react@19.1.1) + '@tldraw/tlschema': 2.4.6(react@19.1.1) + '@tldraw/utils': 2.4.6 + lodash.isequal: 4.5.0 + nanoevents: 7.0.1 + nanoid: 4.0.2 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tldraw/sync@2.4.6(@types/react-dom@18.2.17)(@types/react@18.2.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@tldraw/state': 2.4.6 + '@tldraw/state-react': 2.4.6(react@18.2.0) + '@tldraw/sync-core': 2.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@tldraw/utils': 2.4.6 + lodash.isequal: 4.5.0 + nanoevents: 7.0.1 + nanoid: 4.0.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tldraw: 2.4.6(patch_hash=56e196052862c9a58a11b43e5e121384cd1d6548416afa0f16e9fbfbf0e4080d)(@types/react-dom@18.2.17)(@types/react@18.2.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + ws: 8.18.3 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + - bufferutil + - utf-8-validate + '@tldraw/tlschema@2.4.6(react@18.2.0)': dependencies: '@tldraw/state': 2.4.6 @@ -13665,6 +14376,15 @@ snapshots: nanoid: 4.0.2 react: 18.2.0 + '@tldraw/tlschema@2.4.6(react@19.1.1)': + dependencies: + '@tldraw/state': 2.4.6 + '@tldraw/store': 2.4.6(react@19.1.1) + '@tldraw/utils': 2.4.6 + '@tldraw/validate': 2.4.6 + nanoid: 4.0.2 + react: 19.1.1 + '@tldraw/tlschema@3.14.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@tldraw/state': 3.14.2 @@ -13846,6 +14566,12 @@ snapshots: '@types/linkify-it@5.0.0': {} + '@types/lodash.throttle@4.1.9': + dependencies: + '@types/lodash': 4.17.23 + + '@types/lodash@4.17.23': {} + '@types/markdown-it@14.1.2': dependencies: '@types/linkify-it': 5.0.0 @@ -14536,10 +15262,14 @@ snapshots: dependencies: acorn: 8.15.0 + acorn-walk@8.3.2: {} + acorn-walk@8.3.4: dependencies: acorn: 8.15.0 + acorn@8.14.0: {} + acorn@8.15.0: {} agent-base@6.0.2: @@ -14744,6 +15474,10 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + as-table@1.0.55: + dependencies: + printable-characters: 1.0.42 + assertion-error-formatter@3.0.0: dependencies: diff: 4.0.2 @@ -14841,6 +15575,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + blake3-wasm@2.1.5: {} + body-parser@2.2.0: dependencies: bytes: 3.1.2 @@ -15095,6 +15831,8 @@ snapshots: clone@1.0.4: {} + cloudflare-workers-unfurl@0.0.7: {} + clsx@1.2.1: {} clsx@2.1.1: {} @@ -15304,6 +16042,8 @@ snapshots: damerau-levenshtein@1.0.8: {} + data-uri-to-buffer@2.0.2: {} + data-uri-to-buffer@4.0.1: {} data-uri-to-buffer@6.0.2: {} @@ -15393,6 +16133,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.4: {} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 @@ -15674,6 +16416,31 @@ snapshots: '@esbuild/win32-ia32': 0.17.14 '@esbuild/win32-x64': 0.17.14 + esbuild@0.17.19: + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + esbuild@0.17.3: optionalDependencies: '@esbuild/android-arm': 0.17.3 @@ -16062,6 +16829,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@0.6.1: {} + estree-walker@2.0.2: {} esutils@2.0.3: {} @@ -16127,6 +16896,8 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 3.0.0 + exit-hook@2.2.1: {} + express-rate-limit@7.5.1(express@5.1.0): dependencies: express: 5.1.0 @@ -16163,6 +16934,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.0.8: {} + extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 @@ -16401,6 +17174,11 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-source@2.0.12: + dependencies: + data-uri-to-buffer: 2.0.2 + source-map: 0.6.1 + get-stream@4.1.0: dependencies: pump: 3.0.3 @@ -16439,6 +17217,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -17088,6 +17868,8 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + itty-router@5.0.22: {} + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -17366,6 +18148,10 @@ snapshots: lz-string@1.5.0: {} + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + make-dir@4.0.0: dependencies: semver: 7.7.2 @@ -17614,6 +18400,23 @@ snapshots: min-indent@1.0.1: {} + miniflare@3.20250718.3: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.14.0 + acorn-walk: 8.3.2 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + stoppable: 1.1.0 + undici: 5.29.0 + workerd: 1.20250718.0 + ws: 8.18.0 + youch: 3.3.4 + zod: 3.22.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -17702,6 +18505,8 @@ snapshots: transitivePeerDependencies: - '@types/node' + mustache@4.2.0: {} + mute-stream@0.0.8: {} mute-stream@2.0.0: {} @@ -17712,6 +18517,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nanoevents@7.0.1: {} + nanoid@2.0.4: {} nanoid@3.3.11: {} @@ -17898,6 +18705,8 @@ snapshots: '@types/codemirror': 5.60.8 moment: 2.29.4 + ohash@2.0.11: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -18174,6 +18983,8 @@ snapshots: path-type@4.0.0: {} + pathe@2.0.3: {} + peberminta@0.9.0: {} pend@1.2.0: {} @@ -18308,6 +19119,8 @@ snapshots: dependencies: parse-ms: 2.1.0 + printable-characters@1.0.42: {} + prismjs@1.27.0: {} prismjs@1.30.0: {} @@ -19056,6 +19869,20 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.1 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.1 + rollup-plugin-inject@3.0.2: + dependencies: + estree-walker: 0.6.1 + magic-string: 0.25.9 + rollup-pluginutils: 2.8.2 + + rollup-plugin-node-polyfills@0.2.1: + dependencies: + rollup-plugin-inject: 3.0.2 + + rollup-pluginutils@2.8.2: + dependencies: + estree-walker: 0.6.1 + rope-sequence@1.3.4: {} router@2.2.0: @@ -19232,6 +20059,33 @@ snapshots: - supports-color - typescript + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + optional: true + sharp@0.34.3: dependencies: color: 4.2.3 @@ -19358,6 +20212,8 @@ snapshots: source-map@0.6.1: {} + sourcemap-codec@1.4.8: {} + space-separated-tokens@1.1.5: {} space-separated-tokens@2.0.2: {} @@ -19386,6 +20242,11 @@ snapshots: stackframe@1.3.4: {} + stacktracey@2.1.8: + dependencies: + as-table: 1.0.55 + get-source: 2.0.12 + stat-mode@0.3.0: {} statuses@1.5.0: {} @@ -19403,6 +20264,8 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stoppable@1.1.0: {} + stream-to-array@2.3.0: dependencies: any-promise: 1.3.0 @@ -20071,6 +20934,8 @@ snapshots: uc.micro@2.1.0: {} + ufo@1.6.3: {} + uglify-js@3.19.3: optional: true @@ -20095,6 +20960,14 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 + unenv@2.0.0-rc.14: + dependencies: + defu: 6.1.4 + exsolve: 1.0.8 + ohash: 2.0.11 + pathe: 2.0.3 + ufo: 1.6.3 + unicorn-magic@0.1.0: {} unified@11.0.5: @@ -20415,6 +21288,34 @@ snapshots: wordwrap@1.0.0: {} + workerd@1.20250718.0: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20250718.0 + '@cloudflare/workerd-darwin-arm64': 1.20250718.0 + '@cloudflare/workerd-linux-64': 1.20250718.0 + '@cloudflare/workerd-linux-arm64': 1.20250718.0 + '@cloudflare/workerd-windows-64': 1.20250718.0 + + wrangler@3.114.17(@cloudflare/workers-types@4.20260207.0): + dependencies: + '@cloudflare/kv-asset-handler': 0.3.4 + '@cloudflare/unenv-preset': 2.0.2(unenv@2.0.0-rc.14)(workerd@1.20250718.0) + '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) + '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) + blake3-wasm: 2.1.5 + esbuild: 0.17.19 + miniflare: 3.20250718.3 + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.14 + workerd: 1.20250718.0 + optionalDependencies: + '@cloudflare/workers-types': 4.20260207.0 + fsevents: 2.3.3 + sharp: 0.33.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -20440,6 +21341,8 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 + ws@8.18.0: {} + ws@8.18.3: {} xdg-app-paths@5.1.0: @@ -20520,6 +21423,12 @@ snapshots: yoctocolors-cjs@2.1.3: {} + youch@3.3.4: + dependencies: + cookie: 0.7.2 + mustache: 4.2.0 + stacktracey: 2.1.8 + yup@1.7.0: dependencies: property-expr: 2.0.6 @@ -20541,6 +21450,8 @@ snapshots: dependencies: zod: 3.25.76 + zod@3.22.3: {} + zod@3.22.4: {} zod@3.25.76: {}