diff --git a/.gitignore b/.gitignore index f6f6a43c7..5cde76776 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ plugins/faustwp/.docker/plugins/index.php # Ignore the WordPress source where used by various development environments wordpress/ +node_modules/ \ No newline at end of file diff --git a/examples/nextjs/kitchen-sink/package.json b/examples/nextjs/kitchen-sink/package.json index 3faedb491..34a66a77e 100644 --- a/examples/nextjs/kitchen-sink/package.json +++ b/examples/nextjs/kitchen-sink/package.json @@ -9,14 +9,17 @@ "lint": "next lint" }, "dependencies": { - "@faustjs/nextjs": "workspace:*", - "@faustjs/template-hierarchy": "workspace:*", + "@faustjs/auth": "workspace:*", "@faustjs/data-fetching": "workspace:*", + "@faustjs/nextjs": "workspace:*", "graphql": "^16.11.0", "graphql-tag": "^2.12.6", + "iron-session": "^8.0.4", "next": "15.2.4", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "@wpengine/faust-next-toolbar": "file:/../../../packages/faust-next-toolbar", + "@wpengine/hwp-toolbar": "github:wpengine/hwptoolkit#toolbar" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/examples/nextjs/kitchen-sink/src/components/BlogPostItem.js b/examples/nextjs/kitchen-sink/src/components/BlogPostItem.js index 58740f7f0..ce23c70eb 100644 --- a/examples/nextjs/kitchen-sink/src/components/BlogPostItem.js +++ b/examples/nextjs/kitchen-sink/src/components/BlogPostItem.js @@ -1,32 +1,42 @@ -import Link from "next/link"; +import Link from 'next/link'; export function BlogPostItem({ post }) { - const { title, date, excerpt, uri, featuredImage } = post ?? {}; + const { title, date, excerpt, uri, featuredImage } = post ?? {}; - return ( -
- + return ( +
+ - {featuredImage && ( - - )} + {featuredImage && ( + + )} -

- - {title} - -

+

+ + {title} + +

-
+
- - Read more - -
- ); + + Read more + +
+ ); } diff --git a/examples/nextjs/kitchen-sink/src/components/Header.js b/examples/nextjs/kitchen-sink/src/components/Header.js index 2d6ad7137..9bcc0e80c 100644 --- a/examples/nextjs/kitchen-sink/src/components/Header.js +++ b/examples/nextjs/kitchen-sink/src/components/Header.js @@ -1,20 +1,100 @@ -/* eslint-disable @next/next/no-html-link-for-pages */ import Link from 'next/link'; +import { useState } from 'react'; +import Login from './Login'; +import { useLogout, useUser } from '@faustjs/nextjs/pages'; +import { useRouter } from 'next/router'; export default function Header() { + const { user, refetch, isAuthenticated } = useUser(); + const { logout } = useLogout(); + const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); + const route = useRouter(); + + const openLoginModal = () => setIsLoginModalOpen(true); + const closeLoginModal = () => setIsLoginModalOpen(false); + + const onLoggedIn = () => { + closeLoginModal(); + refetch(); + }; + + const logoutUser = () => { + logout({ + onSuccess: () => { + refetch(); + }, + }); + }; + return ( -
-
-
- Headless + <> +
+
+
+ Headless +
+ +
+
- -
-
+ {isLoginModalOpen && ( +
+
+ +

Login

+ + +
+
+ )} + ); } diff --git a/examples/nextjs/kitchen-sink/src/components/Layout.js b/examples/nextjs/kitchen-sink/src/components/Layout.js index 773b4f45c..eb1153cdf 100644 --- a/examples/nextjs/kitchen-sink/src/components/Layout.js +++ b/examples/nextjs/kitchen-sink/src/components/Layout.js @@ -1,17 +1,13 @@ -import { useRouter } from 'next/router'; import Header from './Header'; -import PreviewButton from './PreviewButton'; export default function Layout({ children }) { - const router = useRouter(); +; return ( <>
{children} - - {router.isPreview && }
); diff --git a/examples/nextjs/kitchen-sink/src/components/Login.js b/examples/nextjs/kitchen-sink/src/components/Login.js new file mode 100644 index 000000000..d6fb9c152 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/components/Login.js @@ -0,0 +1,64 @@ +import { useLogin } from '@faustjs/nextjs/pages'; +import { useState } from 'react'; + +export default function Login({ closeModal = () => {}, onSuccess = () => {} }) { + const [usernameEmail, setUsernameEmail] = useState(''); + const [password, setPassword] = useState(''); + + const { login, isLoading, error } = useLogin(); + + const submitForm = (e) => { + e.preventDefault(); + + login({ + input: { + credentials: { + password, + username: usernameEmail, + }, + }, + onSuccess: () => { + onSuccess(); + closeModal(); + }, + }); + }; + + return ( +
+
+ setUsernameEmail(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" + required + /> +
+
+ setPassword(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" + required + /> +
+ {error && ( +
+ {error.message || 'Login failed. Please try again.'} +
+ )} + +
+ ); +} diff --git a/examples/nextjs/kitchen-sink/src/components/PreviewButton.js b/examples/nextjs/kitchen-sink/src/components/PreviewButton.js deleted file mode 100644 index 3c8a2122f..000000000 --- a/examples/nextjs/kitchen-sink/src/components/PreviewButton.js +++ /dev/null @@ -1,17 +0,0 @@ -import { useRouter } from "next/router"; - -export default function PreviewButton() { - const router = useRouter(); - - return ( -
- Preview mode is on - - -
- ); -} diff --git a/examples/nextjs/kitchen-sink/src/constants/sessionConfig.js b/examples/nextjs/kitchen-sink/src/constants/sessionConfig.js new file mode 100644 index 000000000..08451c28b --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/constants/sessionConfig.js @@ -0,0 +1,7 @@ +import { defaultIronOptions } from '@faustjs/auth'; + +export const sessionConfig = { + ...defaultIronOptions, + password: process.env.SESSION_PASSWORD, + cookieName: 'faust-auth', +}; diff --git a/examples/nextjs/kitchen-sink/src/lib/resolveWpRoute.js b/examples/nextjs/kitchen-sink/src/lib/resolveWpRoute.js new file mode 100644 index 000000000..62521b7a4 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/lib/resolveWpRoute.js @@ -0,0 +1,52 @@ +import availableTemplates from '@/wp-templates'; +import availableQueries from '@/wp-templates/templateQueries'; +import { fetchTemplateQueries } from '@faustjs/data-fetching'; +import { uriToTemplate } from '@faustjs/nextjs/pages'; + +// Resolves the WordPress route based on the identifier (slug or ID) and fetches the necessary data +// for the corresponding template. It returns the props needed for rendering the page. +// If the route or template is not found, it returns a 404 response. + +export async function resolveWpRoute(identifier, isPreview, client) { + const uri = identifier ? `/${identifier.join('/')}/` : '/'; + + const variables = isPreview + ? { + id: identifier?.[0], + asPreview: true, + } + : { uri }; + + try { + const templateData = await uriToTemplate({ + ...variables, + availableTemplates: Object.keys(availableTemplates), + wordpressUrl: process.env.NEXT_PUBLIC_WORDPRESS_URL, + }); + + if ( + !templateData?.template?.id || + templateData?.template?.id === '404 Not Found' + ) { + return { notFound: true }; + } + + const queriesData = await fetchTemplateQueries({ + availableQueries, + templateData, + client, + locale: templateData?.seedNode?.locale, + }); + + return { + props: { + uri, + templateData: JSON.parse(JSON.stringify(templateData)), + queriesData, + }, + }; + } catch (error) { + console.error('Error resolving template:', error); + return { notFound: true }; + } +} diff --git a/examples/nextjs/kitchen-sink/src/pages/[[...identifier]].js b/examples/nextjs/kitchen-sink/src/pages/[[...identifier]].js index 3eea8f967..8f3815de1 100644 --- a/examples/nextjs/kitchen-sink/src/pages/[[...identifier]].js +++ b/examples/nextjs/kitchen-sink/src/pages/[[...identifier]].js @@ -1,19 +1,57 @@ +import { resolveWpRoute } from '@/lib/resolveWpRoute'; +import { Toolbar } from '@/toolbar'; import { getAuthString } from '@/utils/getAuthString'; import availableTemplates from '@/wp-templates'; -import availableQueries from '@/wp-templates/templateQueries'; import { createDefaultClient, setGraphQLClient, - uriToTemplate, + useUser, } from '@faustjs/nextjs/pages'; -import { fetchTemplateQueries } from '@faustjs/data-fetching'; +import { useRouter } from 'next/router'; + +// This is a catch-all dynamic route to handle all WordPress pages and posts. +// It uses getStaticProps and getStaticPaths for SSG with fallback blocking. +// It also supports Draft Mode previews with application passwords. export default function Page(props) { - const { templateData } = props; + const router = useRouter(); + const { templateData, queriesData } = props; + const { user = {}, isAuthenticated } = useUser(); + + const { getPage, getPost } = queriesData || {}; + const result = getPage || getPost; + const content = result?.data?.page || result?.data?.post; const PageTemplate = availableTemplates[templateData?.template?.id]; - return ; + return ( + <> + {isAuthenticated && ( + + )} + + + ); } export async function getStaticProps({ @@ -34,47 +72,7 @@ export async function getStaticProps({ setGraphQLClient(client); - const uri = params?.identifier ? `/${params.identifier.join('/')}/` : '/'; - - const variables = isDraftModeEnabled - ? { - id: params.identifier?.[0], - asPreview: true, - } - : { uri }; - - try { - const templateData = await uriToTemplate({ - ...variables, - availableTemplates: Object.keys(availableTemplates), - wordpressUrl: process.env.NEXT_PUBLIC_WORDPRESS_URL, - }); - - if ( - !templateData?.template?.id || - templateData?.template?.id === '404 Not Found' - ) { - return { notFound: true }; - } - - const queriesData = await fetchTemplateQueries({ - availableQueries, - templateData, - client, - locale: templateData?.seedNode?.locale, - }); - - return { - props: { - uri, - templateData: JSON.parse(JSON.stringify(templateData)), - queriesData, - }, - }; - } catch (error) { - console.error('Error resolving template:', error); - return { notFound: true }; - } + return await resolveWpRoute(params?.identifier, isDraftModeEnabled, client); } export async function getStaticPaths() { diff --git a/examples/nextjs/kitchen-sink/src/pages/_app.js b/examples/nextjs/kitchen-sink/src/pages/_app.js index af8ade7e2..ac53fddec 100644 --- a/examples/nextjs/kitchen-sink/src/pages/_app.js +++ b/examples/nextjs/kitchen-sink/src/pages/_app.js @@ -1,5 +1,6 @@ import Layout from '@/components/Layout'; import '@/styles/globals.css'; +import '@/toolbar/default.css'; export default function App({ Component, pageProps }) { return ( diff --git a/examples/nextjs/kitchen-sink/src/pages/api/preview.js b/examples/nextjs/kitchen-sink/src/pages/api/preview.js index 7cbca238b..e8d7c4773 100644 --- a/examples/nextjs/kitchen-sink/src/pages/api/preview.js +++ b/examples/nextjs/kitchen-sink/src/pages/api/preview.js @@ -1,6 +1,9 @@ import { getAuthString } from '@/utils/getAuthString'; import { enablePreview } from '@faustjs/nextjs/pages'; +// This is a preview approach using Next.js Draft Mode with application passwords. +// To enable Draft Mode previews, set the HWP Previews setting to: http://your.frontend/api/preview?secret=YOURSECRET&id={ID} + export default enablePreview({ wordpressUrl: process.env.NEXT_PUBLIC_WORDPRESS_URL, expectedSecret: process.env.WP_PREVIEW_SECRET, diff --git a/examples/nextjs/kitchen-sink/src/pages/api/session/[action].js b/examples/nextjs/kitchen-sink/src/pages/api/session/[action].js new file mode 100644 index 000000000..7d0ceb980 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/pages/api/session/[action].js @@ -0,0 +1,19 @@ +import { sessionConfig } from '@/constants/sessionConfig'; +import { authRouter } from '@faustjs/auth'; +import { createDefaultClient, setGraphQLClient } from '@faustjs/nextjs/pages'; + +export default function handler(req, res) { + const { action } = req.query; + + const client = createDefaultClient(process.env.NEXT_PUBLIC_WORDPRESS_URL); + setGraphQLClient(client); + + return authRouter({ + client, + ironOptions: sessionConfig, + action, + loginProvider: 'PASSWORD', + req, + res, + }); +} diff --git a/examples/nextjs/kitchen-sink/src/pages/login.js b/examples/nextjs/kitchen-sink/src/pages/login.js new file mode 100644 index 000000000..8b90b18ab --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/pages/login.js @@ -0,0 +1,17 @@ +import { default as LoginComponent } from '@/components/Login'; +import { useRouter } from 'next/router'; + +export default function Login() { + const router = useRouter(); + + return ( +
+

Login

+ { + router.push('/'); + }} + /> +
+ ); +} diff --git a/examples/nextjs/kitchen-sink/src/pages/preview/[...identifier].js b/examples/nextjs/kitchen-sink/src/pages/preview/[...identifier].js new file mode 100644 index 000000000..ff4c6b0bb --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/pages/preview/[...identifier].js @@ -0,0 +1,38 @@ +import { sessionConfig } from '@/constants/sessionConfig'; +import { resolveWpRoute } from '@/lib/resolveWpRoute'; +import availableTemplates from '@/wp-templates'; +import { createDefaultClient, setGraphQLClient } from '@faustjs/nextjs/pages'; +import { getIronSession } from 'iron-session'; + +// This is an alternative preview approach to use the user credentials stored in the session +// instead of the Next.js Draft Mode with application passwords. +// To enable previews, set the HWP Previews plugin setting to: http://your.frontend/preview/{ID} + +export default function Preview(props) { + const { templateData } = props; + + const PageTemplate = availableTemplates[templateData?.template?.id]; + + return ; +} + +export async function getServerSideProps({ req, res, params }) { + const session = await getIronSession(req, res, sessionConfig); + + if (!session?.authToken) { + return { + redirect: { + destination: '/login', + permanent: false, + }, + }; + } + + const client = createDefaultClient(process.env.NEXT_PUBLIC_WORDPRESS_URL, { + Authorization: `Bearer ${session?.authToken}`, + }); + + setGraphQLClient(client); + + return await resolveWpRoute(params?.identifier, true, client); +} diff --git a/examples/nextjs/kitchen-sink/src/queries/getPage.js b/examples/nextjs/kitchen-sink/src/queries/getPage.js index 76646f1a5..0091f0d7d 100644 --- a/examples/nextjs/kitchen-sink/src/queries/getPage.js +++ b/examples/nextjs/kitchen-sink/src/queries/getPage.js @@ -3,9 +3,13 @@ import gql from 'graphql-tag'; export const GET_PAGE = gql` query GetPage($databaseId: ID!, $asPreview: Boolean = false) { page(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) { + databaseId title content date + slug + status + contentTypeName author { node { name diff --git a/examples/nextjs/kitchen-sink/src/queries/getPost.js b/examples/nextjs/kitchen-sink/src/queries/getPost.js index 30ba7b8d7..70e8e7674 100644 --- a/examples/nextjs/kitchen-sink/src/queries/getPost.js +++ b/examples/nextjs/kitchen-sink/src/queries/getPost.js @@ -3,9 +3,13 @@ import gql from 'graphql-tag'; export const GET_POST = gql` query GetPost($databaseId: ID!, $asPreview: Boolean = false) { post(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) { + databaseId title content date + slug + status + contentTypeName author { node { name diff --git a/examples/nextjs/kitchen-sink/src/toolbar/Toolbar.jsx b/examples/nextjs/kitchen-sink/src/toolbar/Toolbar.jsx new file mode 100644 index 000000000..f7cf4bfc3 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/Toolbar.jsx @@ -0,0 +1,87 @@ +import { useToolbar } from '@wpengine/hwp-toolbar/react'; +import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react'; +import { createToolbar } from './createToolbar'; +import { registerDefaultNodes } from './registerNodes'; +import { renderNode } from './renderNode'; + +export const Toolbar = forwardRef(function Toolbar({ user, post, site, config = {}, onToolbarReady, isPreview, disablePreviewUrl }, ref) { + const toolbar = useMemo(() => createToolbar(config), []); + const { nodes } = useToolbar(toolbar); + const [position, setPosition] = useState(config.position || 'bottom'); + const [activePanel, setActivePanel] = useState(null); + + // Expose toolbar instance to parent components + useImperativeHandle(ref, () => ({ + toolbar, + register: (id, nodeConfig) => toolbar.register(id, nodeConfig), + unregister: (id) => toolbar.unregister(id), + getNode: (id) => toolbar.getNode(id), + update: (id, updates) => toolbar.update(id, updates), + setConfig: (config) => toolbar.setConfig(config), + getConfig: () => toolbar.getConfig() + }), [toolbar]); + + // Notify parent when toolbar is ready + useEffect(() => { + if (onToolbarReady && typeof onToolbarReady === 'function') { + onToolbarReady(toolbar); + } + }, [toolbar, onToolbarReady]); + + useEffect(() => { + if (user || post || site) { + toolbar.setWordPressContext({ user, site, post }); + } + }, [toolbar, user, post, site]); + + useEffect(() => { + const currentPosition = toolbar.getConfig()?.position || 'top'; + setPosition(currentPosition); + + const cleanup = registerDefaultNodes(toolbar, { position, setPosition, user, site, isPreview, disablePreviewUrl }); + return cleanup; + }, [toolbar, position, user, site]); + + useEffect(() => { + const currentPosition = toolbar.getConfig()?.position || 'bottom'; + document.body.classList.add(`faust-toolbar-has-toolbar-${currentPosition}`); + + const unsubscribe = toolbar.subscribe(() => { + const config = toolbar.getConfig(); + const newPosition = config?.position || 'bottom'; + if (currentPosition !== newPosition) { + document.body.classList.remove(`faust-toolbar-has-toolbar-${currentPosition}`); + document.body.classList.add(`faust-toolbar-has-toolbar-${newPosition}`); + } + }); + + return () => { + unsubscribe(); + document.body.classList.remove(`faust-toolbar-has-toolbar-${currentPosition}`); + }; + }, [toolbar]); + + return ( +
+
+
+
+ {nodes.filter(node => node.position !== 'right').map(node => renderNode(node, { activePanel, setActivePanel }))} +
+
+ {nodes.filter(node => node.position === 'right').map(node => renderNode(node, { activePanel, setActivePanel }))} +
+
+
+ + {activePanel && ( +
+ {(() => { + const activeNode = nodes.find(node => node.panel === activePanel); + return activeNode?.panelComponent ||
No content
; + })()} +
+ )} +
+ ); +}); diff --git a/examples/nextjs/kitchen-sink/src/toolbar/assets/faust-logo.svg b/examples/nextjs/kitchen-sink/src/toolbar/assets/faust-logo.svg new file mode 100644 index 000000000..ac1e4c102 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/assets/faust-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/edit.svg b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/edit.svg new file mode 100644 index 000000000..86e69b263 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/eye-off.svg b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/eye-off.svg new file mode 100644 index 000000000..bf0df607b --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/eye-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/eye.svg b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/eye.svg new file mode 100644 index 000000000..3b43a992f --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/eye.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/settings.svg b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/settings.svg new file mode 100644 index 000000000..6274b15be --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/wordpress.svg b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/wordpress.svg new file mode 100644 index 000000000..a0f2b691f --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/assets/icons/wordpress.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/nextjs/kitchen-sink/src/toolbar/createToolbar.js b/examples/nextjs/kitchen-sink/src/toolbar/createToolbar.js new file mode 100644 index 000000000..60e6cb8f9 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/createToolbar.js @@ -0,0 +1,21 @@ +import { Toolbar as BaseToolbar } from '@wpengine/hwp-toolbar'; + +/** + * Creates and configures the toolbar instance + */ +export function createToolbar(config = {}) { + return new BaseToolbar({ + position: 'top', + onPreviewChange: (enabled) => { + // Emit event for Next.js integration + if (typeof window !== 'undefined') { + window.dispatchEvent( + new CustomEvent('faust:preview-change', { + detail: { enabled }, + }), + ); + } + }, + ...config, + }); +} diff --git a/examples/nextjs/kitchen-sink/src/toolbar/default.css b/examples/nextjs/kitchen-sink/src/toolbar/default.css new file mode 100644 index 000000000..6a83d2ce3 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/default.css @@ -0,0 +1,158 @@ +/* CSS Custom Properties (Variables) */ +:root { + --faust-toolbar-text-color: #ffffff; + --faust-toolbar-border-color: #555962; + --faust-toolbar-bg-primary: #23262F; + --faust-toolbar-btn-hover: #383b43; +} + +/* Body spacing when toolbar is active */ +body.faust-toolbar-has-toolbar-bottom { + margin-bottom: 36px; +} + +body.faust-toolbar-has-toolbar-top { + margin-top: 36px; +} + +/* Faust toolbar button styles */ +.faust-toolbar-btn { + display: flex; + align-items: center; + padding: 0 12px; + height: 36px; + background: none; + border: none; + cursor: pointer; + font-size: 14px; + color: var(--faust-toolbar-text-color); + transition: background-color 0.2s ease; +} + +.faust-toolbar-btn:hover { + background-color: var(--faust-toolbar-btn-hover); +} + +.faust-toolbar-btn-right { + border-left: 1px solid var(--faust-toolbar-border-color); +} + +.faust-toolbar-btn:not(.faust-toolbar-btn-right) { + border-right: 1px solid var(--faust-toolbar-border-color); +} + +.faust-toolbar-btn-icon { + width: 16px; + height: 16px; +} + +.faust-toolbar-btn-icon-faust { + width: 22px; + height: 22px; +} + +.faust-toolbar-btn-label { + margin-left: 6px; +} + +/* Settings panel styles */ +.faust-toolbar-settings-panel { + padding: 15px; + min-width: 200px; +} + +.faust-toolbar-settings-row { + display: flex; + align-items: center; + gap: 10px; + color: var(--faust-toolbar-text-color); +} + +.faust-toolbar-settings-label { + font-weight: 500; + min-width: 80px; +} + +.faust-toolbar-settings-select { + padding: 6px 8px; + border: 1px solid var(--faust-toolbar-border-color); + border-radius: 4px; + background: var(--faust-toolbar-bg-primary); + font-size: 14px; + +} + +/* User link styles */ +.faust-toolbar-user-link { + display: flex; + align-items: center; + padding: 0 12px; + height: 36px; + text-decoration: none; + color: var(--faust-toolbar-text-color); + font-size: 14px; +} + +.faust-toolbar-user-avatar { + width: 20px; + height: 20px; + border-radius: 50%; + margin-right: 6px; +} + +/* Main toolbar container */ +.faust-toolbar { + position: fixed; + left: 0; + right: 0; + z-index: 9999; + background: var(--faust-toolbar-bg-primary); + font-size: 14px; +} + +.faust-toolbar-top { + top: 0; +} + +.faust-toolbar-bottom { + bottom: 0; +} + +/* Toolbar header */ +.faust-toolbar-header { + display: flex; + justify-content: space-between; + align-items: center; + height: 36px; + background: var(--faust-toolbar-bg-primary); + border-bottom: 1px solid var(--faust-toolbar-border-color); +} + +.faust-toolbar-content { + display: flex; + align-items: center; + width: 100%; + justify-content: space-between; +} + +.faust-toolbar-left { + display: flex; + align-items: center; +} + +.faust-toolbar-right { + display: flex; + align-items: center; +} + +/* Panel container */ +.faust-toolbar-panel-container { + background: var(--faust-toolbar-bg-primary); + border-bottom: 1px solid var(--faust-toolbar-border-color); + max-height: 300px; + overflow-y: auto; +} + +.faust-toolbar-no-content { + padding: 15px; +} diff --git a/examples/nextjs/kitchen-sink/src/toolbar/index.js b/examples/nextjs/kitchen-sink/src/toolbar/index.js new file mode 100644 index 000000000..8a2a32015 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/index.js @@ -0,0 +1,9 @@ +// Main toolbar component +export { Toolbar } from './Toolbar'; + +// Utilities +export { createToolbar } from './createToolbar'; +export { registerDefaultNodes } from './registerNodes'; +export { renderNode } from './renderNode'; + + diff --git a/examples/nextjs/kitchen-sink/src/toolbar/registerNodes.js b/examples/nextjs/kitchen-sink/src/toolbar/registerNodes.js new file mode 100644 index 000000000..0a40e055d --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/registerNodes.js @@ -0,0 +1,82 @@ +import React from 'react'; +import FaustLogo from './assets/faust-logo.svg'; +import SettingsIcon from './assets/icons/settings.svg'; +import EyeOffIcon from './assets/icons/eye-off.svg'; + +/** + * Registers default Faust.js nodes to the toolbar + */ +export function registerDefaultNodes(toolbar, { position, setPosition, user, site, isPreview, disablePreviewUrl }) { + // Unregister default preview node + toolbar.unregister('preview'); + + if (isPreview) { + toolbar.register('preview', { + label:"Exit preview mode", + icon: EyeOffIcon.src, + order: 3, + onClick: ()=> { + disablePreviewUrl && (window.location.href = disablePreviewUrl); + }, + }); + } + + // Register Faust branding + toolbar.register('faust-brand', { + icon: {"Faust.js"}, + order: 0, + onClick: ()=> { + window.open("https://faustjs.org" , "_blank") + }, + }); + + // Register settings panel + toolbar.register('settings', { + icon: SettingsIcon.src, + panel: 'settings', + position: 'right', + order: 99, + panelComponent: ( +
+
+ + +
+
+ ) + }); + + // Register WordPress user node if available + if (user) { + toolbar.register('wp-user', { + label: user.name, + icon: user.avatar, + position: 'right', + order: 98, + onClick: ()=> { + window.open(`${site?.adminUrl}/profile.php`, "_blank") + } + }); + } + + // Return cleanup function + return () => { + toolbar.unregister('faust-brand'); + toolbar.unregister('settings'); + if (user) toolbar.unregister('wp-user'); + }; +} diff --git a/examples/nextjs/kitchen-sink/src/toolbar/renderNode.js b/examples/nextjs/kitchen-sink/src/toolbar/renderNode.js new file mode 100644 index 000000000..e75a4e867 --- /dev/null +++ b/examples/nextjs/kitchen-sink/src/toolbar/renderNode.js @@ -0,0 +1,59 @@ +import React from 'react'; + +/** + * Renders a toolbar node with proper handling for components and default buttons + */ +export function renderNode(node, { activePanel, setActivePanel }) { + // Generate className for consistent styling across all node types + const nodeClassName = `faust-toolbar-btn ${ + node.position ? `faust-toolbar-btn-${node.position}` : '' + }`; + + if (node.component) { + // If it's a React component, render it + if (React.isValidElement(node.component)) { + return React.cloneElement(node.component, { + key: node.id, + className: nodeClassName, + }); + } + const Component = node.component; + return ; + } + + // Default tab rendering + return ( + + ); +} diff --git a/packages/auth/README.md b/packages/auth/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/packages/auth/config/defaults.js b/packages/auth/config/defaults.js new file mode 100644 index 000000000..b55662863 --- /dev/null +++ b/packages/auth/config/defaults.js @@ -0,0 +1,3 @@ +export const defaultIronOptions = { + cookieName: 'faust-auth-session', +}; diff --git a/packages/auth/handlers/AuthHandler.js b/packages/auth/handlers/AuthHandler.js new file mode 100644 index 000000000..8b45c7cfc --- /dev/null +++ b/packages/auth/handlers/AuthHandler.js @@ -0,0 +1,508 @@ +import { getIronSession } from 'iron-session'; +import { defaultIronOptions } from '../config/defaults'; + +// Default configuration +const defaultConfig = { + ironOptions: defaultIronOptions, + tokenExpirationBuffer: 60000, // 1 minute buffer + errorMessages: { + notLoggedIn: 'User is not logged in.', + tokenExpired: 'Token has expired.', + invalidCredentials: 'Invalid credentials.', + serverError: 'Internal server error.', + unknownAction: 'Unknown action', + missingPassword: + 'Cookie password is not set. Please set it to a secure password of at least 32 characters.', + }, + supportedActions: ['login', 'logout', 'me', 'introspect', 'refresh', 'query'], +}; + +// Pure utility functions +const validateIronOptions = (ironOptions, config) => { + if (!ironOptions?.password) { + throw new Error(config.errorMessages.missingPassword); + } + return true; +}; + +const sendError = (res, statusCode, message, details = null) => + res.status(statusCode).json({ + error: true, + message, + details, + timestamp: new Date().toISOString(), + }); + +const sendSuccess = (res, data = null, message = 'Success') => + res.status(200).json({ + success: true, + message, + data, + timestamp: new Date().toISOString(), + }); + +const isTokenExpired = ( + token, + bufferMs = defaultConfig.tokenExpirationBuffer, +) => { + try { + const decodedToken = JSON.parse( + Buffer.from(token.split('.')[1], 'base64').toString(), + ); + + if (!decodedToken?.exp) { + return false; + } + + const expiresAt = new Date(decodedToken.exp * 1000); + const now = new Date(); + + return now.getTime() > expiresAt.getTime() - bufferMs; + } catch (error) { + return true; // If we can't decode, consider it expired + } +}; + +// Async utility functions +const refreshAuthToken = async (client, refreshToken) => { + const query = ` + mutation GetAuthToken($refreshToken: String!) { + refreshToken(input: { refreshToken: $refreshToken }) { + authToken + authTokenExpiration + success + } + } + `; + + try { + const res = await client.request(query, { + refreshToken, + }); + + if (res?.errors) { + throw new Error(res.errors[0].message); + } + + return res?.data?.refreshToken; + } catch (error) { + throw new Error(`Token refresh failed: ${error.message}`); + } +}; + +const getOrRefreshSession = async ({ + req, + res, + client, + ironOptions, + config = defaultConfig, +}) => { + const session = await getIronSession(req, res, ironOptions); + const { refreshToken, authToken } = session ?? {}; + + // No refresh token means not logged in + if (!refreshToken) { + session.destroy(); + return { success: false, error: 'NO_REFRESH_TOKEN' }; + } + + // Check if auth token needs refresh + if (!authToken || isTokenExpired(authToken, config.tokenExpirationBuffer)) { + try { + const refreshRes = await refreshAuthToken(client, refreshToken); + + if (!refreshRes?.authToken) { + session.destroy(); + return { success: false, error: 'TOKEN_REFRESH_FAILED' }; + } + + session.authToken = refreshRes.authToken; + await session.save(); + + return { success: true, session, refreshRes }; + } catch (error) { + // Keep session but remove auth token + delete session.authToken; + await session.save(); + return { + success: false, + error: 'TOKEN_REFRESH_ERROR', + details: error.message, + }; + } + } + + return { success: true, session }; +}; + +// Higher-order function for error handling +const withErrorHandling = + (handler, config = defaultConfig) => + async (params) => { + try { + return await handler(params); + } catch (error) { + return sendError( + params.res, + 500, + config.errorMessages.serverError, + error.message, + ); + } + }; + +// Individual handler functions +const loginHandler = async ({ + client, + ironOptions, + loginProvider = 'PASSWORD', + req, + res, + config = defaultConfig, +}) => { + const session = await getIronSession(req, res, ironOptions); + + const query = ` + mutation Login($input: LoginInput!) { + login(input: $input) { + authToken + refreshToken + } + } + `; + + try { + const response = await client.request(query, { + input: { ...req.body, provider: loginProvider }, + }); + + if (response?.errors) { + throw new Error(response.errors[0].message); + } + + const data = response?.data?.login; + + session.authToken = data.authToken; + session.refreshToken = data.refreshToken; + await session.save(); + + return sendSuccess( + res, + { authToken: data.authToken }, + 'Logged in successfully', + ); + } catch (error) { + return sendError( + res, + 401, + config.errorMessages.invalidCredentials, + error.message, + ); + } +}; + +const logoutHandler = async ({ + ironOptions, + req, + res, + config = defaultConfig, +}) => { + try { + const session = await getIronSession(req, res, ironOptions); + session.destroy(); + return sendSuccess(res, null, 'Logged out successfully'); + } catch (error) { + return sendError(res, 500, config.errorMessages.serverError, error.message); + } +}; + +const meHandler = async ({ + client, + ironOptions, + req, + res, + config = defaultConfig, +}) => { + const result = await getOrRefreshSession({ + req, + res, + client, + ironOptions, + config, + }); + + if (!result.success) { + return sendError(res, 401, config.errorMessages.notLoggedIn); + } + + // Fetch user data from GraphQL API + try { + // TODO make customizable + const query = + config.viewerQuery || + ` + query GetCurrentUser { + viewer { + id + databaseId + email + name + firstName + lastName + username + avatar { + url + } + } + }`; + + const response = await client.request( + query, + {}, + { + Authorization: `Bearer ${result.session.authToken}`, + }, + ); + + return sendSuccess( + res, + { + isAuthenticated: true, + user: response?.data?.viewer || null, + }, + 'User information retrieved', + ); + } catch (error) { + return sendError(res, 500, 'Failed to fetch user data', error.message); + } +}; + +// Authenticated query handler - forwards GQL queries with auth token +const authenticatedQueryHandler = async ({ + client, + ironOptions, + req, + res, + config = defaultConfig, +}) => { + // First verify and refresh session if needed + const result = await getOrRefreshSession({ + req, + res, + client, + ironOptions, + config, + }); + + if (!result.success) { + return sendError(res, 401, config.errorMessages.notLoggedIn); + } + + try { + // Extract the GraphQL query, variables and operationName from the request + const { query, variables, operationName } = req.body; + + if (!query) { + return sendError(res, 400, 'GraphQL query is required'); + } + + // Forward the query to the GraphQL API with the auth token + const response = await client.request(query, variables || {}, { + Authorization: `Bearer ${result.session.authToken}`, + ...(req.headers?.['content-type'] && { + 'Content-Type': req.headers['content-type'], + }), + }); + + return res.status(200).json(response); + } catch (error) { + return sendError( + res, + 500, + 'Failed to execute authenticated query', + error.message, + ); + } +}; + +// Pure introspect handler - no side effects, just checks current auth state +const introspectHandler = async ({ + client, + ironOptions, + req, + res, + config = defaultConfig, +}) => { + const session = await getIronSession(req, res, ironOptions); + const { refreshToken, authToken } = session ?? {}; + + // No refresh token means not logged in + if (!refreshToken) { + return sendError(res, 401, config.errorMessages.notLoggedIn, { + isAuthenticated: false, + hasRefreshToken: false, + hasAuthToken: false, + tokenExpired: null, + }); + } + + // Check if auth token exists and if it's expired + const tokenExpired = authToken + ? isTokenExpired(authToken, config.tokenExpirationBuffer) + : null; + const hasValidToken = authToken && !tokenExpired; + + // If not authenticated, use sendError for consistency + if (!hasValidToken) { + return sendError(res, 401, config.errorMessages.tokenExpired, { + isAuthenticated: false, + hasRefreshToken: true, + hasAuthToken: !!authToken, + tokenExpired, + }); + } + + // If authenticated, use sendSuccess + return sendSuccess( + res, + { + isAuthenticated: true, + hasRefreshToken: true, + hasAuthToken: true, + tokenExpired: false, + }, + 'User is authenticated', + ); +}; + +// Dedicated refresh handler - only refreshes tokens +const refreshHandler = async ({ + client, + ironOptions, + req, + res, + config = defaultConfig, +}) => { + const session = await getIronSession(req, res, ironOptions); + const { refreshToken } = session ?? {}; + + // No refresh token means can't refresh + if (!refreshToken) { + session.destroy(); + return sendError(res, 401, config.errorMessages.notLoggedIn); + } + + try { + const refreshRes = await refreshAuthToken(client, refreshToken); + + if (!refreshRes?.authToken) { + session.destroy(); + return sendError(res, 401, 'Failed to refresh authentication token'); + } + + session.authToken = refreshRes.authToken; + await session.save(); + + return sendSuccess( + res, + { + authToken: refreshRes.authToken, + refreshed: true, + }, + 'Tokens refreshed successfully', + ); + } catch (error) { + // Keep session but remove auth token + delete session.authToken; + await session.save(); + return sendError(res, 401, 'Failed to refresh token', error.message); + } +}; + +// Action handler mapping +const actionHandlers = { + login: loginHandler, + logout: logoutHandler, + me: meHandler, + introspect: introspectHandler, + refresh: refreshHandler, + query: authenticatedQueryHandler, +}; + +// Main routing function with currying for configuration +const createAuthRouter = (userConfig = {}) => { + const config = { ...defaultConfig, ...userConfig }; + + return async ({ + client, + ironOptions = config.ironOptions, + action, + loginProvider = 'PASSWORD', + req, + res, + }) => { + // Validate iron options + validateIronOptions(ironOptions, config); + + // Check if action is supported + if (!config.supportedActions.includes(action)) { + return sendError( + res, + 400, + `${config.errorMessages.unknownAction}: ${action}`, + ); + } + + const handler = actionHandlers[action]; + if (!handler) { + return sendError( + res, + 400, + `${config.errorMessages.unknownAction}: ${action}`, + ); + } + + // Apply error handling wrapper and call handler + const safeHandler = withErrorHandling(handler, config); + + // All handlers now use the same named parameter structure + return safeHandler({ + client, + ironOptions, + loginProvider, + req, + res, + config, + }); + }; +}; + +// Create default auth router +const authRouter = createAuthRouter(); + +// Export everything +export { + // Main functions + createAuthRouter, + authRouter, + + // Individual handlers + loginHandler, + logoutHandler, + meHandler, + introspectHandler, + refreshHandler, + authenticatedQueryHandler, + + // Utilities + validateIronOptions, + sendError, + sendSuccess, + isTokenExpired, + refreshAuthToken, + getOrRefreshSession, + withErrorHandling, + + // Configuration + defaultConfig, +}; diff --git a/packages/auth/index.js b/packages/auth/index.js new file mode 100644 index 000000000..d76cedba1 --- /dev/null +++ b/packages/auth/index.js @@ -0,0 +1 @@ +export * from './handlers/AuthHandler'; diff --git a/packages/auth/package.json b/packages/auth/package.json new file mode 100644 index 000000000..f7acef262 --- /dev/null +++ b/packages/auth/package.json @@ -0,0 +1,24 @@ +{ + "name": "@faustjs/auth", + "version": "4.0.0-alpha.0", + "description": "Authentication utilities for Faust.js", + "type": "module", + "exports": { + ".": "./index.js" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "graphql", + "wordpress", + "faustjs", + "client" + ], + "license": "MIT", + "packageManager": "pnpm@9.15.0", + "peerDependencies": { + "iron-session": "^6.3.1", + "jsonwebtoken": "^9.0.0" + } +} diff --git a/packages/graphql/client.js b/packages/graphql/client.js index 87552e174..29bb902ed 100644 --- a/packages/graphql/client.js +++ b/packages/graphql/client.js @@ -34,27 +34,34 @@ export function buildGraphQLEndpoint(wordpressUrl) { /** * Create a default GraphQL client using fetch - * @param {string} wordpressUrl - WordPress URL to use (required) + * @param {string} requestUrl - WordPress URL to use (required) * @returns {import('./types.js').GraphQLClient} A basic GraphQL client * @throws {Error} If no WordPress URL is provided */ -export function createDefaultGraphQLClient(wordpressUrl, headers = {}) { - if (!wordpressUrl) { +export function createDefaultGraphQLClient( + requestUrl, + headers = {}, + options = {}, +) { + if (!requestUrl) { throw new Error( - 'WordPress URL is required to create a default GraphQL client.', + 'Request URL is required to create a default GraphQL client.', ); } - const endpoint = buildGraphQLEndpoint(wordpressUrl); + const endpoint = options.useRawUrl + ? requestUrl + : buildGraphQLEndpoint(requestUrl); return { - async request(query, variables = {}) { + async request(query, variables = {}, requestHeaders = {}) { try { const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers, + ...requestHeaders, }, body: JSON.stringify({ query, variables }), }); diff --git a/packages/nextjs/pages/auth/hooks/index.js b/packages/nextjs/pages/auth/hooks/index.js new file mode 100644 index 000000000..74ad63b5b --- /dev/null +++ b/packages/nextjs/pages/auth/hooks/index.js @@ -0,0 +1,3 @@ +export { useLogin } from './useLogin.js'; +export { useLogout } from './useLogout.js'; +export { useUser } from './useUser.js'; diff --git a/packages/nextjs/pages/auth/hooks/useLogin.js b/packages/nextjs/pages/auth/hooks/useLogin.js new file mode 100644 index 000000000..7da7ddbb3 --- /dev/null +++ b/packages/nextjs/pages/auth/hooks/useLogin.js @@ -0,0 +1,82 @@ +import { useState } from 'react'; + +const DEFAULT_LOGIN_URL = '/api/session/login'; + +function useLogin() { + const [error, setError] = useState(); + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(false); + + async function login({ + loginUrl = DEFAULT_LOGIN_URL, + input, + onSuccess = () => {}, + onError = () => {}, + }) { + if (!input) { + // Input validation + const errorData = { + error: true, + message: 'Input is required', + details: 'No login credentials were provided', + }; + setError(errorData); + onError(errorData); + return; + } + + setIsLoading(true); + setError(undefined); + + try { + const res = await fetch(loginUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(input), + }); + + const responseData = await res.json(); + + if (!res.ok) { + // Transform API error to standard format if needed + const errorData = responseData.error + ? responseData + : { + error: true, + message: 'Login failed', + details: responseData.message || JSON.stringify(responseData), + }; + + setError(errorData); + onError(errorData); + setIsLoading(false); + return; + } + + setIsLoading(false); + setData(responseData); + onSuccess(responseData); + } catch (err) { + const errorData = { + error: true, + message: 'Network error or server unavailable', + details: + err instanceof Error ? err.message : 'An unknown error occurred', + }; + setError(errorData); + onError(errorData); + setIsLoading(false); + } + } + + return { + login, + isLoading, + data, + error, + }; +} + +export { useLogin }; diff --git a/packages/nextjs/pages/auth/hooks/useLogout.js b/packages/nextjs/pages/auth/hooks/useLogout.js new file mode 100644 index 000000000..a94736466 --- /dev/null +++ b/packages/nextjs/pages/auth/hooks/useLogout.js @@ -0,0 +1,65 @@ +import { useState } from 'react'; + +const DEFAULT_LOGOUT_URL = '/api/session/logout'; + +export function useLogout() { + const [error, setError] = useState(); + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(false); + + async function logout({ + logoutUrl = DEFAULT_LOGOUT_URL, + onSuccess = () => {}, + onError = () => {}, + } = {}) { + setIsLoading(true); + setError(undefined); + + try { + const res = await fetch(logoutUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }); + + const responseData = await res.json(); + + if (!res.ok) { + // Transform API error to standard format if needed + const errorData = responseData.error + ? responseData + : { + error: true, + message: 'Logout failed', + details: responseData.message || JSON.stringify(responseData), + }; + + setError(errorData); + onError(errorData); + return; + } + + setData(responseData); + onSuccess(responseData); + } catch (err) { + const errorData = { + error: true, + message: 'Network error or server unavailable', + details: + err instanceof Error ? err.message : 'An unknown error occurred', + }; + setError(errorData); + onError(errorData); + } finally { + setIsLoading(false); + } + } + + return { + logout, + isLoading, + data, + error, + }; +} diff --git a/packages/nextjs/pages/auth/hooks/useUser.js b/packages/nextjs/pages/auth/hooks/useUser.js new file mode 100644 index 000000000..d4215af5d --- /dev/null +++ b/packages/nextjs/pages/auth/hooks/useUser.js @@ -0,0 +1,85 @@ +import { useState, useEffect } from 'react'; + +const DEFAULT_ME_URL = '/api/session/me'; + +function useUser({ autoFetch = true } = {}) { + const [error, setError] = useState(); + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(false); + const [isAuthenticated, setIsAuthenticated] = useState(false); + + async function fetchUser({ + meUrl = DEFAULT_ME_URL, + onSuccess = () => {}, + onError = () => {}, + } = {}) { + setIsLoading(true); + setError(null); + + try { + const res = await fetch(meUrl, { + method: 'GET', + credentials: 'include', // Include cookies for session + headers: { + 'Content-Type': 'application/json', + }, + }); + + const responseData = await res.json(); + + if (!res.ok) { + // Transform API error to standard format if needed + const errorData = responseData.error + ? responseData + : { + error: true, + message: 'Failed to fetch user data', + details: responseData.message || JSON.stringify(responseData), + }; + + setError(errorData); + setIsAuthenticated(false); + setData(null); + onError(errorData); + setIsLoading(false); + return; + } + + setIsAuthenticated(responseData.data?.isAuthenticated || false); + setData(responseData.data?.user || null); + onSuccess(responseData.data); + } catch (err) { + const errorData = { + error: true, + message: 'Network error or server unavailable', + details: err.message, + }; + setError(errorData); + setIsAuthenticated(false); + setData(null); + onError(errorData); + } finally { + setIsLoading(false); + } + } + + // Auto-fetch user data on mount if enabled + useEffect(() => { + if (autoFetch) { + fetchUser(); + } + }, [autoFetch]); + + const refetch = () => fetchUser(); + + return { + fetchUser, + refetch, + isLoading, + user: data, + error, + isAuthenticated, + }; +} + +export { useUser }; diff --git a/packages/nextjs/pages/auth/index.js b/packages/nextjs/pages/auth/index.js new file mode 100644 index 000000000..e8506b981 --- /dev/null +++ b/packages/nextjs/pages/auth/index.js @@ -0,0 +1,2 @@ +// Hooks +export * from './hooks/index.js'; diff --git a/packages/nextjs/pages/index.js b/packages/nextjs/pages/index.js index 12278c47f..c815f302f 100644 --- a/packages/nextjs/pages/index.js +++ b/packages/nextjs/pages/index.js @@ -11,10 +11,12 @@ export { uriToTemplate } from './templateHierarchy.js'; // Export Next.js preview enabling api handler export { enablePreview } from './enablePreview.js'; -// Export GraphQL client configuration // Export GraphQL client configuration export { setGraphQLClient, getGraphQLClient, createDefaultGraphQLClient as createDefaultClient, } from '@faustjs/graphql'; + +// Export authentication hooks and utilities +export * from './auth/index.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d1ecb3d0..26c141150 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -121,21 +121,30 @@ importers: examples/nextjs/kitchen-sink: dependencies: + '@faustjs/auth': + specifier: workspace:* + version: link:../../../packages/auth '@faustjs/data-fetching': specifier: workspace:* version: link:../../../packages/data-fetching '@faustjs/nextjs': specifier: workspace:* version: link:../../../packages/nextjs - '@faustjs/template-hierarchy': - specifier: workspace:* - version: link:../../../packages/template-hierarchy + '@wpengine/faust-next-toolbar': + specifier: file:/../../../packages/faust-next-toolbar + version: file:packages/faust-next-toolbar(@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea)(esbuild@0.25.8)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@wpengine/hwp-toolbar': + specifier: github:wpengine/hwptoolkit#toolbar + version: '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea' graphql: specifier: ^16.11.0 version: 16.11.0 graphql-tag: specifier: ^2.12.6 version: 2.12.6(graphql@16.11.0) + iron-session: + specifier: ^8.0.4 + version: 8.0.4 next: specifier: 15.2.4 version: 15.2.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -297,6 +306,15 @@ importers: specifier: ^16.8.1 version: 16.11.0 + packages/auth: + dependencies: + iron-session: + specifier: ^6.3.1 + version: 6.3.1(next@15.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + jsonwebtoken: + specifier: ^9.0.0 + version: 9.0.2 + packages/data-fetching: dependencies: graphql: @@ -306,6 +324,31 @@ importers: specifier: ^2.12.0 version: 2.12.6(graphql@16.11.0) + packages/faust-next-toolbar: + dependencies: + '@wpengine/hwp-toolbar': + specifier: github:wpengine/hwptoolkit + version: '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/35cd7edb8522a899bf90154db315f3fa78dde545' + esbuild-node-externals: + specifier: ^1.18.0 + version: 1.18.0(esbuild@0.24.2) + esbuild-plugin-glob: + specifier: ^2.2.3 + version: 2.2.3(esbuild@0.24.2) + react: + specifier: ^19.0.0 + version: 19.2.0 + react-dom: + specifier: ^19.0.0 + version: 19.2.0(react@19.2.0) + devDependencies: + esbuild: + specifier: ^0.24.0 + version: 0.24.2 + eslint: + specifier: ^9.37.0 + version: 9.37.0(jiti@2.5.1) + packages/graphql: {} packages/nextjs: @@ -321,10 +364,10 @@ importers: version: 16.11.0 next: specifier: ^13.0.0 || ^14.0.0 || ^15.0.0 - version: 14.2.31(react-dom@19.1.1(react@18.3.1))(react@18.3.1) + version: 14.2.31(react-dom@19.2.0(react@19.1.1))(react@19.1.1) react: specifier: ^18.0.0 || ^19.0.0 - version: 18.3.1 + version: 19.1.1 packages/sveltekit: dependencies: @@ -409,6 +452,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.27.0': resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} engines: {node: '>=6.9.0'} @@ -421,6 +469,10 @@ packages: resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + '@capsizecss/unpack@2.4.0': resolution: {integrity: sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==} @@ -488,126 +540,252 @@ packages: '@emnapi/wasi-threads@1.0.4': resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.25.8': resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.8': resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.8': resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.8': resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.8': resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.8': resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.8': resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.8': resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.25.8': resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.8': resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.8': resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.8': resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.8': resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.8': resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.8': resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.8': resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.8': resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.25.8': resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.8': resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.25.8': resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.8': resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} engines: {node: '>=18'} @@ -620,24 +798,48 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.8': resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.8': resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.8': resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.8': resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} engines: {node: '>=18'} @@ -650,6 +852,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -662,10 +870,18 @@ packages: resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.4.0': + resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.15.2': resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -682,6 +898,10 @@ packages: resolution: {integrity: sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -690,6 +910,10 @@ packages: resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.4.0': + resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1162,6 +1386,17 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@peculiar/asn1-schema@2.4.0': + resolution: {integrity: sha512-umbembjIWOrPSOzEGG5vxFLkeM8kzIhLkgigtsOrfLKnuzxWxejAcUX+q/SoZCdemlODOcr5WiYa7+dIEzBXZQ==} + + '@peculiar/json-schema@1.1.12': + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} + + '@peculiar/webcrypto@1.5.0': + resolution: {integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==} + engines: {node: '>=10.12.0'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -1531,30 +1766,72 @@ packages: '@tybys/wasm-util@0.10.0': resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/content-disposition@0.5.9': + resolution: {integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==} + + '@types/cookie@0.5.4': + resolution: {integrity: sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cookies@0.9.1': + resolution: {integrity: sha512-E/DPgzifH4sM1UMadJMWd6mO2jOd4g1Ejwzx8/uRCDpJis1IrlyQEcGAYEomtAqRYmD5ORbNXMeI9U0RiVGZbg==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/express-serve-static-core@4.19.6': + resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} + + '@types/express@4.17.23': + resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} + '@types/fontkit@2.0.8': resolution: {integrity: sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==} '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-assert@1.5.6': + resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/keygrip@1.0.6': + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + + '@types/koa-compose@3.2.8': + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + + '@types/koa@2.15.0': + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -1564,9 +1841,24 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/semver@7.7.0': resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -1734,6 +2026,23 @@ packages: peerDependencies: '@urql/core': ^5.0.0 + '@wpengine/faust-next-toolbar@file:packages/faust-next-toolbar': + resolution: {directory: packages/faust-next-toolbar, type: directory} + peerDependencies: + '@wpengine/hwp-toolbar': 0.0.1 + react: ^19.0.0 + react-dom: ^19.0.0 + + '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea': + resolution: {tarball: https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea} + version: 1.0.0 + engines: {node: '>=18', pnpm: '>=10'} + + '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/35cd7edb8522a899bf90154db315f3fa78dde545': + resolution: {tarball: https://codeload.github.com/wpengine/hwptoolkit/tar.gz/35cd7edb8522a899bf90154db315f3fa78dde545} + version: 1.0.0 + engines: {node: '>=18', pnpm: '>=10'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1832,6 +2141,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + asn1js@3.0.6: + resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + engines: {node: '>=12.0.0'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -1872,6 +2185,10 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + blob-to-buffer@1.2.9: resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==} @@ -1882,6 +2199,9 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1889,6 +2209,12 @@ packages: brotli@1.3.3: resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1939,6 +2265,10 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -2003,10 +2333,18 @@ packages: cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -2141,6 +2479,9 @@ packages: duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -2197,6 +2538,24 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild-node-externals@1.18.0: + resolution: {integrity: sha512-suFVX3SzZlXrGIS9Yqx+ZaHL4w1p0e/j7dQbOM9zk8SfFpnAGnDplHUKXIf9kcPEAfZRL66JuYeVSVlsSEQ5Eg==} + engines: {node: '>=12'} + peerDependencies: + esbuild: 0.12 - 0.25 + + esbuild-plugin-glob@2.2.3: + resolution: {integrity: sha512-Ee6clR2o8K1BIz94hyfRlHB7//UOeAT5JUPfaCSi1Sqx/Y3GDV2BcO0AOvnbxtZWVL8fHfG9WB92FWzwBZwy4Q==} + engines: {node: '>=14'} + deprecated: Esbuild natively supports glob-style imports since version 0.19 + peerDependencies: + esbuild: ^0.x.x + + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.25.8: resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} engines: {node: '>=18'} @@ -2371,6 +2730,16 @@ packages: jiti: optional: true + eslint@9.37.0: + resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + esm-env@1.2.2: resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} @@ -2682,6 +3051,9 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2708,6 +3080,27 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + iron-session@6.3.1: + resolution: {integrity: sha512-3UJ7y2vk/WomAtEySmPgM6qtYF1cZ3tXuWX5GsVX4PJXAcs5y/sV9HuSfpjKS6HkTL/OhZcTDWJNLZ7w+Erx3A==} + engines: {node: '>=12'} + peerDependencies: + express: '>=4' + koa: '>=2' + next: '>=10' + peerDependenciesMeta: + express: + optional: true + koa: + optional: true + next: + optional: true + + iron-session@8.0.4: + resolution: {integrity: sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA==} + + iron-webcrypto@0.2.8: + resolution: {integrity: sha512-YPdCvjFMOBjXaYuDj5tiHst5CEk6Xw84Jo8Y2+jzhMceclAnb3+vNPP/CTtb5fO2ZEuXEaO4N+w62Vfko757KA==} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -2726,6 +3119,10 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -2891,10 +3288,20 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2992,9 +3399,30 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -3161,6 +3589,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -3290,6 +3722,10 @@ packages: node-mock-http@1.0.2: resolution: {integrity: sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==} + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3499,6 +3935,13 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} @@ -3518,6 +3961,11 @@ packages: peerDependencies: react: ^19.1.1 + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + peerDependencies: + react: ^19.2.0 + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -3533,10 +3981,18 @@ packages: resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} engines: {node: '>=0.10.0'} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + engines: {node: '>=0.10.0'} + read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3589,6 +4045,9 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -3649,6 +4108,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -3666,6 +4128,9 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3891,6 +4356,9 @@ packages: tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -4037,6 +4505,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + unixify@1.0.0: + resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} + engines: {node: '>=0.10.0'} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -4162,6 +4634,9 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webcrypto-core@1.8.1: + resolution: {integrity: sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -4358,6 +4833,10 @@ snapshots: dependencies: '@babel/types': 7.27.1 + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + '@babel/runtime@7.27.0': dependencies: regenerator-runtime: 0.14.1 @@ -4365,14 +4844,19 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@babel/types@7.27.1': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@capsizecss/unpack@2.4.0': dependencies: blob-to-buffer: 1.2.9 @@ -4539,81 +5023,156 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.24.2': + optional: true + '@esbuild/aix-ppc64@0.25.8': optional: true + '@esbuild/android-arm64@0.24.2': + optional: true + '@esbuild/android-arm64@0.25.8': optional: true + '@esbuild/android-arm@0.24.2': + optional: true + '@esbuild/android-arm@0.25.8': optional: true + '@esbuild/android-x64@0.24.2': + optional: true + '@esbuild/android-x64@0.25.8': optional: true + '@esbuild/darwin-arm64@0.24.2': + optional: true + '@esbuild/darwin-arm64@0.25.8': optional: true + '@esbuild/darwin-x64@0.24.2': + optional: true + '@esbuild/darwin-x64@0.25.8': optional: true + '@esbuild/freebsd-arm64@0.24.2': + optional: true + '@esbuild/freebsd-arm64@0.25.8': optional: true + '@esbuild/freebsd-x64@0.24.2': + optional: true + '@esbuild/freebsd-x64@0.25.8': optional: true + '@esbuild/linux-arm64@0.24.2': + optional: true + '@esbuild/linux-arm64@0.25.8': optional: true + '@esbuild/linux-arm@0.24.2': + optional: true + '@esbuild/linux-arm@0.25.8': optional: true + '@esbuild/linux-ia32@0.24.2': + optional: true + '@esbuild/linux-ia32@0.25.8': optional: true + '@esbuild/linux-loong64@0.24.2': + optional: true + '@esbuild/linux-loong64@0.25.8': optional: true + '@esbuild/linux-mips64el@0.24.2': + optional: true + '@esbuild/linux-mips64el@0.25.8': optional: true + '@esbuild/linux-ppc64@0.24.2': + optional: true + '@esbuild/linux-ppc64@0.25.8': optional: true + '@esbuild/linux-riscv64@0.24.2': + optional: true + '@esbuild/linux-riscv64@0.25.8': optional: true + '@esbuild/linux-s390x@0.24.2': + optional: true + '@esbuild/linux-s390x@0.25.8': optional: true + '@esbuild/linux-x64@0.24.2': + optional: true + '@esbuild/linux-x64@0.25.8': optional: true + '@esbuild/netbsd-arm64@0.24.2': + optional: true + '@esbuild/netbsd-arm64@0.25.8': optional: true + '@esbuild/netbsd-x64@0.24.2': + optional: true + '@esbuild/netbsd-x64@0.25.8': optional: true + '@esbuild/openbsd-arm64@0.24.2': + optional: true + '@esbuild/openbsd-arm64@0.25.8': optional: true + '@esbuild/openbsd-x64@0.24.2': + optional: true + '@esbuild/openbsd-x64@0.25.8': optional: true '@esbuild/openharmony-arm64@0.25.8': optional: true + '@esbuild/sunos-x64@0.24.2': + optional: true + '@esbuild/sunos-x64@0.25.8': optional: true + '@esbuild/win32-arm64@0.24.2': + optional: true + '@esbuild/win32-arm64@0.25.8': optional: true + '@esbuild/win32-ia32@0.24.2': + optional: true + '@esbuild/win32-ia32@0.25.8': optional: true + '@esbuild/win32-x64@0.24.2': + optional: true + '@esbuild/win32-x64@0.25.8': optional: true @@ -4627,6 +5186,11 @@ snapshots: eslint: 9.33.0(jiti@2.5.1) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0(jiti@2.5.1))': + dependencies: + eslint: 9.37.0(jiti@2.5.1) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.21.0': @@ -4639,10 +5203,18 @@ snapshots: '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.4.0': + dependencies: + '@eslint/core': 0.16.0 + '@eslint/core@0.15.2': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 @@ -4675,6 +5247,8 @@ snapshots: '@eslint/js@9.33.0': {} + '@eslint/js@9.37.0': {} + '@eslint/object-schema@2.1.6': {} '@eslint/plugin-kit@0.3.5': @@ -4682,6 +5256,11 @@ snapshots: '@eslint/core': 0.15.2 levn: 0.4.1 + '@eslint/plugin-kit@0.4.0': + dependencies: + '@eslint/core': 0.16.0 + levn: 0.4.1 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -5024,6 +5603,24 @@ snapshots: '@oslojs/encoding@1.1.0': {} + '@peculiar/asn1-schema@2.4.0': + dependencies: + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/json-schema@1.1.12': + dependencies: + tslib: 2.8.1 + + '@peculiar/webcrypto@1.5.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/json-schema': 1.1.12 + pvtsutils: 1.3.6 + tslib: 2.8.1 + webcrypto-core: 1.8.1 + '@polka/url@1.0.0-next.29': {} '@rollup/pluginutils@5.2.0(rollup@4.46.2)': @@ -5345,14 +5942,52 @@ snapshots: tslib: 2.8.1 optional: true + '@types/accepts@1.3.7': + dependencies: + '@types/node': 17.0.45 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 17.0.45 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 17.0.45 + + '@types/content-disposition@0.5.9': {} + + '@types/cookie@0.5.4': {} + '@types/cookie@0.6.0': {} + '@types/cookies@0.9.1': + dependencies: + '@types/connect': 3.4.38 + '@types/express': 4.17.23 + '@types/keygrip': 1.0.6 + '@types/node': 17.0.45 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 '@types/estree@1.0.8': {} + '@types/express-serve-static-core@4.19.6': + dependencies: + '@types/node': 17.0.45 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express@4.17.23': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.14.0 + '@types/serve-static': 1.15.8 + '@types/fontkit@2.0.8': dependencies: '@types/node': 12.20.55 @@ -5361,14 +5996,37 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/http-assert@1.5.6': {} + + '@types/http-errors@2.0.5': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} + '@types/keygrip@1.0.6': {} + + '@types/koa-compose@3.2.8': + dependencies: + '@types/koa': 2.15.0 + + '@types/koa@2.15.0': + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.9 + '@types/cookies': 0.9.1 + '@types/http-assert': 1.5.6 + '@types/http-errors': 2.0.5 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 17.0.45 + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 + '@types/mime@1.3.5': {} + '@types/ms@2.1.0': {} '@types/nlcst@2.0.3': @@ -5377,8 +6035,25 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@17.0.45': {} + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + '@types/semver@7.7.0': {} + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 17.0.45 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 17.0.45 + '@types/send': 0.17.5 + '@types/unist@3.0.3': {} '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': @@ -5596,6 +6271,20 @@ snapshots: '@urql/core': 5.2.0(graphql@16.11.0) wonka: 6.3.5 + '@wpengine/faust-next-toolbar@file:packages/faust-next-toolbar(@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea)(esbuild@0.25.8)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@wpengine/hwp-toolbar': '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea' + esbuild-node-externals: 1.18.0(esbuild@0.25.8) + esbuild-plugin-glob: 2.2.3(esbuild@0.25.8) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + transitivePeerDependencies: + - esbuild + + '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/085b4399fc62abb7707b12116fd3c89c6da593ea': {} + + '@wpengine/hwp-toolkit@https://codeload.github.com/wpengine/hwptoolkit/tar.gz/35cd7edb8522a899bf90154db315f3fa78dde545': {} + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: acorn: 8.14.1 @@ -5717,6 +6406,12 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + asn1js@3.0.6: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.3 + tslib: 2.8.1 + ast-types-flow@0.0.8: {} astro@5.12.8(jiti@2.5.1)(lightningcss@1.30.1)(rollup@4.46.2)(typescript@5.8.3)(yaml@2.8.1): @@ -5842,6 +6537,8 @@ snapshots: dependencies: is-windows: 1.0.2 + binary-extensions@2.3.0: {} + blob-to-buffer@1.2.9: {} boxen@8.0.1: @@ -5860,6 +6557,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -5868,6 +6569,13 @@ snapshots: dependencies: base64-js: 1.5.1 + buffer-equal-constant-time@1.0.1: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -5912,6 +6620,18 @@ snapshots: chardet@0.7.0: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -5960,8 +6680,12 @@ snapshots: cookie-es@1.2.2: {} + cookie@0.5.0: {} + cookie@0.6.0: {} + cookie@0.7.2: {} + cookie@1.0.2: {} cross-fetch@3.2.0: @@ -6083,6 +6807,10 @@ snapshots: duplexer@0.1.2: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} @@ -6201,6 +6929,62 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild-node-externals@1.18.0(esbuild@0.24.2): + dependencies: + esbuild: 0.24.2 + find-up: 5.0.0 + + esbuild-node-externals@1.18.0(esbuild@0.25.8): + dependencies: + esbuild: 0.25.8 + find-up: 5.0.0 + + esbuild-plugin-glob@2.2.3(esbuild@0.24.2): + dependencies: + chokidar: 3.6.0 + esbuild: 0.24.2 + fast-glob: 3.3.3 + minimatch: 9.0.5 + tiny-invariant: 1.3.3 + unixify: 1.0.0 + + esbuild-plugin-glob@2.2.3(esbuild@0.25.8): + dependencies: + chokidar: 3.6.0 + esbuild: 0.25.8 + fast-glob: 3.3.3 + minimatch: 9.0.5 + tiny-invariant: 1.3.3 + unixify: 1.0.0 + + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + esbuild@0.25.8: optionalDependencies: '@esbuild/aix-ppc64': 0.25.8 @@ -6626,6 +7410,48 @@ snapshots: transitivePeerDependencies: - supports-color + eslint@9.37.0(jiti@2.5.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.5.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.4.0 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.0 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.5.1 + transitivePeerDependencies: + - supports-color + esm-env@1.2.2: {} espree@10.4.0: @@ -7017,6 +7843,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} import-fresh@3.3.1: @@ -7041,6 +7869,28 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + iron-session@6.3.1(next@15.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)): + dependencies: + '@peculiar/webcrypto': 1.5.0 + '@types/cookie': 0.5.4 + '@types/express': 4.17.23 + '@types/koa': 2.15.0 + '@types/node': 17.0.45 + cookie: 0.5.0 + iron-webcrypto: 0.2.8 + optionalDependencies: + next: 15.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + + iron-session@8.0.4: + dependencies: + cookie: 0.7.2 + iron-webcrypto: 1.2.1 + uncrypto: 0.1.3 + + iron-webcrypto@0.2.8: + dependencies: + buffer: 6.0.3 + iron-webcrypto@1.2.1: {} is-array-buffer@3.0.5: @@ -7064,6 +7914,10 @@ snapshots: dependencies: has-bigints: 1.1.0 + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -7221,6 +8075,19 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.2 + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -7228,6 +8095,17 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + jwa@1.4.2: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.2 + safe-buffer: 5.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -7302,8 +8180,22 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + lodash.merge@4.6.2: {} + lodash.once@4.1.1: {} + lodash.startcase@4.4.0: {} lodash@4.17.21: {} @@ -7654,6 +8546,10 @@ snapshots: dependencies: brace-expansion: 1.1.11 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + minimist@1.2.8: {} minipass@7.1.2: {} @@ -7682,7 +8578,7 @@ snapshots: neotraverse@0.6.18: {} - next@14.2.31(react-dom@19.1.1(react@18.3.1))(react@18.3.1): + next@14.2.31(react-dom@19.2.0(react@19.1.1))(react@19.1.1): dependencies: '@next/env': 14.2.31 '@swc/helpers': 0.5.5 @@ -7690,9 +8586,9 @@ snapshots: caniuse-lite: 1.0.30001731 graceful-fs: 4.2.11 postcss: 8.4.31 - react: 18.3.1 - react-dom: 19.1.1(react@18.3.1) - styled-jsx: 5.1.1(react@18.3.1) + react: 19.1.1 + react-dom: 19.2.0(react@19.1.1) + styled-jsx: 5.1.1(react@19.1.1) optionalDependencies: '@next/swc-darwin-arm64': 14.2.31 '@next/swc-darwin-x64': 14.2.31 @@ -7755,6 +8651,30 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + '@next/env': 15.4.5 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001731 + postcss: 8.4.31 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + styled-jsx: 5.1.6(react@19.2.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.4.5 + '@next/swc-darwin-x64': 15.4.5 + '@next/swc-linux-arm64-gnu': 15.4.5 + '@next/swc-linux-arm64-musl': 15.4.5 + '@next/swc-linux-x64-gnu': 15.4.5 + '@next/swc-linux-x64-musl': 15.4.5 + '@next/swc-win32-arm64-msvc': 15.4.5 + '@next/swc-win32-x64-msvc': 15.4.5 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + optional: true + nlcst-to-string@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -7767,6 +8687,10 @@ snapshots: node-mock-http@1.0.2: {} + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} object-assign@4.1.1: {} @@ -7971,6 +8895,12 @@ snapshots: punycode@2.3.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.3: {} + quansync@0.2.10: {} queue-microtask@1.2.3: {} @@ -7983,15 +8913,20 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-dom@19.1.1(react@18.3.1): + react-dom@19.1.1(react@19.1.1): dependencies: - react: 18.3.1 + react: 19.1.1 scheduler: 0.26.0 - react-dom@19.1.1(react@19.1.1): + react-dom@19.2.0(react@19.1.1): dependencies: react: 19.1.1 - scheduler: 0.26.0 + scheduler: 0.27.0 + + react-dom@19.2.0(react@19.2.0): + dependencies: + react: 19.2.0 + scheduler: 0.27.0 react-is@16.13.1: {} @@ -8003,6 +8938,8 @@ snapshots: react@19.1.1: {} + react@19.2.0: {} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -8010,6 +8947,10 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + readdirp@4.1.2: {} reflect.getprototypeof@1.0.10: @@ -8110,6 +9051,8 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 + remove-trailing-separator@1.1.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -8203,6 +9146,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -8222,6 +9167,8 @@ snapshots: scheduler@0.26.0: {} + scheduler@0.27.0: {} + semver@6.3.1: {} semver@7.7.2: {} @@ -8471,10 +9418,10 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.1(react@18.3.1): + styled-jsx@5.1.1(react@19.1.1): dependencies: client-only: 0.0.1 - react: 18.3.1 + react: 19.1.1 styled-jsx@5.1.6(react@18.3.1): dependencies: @@ -8486,6 +9433,12 @@ snapshots: client-only: 0.0.1 react: 19.1.1 + styled-jsx@5.1.6(react@19.2.0): + dependencies: + client-only: 0.0.1 + react: 19.2.0 + optional: true + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -8542,6 +9495,8 @@ snapshots: tiny-inflate@1.0.3: {} + tiny-invariant@1.3.3: {} + tinyexec@0.3.2: {} tinyglobby@0.2.14: @@ -8713,6 +9668,10 @@ snapshots: universalify@0.1.2: {} + unixify@1.0.0: + dependencies: + normalize-path: 2.1.1 + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.2 @@ -8787,6 +9746,14 @@ snapshots: web-namespaces@2.0.1: {} + webcrypto-core@1.8.1: + dependencies: + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/json-schema': 1.1.12 + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + webidl-conversions@3.0.1: {} webpack-bundle-analyzer@4.7.0: