From 0795b41b5554cc91ef662f19fd9fb32b82da9359 Mon Sep 17 00:00:00 2001 From: Arek Nawo Date: Mon, 17 Jul 2023 13:39:34 +0200 Subject: [PATCH] refactor: loading optimization --- apps/backend/api/src/index.ts | 2 +- apps/docs/src/pages/[...slug].astro | 18 +- apps/web/package.json | 2 +- apps/web/public/sandbox.js | 56 +-- apps/web/src/app.tsx | 12 +- .../components/fragments/mini-code-editor.tsx | 54 ++- apps/web/src/config/index.ts | 2 +- apps/web/src/context/cache/index.tsx | 32 +- .../src/context/cache/opened-content-piece.ts | 2 +- apps/web/src/index.tsx | 3 +- apps/web/src/layout/side-panel.tsx | 20 +- apps/web/src/lib/code-editor/format.ts | 5 +- apps/web/src/lib/code-editor/index.ts | 1 - .../src/lib/code-editor/suggest-language.ts | 26 +- .../lib/editor/extensions/code-block/menu.tsx | 4 +- .../lib/editor/extensions/code-block/node.tsx | 31 +- .../lib/editor/extensions/code-block/view.tsx | 56 ++- apps/web/src/lib/{code-editor => }/monaco.ts | 0 .../views/content-piece/sections/variants.tsx | 13 +- apps/web/src/views/dashboard/column.tsx | 144 +------- apps/web/src/views/dashboard/index.tsx | 67 +--- apps/web/src/views/editor/index.tsx | 40 +- .../src/views/editor/menus/bubble-menu.tsx | 5 +- apps/web/src/views/extensions/index.tsx | 3 +- apps/web/src/views/getting-started/index.tsx | 3 +- apps/web/src/views/settings/metadata.tsx | 14 +- apps/web/src/views/settings/view.tsx | 3 +- package.json | 2 +- .../src/database/content-piece-variants.ts | 1 + .../backend/src/database/content-variants.ts | 6 +- packages/backend/src/lib/workspace.ts | 20 +- packages/backend/src/plugins/database.ts | 10 +- packages/backend/src/routes/content-groups.ts | 12 + packages/backend/src/routes/content-pieces.ts | 86 +++-- .../src/routes/workspace-memberships.ts | 1 + packages/backend/src/routes/workspaces.ts | 2 +- packages/sdk/javascript/package.json | 2 +- packages/sdk/javascript/src/api/client.ts | 3 + .../sdk/javascript/src/api/content-pieces.ts | 6 +- packages/sdk/javascript/src/api/index.ts | 1 + packages/sdk/javascript/src/api/variants.ts | 51 +++ packages/sdk/javascript/src/api/webhooks.ts | 6 +- .../sdk/javascript/src/astro/content.astro | 4 +- packages/sdk/javascript/src/astro/utils.ts | 4 +- packages/sdk/javascript/src/types/index.d.ts | 2 + pnpm-lock.yaml | 345 ++++++++++++++++-- 46 files changed, 741 insertions(+), 441 deletions(-) rename apps/web/src/lib/{code-editor => }/monaco.ts (100%) create mode 100644 packages/sdk/javascript/src/api/variants.ts diff --git a/apps/backend/api/src/index.ts b/apps/backend/api/src/index.ts index 6d802894..2e6a764c 100644 --- a/apps/backend/api/src/index.ts +++ b/apps/backend/api/src/index.ts @@ -11,7 +11,7 @@ import { createServer, appRouter } from "@vrite/backend"; generateOpenApiDocument(appRouter, { baseUrl: "https://api.vrite.io", title: "Vrite API", - version: "2023.7.12" + version: "2023.7.17" }) ); }); diff --git a/apps/docs/src/pages/[...slug].astro b/apps/docs/src/pages/[...slug].astro index 55e2185b..582199bf 100644 --- a/apps/docs/src/pages/[...slug].astro +++ b/apps/docs/src/pages/[...slug].astro @@ -8,17 +8,21 @@ if (slug === undefined) { return Astro.redirect("/getting-started"); } -const collection = await getCollection("docs"); -const entry = await getEntry("docs", slug); - -if (entry === undefined) { - return Astro.redirect("/"); -} - +const { entry } = Astro.props; const { Content, headings } = await entry.render(); +const collection = await getCollection("docs"); const entryIndex = collection.findIndex((e) => e.slug === slug); const nextEntry = collection[entryIndex + 1]; const previousEntry = collection[entryIndex - 1]; + +export const prerender = true; +export async function getStaticPaths() { + const blogEntries = await getCollection("docs"); + return blogEntries.map((entry) => ({ + params: { slug: entry.slug }, + props: { entry } + })); +} --- C + createClient: () => v }); - var import_isomorphic_unfetch, b, y, o, k, G, T, g, s, w, U, S, c, L, x, D, p, O, m, j, I, h, f, C; + var import_isomorphic_unfetch, b, k, o, G, g, m, w, s, U, S, L, c, D, O, x, p, j, d, I, f, h, C, l, W, v; var init_api = __esm({ "../../packages/sdk/javascript/dist/api.mjs"() { import_isomorphic_unfetch = __toESM(require_browser(), 1); b = "/content-groups"; - y = (r) => ({ list: () => r("GET", `${b}/list`), create: (t) => r("POST", `${b}`, { body: t }), update: (t) => r("PUT", `${b}`, { body: t }), delete: (t) => r("DELETE", `${b}`, { params: t }) }); + k = (r) => ({ list: () => r("GET", `${b}/list`), create: (t) => r("POST", `${b}`, { body: t }), update: (t) => r("PUT", `${b}`, { body: t }), delete: (t) => r("DELETE", `${b}`, { params: t }) }); o = "/content-pieces"; - k = (r) => ({ get: (t) => r("GET", `${o}`, { params: t }), create: (t) => r("POST", `${o}`, { body: t }), update: (t) => r("PUT", `${o}`, { body: t }), delete: (t) => r("DELETE", `${o}`, { params: t }), list: (t) => r("GET", `${o}/list`, { params: t }) }); - G = (r) => { - let t = r.baseURL || "https://api.vrite.io", e = r.extensionId || "", u = r.headers || {}, { token: l } = r; - return { sendRequest: async (a, d, i) => { + G = (r) => ({ get: (t) => r("GET", `${o}`, { params: t }), create: (t) => r("POST", `${o}`, { body: t }), update: (t) => r("PUT", `${o}`, { body: t }), delete: (t) => r("DELETE", `${o}`, { params: t }), list: (t) => r("GET", `${o}/list`, { params: t }) }); + g = (r) => { + let t = r.baseURL || "https://api.vrite.io", e = r.extensionId || "", u = r.headers || {}, { token: T } = r; + return { sendRequest: async (a, P, i) => { try { - const n = await fetch(`${t}${d}/?${encodeURI(Object.entries(i?.params || {}).filter(([, $]) => $).map(([$, P]) => `${$}=${P}`).join("&"))}`, { headers: { Authorization: `Bearer ${l}`, Accept: "application/json", ...i?.body ? { "Content-Type": "application/json" } : {}, ...e ? { "X-Vrite-Extension-ID": e } : {}, ...u }, body: i?.body ? JSON.stringify(i.body) : null, method: a }); + const n = await fetch(`${t}${P}/?${encodeURI(Object.entries(i?.params || {}).filter(([, $]) => $).map(([$, y]) => `${$}=${y}`).join("&"))}`, { headers: { Authorization: `Bearer ${T}`, Accept: "application/json", ...i?.body ? { "Content-Type": "application/json" } : {}, ...e ? { "X-Vrite-Extension-ID": e } : {}, ...u }, body: i?.body ? JSON.stringify(i.body) : null, method: a }); let E = null; try { if (E = await n.json(), !E) @@ -106,29 +106,31 @@ throw console.error(n), n; } }, reconfigure: (a) => { - t = a.baseURL || t, l = a.token || l, e = a.extensionId || e, u = a.headers || u; + t = a.baseURL || t, T = a.token || T, e = a.extensionId || e, u = a.headers || u; } }; }; - T = "/user-settings"; - g = (r) => ({ get: () => r("GET", `${T}`), update: (t) => r("PUT", `${T}`, { body: t }) }); + m = "/user-settings"; + w = (r) => ({ get: () => r("GET", `${m}`), update: (t) => r("PUT", `${m}`, { body: t }) }); s = "/tags"; - w = (r) => ({ get: (t) => r("GET", `${s}`, { params: t }), update: (t) => r("PUT", `${s}`, { body: t }), create: (t) => r("PUT", `${s}`, { body: t }), delete: (t) => r("DELETE", `${s}`, { params: t }), list: (t) => r("GET", `${s}/list`, { params: t }) }); - U = "/profile"; - S = (r) => ({ get: () => r("GET", `${U}`) }); + U = (r) => ({ get: (t) => r("GET", `${s}`, { params: t }), update: (t) => r("PUT", `${s}`, { body: t }), create: (t) => r("PUT", `${s}`, { body: t }), delete: (t) => r("DELETE", `${s}`, { params: t }), list: (t) => r("GET", `${s}/list`, { params: t }) }); + S = "/profile"; + L = (r) => ({ get: () => r("GET", `${S}`) }); c = "/webhooks"; - L = (r) => ({ get: (t) => r("GET", `${c}`, { params: t }), create: (t) => r("POST", `${c}`, { body: t }), update: (t) => r("PUT", `${c}`, { body: t }), delete: (t) => r("DELETE", `${c}`, { params: t }), list: (t) => r("GET", `${c}/list`, { params: t }) }); - x = "/workspace"; - D = (r) => ({ get: () => r("GET", `${x}`) }); + D = (r) => ({ get: (t) => r("GET", `${c}`, { params: t }), create: (t) => r("POST", `${c}`, { body: t }), update: (t) => r("PUT", `${c}`, { body: t }), delete: (t) => r("DELETE", `${c}`, { params: t }), list: (t) => r("GET", `${c}/list`, { params: t }) }); + O = "/workspace"; + x = (r) => ({ get: () => r("GET", `${O}`) }); p = "/roles"; - O = (r) => ({ get: (t) => r("GET", `${p}`, { params: t }), create: (t) => r("POST", `${p}`, { body: t }), update: (t) => r("PUT", `${p}`, { body: t }), delete: (t) => r("DELETE", `${p}`, { params: t }), list: (t) => r("GET", `${p}/list`, { params: t }) }); - m = "/workspace-settings"; - j = (r) => ({ get: () => r("GET", `${m}`), update: (t) => r("PUT", `${m}`, { body: t }) }); - I = (r) => ({ listMembers: (t) => r("GET", "/workspace-memberships/list-members", { params: t }), listWorkspaces: (t) => r("GET", "/workspace-memberships/list-workspaces", { params: t }), create: (t) => r("POST", "/workspace-memberships", { body: t }), update: (t) => r("PUT", "/workspace-memberships", { body: t }), delete: (t) => r("DELETE", "/workspace-memberships", { params: t }) }); + j = (r) => ({ get: (t) => r("GET", `${p}`, { params: t }), create: (t) => r("POST", `${p}`, { body: t }), update: (t) => r("PUT", `${p}`, { body: t }), delete: (t) => r("DELETE", `${p}`, { params: t }), list: (t) => r("GET", `${p}/list`, { params: t }) }); + d = "/workspace-settings"; + I = (r) => ({ get: () => r("GET", `${d}`), update: (t) => r("PUT", `${d}`, { body: t }) }); + f = (r) => ({ listMembers: (t) => r("GET", "/workspace-memberships/list-members", { params: t }), listWorkspaces: (t) => r("GET", "/workspace-memberships/list-workspaces", { params: t }), create: (t) => r("POST", "/workspace-memberships", { body: t }), update: (t) => r("PUT", "/workspace-memberships", { body: t }), delete: (t) => r("DELETE", "/workspace-memberships", { params: t }) }); h = "/extension"; - f = (r) => ({ get: () => r("GET", `${h}`), updateContentPieceData: (t) => r("POST", `${h}/content-piece-data`, { body: t }) }); - C = (r) => { - const { sendRequest: t, reconfigure: e } = G(r); - return { contentGroups: y(t), contentPieces: k(t), tags: w(t), profile: S(t), userSettings: g(t), webhooks: L(t), workspace: D(t), roles: O(t), workspaceSettings: j(t), workspaceMemberships: I(t), extension: f(t), reconfigure: e }; + C = (r) => ({ get: () => r("GET", `${h}`), updateContentPieceData: (t) => r("POST", `${h}/content-piece-data`, { body: t }) }); + l = "/variants"; + W = (r) => ({ create: (t) => r("POST", `${l}`, { body: t }), update: (t) => r("PUT", `${l}`, { body: t }), delete: (t) => r("DELETE", `${l}`, { params: t }), list: () => r("GET", `${l}/list`) }); + v = (r) => { + const { sendRequest: t, reconfigure: e } = g(r); + return { contentGroups: k(t), contentPieces: G(t), tags: U(t), profile: L(t), userSettings: w(t), webhooks: D(t), workspace: x(t), roles: j(t), workspaceSettings: I(t), workspaceMemberships: f(t), extension: C(t), variants: W(t), reconfigure: e }; }; } }); diff --git a/apps/web/src/app.tsx b/apps/web/src/app.tsx index e3c4df1d..462bfa92 100644 --- a/apps/web/src/app.tsx +++ b/apps/web/src/app.tsx @@ -1,10 +1,18 @@ import { Component, Match, Switch, lazy } from "solid-js"; import { Outlet, Route, Routes } from "@solidjs/router"; -import { AuthView } from "#views/auth"; import { StandaloneLayout, SecuredLayout } from "#layout"; -import { VerifyView } from "#views/verify"; import { isEditorApp } from "#lib/utils"; +const AuthView = lazy(async () => { + const { AuthView } = await import("#views/auth"); + + return { default: AuthView }; +}); +const VerifyView = lazy(async () => { + const { VerifyView } = await import("#views/verify"); + + return { default: VerifyView }; +}); const StandaloneEditorView = lazy(async () => { const { StandaloneEditorView } = await import("#views/standalone-editor"); diff --git a/apps/web/src/components/fragments/mini-code-editor.tsx b/apps/web/src/components/fragments/mini-code-editor.tsx index 5612a5ec..0c4635c5 100644 --- a/apps/web/src/components/fragments/mini-code-editor.tsx +++ b/apps/web/src/components/fragments/mini-code-editor.tsx @@ -1,13 +1,23 @@ -import { Component, createEffect, createSignal, on, onCleanup, onMount, Show } from "solid-js"; +import { + Component, + createEffect, + createSignal, + lazy, + on, + onCleanup, + onMount, + Show +} from "solid-js"; import { nanoid } from "nanoid"; import clsx from "clsx"; import { mdiCheckCircleOutline } from "@mdi/js"; +import type { monaco } from "#lib/monaco"; import { createRef } from "#lib/utils"; -import { monaco } from "#lib/code-editor"; import { useAppearanceContext } from "#context"; import { IconButton } from "#components/primitives"; interface MiniCodeEditorProps { + monaco: typeof monaco; wrapperClass?: string; wrap?: boolean; class?: string; @@ -29,6 +39,13 @@ const MiniCodeEditor: Component = (props) => { const [codeEditor, setCodeEditor] = createSignal( null ); + const getUri = (): monaco.Uri => { + if (props.fileName) { + return props.monaco.Uri.file(props.fileName); + } + + return props.monaco.Uri.parse(`file:///${nanoid()}`); + }; onMount(() => { const editorContainer = editorContainerRef(); @@ -53,7 +70,7 @@ const MiniCodeEditor: Component = (props) => { }; if (editorContainer) { - const codeEditor = monaco.editor.create(editorContainer, { + const codeEditor = props.monaco.editor.create(editorContainer, { automaticLayout: true, minimap: { enabled: false }, contextmenu: false, @@ -77,13 +94,9 @@ const MiniCodeEditor: Component = (props) => { } codeEditor.setModel( - monaco.editor.createModel( - props.code || "", - props.language || "json", - props.fileName ? monaco.Uri.file(props.fileName) : monaco.Uri.parse(`file:///${nanoid()}`) - ) + props.monaco.editor.createModel(props.code || "", props.language || "json", getUri()) ); - codeEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, async () => { + codeEditor.addCommand(props.monaco.KeyMod.CtrlCmd | props.monaco.KeyCode.KeyS, async () => { props.onSave?.(codeEditor.getValue()); }); @@ -111,20 +124,24 @@ const MiniCodeEditor: Component = (props) => { () => props.language, () => { if (props.readOnly) { - let fileName = monaco.Uri.parse(`file:///${nanoid()}`); + let fileName = props.monaco.Uri.parse(`file:///${nanoid()}`); - if (props.fileName) fileName = monaco.Uri.file(props.fileName); + if (props.fileName) fileName = props.monaco.Uri.file(props.fileName); codeEditor.getModel()?.dispose(); codeEditor.setModel( - monaco.editor.createModel(props.code || "", props.language || "json", fileName) + props.monaco.editor.createModel( + props.code || "", + props.language || "json", + fileName + ) ); } } ) ); createEffect(() => { - monaco.editor.setTheme( + props.monaco.editor.setTheme( `${codeEditorTheme()}${props.color === "contrast" ? "-contrast" : ""}` ); }); @@ -161,5 +178,14 @@ const MiniCodeEditor: Component = (props) => { ); }; +const MiniCodeEditorWrapper = lazy(async () => { + const { monaco } = await import("#lib/monaco"); + + return { + default: (props: Omit) => ( + + ) + }; +}); -export { MiniCodeEditor }; +export { MiniCodeEditorWrapper as MiniCodeEditor }; diff --git a/apps/web/src/config/index.ts b/apps/web/src/config/index.ts index 1b593c45..7a752374 100644 --- a/apps/web/src/config/index.ts +++ b/apps/web/src/config/index.ts @@ -1,5 +1,5 @@ const config = { - version: "Beta, v2023.7.12" + version: "Beta, v2023.7.17" }; export { config }; diff --git a/apps/web/src/context/cache/index.tsx b/apps/web/src/context/cache/index.tsx index 1f6f3112..ff27bcc6 100644 --- a/apps/web/src/context/cache/index.tsx +++ b/apps/web/src/context/cache/index.tsx @@ -1,19 +1,45 @@ +import { UseContentGroups, useContentGroups } from "./content-groups"; +import { UseContentPieces, useContentPieces } from "./content-pieces"; import { UseOpenedContentPiece, useOpenedContentPiece } from "./opened-content-piece"; -import { ParentComponent, createContext, useContext } from "solid-js"; +import { ParentComponent, createContext, getOwner, runWithOwner, useContext } from "solid-js"; interface CacheContextData { useOpenedContentPiece(): UseOpenedContentPiece; + useContentGroups(): UseContentGroups; + useContentPieces(contentGroupId: string): UseContentPieces; } const CacheContext = createContext(); const CacheContextProvider: ParentComponent = (props) => { - const useOpenedContentPieceCache = useOpenedContentPiece(); + const cacheOwner = getOwner(); + const useContentPiecesCache = new Map(); + + let useOpenedContentPieceCache: UseOpenedContentPiece | null = null; + let useContentGroupsCache: UseContentGroups | null = null; return ( { + return ( + useOpenedContentPieceCache || (useOpenedContentPieceCache = useOpenedContentPiece()) + ); + })!; + }, + useContentGroups() { + return runWithOwner(cacheOwner, () => { + return useContentGroupsCache || (useContentGroupsCache = useContentGroups()); + })!; + }, + useContentPieces(contentGroupId) { + return runWithOwner(cacheOwner, () => { + if (!useContentPiecesCache.has(contentGroupId)) { + useContentPiecesCache.set(contentGroupId, useContentPieces(contentGroupId)); + } + + return useContentPiecesCache.get(contentGroupId)!; + })!; } }} > diff --git a/apps/web/src/context/cache/opened-content-piece.ts b/apps/web/src/context/cache/opened-content-piece.ts index b6253ea3..69dc4a52 100644 --- a/apps/web/src/context/cache/opened-content-piece.ts +++ b/apps/web/src/context/cache/opened-content-piece.ts @@ -68,7 +68,7 @@ const useOpenedContentPiece = (): UseOpenedContentPiece => { on( () => state.contentPiece?.contentGroupId, (contentGroupId, previousContentGroupId) => { - if (!contentGroupId || contentGroupId === previousContentGroupId) return; + if (!contentGroupId) return; const contentPiecesChanges = client.contentPieces.changes.subscribe( { diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index 482a8cef..39d9fcf8 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -10,8 +10,7 @@ import { ClientContextProvider, ConfirmationContextProvider, NotificationsProvider, - UIContextProvider, - CacheContextProvider + UIContextProvider } from "#context"; const container = document.querySelector("#root"); diff --git a/apps/web/src/layout/side-panel.tsx b/apps/web/src/layout/side-panel.tsx index a50a6af0..056961aa 100644 --- a/apps/web/src/layout/side-panel.tsx +++ b/apps/web/src/layout/side-panel.tsx @@ -5,23 +5,15 @@ import { Dynamic } from "solid-js/web"; import { useUIContext } from "#context"; import { createRef } from "#lib/utils"; import { ContentPieceView } from "#views/content-piece"; -import { SettingsView } from "#views/settings/view"; -import { GettingStartedView } from "#views/getting-started"; +import { SettingsView } from "#views/settings"; import { ExtensionsView } from "#views/extensions"; +import { GettingStartedView } from "#views/getting-started"; const sidePanelViews: Record>> = { - contentPiece: () => { - return ; - }, - settings: () => { - return ; - }, - extensions: () => { - return ; - }, - default: () => { - return ; - } + contentPiece: ContentPieceView, + settings: SettingsView, + extensions: ExtensionsView, + default: GettingStartedView }; const SidePanel: Component = () => { const { storage, setStorage } = useUIContext(); diff --git a/apps/web/src/lib/code-editor/format.ts b/apps/web/src/lib/code-editor/format.ts index 6281730b..aaa68bf2 100644 --- a/apps/web/src/lib/code-editor/format.ts +++ b/apps/web/src/lib/code-editor/format.ts @@ -3,7 +3,7 @@ import type { Options, Plugin } from "prettier"; const languageParserMap = { javascript: "babel", - typescript: "typescript", + typescript: "babel-ts", json: "yaml", graphql: "graphql", html: "html", @@ -23,9 +23,8 @@ const isFormattable = (language: string): boolean => { const loadParserPlugin = async (language: string): Promise => { switch (language as SupportedLanguages) { case "javascript": - return import("prettier/parser-babel"); case "typescript": - return import("prettier/parser-typescript"); + return import("prettier/parser-babel"); case "graphql": return import("prettier/parser-graphql"); case "html": diff --git a/apps/web/src/lib/code-editor/index.ts b/apps/web/src/lib/code-editor/index.ts index 1e44e34f..24b4b95a 100644 --- a/apps/web/src/lib/code-editor/index.ts +++ b/apps/web/src/lib/code-editor/index.ts @@ -1,3 +1,2 @@ export * from "./format"; -export * from "./monaco"; export * from "./suggest-language"; diff --git a/apps/web/src/lib/code-editor/suggest-language.ts b/apps/web/src/lib/code-editor/suggest-language.ts index 4f4f300c..a559648b 100644 --- a/apps/web/src/lib/code-editor/suggest-language.ts +++ b/apps/web/src/lib/code-editor/suggest-language.ts @@ -1,17 +1,23 @@ import MiniSearch from "minisearch"; import { createSignal } from "solid-js"; -import { monaco } from "#lib/code-editor"; +import type { monaco } from "#lib/monaco"; const [searchEngine, setSearchEngine] = createSignal(null); -const languageIds = monaco.languages - .getLanguages() - .filter((language) => { - return !language.id.includes("."); - }) - .map((language) => { - return { id: language.id }; - }); -const useSuggestLanguage = (): ((query: string) => string[]) => { +const getLanguageIds = ( + languages: monaco.languages.ILanguageExtensionPoint[] +): Array<{ id: string }> => { + return languages + .filter((language) => { + return !language.id.includes("."); + }) + .map((language) => { + return { id: language.id }; + }); +}; +const useSuggestLanguage = ( + languages: monaco.languages.ILanguageExtensionPoint[] +): ((query: string) => string[]) => { + const languageIds = getLanguageIds(languages); const engine = searchEngine() || new MiniSearch({ diff --git a/apps/web/src/lib/editor/extensions/code-block/menu.tsx b/apps/web/src/lib/editor/extensions/code-block/menu.tsx index 90cf599d..f26ed795 100644 --- a/apps/web/src/lib/editor/extensions/code-block/menu.tsx +++ b/apps/web/src/lib/editor/extensions/code-block/menu.tsx @@ -2,10 +2,12 @@ import { CodeBlockAttributes } from "./node"; import { useSolidNodeView } from "@vrite/tiptap-solid"; import { mdiCodeTagsCheck } from "@mdi/js"; import { Component, createEffect, createMemo, createSignal, on, Show } from "solid-js"; +import type { monaco } from "#lib/monaco"; import { IconButton, Input, Tooltip } from "#components/primitives"; import { useSuggestLanguage, isFormattable } from "#lib/code-editor"; interface CodeBlockMenuProps { + monaco: typeof monaco; format(): void; changeLanguage(languageId: string | null): void; } @@ -14,7 +16,7 @@ const CodeBlockMenu: Component = (props) => { const { state } = useSolidNodeView(); const attrs = (): CodeBlockAttributes => state().node.attrs; const [suggestions, setSuggestions] = createSignal([]); - const suggestLanguage = useSuggestLanguage(); + const suggestLanguage = useSuggestLanguage(props.monaco.languages.getLanguages()); const formattingAvailable = createMemo(() => { return isFormattable(attrs().lang || ""); }); diff --git a/apps/web/src/lib/editor/extensions/code-block/node.tsx b/apps/web/src/lib/editor/extensions/code-block/node.tsx index cb9394bc..fc90824d 100644 --- a/apps/web/src/lib/editor/extensions/code-block/node.tsx +++ b/apps/web/src/lib/editor/extensions/code-block/node.tsx @@ -9,10 +9,10 @@ import { isNodeSelection } from "@tiptap/core"; import { keymap } from "@tiptap/pm/keymap"; import { TextSelection } from "@tiptap/pm/state"; import { createNanoEvents } from "nanoevents"; -import { onCleanup, onMount } from "solid-js"; -import { createRef } from "#lib/utils"; -import { monaco } from "#lib/code-editor"; +import { Show, createSignal, onCleanup, onMount } from "solid-js"; import { HocuspocusProvider } from "@hocuspocus/provider"; +import type { monaco } from "#lib/monaco"; +import { createRef } from "#lib/utils"; interface CodeBlockOptions extends BaseCodeBlockOptions { provider: HocuspocusProvider | null; @@ -42,12 +42,14 @@ const CodeBlock = BaseCodeBlock.extend({ null ); const [updatingRef, setUpdatingRef] = createRef(false); + const [monacoRef, setMonacoRef] = createRef(null); return SolidNodeViewRenderer( () => { const { state } = useSolidNodeView(); + const [loading, setLoading] = createSignal(true); - onMount(() => { + onMount(async () => { const unbind = emitter.on("focus", () => { if (state().selected) { const tr = state().editor.state.tr.setSelection( @@ -66,16 +68,23 @@ const CodeBlock = BaseCodeBlock.extend({ onCleanup(() => { unbind(); }); + import("#lib/monaco").then(({ monaco }) => { + setMonacoRef(monaco); + setLoading(false); + }); }); return ( - + + + ); }, @@ -120,7 +129,7 @@ const CodeBlock = BaseCodeBlock.extend({ { forceMoveMarkers: true, text: newText.slice(start, newEnd), - range: monaco.Range.fromPositions( + range: monacoRef()!.Range.fromPositions( model.getPositionAt(start), model.getPositionAt(curEnd) ) diff --git a/apps/web/src/lib/editor/extensions/code-block/view.tsx b/apps/web/src/lib/editor/extensions/code-block/view.tsx index 02462749..bad71ae4 100644 --- a/apps/web/src/lib/editor/extensions/code-block/view.tsx +++ b/apps/web/src/lib/editor/extensions/code-block/view.tsx @@ -4,7 +4,8 @@ import { useSolidNodeView } from "@vrite/tiptap-solid"; import clsx from "clsx"; import { Component, createEffect, createSignal, onMount } from "solid-js"; import { nanoid } from "nanoid"; -import { formatCode, monaco } from "#lib/code-editor"; +import type { monaco } from "#lib/monaco"; +import { formatCode } from "#lib/code-editor"; import { Card } from "#components/primitives"; import { createRef, selectionClasses } from "#lib/utils"; import { @@ -15,13 +16,17 @@ import { } from "#context"; interface CodeBlockViewProps { + monaco: typeof monaco; codeEditorRef(): monaco.editor.IStandaloneCodeEditor | null; updatingRef(): boolean | null; setCodeEditorRef(value: monaco.editor.IStandaloneCodeEditor): void; setUpdatingRef(value: boolean): void; } -const getExtension = (language: string): string => { +const getExtension = ( + languages: monaco.languages.ILanguageExtensionPoint[], + language: string +): string => { if (language === "typescript") { return ".tsx"; } else if (language === "javascript") { @@ -29,7 +34,7 @@ const getExtension = (language: string): string => { } return ( - monaco.languages.getLanguages().find((item) => { + languages.find((item) => { return item.id === language; })?.extensions?.[0] || "" ); @@ -74,8 +79,8 @@ const CodeBlockView: Component = (props) => { } const range = model.getFullModelRange(); - const start = model.getOffsetAt(monaco.Range.getStartPosition(range)); - const end = model.getOffsetAt(monaco.Range.getEndPosition(range)); + const start = model.getOffsetAt(props.monaco.Range.getStartPosition(range)); + const end = model.getOffsetAt(props.monaco.Range.getEndPosition(range)); tr.replaceWith(offset + start, offset + end, state().editor.schema.text(formattedCode)); state().editor.view.dispatch(tr); @@ -97,7 +102,7 @@ const CodeBlockView: Component = (props) => { const model = codeEditor()?.getModel(); if (model) { - monaco.editor.setModelLanguage(model, languageId || ""); + props.monaco.editor.setModelLanguage(model, languageId || ""); } }; const updateEditorHeight = (monacoEditor: monaco.editor.IStandaloneCodeEditor): void => { @@ -120,7 +125,7 @@ const CodeBlockView: Component = (props) => { const editorContainer = editorContainerRef(); if (editorContainer) { - const codeEditor = monaco.editor.create(editorContainer, { + const codeEditor = props.monaco.editor.create(editorContainer, { automaticLayout: true, model: null, fontSize: 13, @@ -139,10 +144,12 @@ const CodeBlockView: Component = (props) => { const languageId = attrs().lang || ""; codeEditor.setModel( - monaco.editor.createModel( + props.monaco.editor.createModel( state().node.textContent, languageId, - monaco.Uri.file(`${nanoid()}${getExtension(languageId)}`) + props.monaco.Uri.file( + `${nanoid()}${getExtension(props.monaco.languages.getLanguages(), languageId)}` + ) ) ); setCurrentModelValue(codeEditor.getModel()?.getValue() || ""); @@ -155,14 +162,16 @@ const CodeBlockView: Component = (props) => { if (updating || !codeEditor.hasTextFocus() || !model) return; const { tr } = state().editor.state; - const previousModel = monaco.editor.createModel(currentModelValue() || ""); + const previousModel = props.monaco.editor.createModel(currentModelValue() || ""); let offset = state().getPos() + 1; event.changes.forEach((change) => { if (change.text.length) { - const start = previousModel.getOffsetAt(monaco.Range.getStartPosition(change.range)); - const end = previousModel.getOffsetAt(monaco.Range.getEndPosition(change.range)); + const start = previousModel.getOffsetAt( + props.monaco.Range.getStartPosition(change.range) + ); + const end = previousModel.getOffsetAt(props.monaco.Range.getEndPosition(change.range)); tr.replaceWith( offset + start, @@ -171,8 +180,10 @@ const CodeBlockView: Component = (props) => { ); offset -= end - start; } else { - const start = previousModel.getOffsetAt(monaco.Range.getStartPosition(change.range)); - const end = previousModel.getOffsetAt(monaco.Range.getEndPosition(change.range)); + const start = previousModel.getOffsetAt( + props.monaco.Range.getStartPosition(change.range) + ); + const end = previousModel.getOffsetAt(props.monaco.Range.getEndPosition(change.range)); tr.delete(offset + start, offset + end); } @@ -197,7 +208,7 @@ const CodeBlockView: Component = (props) => { let anchor = model.getOffsetAt(sel.getStartPosition()); let head = model.getOffsetAt(sel.getEndPosition()); - if (sel.getDirection() === monaco.SelectionDirection.RTL) { + if (sel.getDirection() === props.monaco.SelectionDirection.RTL) { const tmp = anchor; anchor = head; @@ -229,7 +240,12 @@ const CodeBlockView: Component = (props) => { } newDecorations.push({ - range: new monaco.Range(start.lineNumber, start.column, end.lineNumber, end.column), + range: new props.monaco.Range( + start.lineNumber, + start.column, + end.lineNumber, + end.column + ), options: { className: clsx( "bg-opacity-40 dark:bg-opacity-40", @@ -247,18 +263,18 @@ const CodeBlockView: Component = (props) => { setDecorationsRef(codeEditor.deltaDecorations(decorationsRef() || [], newDecorations)); }); codeEditor.onKeyDown((event) => { - if (event.keyCode === monaco.KeyCode.Escape) { + if (event.keyCode === props.monaco.KeyCode.Escape) { state().editor.commands.setNodeSelection(state().getPos()); state().editor.commands.focus(); } }); - codeEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, async () => { + codeEditor.addCommand(props.monaco.KeyMod.CtrlCmd | props.monaco.KeyCode.KeyS, async () => { await format(); state().editor.commands.setNodeSelection(state().getPos()); state().editor.commands.focus(); }); createEffect(() => { - monaco.editor.setTheme(codeEditorTheme()); + props.monaco.editor.setTheme(codeEditorTheme()); }); } }); @@ -288,7 +304,7 @@ const CodeBlockView: Component = (props) => { )} contentEditable={false} > - + ); diff --git a/apps/web/src/lib/code-editor/monaco.ts b/apps/web/src/lib/monaco.ts similarity index 100% rename from apps/web/src/lib/code-editor/monaco.ts rename to apps/web/src/lib/monaco.ts diff --git a/apps/web/src/views/content-piece/sections/variants.tsx b/apps/web/src/views/content-piece/sections/variants.tsx index 9d110c2d..d85b593e 100644 --- a/apps/web/src/views/content-piece/sections/variants.tsx +++ b/apps/web/src/views/content-piece/sections/variants.tsx @@ -1,6 +1,5 @@ import { Accessor, Component, For, Show, createSignal, onCleanup } from "solid-js"; import { createStore } from "solid-js/store"; -import clsx from "clsx"; import { App, useClientContext, useUIContext } from "#context"; import { Button, Loader } from "#components/primitives"; @@ -66,17 +65,21 @@ const VariantsSection: Component = (props) => { return (
-
+
No Variants found + + No Variants found + } > - +
+ +
} > @@ -87,7 +90,7 @@ const VariantsSection: Component = (props) => { return (
- +
diff --git a/apps/web/src/views/editor/menus/bubble-menu.tsx b/apps/web/src/views/editor/menus/bubble-menu.tsx index 1c4337e3..de2ef433 100644 --- a/apps/web/src/views/editor/menus/bubble-menu.tsx +++ b/apps/web/src/views/editor/menus/bubble-menu.tsx @@ -314,7 +314,10 @@ const BubbleMenu: Component = (props) => { return ( diff --git a/apps/web/src/views/extensions/index.tsx b/apps/web/src/views/extensions/index.tsx index b62b400a..913d7782 100644 --- a/apps/web/src/views/extensions/index.tsx +++ b/apps/web/src/views/extensions/index.tsx @@ -70,7 +70,6 @@ const ExtensionsView: Component = () => {
{
diff --git a/apps/web/src/views/getting-started/index.tsx b/apps/web/src/views/getting-started/index.tsx index ada5103e..d1f0e0f6 100644 --- a/apps/web/src/views/getting-started/index.tsx +++ b/apps/web/src/views/getting-started/index.tsx @@ -31,7 +31,6 @@ const GettingStartedView: Component = () => {
{
-
+
diff --git a/apps/web/src/views/settings/metadata.tsx b/apps/web/src/views/settings/metadata.tsx index 7dfac2e2..d21f3960 100644 --- a/apps/web/src/views/settings/metadata.tsx +++ b/apps/web/src/views/settings/metadata.tsx @@ -66,9 +66,17 @@ const MetadataSection: SettingsSectionComponent = () => { } }} > - Set the pattern to use for auto-generating canonical links. Use{" "} - {`{{slug}}`} to insert the content piece's - slug. + Set the pattern to use for auto-generating canonical links. You can use the + following variables: +
    +
  • + {`{{slug}}`} the content piece's slug; +
  • +
  • + {`{{variant}}`} the selected Variant + name; +
  • +
diff --git a/apps/web/src/views/settings/view.tsx b/apps/web/src/views/settings/view.tsx index 559bb157..ec6895ce 100644 --- a/apps/web/src/views/settings/view.tsx +++ b/apps/web/src/views/settings/view.tsx @@ -142,7 +142,6 @@ const SettingsView: Component = () => {
{
diff --git a/package.json b/package.json index f2ffa257..56b4333c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vrite", - "version": "2023.7.12", + "version": "2023.7.17", "license": "AGPL-3.0-only", "private": true, "workspaces": [ diff --git a/packages/backend/src/database/content-piece-variants.ts b/packages/backend/src/database/content-piece-variants.ts index f0e76d62..83572d20 100644 --- a/packages/backend/src/database/content-piece-variants.ts +++ b/packages/backend/src/database/content-piece-variants.ts @@ -22,6 +22,7 @@ interface ContentPieceVariantWithAdditionalData extends ContentPieceVariant { + workspaceId: ID; coverWidth?: string; } interface FullContentPieceVariantWithAdditionalData diff --git a/packages/backend/src/database/content-variants.ts b/packages/backend/src/database/content-variants.ts index a27838da..a22a8e5d 100644 --- a/packages/backend/src/database/content-variants.ts +++ b/packages/backend/src/database/content-variants.ts @@ -8,9 +8,13 @@ interface ContentVariant { id: ID; } +interface FullContentVariant extends ContentVariant { + workspaceId: ID; +} + const getContentVariantsCollection = ( db: Db -): Collection>> => { +): Collection>> => { return db.collection("content-variants"); }; diff --git a/packages/backend/src/lib/workspace.ts b/packages/backend/src/lib/workspace.ts index c1199e62..8d10f943 100644 --- a/packages/backend/src/lib/workspace.ts +++ b/packages/backend/src/lib/workspace.ts @@ -12,7 +12,13 @@ import { getWorkspacesCollection } from "#database/workspaces"; import { getWorkspaceMembershipsCollection } from "#database/workspace-memberships"; import { getRolesCollection } from "#database/roles"; import { FullUser } from "#database/users"; -import { getContentPiecesCollection, getContentsCollection } from "#database"; +import { + getContentPieceVariantsCollection, + getContentPiecesCollection, + getContentVariantsCollection, + getContentsCollection, + getVariantsCollection +} from "#database"; import initialContent from "#assets/initial-content.json"; const createWorkspace = async ( @@ -119,6 +125,9 @@ const deleteWorkspace = async (workspaceId: ObjectId, db: Db): Promise => const contentPiecesCollection = getContentPiecesCollection(db); const contentsCollection = getContentsCollection(db); const rolesCollection = getRolesCollection(db); + const variantsCollection = getVariantsCollection(db); + const contentPieceVariantsCollection = getContentPieceVariantsCollection(db); + const contentVariantsCollection = getContentVariantsCollection(db); await workspacesCollection.deleteOne({ _id: workspaceId @@ -138,6 +147,15 @@ const deleteWorkspace = async (workspaceId: ObjectId, db: Db): Promise => await contentsCollection.deleteMany({ workspaceId }); + await variantsCollection.deleteMany({ + workspaceId + }); + await contentPieceVariantsCollection.deleteMany({ + workspaceId + }); + await contentVariantsCollection.deleteMany({ + workspaceId + }); }; export { createWorkspace, deleteWorkspace }; diff --git a/packages/backend/src/plugins/database.ts b/packages/backend/src/plugins/database.ts index a5bdfd3d..d12b7818 100644 --- a/packages/backend/src/plugins/database.ts +++ b/packages/backend/src/plugins/database.ts @@ -33,18 +33,22 @@ const databasePlugin = publicPlugin(async (fastify) => { const extensionsCollection = getWorkspaceSettingsCollection(db); const variantsCollection = getVariantsCollection(db); const contentPieceVariantsCollection = getContentPieceVariantsCollection(db); - const contentVariants = getContentVariantsCollection(db); + const contentVariantsCollection = getContentVariantsCollection(db); await Promise.all([ contentPiecesCollection.createIndex({ workspaceId: 1 }), contentPiecesCollection.createIndex({ contentGroupId: 1 }), contentPiecesCollection.createIndex({ tags: 1 }), - contentPieceVariantsCollection.createIndex({ variantId: 1 }), + contentPieceVariantsCollection.createIndex({ contentPieceId: 1, variantId: 1 }), + contentPieceVariantsCollection.createIndex({ contentPieceId: 1 }), + contentPieceVariantsCollection.createIndex({ workspaceId: 1 }), commentThreadsCollection.createIndex({ contentPieceId: 1, workspaceId: 1 }), commentThreadsCollection.createIndex({ fragment: 1, workspaceId: 1 }), commentsCollection.createIndex({ threadId: 1, workspaceId: 1 }), contentsCollection.createIndex({ contentPieceId: 1 }), - contentVariants.createIndex({ contentPieceId: 1, variantId: 1 }), + contentVariantsCollection.createIndex({ contentPieceId: 1, variantId: 1 }), + contentVariantsCollection.createIndex({ contentPieceId: 1 }), + contentVariantsCollection.createIndex({ workspaceId: 1 }), rolesCollection.createIndex({ workspaceId: 1 }), tagsCollection.createIndex({ workspaceId: 1 }), tokensCollection.createIndex({ workspaceId: 1 }), diff --git a/packages/backend/src/routes/content-groups.ts b/packages/backend/src/routes/content-groups.ts index 2cc351c4..4fb83ee9 100644 --- a/packages/backend/src/routes/content-groups.ts +++ b/packages/backend/src/routes/content-groups.ts @@ -9,6 +9,7 @@ import { getContentPiecesCollection } from "#database/content-pieces"; import { createEventPublisher, createEventSubscription } from "#lib/pub-sub"; import { getContentsCollection } from "#database/contents"; import { runWebhooks } from "#lib/webhooks"; +import { getContentPieceVariantsCollection, getContentVariantsCollection } from "#database"; type ContentGroupEvent = | { @@ -120,6 +121,8 @@ const contentGroupsRouter = router({ const workspacesCollection = getWorkspacesCollection(ctx.db); const contentPiecesCollection = getContentPiecesCollection(ctx.db); const contentsCollection = getContentsCollection(ctx.db); + const contentPieceVariantsCollection = getContentPieceVariantsCollection(ctx.db); + const contentVariantsCollection = getContentVariantsCollection(ctx.db); const workspace = await workspacesCollection.findOne({ _id: ctx.auth.workspaceId }); const contentGroupId = new ObjectId(input.id); const contentGroup = workspace?.contentGroups.find((contentGroup) => { @@ -136,8 +139,17 @@ const contentGroupsRouter = router({ $pull: { contentGroups: { _id: contentGroupId } } } ); + + const contentPieceIds = await contentPiecesCollection + .find({ contentGroupId }) + .project({ _id: true }) + .map(({ _id }) => _id) + .toArray(); + await contentPiecesCollection.deleteMany({ contentGroupId }); await contentsCollection.deleteMany({ contentGroupId }); + await contentPieceVariantsCollection.deleteMany({ contentPieceId: { $in: contentPieceIds } }); + await contentVariantsCollection.deleteMany({ contentPieceId: { $in: contentPieceIds } }); publishEvent(ctx, `${ctx.auth.workspaceId}`, { action: "delete", data: input diff --git a/packages/backend/src/routes/content-pieces.ts b/packages/backend/src/routes/content-pieces.ts index 6314edba..54c61406 100644 --- a/packages/backend/src/routes/content-pieces.ts +++ b/packages/backend/src/routes/content-pieces.ts @@ -148,22 +148,34 @@ const mergeVariantData = ( return { ...contentPiece, ...mergedVariantData }; }; -const getVariantIdFromName = async (db: Db, variantName: string): Promise => { +const getVariantDetails = async ( + db: Db, + variantIdOrName?: string +): Promise<{ variantId: ObjectId | null; variantName: string | null }> => { const variantsCollection = getVariantsCollection(db); - const variant = await variantsCollection.findOne({ name: variantName }); - if (!variant) throw errors.notFound("variant"); + if (!variantIdOrName) return { variantId: null, variantName: null }; - return variant._id; -}; -const getVariantId = async (db: Db, variant?: string): Promise => { - if (!variant) return null; + const isId = ObjectId.isValid(variantIdOrName); + const variant = await variantsCollection.findOne({ + ...(isId && { _id: new ObjectId(variantIdOrName) }), + ...(!isId && { name: variantIdOrName }) + }); - if (ObjectId.isValid(variant)) { - return new ObjectId(variant); - } + if (!variant) throw errors.notFound("variant"); - return await getVariantIdFromName(db, variant); + return { variantId: variant._id || null, variantName: variant.name || null }; +}; +const getCanonicalLinkFromPattern = ( + pattern: string, + data: { slug: string; variant?: string | null } +): string => { + return pattern + .replace(/{{slug}}/g, data.slug) + .replace(/{{variant}}/g, data.variant || "") + .replace(/((?:[^:]))\/\/{1,}/g, (match) => { + return match.replace(/\/{1,}/g, "/"); + }); }; const basePath = "/content-pieces"; const authenticatedProcedure = procedure.use(isAuthenticated); @@ -208,7 +220,7 @@ const contentPiecesRouter = router({ workspaceId: ctx.auth.workspaceId }); const workspace = await workspacesCollection.findOne({ _id: ctx.auth.workspaceId }); - const variantId = await getVariantId(ctx.db, input.variant); + const { variantId, variantName } = await getVariantDetails(ctx.db, input.variant); const baseContentPiece = await contentPiecesCollection.findOne({ _id: new ObjectId(input.id) }); @@ -220,6 +232,7 @@ const contentPiecesRouter = router({ if (variantId) { const contentPieceVariant = await contentPieceVariantsCollection.findOne({ contentPieceId: new ObjectId(input.id), + workspaceId: ctx.auth.workspaceId, variantId }); @@ -245,6 +258,7 @@ const contentPiecesRouter = router({ if (variantId) { const contentVariant = await contentVariantsCollection.findOne({ contentPieceId: new ObjectId(input.id), + workspaceId: ctx.auth.workspaceId, variantId }); @@ -280,9 +294,9 @@ const contentPiecesRouter = router({ ...contentPiece, ...(workspaceSettings?.metadata?.canonicalLinkPattern && typeof contentPiece.canonicalLink !== "string" && { - canonicalLink: workspaceSettings.metadata.canonicalLinkPattern.replace( - /{{slug}}/g, - contentPiece.slug + canonicalLink: getCanonicalLinkFromPattern( + workspaceSettings.metadata.canonicalLinkPattern, + { slug: contentPiece.slug, variant: variantName } ) }), id: `${contentPiece._id}`, @@ -342,7 +356,7 @@ const contentPiecesRouter = router({ const workspace = await workspacesCollection.findOne({ _id: ctx.auth.workspaceId }); - const variantId = await getVariantId(ctx.db, input.variant); + const { variantId, variantName } = await getVariantDetails(ctx.db, input.variant); const contentGroup = workspace?.contentGroups.find((contentGroup) => { return contentGroup._id.equals(input.contentGroupId); }); @@ -371,6 +385,7 @@ const contentPiecesRouter = router({ const contentPieceVariants = await contentPieceVariantsCollection .find({ contentPieceId: { $in: contentPieces.map((contentPiece) => contentPiece._id) }, + workspaceId: ctx.auth.workspaceId, variantId }) .toArray(); @@ -397,9 +412,9 @@ const contentPiecesRouter = router({ ...contentPiece, ...(workspaceSettings?.metadata?.canonicalLinkPattern && typeof contentPiece.canonicalLink !== "string" && { - canonicalLink: workspaceSettings.metadata.canonicalLinkPattern.replace( - /{{slug}}/g, - contentPiece.slug + canonicalLink: getCanonicalLinkFromPattern( + workspaceSettings.metadata.canonicalLinkPattern, + { slug: contentPiece.slug, variant: variantName } ) }), id: `${contentPiece._id}`, @@ -502,9 +517,9 @@ const contentPiecesRouter = router({ ...contentPiece, ...(workspaceSettings?.metadata?.canonicalLinkPattern && typeof contentPiece.canonicalLink !== "string" && { - canonicalLink: workspaceSettings.metadata.canonicalLinkPattern.replace( - /{{slug}}/g, - contentPiece.slug + canonicalLink: getCanonicalLinkFromPattern( + workspaceSettings.metadata.canonicalLinkPattern, + { slug: contentPiece.slug } ) }), id: `${contentPiece._id}`, @@ -565,7 +580,7 @@ const contentPiecesRouter = router({ workspaceId: ctx.auth.workspaceId }); const workspace = await workspacesCollection.findOne({ _id: ctx.auth.workspaceId }); - const variantId = await getVariantId(ctx.db, variant); + const { variantId, variantName } = await getVariantDetails(ctx.db, variant); const baseContentPiece = await contentPiecesCollection.findOne({ _id: new ObjectId(id) }); @@ -577,6 +592,7 @@ const contentPiecesRouter = router({ if (variantId) { const contentPieceVariant = await contentPieceVariantsCollection.findOne({ contentPieceId: new ObjectId(id), + workspaceId: ctx.auth.workspaceId, variantId }); @@ -639,7 +655,7 @@ const contentPiecesRouter = router({ if (variantId) { await contentPieceVariantsCollection.updateOne( { contentPieceId: new ObjectId(id), variantId }, - { $set: contentPieceUpdates }, + { $set: { ...contentPieceUpdates, workspaceId: ctx.auth.workspaceId } }, { upsert: true } ); } else { @@ -654,11 +670,13 @@ const contentPiecesRouter = router({ await contentVariantsCollection.updateOne( { contentPieceId: contentPiece._id, + workspaceId: ctx.auth.workspaceId, variantId }, { $set: { - content: new Binary(jsonToBuffer(htmlToJSON(updatedContent))) + content: new Binary(jsonToBuffer(htmlToJSON(updatedContent))), + workspaceId: ctx.auth.workspaceId } } ); @@ -697,9 +715,9 @@ const contentPiecesRouter = router({ ...newContentPiece, ...(workspaceSettings?.metadata?.canonicalLinkPattern && typeof newContentPiece.canonicalLink !== "string" && { - canonicalLink: workspaceSettings.metadata.canonicalLinkPattern.replace( - /{{slug}}/g, - newContentPiece.slug + canonicalLink: getCanonicalLinkFromPattern( + workspaceSettings.metadata.canonicalLinkPattern, + { slug: newContentPiece.slug, variant: variantName } ) }), id: `${newContentPiece._id}`, @@ -734,10 +752,12 @@ const contentPiecesRouter = router({ await contentPiecesCollection.deleteOne({ _id: contentPiece._id }); await contentsCollection.deleteOne({ contentPieceId: contentPiece._id }); await contentPieceVariantsCollection.deleteMany({ - contentPieceId: contentPiece._id + contentPieceId: contentPiece._id, + workspaceId: ctx.auth.workspaceId }); await contentVariantsCollection.deleteMany({ - contentPieceId: contentPiece._id + contentPieceId: contentPiece._id, + workspaceId: ctx.auth.workspaceId }); publishEvent(ctx, `${contentPiece.contentGroupId}`, { action: "delete", @@ -843,9 +863,9 @@ const contentPiecesRouter = router({ ...contentPiece, ...(workspaceSettings?.metadata?.canonicalLinkPattern && typeof contentPiece.canonicalLink !== "string" && { - canonicalLink: workspaceSettings.metadata.canonicalLinkPattern.replace( - /{{slug}}/g, - contentPiece.slug + canonicalLink: getCanonicalLinkFromPattern( + workspaceSettings.metadata.canonicalLinkPattern, + { slug: contentPiece.slug } ) }), id: `${contentPiece._id}`, diff --git a/packages/backend/src/routes/workspace-memberships.ts b/packages/backend/src/routes/workspace-memberships.ts index e0166454..d8ec5ed5 100644 --- a/packages/backend/src/routes/workspace-memberships.ts +++ b/packages/backend/src/routes/workspace-memberships.ts @@ -149,6 +149,7 @@ const workspaceMembershipsRouter = router({ } } ); + await updateSessionUser(ctx, `${membership.userId}`); publishEvent(ctx, `${ctx.auth.workspaceId}`, { action: "update", data: { diff --git a/packages/backend/src/routes/workspaces.ts b/packages/backend/src/routes/workspaces.ts index 93ed639b..949781f8 100644 --- a/packages/backend/src/routes/workspaces.ts +++ b/packages/backend/src/routes/workspaces.ts @@ -92,7 +92,7 @@ const workspacesRouter = router({ }) .input(z.void()) .output(z.void()) - .mutation(async ({ ctx, input }) => { + .mutation(async ({ ctx }) => { await deleteWorkspace(ctx.auth.workspaceId, ctx.db); publishEvent(ctx, `${ctx.auth.workspaceId}`, { action: "delete", diff --git a/packages/sdk/javascript/package.json b/packages/sdk/javascript/package.json index cc60ca9f..22b17d04 100644 --- a/packages/sdk/javascript/package.json +++ b/packages/sdk/javascript/package.json @@ -1,6 +1,6 @@ { "name": "@vrite/sdk", - "version": "2023.7.13-beta", + "version": "2023.7.17-beta", "private": false, "description": "JavaScript SDK and API client for Vrite - headless CMS for technical content", "license": "MIT", diff --git a/packages/sdk/javascript/src/api/client.ts b/packages/sdk/javascript/src/api/client.ts index 533c9bac..09abdd90 100644 --- a/packages/sdk/javascript/src/api/client.ts +++ b/packages/sdk/javascript/src/api/client.ts @@ -14,6 +14,7 @@ import { createWorkspaceMembershipsEndpoints } from "./workspace-memberships"; import { ExtensionEndpoints, createExtensionEndpoints } from "./extension"; +import { VariantsEndpoints, createVariantsEndpoints } from "./variants"; interface ClientConfig extends APIFetcherConfig {} interface Client { @@ -28,6 +29,7 @@ interface Client { workspaceSettings: WorkspaceSettingsEndpoints; workspaceMemberships: WorkspaceMembershipsEndpoints; extension: ExtensionEndpoints; + variants: VariantsEndpoints; reconfigure(config: ClientConfig): void; } @@ -46,6 +48,7 @@ const createClient = (config: ClientConfig): Client => { workspaceSettings: createWorkspaceSettingsEndpoints(sendRequest), workspaceMemberships: createWorkspaceMembershipsEndpoints(sendRequest), extension: createExtensionEndpoints(sendRequest), + variants: createVariantsEndpoints(sendRequest), reconfigure }; }; diff --git a/packages/sdk/javascript/src/api/content-pieces.ts b/packages/sdk/javascript/src/api/content-pieces.ts index a7466e70..31fa00d9 100644 --- a/packages/sdk/javascript/src/api/content-pieces.ts +++ b/packages/sdk/javascript/src/api/content-pieces.ts @@ -91,6 +91,7 @@ interface ContentPiecesEndpoints { input: Pick & { content?: IncludeContent; description?: "html" | "text"; + variant?: string; } ): Promise>; create = Record>( @@ -124,7 +125,7 @@ interface ContentPiecesEndpoints { | "customData" | "canonicalLink" | "coverWidth" - > & { content?: string } + > & { content?: string; variant?: string } > & Pick, "id"> ): Promise; @@ -134,6 +135,7 @@ interface ContentPiecesEndpoints { contentGroupId: string; tagId?: string; slug?: string; + variant?: string; } ): Promise, "content">>>; } @@ -147,6 +149,7 @@ const createContentPiecesEndpoints = (sendRequest: SendRequest): ContentPiecesEn input: Pick & { content?: IncludeContent; description?: "html" | "text"; + variant?: string; } ) => { return sendRequest>( @@ -192,6 +195,7 @@ const createContentPiecesEndpoints = (sendRequest: SendRequest): ContentPiecesEn contentGroupId: string; tagId?: string; slug?: string; + variant?: string; } ) => { return sendRequest, "content">>>( diff --git a/packages/sdk/javascript/src/api/index.ts b/packages/sdk/javascript/src/api/index.ts index c72d4904..b487d3f3 100644 --- a/packages/sdk/javascript/src/api/index.ts +++ b/packages/sdk/javascript/src/api/index.ts @@ -16,4 +16,5 @@ export type { WorkspaceDetails } from "./workspace"; export type { Block, Embed, Mark, WorkspaceSettings } from "./workspace-settings"; export type { ListedMember, ListedWorkspace } from "./workspace-memberships"; export type { Extension } from "./extension"; +export type { Variant } from "./variants"; export * from "./errors"; diff --git a/packages/sdk/javascript/src/api/variants.ts b/packages/sdk/javascript/src/api/variants.ts new file mode 100644 index 00000000..981e7706 --- /dev/null +++ b/packages/sdk/javascript/src/api/variants.ts @@ -0,0 +1,51 @@ +import { SendRequest } from "./request"; + +interface Variant { + /** + * Variant ID + */ + id: string; + /** + * Variant name + */ + name: string; + /** + * Variant label + */ + label: string; + /** + * Variant description + */ + description?: string; +} +interface VariantsEndpoints { + create(input: Omit): Promise>; + update(input: Partial & Pick): Promise; + delete(query: Pick): Promise; + list(): Promise; +} + +const basePath = `/variants`; +const createVariantsEndpoints = (sendRequest: SendRequest): VariantsEndpoints => ({ + create: (input) => { + return sendRequest("POST", `${basePath}`, { + body: input + }); + }, + update: (input) => { + return sendRequest("PUT", `${basePath}`, { + body: input + }); + }, + delete: (query) => { + return sendRequest("DELETE", `${basePath}`, { + params: query + }); + }, + list: () => { + return sendRequest("GET", `${basePath}/list`); + } +}); + +export { createVariantsEndpoints }; +export type { Variant, VariantsEndpoints }; diff --git a/packages/sdk/javascript/src/api/webhooks.ts b/packages/sdk/javascript/src/api/webhooks.ts index 33dee4f2..acc01caa 100644 --- a/packages/sdk/javascript/src/api/webhooks.ts +++ b/packages/sdk/javascript/src/api/webhooks.ts @@ -48,12 +48,12 @@ interface WebhooksEndpoints { const basePath = `/webhooks`; const createWebhooksEndpoints = (sendRequest: SendRequest): WebhooksEndpoints => ({ get: (input) => { - return sendRequest("GET", `${basePath}`, { + return sendRequest("GET", `${basePath}`, { params: input }); }, create: (input) => { - return sendRequest>("POST", `${basePath}`, { + return sendRequest("POST", `${basePath}`, { body: input }); }, @@ -68,7 +68,7 @@ const createWebhooksEndpoints = (sendRequest: SendRequest): WebhooksEndpoints => }); }, list: (input) => { - return sendRequest("GET", `${basePath}/list`, { + return sendRequest("GET", `${basePath}/list`, { params: input }); } diff --git a/packages/sdk/javascript/src/astro/content.astro b/packages/sdk/javascript/src/astro/content.astro index 84f138ee..3856002a 100644 --- a/packages/sdk/javascript/src/astro/content.astro +++ b/packages/sdk/javascript/src/astro/content.astro @@ -7,6 +7,7 @@ interface Props { contentPieceId?: string; content?: JSONContent; slug?: string; + variant?: string; } let content: JSONContent | null = null; @@ -30,7 +31,8 @@ if (Astro.props.content) { const contentPiece = await (client as Client).contentPieces.get({ id: contentPieceId, content: true, - description: "text" + description: "text", + variant: Astro.props.variant }); content = contentPiece.content; diff --git a/packages/sdk/javascript/src/astro/utils.ts b/packages/sdk/javascript/src/astro/utils.ts index 9b4d299c..6c0d8d9d 100644 --- a/packages/sdk/javascript/src/astro/utils.ts +++ b/packages/sdk/javascript/src/astro/utils.ts @@ -8,6 +8,7 @@ const getContentPieces = async ( limit?: number | "all"; startPage?: number; tagId?: string; + variant?: string; } ): Promise>> => { const contentPieces: Array> = []; @@ -19,7 +20,8 @@ const getContentPieces = async ( contentGroupId, page, perPage: config?.limit === "all" ? 50 : config?.limit || 50, - tagId: config?.tagId + tagId: config?.tagId, + variant: config?.variant }); contentPieces.push(...paginatedContentPieces); diff --git a/packages/sdk/javascript/src/types/index.d.ts b/packages/sdk/javascript/src/types/index.d.ts index 3c841f48..4b983e36 100644 --- a/packages/sdk/javascript/src/types/index.d.ts +++ b/packages/sdk/javascript/src/types/index.d.ts @@ -32,6 +32,7 @@ declare module "virtual:vrite" { export function Content(props: { contentPieceId?: string; slug?: string; + variant?: string; // @ts-ignore content?: import("@vrite/sdk/api").JSONContent; }): any; @@ -47,6 +48,7 @@ declare module "virtual:vrite" { limit?: number | "all"; startPage?: number; tagId?: string; + variant?: string; } // @ts-ignore ): Promise>>; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02652a2f..f8adce80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,7 +155,7 @@ importers: version: 0.48.5(vite@4.2.1) vite: specifier: ^4.2.1 - version: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + version: 4.2.1 devDependencies: '@astrojs/node': specifier: ^5.0.0 @@ -231,7 +231,7 @@ importers: version: 0.48.5(vite@4.2.1) vite: specifier: ^4.2.1 - version: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + version: 4.2.1 devDependencies: astro-robots-txt: specifier: ^0.4.1 @@ -397,13 +397,13 @@ importers: version: 4.9.5 unocss: specifier: ^0.50.6 - version: 0.50.6(postcss@8.4.24)(vite@4.2.1) + version: 0.50.6(postcss@8.4.26)(vite@4.4.4) url-slug: specifier: ^3.0.4 version: 3.0.4 vite: - specifier: ^4.2.1 - version: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + specifier: ^4.4.4 + version: 4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) y-prosemirror: specifier: ^1.2.0 version: 1.2.0(prosemirror-model@1.19.2)(prosemirror-state@1.4.3)(prosemirror-view@1.31.4)(y-protocols@1.0.5)(yjs@13.5.45) @@ -455,10 +455,10 @@ importers: version: 5.17.4 vite-plugin-solid: specifier: ^2.5.0 - version: 2.5.0(solid-js@1.7.0)(vite@4.2.1) + version: 2.5.0(solid-js@1.7.0)(vite@4.4.4) vite-tsconfig-paths: specifier: ^3.6.0 - version: 3.6.0(vite@4.2.1) + version: 3.6.0(vite@4.4.4) packages/backend: dependencies: @@ -3237,6 +3237,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: false optional: true /@esbuild/android-arm64@0.17.19: @@ -3247,6 +3248,14 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm64@0.18.13: + resolution: {integrity: sha512-j7NhycJUoUAG5kAzGf4fPWfd17N6SM3o1X6MlXVqfHvs2buFraCJzos9vbeWjLxOyBKHyPOnuCuipbhvbYtTAg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + /@esbuild/android-arm@0.15.18: resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} engines: {node: '>=12'} @@ -3262,6 +3271,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: false optional: true /@esbuild/android-arm@0.17.19: @@ -3272,12 +3282,21 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm@0.18.13: + resolution: {integrity: sha512-KwqFhxRFMKZINHzCqf8eKxE0XqWlAVPRxwy6rc7CbVFxzUWB2sA/s3hbMZeemPdhN3fKBkqOaFhTbS8xJXYIWQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + /@esbuild/android-x64@0.17.15: resolution: {integrity: sha512-MzDqnNajQZ63YkaUWVl9uuhcWyEyh69HGpMIrf+acR4otMkfLJ4sUCxqwbCyPGicE9dVlrysI3lMcDBjGiBBcQ==} engines: {node: '>=12'} cpu: [x64] os: [android] requiresBuild: true + dev: false optional: true /@esbuild/android-x64@0.17.19: @@ -3288,12 +3307,21 @@ packages: requiresBuild: true optional: true + /@esbuild/android-x64@0.18.13: + resolution: {integrity: sha512-M2eZkRxR6WnWfVELHmv6MUoHbOqnzoTVSIxgtsyhm/NsgmL+uTmag/VVzdXvmahak1I6sOb1K/2movco5ikDJg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + optional: true + /@esbuild/darwin-arm64@0.17.15: resolution: {integrity: sha512-7siLjBc88Z4+6qkMDxPT2juf2e8SJxmsbNVKFY2ifWCDT72v5YJz9arlvBw5oB4W/e61H1+HDB/jnu8nNg0rLA==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] requiresBuild: true + dev: false optional: true /@esbuild/darwin-arm64@0.17.19: @@ -3304,12 +3332,21 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-arm64@0.18.13: + resolution: {integrity: sha512-f5goG30YgR1GU+fxtaBRdSW3SBG9pZW834Mmhxa6terzcboz7P2R0k4lDxlkP7NYRIIdBbWp+VgwQbmMH4yV7w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + /@esbuild/darwin-x64@0.17.15: resolution: {integrity: sha512-NbImBas2rXwYI52BOKTW342Tm3LTeVlaOQ4QPZ7XuWNKiO226DisFk/RyPk3T0CKZkKMuU69yOvlapJEmax7cg==} engines: {node: '>=12'} cpu: [x64] os: [darwin] requiresBuild: true + dev: false optional: true /@esbuild/darwin-x64@0.17.19: @@ -3320,12 +3357,21 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-x64@0.18.13: + resolution: {integrity: sha512-RIrxoKH5Eo+yE5BtaAIMZaiKutPhZjw+j0OCh8WdvKEKJQteacq0myZvBDLU+hOzQOZWJeDnuQ2xgSScKf1Ovw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + /@esbuild/freebsd-arm64@0.17.15: resolution: {integrity: sha512-Xk9xMDjBVG6CfgoqlVczHAdJnCs0/oeFOspFap5NkYAmRCT2qTn1vJWA2f419iMtsHSLm+O8B6SLV/HlY5cYKg==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] requiresBuild: true + dev: false optional: true /@esbuild/freebsd-arm64@0.17.19: @@ -3336,12 +3382,21 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-arm64@0.18.13: + resolution: {integrity: sha512-AfRPhHWmj9jGyLgW/2FkYERKmYR+IjYxf2rtSLmhOrPGFh0KCETFzSjx/JX/HJnvIqHt/DRQD/KAaVsUKoI3Xg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + optional: true + /@esbuild/freebsd-x64@0.17.15: resolution: {integrity: sha512-3TWAnnEOdclvb2pnfsTWtdwthPfOz7qAfcwDLcfZyGJwm1SRZIMOeB5FODVhnM93mFSPsHB9b/PmxNNbSnd0RQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] requiresBuild: true + dev: false optional: true /@esbuild/freebsd-x64@0.17.19: @@ -3352,12 +3407,21 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-x64@0.18.13: + resolution: {integrity: sha512-pGzWWZJBInhIgdEwzn8VHUBang8UvFKsvjDkeJ2oyY5gZtAM6BaxK0QLCuZY+qoj/nx/lIaItH425rm/hloETA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + optional: true + /@esbuild/linux-arm64@0.17.15: resolution: {integrity: sha512-T0MVnYw9KT6b83/SqyznTs/3Jg2ODWrZfNccg11XjDehIved2oQfrX/wVuev9N936BpMRaTR9I1J0tdGgUgpJA==} engines: {node: '>=12'} cpu: [arm64] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-arm64@0.17.19: @@ -3368,12 +3432,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm64@0.18.13: + resolution: {integrity: sha512-hCzZbVJEHV7QM77fHPv2qgBcWxgglGFGCxk6KfQx6PsVIdi1u09X7IvgE9QKqm38OpkzaAkPnnPqwRsltvLkIQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-arm@0.17.15: resolution: {integrity: sha512-MLTgiXWEMAMr8nmS9Gigx43zPRmEfeBfGCwxFQEMgJ5MC53QKajaclW6XDPjwJvhbebv+RzK05TQjvH3/aM4Xw==} engines: {node: '>=12'} cpu: [arm] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-arm@0.17.19: @@ -3384,12 +3457,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm@0.18.13: + resolution: {integrity: sha512-4iMxLRMCxGyk7lEvkkvrxw4aJeC93YIIrfbBlUJ062kilUUnAiMb81eEkVvCVoh3ON283ans7+OQkuy1uHW+Hw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-ia32@0.17.15: resolution: {integrity: sha512-wp02sHs015T23zsQtU4Cj57WiteiuASHlD7rXjKUyAGYzlOKDAjqK6bk5dMi2QEl/KVOcsjwL36kD+WW7vJt8Q==} engines: {node: '>=12'} cpu: [ia32] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-ia32@0.17.19: @@ -3400,6 +3482,14 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ia32@0.18.13: + resolution: {integrity: sha512-I3OKGbynl3AAIO6onXNrup/ttToE6Rv2XYfFgLK/wnr2J+1g+7k4asLrE+n7VMhaqX+BUnyWkCu27rl+62Adug==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-loong64@0.15.18: resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} @@ -3415,6 +3505,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-loong64@0.17.19: @@ -3425,12 +3516,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-loong64@0.18.13: + resolution: {integrity: sha512-8pcKDApAsKc6WW51ZEVidSGwGbebYw2qKnO1VyD8xd6JN0RN6EUXfhXmDk9Vc4/U3Y4AoFTexQewQDJGsBXBpg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-mips64el@0.17.15: resolution: {integrity: sha512-ZLWk6czDdog+Q9kE/Jfbilu24vEe/iW/Sj2d8EVsmiixQ1rM2RKH2n36qfxK4e8tVcaXkvuV3mU5zTZviE+NVQ==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-mips64el@0.17.19: @@ -3441,12 +3541,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-mips64el@0.18.13: + resolution: {integrity: sha512-6GU+J1PLiVqWx8yoCK4Z0GnfKyCGIH5L2KQipxOtbNPBs+qNDcMJr9euxnyJ6FkRPyMwaSkjejzPSISD9hb+gg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-ppc64@0.17.15: resolution: {integrity: sha512-mY6dPkIRAiFHRsGfOYZC8Q9rmr8vOBZBme0/j15zFUKM99d4ILY4WpOC7i/LqoY+RE7KaMaSfvY8CqjJtuO4xg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-ppc64@0.17.19: @@ -3457,12 +3566,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ppc64@0.18.13: + resolution: {integrity: sha512-pfn/OGZ8tyR8YCV7MlLl5hAit2cmS+j/ZZg9DdH0uxdCoJpV7+5DbuXrR+es4ayRVKIcfS9TTMCs60vqQDmh+w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-riscv64@0.17.15: resolution: {integrity: sha512-EcyUtxffdDtWjjwIH8sKzpDRLcVtqANooMNASO59y+xmqqRYBBM7xVLQhqF7nksIbm2yHABptoioS9RAbVMWVA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-riscv64@0.17.19: @@ -3473,12 +3591,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-riscv64@0.18.13: + resolution: {integrity: sha512-aIbhU3LPg0lOSCfVeGHbmGYIqOtW6+yzO+Nfv57YblEK01oj0mFMtvDJlOaeAZ6z0FZ9D13oahi5aIl9JFphGg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-s390x@0.17.15: resolution: {integrity: sha512-BuS6Jx/ezxFuHxgsfvz7T4g4YlVrmCmg7UAwboeyNNg0OzNzKsIZXpr3Sb/ZREDXWgt48RO4UQRDBxJN3B9Rbg==} engines: {node: '>=12'} cpu: [s390x] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-s390x@0.17.19: @@ -3489,12 +3616,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-s390x@0.18.13: + resolution: {integrity: sha512-Pct1QwF2sp+5LVi4Iu5Y+6JsGaV2Z2vm4O9Dd7XZ5tKYxEHjFtb140fiMcl5HM1iuv6xXO8O1Vrb1iJxHlv8UA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/linux-x64@0.17.15: resolution: {integrity: sha512-JsdS0EgEViwuKsw5tiJQo9UdQdUJYuB+Mf6HxtJSPN35vez1hlrNb1KajvKWF5Sa35j17+rW1ECEO9iNrIXbNg==} engines: {node: '>=12'} cpu: [x64] os: [linux] requiresBuild: true + dev: false optional: true /@esbuild/linux-x64@0.17.19: @@ -3505,12 +3641,21 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-x64@0.18.13: + resolution: {integrity: sha512-zTrIP0KzYP7O0+3ZnmzvUKgGtUvf4+piY8PIO3V8/GfmVd3ZyHJGz7Ht0np3P1wz+I8qJ4rjwJKqqEAbIEPngA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + /@esbuild/netbsd-x64@0.17.15: resolution: {integrity: sha512-R6fKjtUysYGym6uXf6qyNephVUQAGtf3n2RCsOST/neIwPqRWcnc3ogcielOd6pT+J0RDR1RGcy0ZY7d3uHVLA==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] requiresBuild: true + dev: false optional: true /@esbuild/netbsd-x64@0.17.19: @@ -3521,12 +3666,21 @@ packages: requiresBuild: true optional: true + /@esbuild/netbsd-x64@0.18.13: + resolution: {integrity: sha512-I6zs10TZeaHDYoGxENuksxE1sxqZpCp+agYeW039yqFwh3MgVvdmXL5NMveImOC6AtpLvE4xG5ujVic4NWFIDQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + optional: true + /@esbuild/openbsd-x64@0.17.15: resolution: {integrity: sha512-mVD4PGc26b8PI60QaPUltYKeSX0wxuy0AltC+WCTFwvKCq2+OgLP4+fFd+hZXzO2xW1HPKcytZBdjqL6FQFa7w==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] requiresBuild: true + dev: false optional: true /@esbuild/openbsd-x64@0.17.19: @@ -3537,12 +3691,21 @@ packages: requiresBuild: true optional: true + /@esbuild/openbsd-x64@0.18.13: + resolution: {integrity: sha512-W5C5nczhrt1y1xPG5bV+0M12p2vetOGlvs43LH8SopQ3z2AseIROu09VgRqydx5qFN7y9qCbpgHLx0kb0TcW7g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + optional: true + /@esbuild/sunos-x64@0.17.15: resolution: {integrity: sha512-U6tYPovOkw3459t2CBwGcFYfFRjivcJJc1WC8Q3funIwX8x4fP+R6xL/QuTPNGOblbq/EUDxj9GU+dWKX0oWlQ==} engines: {node: '>=12'} cpu: [x64] os: [sunos] requiresBuild: true + dev: false optional: true /@esbuild/sunos-x64@0.17.19: @@ -3553,12 +3716,21 @@ packages: requiresBuild: true optional: true + /@esbuild/sunos-x64@0.18.13: + resolution: {integrity: sha512-X/xzuw4Hzpo/yq3YsfBbIsipNgmsm8mE/QeWbdGdTTeZ77fjxI2K0KP3AlhZ6gU3zKTw1bKoZTuKLnqcJ537qw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + optional: true + /@esbuild/win32-arm64@0.17.15: resolution: {integrity: sha512-W+Z5F++wgKAleDABemiyXVnzXgvRFs+GVKThSI+mGgleLWluv0D7Diz4oQpgdpNzh4i2nNDzQtWbjJiqutRp6Q==} engines: {node: '>=12'} cpu: [arm64] os: [win32] requiresBuild: true + dev: false optional: true /@esbuild/win32-arm64@0.17.19: @@ -3569,12 +3741,21 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-arm64@0.18.13: + resolution: {integrity: sha512-4CGYdRQT/ILd+yLLE5i4VApMPfGE0RPc/wFQhlluDQCK09+b4JDbxzzjpgQqTPrdnP7r5KUtGVGZYclYiPuHrw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + /@esbuild/win32-ia32@0.17.15: resolution: {integrity: sha512-Muz/+uGgheShKGqSVS1KsHtCyEzcdOn/W/Xbh6H91Etm+wiIfwZaBn1W58MeGtfI8WA961YMHFYTthBdQs4t+w==} engines: {node: '>=12'} cpu: [ia32] os: [win32] requiresBuild: true + dev: false optional: true /@esbuild/win32-ia32@0.17.19: @@ -3585,12 +3766,21 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-ia32@0.18.13: + resolution: {integrity: sha512-D+wKZaRhQI+MUGMH+DbEr4owC2D7XnF+uyGiZk38QbgzLcofFqIOwFs7ELmIeU45CQgfHNy9Q+LKW3cE8g37Kg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + /@esbuild/win32-x64@0.17.15: resolution: {integrity: sha512-DjDa9ywLUUmjhV2Y9wUTIF+1XsmuFGvZoCmOWkli1XcNAh5t25cc7fgsCx4Zi/Uurep3TTLyDiKATgGEg61pkA==} engines: {node: '>=12'} cpu: [x64] os: [win32] requiresBuild: true + dev: false optional: true /@esbuild/win32-x64@0.17.19: @@ -3601,6 +3791,14 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-x64@0.18.13: + resolution: {integrity: sha512-iVl6lehAfJS+VmpF3exKpNQ8b0eucf5VWfzR8S7xFve64NBNz2jPUgx1X93/kfnkfgP737O+i1k54SVQS7uVZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.15.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5865,12 +6063,12 @@ packages: - vite dev: false - /@unocss/astro@0.50.6(vite@4.2.1): + /@unocss/astro@0.50.6(vite@4.4.4): resolution: {integrity: sha512-gSGQIh+hBCor7KbAylu4wBQaMZp3AkT8dW9E6jrecpluVxzGGdar93a79Wqs76OlWiu7hr8zOyRbSDgfkwDung==} dependencies: '@unocss/core': 0.50.6 '@unocss/reset': 0.50.6 - '@unocss/vite': 0.50.6(vite@4.2.1) + '@unocss/vite': 0.50.6(vite@4.4.4) transitivePeerDependencies: - rollup - vite @@ -5958,7 +6156,7 @@ packages: sirv: 2.0.3 dev: false - /@unocss/postcss@0.50.6(postcss@8.4.24): + /@unocss/postcss@0.50.6(postcss@8.4.26): resolution: {integrity: sha512-pRPBVPmwjsVu3v1T0hQuqq3L4K74Wobo6pGDypvK/MuzWdWDhHiktWwmXGNxlYSWK7mGJBIa+vI10pp4e15OUw==} engines: {node: '>=14'} peerDependencies: @@ -5969,7 +6167,7 @@ packages: css-tree: 2.3.1 fast-glob: 3.2.12 magic-string: 0.30.0 - postcss: 8.4.24 + postcss: 8.4.26 dev: false /@unocss/preset-attributify@0.48.5: @@ -6166,12 +6364,12 @@ packages: chokidar: 3.5.3 fast-glob: 3.2.12 magic-string: 0.27.0 - vite: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + vite: 4.2.1 transitivePeerDependencies: - rollup dev: false - /@unocss/vite@0.50.6(vite@4.2.1): + /@unocss/vite@0.50.6(vite@4.4.4): resolution: {integrity: sha512-BBfNHWRTD69ToNX4NlYdORFG6uH51HCjX+vZ8HAVgYHpSeVWziG3srnGYOk5IS0pKPzQGoLBlz8rstMsGhrAjA==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 @@ -6186,7 +6384,7 @@ packages: chokidar: 3.5.3 fast-glob: 3.2.12 magic-string: 0.30.0 - vite: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + vite: 4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) transitivePeerDependencies: - rollup dev: false @@ -6464,13 +6662,14 @@ packages: typescript: 4.9.5 unist-util-visit: 4.1.2 vfile: 5.3.7 - vite: 4.3.9 - vitefu: 0.2.4(vite@4.3.9) + vite: 4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + vitefu: 0.2.4(vite@4.4.4) yargs-parser: 21.1.1 zod: 3.21.4 transitivePeerDependencies: - '@types/node' - less + - lightningcss - sass - stylus - sugarss @@ -7595,6 +7794,7 @@ packages: '@esbuild/win32-arm64': 0.17.15 '@esbuild/win32-ia32': 0.17.15 '@esbuild/win32-x64': 0.17.15 + dev: false /esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} @@ -7625,6 +7825,35 @@ packages: '@esbuild/win32-ia32': 0.17.19 '@esbuild/win32-x64': 0.17.19 + /esbuild@0.18.13: + resolution: {integrity: sha512-vhg/WR/Oiu4oUIkVhmfcc23G6/zWuEQKFS+yiosSHe4aN6+DQRXIfeloYGibIfVhkr4wyfuVsGNLr+sQU1rWWw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.13 + '@esbuild/android-arm64': 0.18.13 + '@esbuild/android-x64': 0.18.13 + '@esbuild/darwin-arm64': 0.18.13 + '@esbuild/darwin-x64': 0.18.13 + '@esbuild/freebsd-arm64': 0.18.13 + '@esbuild/freebsd-x64': 0.18.13 + '@esbuild/linux-arm': 0.18.13 + '@esbuild/linux-arm64': 0.18.13 + '@esbuild/linux-ia32': 0.18.13 + '@esbuild/linux-loong64': 0.18.13 + '@esbuild/linux-mips64el': 0.18.13 + '@esbuild/linux-ppc64': 0.18.13 + '@esbuild/linux-riscv64': 0.18.13 + '@esbuild/linux-s390x': 0.18.13 + '@esbuild/linux-x64': 0.18.13 + '@esbuild/netbsd-x64': 0.18.13 + '@esbuild/openbsd-x64': 0.18.13 + '@esbuild/sunos-x64': 0.18.13 + '@esbuild/win32-arm64': 0.18.13 + '@esbuild/win32-ia32': 0.18.13 + '@esbuild/win32-x64': 0.18.13 + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -10535,6 +10764,14 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.26: + resolution: {integrity: sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -11092,6 +11329,13 @@ packages: optionalDependencies: fsevents: 2.3.2 + /rollup@3.26.2: + resolution: {integrity: sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + /rope-sequence@1.3.4: resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} dev: false @@ -12081,7 +12325,7 @@ packages: - vite dev: false - /unocss@0.50.6(postcss@8.4.24)(vite@4.2.1): + /unocss@0.50.6(postcss@8.4.26)(vite@4.4.4): resolution: {integrity: sha512-7cKiIB/ssAPvCDUcFMs0jm0FzIyQKfgIjUzBYZ5dVFthOvN5dcFh7bCZE9dIM862n7oW8FjbkTxwdTbRqqJQVQ==} engines: {node: '>=14'} peerDependencies: @@ -12090,10 +12334,10 @@ packages: '@unocss/webpack': optional: true dependencies: - '@unocss/astro': 0.50.6(vite@4.2.1) + '@unocss/astro': 0.50.6(vite@4.4.4) '@unocss/cli': 0.50.6 '@unocss/core': 0.50.6 - '@unocss/postcss': 0.50.6(postcss@8.4.24) + '@unocss/postcss': 0.50.6(postcss@8.4.26) '@unocss/preset-attributify': 0.50.6 '@unocss/preset-icons': 0.50.6 '@unocss/preset-mini': 0.50.6 @@ -12107,7 +12351,7 @@ packages: '@unocss/transformer-compile-class': 0.50.6 '@unocss/transformer-directives': 0.50.6 '@unocss/transformer-variant-group': 0.50.6 - '@unocss/vite': 0.50.6(vite@4.2.1) + '@unocss/vite': 0.50.6(vite@4.4.4) transitivePeerDependencies: - postcss - rollup @@ -12227,7 +12471,7 @@ packages: unist-util-stringify-position: 3.0.3 vfile-message: 3.1.4 - /vite-plugin-solid@2.5.0(solid-js@1.7.0)(vite@4.2.1): + /vite-plugin-solid@2.5.0(solid-js@1.7.0)(vite@4.4.4): resolution: {integrity: sha512-VneGd3RyFJvwaiffsqgymeMaofn0IzQLPwDzafTV2f1agoWeeJlk5VrI5WqT9BTtLe69vNNbCJWqLhHr9fOdDw==} peerDependencies: solid-js: ^1.3.17 || ^1.4.0 || ^1.5.0 || ^1.6.0 @@ -12239,13 +12483,13 @@ packages: merge-anything: 5.1.7 solid-js: 1.7.0 solid-refresh: 0.4.3(solid-js@1.7.0) - vite: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) - vitefu: 0.2.4(vite@4.2.1) + vite: 4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + vitefu: 0.2.4(vite@4.4.4) transitivePeerDependencies: - supports-color dev: true - /vite-tsconfig-paths@3.6.0(vite@4.2.1): + /vite-tsconfig-paths@3.6.0(vite@4.4.4): resolution: {integrity: sha512-UfsPYonxLqPD633X8cWcPFVuYzx/CMNHAjZTasYwX69sXpa4gNmQkR0XCjj82h7zhLGdTWagMjC1qfb9S+zv0A==} peerDependencies: vite: '>2.0.0-0' @@ -12254,12 +12498,12 @@ packages: globrex: 0.1.2 recrawl-sync: 2.2.3 tsconfig-paths: 4.2.0 - vite: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + vite: 4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) transitivePeerDependencies: - supports-color dev: true - /vite@4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4): + /vite@4.2.1: resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -12284,15 +12528,13 @@ packages: terser: optional: true dependencies: - '@types/node': 18.7.1 esbuild: 0.17.15 postcss: 8.4.24 resolve: 1.22.2 rollup: 3.25.1 - sass: 1.58.0 - terser: 5.17.4 optionalDependencies: fsevents: 2.3.2 + dev: false /vite@4.3.9: resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} @@ -12324,6 +12566,44 @@ packages: rollup: 3.25.1 optionalDependencies: fsevents: 2.3.2 + dev: true + + /vite@4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4): + resolution: {integrity: sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.7.1 + esbuild: 0.18.13 + postcss: 8.4.26 + rollup: 3.26.2 + sass: 1.58.0 + terser: 5.17.4 + optionalDependencies: + fsevents: 2.3.2 /vitefu@0.2.4(vite@4.2.1): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} @@ -12333,9 +12613,10 @@ packages: vite: optional: true dependencies: - vite: 4.2.1(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) + vite: 4.2.1 + dev: false - /vitefu@0.2.4(vite@4.3.9): + /vitefu@0.2.4(vite@4.4.4): resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} peerDependencies: vite: ^3.0.0 || ^4.0.0 @@ -12343,7 +12624,7 @@ packages: vite: optional: true dependencies: - vite: 4.3.9 + vite: 4.4.4(@types/node@18.7.1)(sass@1.58.0)(terser@5.17.4) /vscode-css-languageservice@6.2.6: resolution: {integrity: sha512-SA2WkeOecIpUiEbZnjOsP/fI5CRITZEiQGSHXKiDQDwLApfKcnLhZwMtOBbIifSzESVcQa7b/shX/nbnF4NoCg==}