diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 913f8189c..7061f94f0 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -28,8 +28,3 @@ RUN chmod a+x phpactor.phar RUN sudo mv phpactor.phar /usr/local/bin/phpactor # Install gh cli RUN sudo install-packages gh - -RUN curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_0.40.2_Linux_x86_64.tar.gz" \ - && tar xf lazygit.tar.gz lazygit \ - && sudo install lazygit /usr/local/bin - diff --git a/.gitpod.yml b/.gitpod.yml index b8f8aab49..ccd012215 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,6 +1,6 @@ tasks: - name: Setup - init: pnpm install && pnpm turbo build --no-daemon --go-fallback && gp sync-done setup + init: pnpm install && pnpm turbo build gp sync-done setup env: NETLIFY_URL: http://localhost:8000 DRUPAL_EXTERNAL_URL: http://localhost:8888 diff --git a/INIT.md b/INIT.md index 679a143b6..3d69cc6da 100644 --- a/INIT.md +++ b/INIT.md @@ -70,17 +70,28 @@ replace( 'PROJECT_NAME=example', 'PROJECT_NAME=' + process.env.PROJECT_NAME_MACHINE, ); -const clientSecret = randomString(32); +const publisherClientSecret = randomString(32); replace( ['apps/cms/.lagoon.env', 'apps/website/.lagoon.env'], 'PUBLISHER_OAUTH2_CLIENT_SECRET=REPLACE_ME', - 'PUBLISHER_OAUTH2_CLIENT_SECRET=' + clientSecret, + `PUBLISHER_OAUTH2_CLIENT_SECRET=${publisherClientSecret}`, ); -const sessionSecret = randomString(32); +const publisherSessionSecret = randomString(32); replace( ['apps/website/.lagoon.env'], 'PUBLISHER_OAUTH2_SESSION_SECRET=REPLACE_ME', - 'PUBLISHER_OAUTH2_SESSION_SECRET=' + sessionSecret, + `PUBLISHER_OAUTH2_SESSION_SECRET=${publisherSessionSecret}`, +); +const websiteClientSecret = randomString(32); +replace( + ['apps/cms/.lagoon.env'], + 'WEBSITE_OAUTH2_CLIENT_SECRET=REPLACE_ME', + `WEBSITE_OAUTH2_CLIENT_SECRET=${websiteClientSecret}`, +); +console.log( + 'Website OAuth2 environment variables to be set in Netlify', + `AUTH_DRUPAL_ID: website`, + `AUTH_DRUPAL_SECRET: ${websiteClientSecret}`, ); // Template's prod domain is special. replace( diff --git a/README.md b/README.md index 9e4a6a21c..eacf844f5 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ Other steps - [Create a new Lagoon project](https://amazeelabs.atlassian.net/wiki/spaces/ALU/pages/368115717/Create+a+new+Lagoon+project) - [Create a new Netlify project](https://amazeelabs.atlassian.net/wiki/spaces/ALU/pages/368017428/Create+a+new+Netlify+project) +- Set `AUTH_DRUPAL_ID` and `AUTH_DRUPAL_SECRET` in + [Netlify environment variables](#gatsby-authentication--sso) - Check the [Environment overrides](#environment-overrides) section below - Check the [Choose a CMS](#choose-a-cms) section below - Create `dev` and `prod` branches (and optionally `stage`) from `release` @@ -217,6 +219,63 @@ lagoon runtime configuration. lagoon add variable -p [project name] -e dev -N NETLIFY_SITE_ID -V [netlify site id] ``` +### Gatsby authentication / SSO + +Authentication providers are relying on Auth.js (formerly Next-Auth) and can be +configured in `/apps/website/nextauth.config.js` + +An example provider is available for Drupal. + +On Netlify, several environment variables are required to be set: + +#### For all providers + +- `NEXTAUTH_URL` The URL of the frontend. This is used for the callback. +- `NEXTAUTH_SECRET` A random string used for encryption. + +Generate the secret with e.g. `openssl rand -base64 32` + +#### For Drupal + +- `AUTH_DRUPAL_ID` The client ID of the Drupal Consumer +- `AUTH_DRUPAL_SECRET` The client secret of the Drupal Consumer + +Drupal environment variables are displayed in the console when running +`pnpx @amazeelabs/mzx run INIT.md`. + +
+ How it works +A `Website` consumer is created in Drupal `/admin/config/services/consumer` with + +- Label: `Website` +- Client ID: `website` +- Secret: a random string matching `AUTH_DRUPAL_SECRET` +- Redirect URI: `[netlify-gatsby-site-url]/api/auth/callback/drupal` + +#### Other providers + +Refer to [Auth.js documentation](https://next-auth.js.org/providers/). + +
+ +
+ Local development + +#### Start Drupal and Gatsby + +- Drupal: in `/apps/cms` - `pnpm start` use http://127.0.0.1:8888 +- Gatsby: in `/apps/website` - `pnpm gatsby:develop` use + http://localhost:8000/en + +#### Basic troubleshooting + +- Make sure to have keys generated + http://127.0.0.1:8888/en/admin/config/people/simple_oauth +- Make sure to have the correct client id and secret set + http://127.0.0.1:8888/en/admin/config/services/consumer/2/edit + +
+ ### Publisher authentication with Drupal Publisher can require to authenticate with Drupal based on OAuth2. It is only diff --git a/apps/cms/.lagoon.env b/apps/cms/.lagoon.env index 3c8c78842..6122ed6e2 100644 --- a/apps/cms/.lagoon.env +++ b/apps/cms/.lagoon.env @@ -2,5 +2,8 @@ PUBLISHER_URL="https://build.${LAGOON_ENVIRONMENT}.${LAGOON_PROJECT}.ch4.amazee. NETLIFY_URL="https://build.${LAGOON_ENVIRONMENT}.${LAGOON_PROJECT}.ch4.amazee.io" PREVIEW_URL="https://preview.${LAGOON_ENVIRONMENT}.${LAGOON_PROJECT}.ch4.amazee.io" -# Used to set the original client secret. +# Used to set the original client secret for Publisher. PUBLISHER_OAUTH2_CLIENT_SECRET=REPLACE_ME + +# Used to set the original client secret for the Website. +WEBSITE_OAUTH2_CLIENT_SECRET=REPLACE_ME diff --git a/apps/website/api/auth/[nextauth].js b/apps/website/api/auth/[nextauth].js new file mode 100644 index 000000000..a44fa5cdc --- /dev/null +++ b/apps/website/api/auth/[nextauth].js @@ -0,0 +1,10 @@ +import NextAuth from 'next-auth'; + +import { authConfig } from '../../nextauth.config'; + +// @ts-ignore +export default async function handler(req, res) { + const { nextauth, provider, ...rest } = req.query; + req.query = { nextauth: [nextauth, provider], ...rest }; + return await NextAuth(req, res, authConfig); +} diff --git a/apps/website/api/auth/[nextauth]/[provider].js b/apps/website/api/auth/[nextauth]/[provider].js new file mode 100644 index 000000000..3c45f5db8 --- /dev/null +++ b/apps/website/api/auth/[nextauth]/[provider].js @@ -0,0 +1 @@ +export { default } from '../[nextauth]'; diff --git a/apps/website/gatsby-browser.ts b/apps/website/gatsby-browser.ts index 971084011..15fe7d455 100644 --- a/apps/website/gatsby-browser.ts +++ b/apps/website/gatsby-browser.ts @@ -2,6 +2,10 @@ import '@custom/ui/styles.css'; import { GatsbyBrowser } from 'gatsby'; +import { WrapRootElement } from './src/utils/wrapRootElement'; + +export const wrapRootElement = WrapRootElement; + export const shouldUpdateScroll: GatsbyBrowser['shouldUpdateScroll'] = ( args, ) => { diff --git a/apps/website/gatsby-config.mjs b/apps/website/gatsby-config.mjs index 842076dff..81457209e 100644 --- a/apps/website/gatsby-config.mjs +++ b/apps/website/gatsby-config.mjs @@ -6,8 +6,6 @@ // TS file name should be different from gastby-config.ts, otherwise Gatsby will // pick it up instead of the JS file. -import { existsSync } from 'fs'; - process.env.NETLIFY_URL = process.env.NETLIFY_URL || 'http://127.0.0.1:8000'; process.env.CLOUDINARY_API_KEY = process.env.CLOUDINARY_API_KEY || 'test'; diff --git a/apps/website/gatsby-node.mjs b/apps/website/gatsby-node.mjs index ffce8e751..f7e865626 100644 --- a/apps/website/gatsby-node.mjs +++ b/apps/website/gatsby-node.mjs @@ -97,6 +97,14 @@ export const createPages = async ({ actions }) => { }); }); + // Create a profile page in each language. + Object.values(Locale).forEach((locale) => { + actions.createPage({ + path: `/${locale}/profile`, + component: resolve(`./src/templates/profile.tsx`), + }); + }); + // Broken Gatsby links will attempt to load page-data.json files, which don't exist // and also should not be piped into the strangler function. Thats why they // are caught right here. diff --git a/apps/website/nextauth.config.js b/apps/website/nextauth.config.js new file mode 100644 index 000000000..fab2e76aa --- /dev/null +++ b/apps/website/nextauth.config.js @@ -0,0 +1,57 @@ +const AUTH_DRUPAL_URL = process.env.AUTH_DRUPAL_URL || 'http://127.0.0.1:8888'; + +/** @type {import("next-auth").NextAuthOptions} */ +export const authConfig = { + providers: [ + // Drupal provider. + // Other providers can be added here (e.g. Google, Keycloak). + { + // Client ID and secret are set in the Drupal Consumer. + clientId: process.env.AUTH_DRUPAL_ID || 'website', + clientSecret: process.env.AUTH_DRUPAL_SECRET || 'banana', + id: 'drupal', + name: 'Drupal', + type: 'oauth', + // Language prefix is added to prevent 301 + // that will not be handled by NextAuth. + authorization: { + url: `${AUTH_DRUPAL_URL}/en/oauth/authorize`, + params: { + scope: 'authenticated', + }, + }, + token: `${AUTH_DRUPAL_URL}/en/oauth/token`, + userinfo: { + // Additional userinfo can be fetched with an extra request + // using the access token. + url: `${AUTH_DRUPAL_URL}/en/oauth/userinfo`, + }, + profile(profile, tokens) { + return { + id: profile.sub, + name: profile.name, + email: profile.email, + tokens: tokens, + }; + }, + }, + ], + // Persist the user object in the session. + // @todo use the refresh token + // https://github.com/nextauthjs/next-auth-refresh-token-example/blob/main/pages/api/auth/%5B...nextauth%5D.js + callbacks: { + async jwt({ token, user }) { + return { ...token, ...user }; + }, + async session({ session, token }) { + session.user = token; + return session; + }, + }, + secret: process.env.NEXTAUTH_SECRET, + theme: { + logo: 'https://www.amazeelabs.com/images/icon.png', + colorScheme: 'light', + brandColor: '#951B81', + }, +}; diff --git a/apps/website/package.json b/apps/website/package.json index 2954fe07e..2de2a1980 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -15,6 +15,11 @@ "@custom/decap": "workspace:*", "@custom/schema": "workspace:*", "@custom/ui": "workspace:*", + "@gatsbyjs/reach-router": "^2.0.1", + "@netlify/plugin-gatsby": "^3.8.1", + "@netlify/plugin-nextjs": "^4.41.3", + "babel-loader": "^9.1.3", + "body-parser": "^1.20.2", "gatsby": "^5.13.1", "gatsby-plugin-layout": "^4.13.0", "gatsby-plugin-manifest": "^5.13.0", @@ -27,7 +32,10 @@ "gatsby-source-filesystem": "^5.13.0", "image-size": "^1.1.1", "mime-types": "^2.1.35", + "multer": "1.4.5-lts.1", "netlify-cli": "^17.21.1", + "next": "^14.2.3", + "next-auth": "^4.24.7", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -53,7 +61,7 @@ "serve": "netlify dev --cwd=. --dir=public --port=8000", "dev": "pnpm clean && publisher", "open": "open http://127.0.0.1:8000/___status/", - "gatsby:develop": "ENABLE_GATSBY_REFRESH_ENDPOINT=true pnpm gatsby develop", + "gatsby:develop": "NEXTAUTH_URL=http://localhost:8000 NEXTAUTH_SECRET=banana ENABLE_GATSBY_REFRESH_ENDPOINT=true pnpm gatsby develop", "gatsby:refresh": "curl -X POST http://localhost:8000/__refresh", "clean": "gatsby clean" } diff --git a/apps/website/src/templates/page.tsx b/apps/website/src/templates/page.tsx index f707acbb5..a85fb4eb0 100644 --- a/apps/website/src/templates/page.tsx +++ b/apps/website/src/templates/page.tsx @@ -38,7 +38,7 @@ export function Head({ data }: HeadProps) { export default function PageTemplate({ data }: PageProps) { // Retrieve the current location and prefill the // "ViewPageQuery" with these arguments. - // That makes shure the `useOperation(ViewPageQuery, ...)` with this + // That makes sure the `useOperation(ViewPageQuery, ...)` with this // path immediately returns this data. const [location] = useLocation(); return ( diff --git a/apps/website/src/templates/profile.tsx b/apps/website/src/templates/profile.tsx new file mode 100644 index 000000000..f6040496d --- /dev/null +++ b/apps/website/src/templates/profile.tsx @@ -0,0 +1,38 @@ +import { CurrentUserQuery, OperationExecutor } from '@custom/schema'; +import { UserProfile } from '@custom/ui/routes/UserProfile'; +import { useSession } from 'next-auth/react'; +import React from 'react'; + +import { drupalExecutor } from '../utils/drupal-executor'; + +export default function ProfilePage() { + const session = useSession(); + let accessToken: string | undefined = undefined; + if (session && session.status === 'authenticated') { + // @ts-ignore + accessToken = session.data.user.tokens.access_token; + const authenticatedExecutor = drupalExecutor( + `${process.env.GATSBY_DRUPAL_URL}/graphql`, + false, + ); + return ( + { + const data = await authenticatedExecutor( + CurrentUserQuery, + {}, + accessToken, + ); + return { + currentUser: data.currentUser, + }; + }} + id={CurrentUserQuery} + > + + + ); + } else { + return ; + } +} diff --git a/apps/website/src/utils/drupal-executor.ts b/apps/website/src/utils/drupal-executor.ts index f38a98fc5..0dd78d8b5 100644 --- a/apps/website/src/utils/drupal-executor.ts +++ b/apps/website/src/utils/drupal-executor.ts @@ -7,10 +7,39 @@ export function drupalExecutor(endpoint: string, forward: boolean = true) { return async function ( id: OperationId, variables?: OperationVariables, + accessToken?: string, ) { const url = new URL(endpoint, window.location.origin); const isMutation = id.includes('Mutation:'); - if (isMutation) { + const isAuthenticated = accessToken !== undefined; + + if (isAuthenticated) { + const { data, errors } = await ( + await fetch(url, { + method: 'POST', + body: JSON.stringify({ + queryId: id, + variables: variables, + }), + headers: forward + ? { + 'SLB-Forwarded-Proto': window.location.protocol.slice(0, -1), + 'SLB-Forwarded-Host': window.location.hostname, + 'SLB-Forwarded-Port': window.location.port, + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + } + : { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + }) + ).json(); + if (errors) { + throw errors; + } + return data; + } else if (isMutation) { const { data, errors } = await ( await fetch(url, { method: 'POST', diff --git a/apps/website/src/utils/wrapRootElement.tsx b/apps/website/src/utils/wrapRootElement.tsx new file mode 100644 index 000000000..68dbae1bf --- /dev/null +++ b/apps/website/src/utils/wrapRootElement.tsx @@ -0,0 +1,7 @@ +import { GatsbyBrowser, WrapRootElementBrowserArgs } from 'gatsby'; +import { SessionProvider } from 'next-auth/react'; +import React from 'react'; + +export const WrapRootElement: GatsbyBrowser['wrapRootElement'] = ({ + element, +}: WrapRootElementBrowserArgs) => {element}; diff --git a/packages/drupal/custom/custom.deploy.php b/packages/drupal/custom/custom.deploy.php index 29177aad4..e950e51d6 100644 --- a/packages/drupal/custom/custom.deploy.php +++ b/packages/drupal/custom/custom.deploy.php @@ -14,8 +14,6 @@ function custom_deploy_set_consumers(array &$sandbox): string { } // Check requirements. - $entityTypeManager = \Drupal::entityTypeManager(); - $publisherUrl = getenv('PUBLISHER_URL'); if (!$publisherUrl) { throw new \Exception('PUBLISHER_URL environment variable is not set. It is required to setup the Publisher OAuth Consumer.'); @@ -26,11 +24,12 @@ function custom_deploy_set_consumers(array &$sandbox): string { throw new \Exception('PUBLISHER_OAUTH2_CLIENT_SECRET environment variable is not set. It is required to setup the Publisher OAuth Consumer.'); } + $entityTypeManager = \Drupal::entityTypeManager(); $consumersStorage = $entityTypeManager->getStorage('consumer'); $existingConsumers = $consumersStorage->loadMultiple(); $hasPublisherConsumer = FALSE; /** @var \Drupal\consumers\Entity\ConsumerInterface $consumer */ - foreach($existingConsumers as $consumer) { + foreach ($existingConsumers as $consumer) { // As a side effect, delete the default consumer. // It is installed by the Consumers module. if ($consumer->getClientId() === 'default_consumer') { @@ -56,3 +55,50 @@ function custom_deploy_set_consumers(array &$sandbox): string { return t('Publisher OAuth Consumer already exists.'); } + +/** + * Set up the Website OAuth Consumer. + */ +function custom_deploy_set_website_consumer(array &$sandbox): string { + // Skip for Silverback environments. + if (getenv('SB_ENVIRONMENT')) { + return t('Skipping for Silverback environment.'); + } + + // Check requirements. + $websiteUrl = getenv('NETLIFY_URL'); + if (!$websiteUrl) { + throw new \Exception('NETLIFY_URL environment variable is not set. It is required to setup the Website OAuth Consumer.'); + } + + $clientSecret = getenv('WEBSITE_OAUTH2_CLIENT_SECRET'); + if (!$clientSecret) { + throw new \Exception('WEBSITE_OAUTH2_CLIENT_SECRET environment variable is not set. It is required to setup the Website OAuth Consumer.'); + } + + $entityTypeManager = \Drupal::entityTypeManager(); + $consumersStorage = $entityTypeManager->getStorage('consumer'); + $existingConsumers = $consumersStorage->loadMultiple(); + $hasWebsiteConsumer = FALSE; + /** @var \Drupal\consumers\Entity\ConsumerInterface $consumer */ + foreach ($existingConsumers as $consumer) { + if ($consumer->getClientId() === 'website') { + $hasWebsiteConsumer = TRUE; + } + } + + // Create the Website Consumer if it does not exist. + if (!$hasWebsiteConsumer) { + $oAuthCallback = $websiteUrl . '/api/auth/callback/drupal'; + $consumersStorage->create([ + 'label' => 'Website', + 'client_id' => 'website', + 'is_default' => FALSE, + 'secret' => $clientSecret, + 'redirect' => $oAuthCallback, + ])->save(); + return t('Created Website OAuth Consumer.'); + } + + return t('Website OAuth Consumer already exists.'); +} diff --git a/packages/drupal/custom/custom.services.yml b/packages/drupal/custom/custom.services.yml index 5adbb9068..3ffbc2c60 100644 --- a/packages/drupal/custom/custom.services.yml +++ b/packages/drupal/custom/custom.services.yml @@ -8,5 +8,9 @@ services: class: Drupal\custom\Webform arguments: ['@renderer', '@entity_type.manager', '@serializer'] + custom.directives: + class: Drupal\custom\Directives + arguments: ['@current_user', '@date.formatter'] + custom.menus: class: Drupal\custom\Menus diff --git a/packages/drupal/custom/src/Directives.php b/packages/drupal/custom/src/Directives.php new file mode 100644 index 000000000..a2d3a925a --- /dev/null +++ b/packages/drupal/custom/src/Directives.php @@ -0,0 +1,42 @@ +currentUser->isAnonymous()) { + return NULL; + } + else { + $user = User::load($account->id()); + $memberFor = $this->dateFormatter->formatTimeDiffSince($user->getCreatedTime()); + return [ + 'id' => $account->id(), + 'name' => $account->getDisplayName(), + 'email' => $account->getEmail(), + 'memberFor' => $memberFor, + ]; + } + } + +} diff --git a/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx b/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx index c0bbaacdc..83bfd63de 100644 --- a/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx +++ b/packages/drupal/gutenberg_blocks/src/blocks/cta.tsx @@ -5,7 +5,7 @@ import { RichText, } from 'wordpress__block-editor'; import { registerBlockType } from 'wordpress__blocks'; -import { PanelBody, ToggleControl } from 'wordpress__components'; +import { PanelBody, SelectControl, ToggleControl } from 'wordpress__components'; import { compose, withState } from 'wordpress__compose'; // @ts-ignore @@ -37,6 +37,14 @@ registerBlockType('custom/cta', { openInNewTab: { type: 'boolean', }, + icon: { + type: 'string', + default: 'NONE', + }, + iconPosition: { + type: 'string', + default: 'AFTER', + }, }, // @ts-ignore edit: compose(withState({}))((props) => { @@ -116,6 +124,35 @@ registerBlockType('custom/cta', { }); }} /> + { + props.setAttributes({ + icon, + }); + }} + /> + {typeof props.attributes.icon !== 'undefined' && + props.attributes.icon !== 'NONE' && ( + { + props.setAttributes({ + iconPosition, + }); + }} + /> + )} diff --git a/packages/drupal/gutenberg_blocks/src/blocks/horizontal-separator.tsx b/packages/drupal/gutenberg_blocks/src/blocks/horizontal-separator.tsx new file mode 100644 index 000000000..720094b1f --- /dev/null +++ b/packages/drupal/gutenberg_blocks/src/blocks/horizontal-separator.tsx @@ -0,0 +1,21 @@ +import { registerBlockType } from 'wordpress__blocks'; +import { compose, withState } from 'wordpress__compose'; + +declare const Drupal: { t: (s: string) => string }; + +const { t: __ } = Drupal; + +// @ts-ignore +registerBlockType(`custom/horizontal-separator`, { + title: __('Horizontal separator'), + icon: 'minus', + category: 'text', + attributes: {}, + // @ts-ignore + edit: compose(withState())(() => { + return
; + }), + save() { + return null; + }, +}); diff --git a/packages/drupal/gutenberg_blocks/src/index.ts b/packages/drupal/gutenberg_blocks/src/index.ts index 2763bdad8..2c86b28df 100644 --- a/packages/drupal/gutenberg_blocks/src/index.ts +++ b/packages/drupal/gutenberg_blocks/src/index.ts @@ -9,3 +9,4 @@ import './blocks/image-with-text'; import './filters/list'; import './blocks/cta'; import './blocks/quote'; +import './blocks/horizontal-separator'; diff --git a/packages/drupal/test_content/content/consumer/73dc184f-806f-4c22-8014-6686390a06cf.yml b/packages/drupal/test_content/content/consumer/73dc184f-806f-4c22-8014-6686390a06cf.yml new file mode 100644 index 000000000..3115d8d97 --- /dev/null +++ b/packages/drupal/test_content/content/consumer/73dc184f-806f-4c22-8014-6686390a06cf.yml @@ -0,0 +1,38 @@ +_meta: + version: '1.0' + entity_type: consumer + uuid: 73dc184f-806f-4c22-8014-6686390a06cf + default_langcode: en +default: + owner_id: + - + target_id: 1 + client_id: + - + value: website + label: + - + value: Website + description: + - + value: banana + third_party: + - + value: true + is_default: + - + value: false + secret: + - + value: $2y$10$VFVObrn/AcLeb7Va.xcy8OUQTPYC3t7DzJsfwZK8DzHzlZ05njMSS + existing: '' + pre_hashed: false + confidential: + - + value: true + redirect: + - + value: 'http://localhost:8000/api/auth/callback/drupal' + pkce: + - + value: false diff --git a/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml b/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml index 21dda2902..6e00ecc93 100644 --- a/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml +++ b/packages/drupal/test_content/content/node/a397ca48-8fad-411e-8901-0eba2feb989c.yml @@ -11,43 +11,31 @@ _meta: 5dfc1856-e9e4-4f02-9cd6-9d888870ce1a: media default: revision_uid: - - - target_id: 1 + - target_id: 1 status: - - - value: true + - value: true uid: - - - target_id: 1 + - target_id: 1 title: - - - value: 'Blocks: complete' + - value: 'Blocks: complete' created: - - - value: 1686759493 + - value: 1686759493 promote: - - - value: false + - value: false sticky: - - - value: false + - value: false moderation_state: - - - value: published + - value: published path: - - - alias: /blocks-complete + - alias: /blocks-complete langcode: en pathauto: 0 content_translation_source: - - - value: und + - value: und content_translation_outdated: - - - value: false + - value: false body: - - - value: |- + - value: |- @@ -55,6 +43,8 @@ default:

A standalone paragraph with markup and link

+ + @@ -108,9 +98,9 @@ default: - + - + @@ -123,40 +113,29 @@ default: translations: de: status: - - - value: true + - value: true uid: - - - target_id: 1 + - target_id: 1 title: - - - value: 'Blocks: complete DE' + - value: 'Blocks: complete DE' created: - - - value: 1687338353 + - value: 1687338353 promote: - - - value: false + - value: false sticky: - - - value: false + - value: false moderation_state: - - - value: published + - value: published path: - - - alias: /blocks-complete + - alias: /blocks-complete langcode: de pathauto: 0 content_translation_source: - - - value: en + - value: en content_translation_outdated: - - - value: false + - value: false body: - - - value: |- + - value: |- diff --git a/packages/drupal/test_content/export.php b/packages/drupal/test_content/export.php index e7daf34b4..7572d38a0 100644 --- a/packages/drupal/test_content/export.php +++ b/packages/drupal/test_content/export.php @@ -13,6 +13,7 @@ // troubles if exported to the default content. 'path_alias', 'content_moderation_state', + 'oauth2_token', 'redirect', 'webform_submission', 'consumer', diff --git a/packages/schema/codegen.ts b/packages/schema/codegen.ts index 7dc41889a..7a0ea92c9 100644 --- a/packages/schema/codegen.ts +++ b/packages/schema/codegen.ts @@ -40,7 +40,7 @@ const config: CodegenConfig = { context: ['gatsby'], }, }, - // Directive autoloader for Drupla. + // Directive autoloader for Drupal. 'build/drupal-autoload.json': { plugins: ['@amazeelabs/codegen-autoloader'], config: { diff --git a/packages/schema/src/fragments/Page.gql b/packages/schema/src/fragments/Page.gql index cbf2227eb..3ecddb3c9 100644 --- a/packages/schema/src/fragments/Page.gql +++ b/packages/schema/src/fragments/Page.gql @@ -35,6 +35,7 @@ fragment Page on Page { ...BlockCta ...BlockImageWithText ...BlockQuote + ...BlockHorizontalSeparator } metaTags { tag diff --git a/packages/schema/src/fragments/PageContent/BlockCta.gql b/packages/schema/src/fragments/PageContent/BlockCta.gql index 2dae0fda2..db56f5580 100644 --- a/packages/schema/src/fragments/PageContent/BlockCta.gql +++ b/packages/schema/src/fragments/PageContent/BlockCta.gql @@ -2,4 +2,6 @@ fragment BlockCta on BlockCta { url text openInNewTab + icon + iconPosition } diff --git a/packages/schema/src/fragments/PageContent/BlockHorizontalSeparator.gql b/packages/schema/src/fragments/PageContent/BlockHorizontalSeparator.gql new file mode 100644 index 000000000..ef992a139 --- /dev/null +++ b/packages/schema/src/fragments/PageContent/BlockHorizontalSeparator.gql @@ -0,0 +1,5 @@ +fragment BlockHorizontalSeparator on BlockHorizontalSeparator { + # This is not really used, but until we have other real setting fields, we + # have to provide a dummy one. + markup +} diff --git a/packages/schema/src/operations/CurrentUser.gql b/packages/schema/src/operations/CurrentUser.gql new file mode 100644 index 000000000..56047b3d8 --- /dev/null +++ b/packages/schema/src/operations/CurrentUser.gql @@ -0,0 +1,8 @@ +query CurrentUser { + currentUser { + id + name + email + memberFor + } +} diff --git a/packages/schema/src/schema.graphql b/packages/schema/src/schema.graphql index ca52d0ef3..6ed19968b 100644 --- a/packages/schema/src/schema.graphql +++ b/packages/schema/src/schema.graphql @@ -73,6 +73,14 @@ directive @route( path: String! ) repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT +""" +Retrieve the properties of the current user. + +Provided by the "silverback_gatsby" module. +implementation(drupal): custom.directives::currentUser +""" +directive @currentUser repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT + enum Locale @default @value(string: "en") { en de @@ -206,6 +214,7 @@ union PageContent @resolveEditorBlockType = | BlockCta | BlockImageWithText | BlockQuote + | BlockHorizontalSeparator type BlockForm @type(id: "custom/form") { url: Url @resolveEditorBlockAttribute(key: "formId") @webformIdToUrl(id: "$") @@ -249,6 +258,31 @@ type BlockCta @type(id: "custom/cta") { url: Url @resolveEditorBlockAttribute(key: "url") text: String @resolveEditorBlockAttribute(key: "text") openInNewTab: Boolean @resolveEditorBlockAttribute(key: "openInNewTab") + icon: CTAIconType @resolveEditorBlockAttribute(key: "icon") + iconPosition: CTAIconPosition + @resolveEditorBlockAttribute(key: "iconPosition") +} + +enum CTAIconType { + NONE + ARROW +} + +enum CTAIconPosition { + AFTER + BEFORE +} + +type BlockImageWithText @type(id: "custom/image-with-text") { + image: MediaImage @resolveEditorBlockMedia + textContent: BlockMarkup @resolveEditorBlockChildren @seek(pos: 0) +} + +type BlockQuote @type(id: "custom/quote") { + quote: Markup @resolveEditorBlockAttribute(key: "quote") + author: String @resolveEditorBlockAttribute(key: "author") + role: String @resolveEditorBlockAttribute(key: "role") + image: MediaImage @resolveEditorBlockMedia } type BlockImageWithText @type(id: "custom/image-with-text") { @@ -270,6 +304,10 @@ type BlockQuote @type(id: "custom/quote") { image: MediaImage @resolveEditorBlockMedia } +type BlockHorizontalSeparator @type(id: "custom/horizontal-separator") { + markup: Markup! @resolveEditorBlockMarkup +} + input PaginationInput { limit: Int! offset: Int! @@ -298,6 +336,8 @@ type Query { stringTranslations: [TranslatableString!] @gatsbyNodes(type: "TranslatableString") + + currentUser: User @currentUser } type Mutation { @@ -322,6 +362,13 @@ type MutationError { field: String } +type User { + id: ID! + name: String! + email: String! + memberFor: String! +} + type MetaTag { tag: String! attributes: MetaTagAttributes! diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts index ca58622e6..c8db1415e 100644 --- a/packages/ui/.storybook/main.ts +++ b/packages/ui/.storybook/main.ts @@ -23,6 +23,10 @@ const config: StorybookConfig = { pluginTurbosnap({ rootDir: config.root ?? process.cwd() }), imagetools(), ], + // https://github.com/nextauthjs/next-auth/discussions/4566 + define: { + 'process.env': process.env, + }, } satisfies UserConfig), staticDirs: ['../static/public', '../static/stories'], stories: ['../src/**/*.stories.@(ts|tsx|mdx)'], diff --git a/packages/ui/.storybook/preview.tsx b/packages/ui/.storybook/preview.tsx index 28c5c4eb6..08121d802 100644 --- a/packages/ui/.storybook/preview.tsx +++ b/packages/ui/.storybook/preview.tsx @@ -5,6 +5,13 @@ import { Decorator } from '@storybook/react'; import React from 'react'; import { IntlProvider } from 'react-intl'; import { SWRConfig, useSWRConfig } from 'swr'; +import { + SessionContext as NextSessionContext, + SessionProvider, +} from 'next-auth/react'; +import { faker } from '@faker-js/faker'; +import { Session } from 'next-auth'; +import { useMemo } from 'react'; // Every story is wrapped in an IntlProvider by default. const IntlDecorator: Decorator = (Story) => ( @@ -21,6 +28,68 @@ const LocationDecorator: Decorator = (Story, ctx) => { ); }; +type AuthState = + | { data: Session; status: 'authenticated' } + | { data: null; status: 'unauthenticated' | 'loading' }; + +export const AUTH_STATES: Record< + string, + { title: string; session: AuthState | undefined } +> = { + unknown: { + title: 'Session Unknown', + session: undefined, + }, + loading: { + title: 'Session Loading', + session: { + data: null, + status: 'loading', + }, + }, + unauthenticated: { + title: 'Not Authenticated', + session: { + data: null, + status: 'unauthenticated', + }, + }, + authenticated: { + title: 'Authenticated', + session: { + data: { + user: { + name: faker.person.fullName(), + email: faker.internet.email(), + image: faker.image.avatar(), + }, + expires: faker.date.future().toString(), + }, + status: 'authenticated', + }, + }, +}; + +const SessionContext: React.FC<{ session: AuthState }> = ({ + session, + children, +}) => { + const value = useMemo((): AuthState => { + return session ? session : { data: undefined, status: 'unauthenticated' }; + }, [session]); + + return {children}; +}; + +const SessionDecorator: Decorator = (Story, context) => { + const session = AUTH_STATES[context.globals.authState]?.session; + return ( + + + + ); +}; + declare global { interface Window { __STORYBOOK_PREVIEW__: { @@ -64,4 +133,9 @@ export const parameters = { chromatic: { viewports: [320, 840, 1440] }, }; -export const decorators = [LocationDecorator, IntlDecorator, SWRCacheDecorator]; +export const decorators = [ + LocationDecorator, + IntlDecorator, + SWRCacheDecorator, + SessionDecorator, +]; diff --git a/packages/ui/package.json b/packages/ui/package.json index f3320b5ce..4694ac3a3 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -40,6 +40,7 @@ "dependencies": { "@amazeelabs/silverback-iframe": "^1.3.0", "@custom/schema": "workspace:*", + "@faker-js/faker": "^8.4.1", "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.1.1", "@hookform/resolvers": "^3.3.3", @@ -47,6 +48,7 @@ "framer-motion": "^10.17.4", "hast-util-is-element": "^2.1.3", "hast-util-select": "^5.0.5", + "next-auth": "^4.24.7", "query-string": "^9.0.0", "react-hook-form": "^7.49.2", "react-intl": "^6.6.2", diff --git a/packages/ui/src/components/Molecules/UserButton.stories.tsx b/packages/ui/src/components/Molecules/UserButton.stories.tsx new file mode 100644 index 000000000..644b6244c --- /dev/null +++ b/packages/ui/src/components/Molecules/UserButton.stories.tsx @@ -0,0 +1,11 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { UserButton } from './UserButton'; + +export default { + component: UserButton, +} satisfies Meta; + +// @todo add signed in and signed out states. +// @todo fix next-auth errors +export const UserButtonStory = {} satisfies StoryObj; diff --git a/packages/ui/src/components/Molecules/UserButton.tsx b/packages/ui/src/components/Molecules/UserButton.tsx new file mode 100644 index 000000000..cba33168a --- /dev/null +++ b/packages/ui/src/components/Molecules/UserButton.tsx @@ -0,0 +1,54 @@ +import { Link, Url } from '@custom/schema'; +import { signIn, signOut, useSession } from 'next-auth/react'; +import React from 'react'; +import { useIntl } from 'react-intl'; + +export function UserButton() { + const session = useSession(); + const intl = useIntl(); + return ( + <> + {!session && <>} + {session?.status !== 'authenticated' && ( + { + e.preventDefault(); + signIn(); + }} + > + {intl.formatMessage({ + defaultMessage: 'Sign in', + id: 'SQJto2', + })} + + )} + {session?.status === 'authenticated' && session.data.user && ( + <> + + {session.data.user.name + ? session.data.user.name + : session.data.user.email} + + { + e.preventDefault(); + signOut(); + }} + > + {intl.formatMessage({ + defaultMessage: 'Sign out', + id: 'xXbJso', + })} + + + )} + + ); +} diff --git a/packages/ui/src/components/Organisms/Header.tsx b/packages/ui/src/components/Organisms/Header.tsx index dacd4f058..4bcd4afe0 100644 --- a/packages/ui/src/components/Organisms/Header.tsx +++ b/packages/ui/src/components/Organisms/Header.tsx @@ -18,6 +18,7 @@ import { MobileMenuProvider, } from '../Client/MobileMenu'; import { LanguageSwitcher } from '../Molecules/LanguageSwitcher'; +import { UserButton } from '../Molecules/UserButton'; function useHeaderNavigation(lang: string = 'en') { return ( @@ -76,6 +77,13 @@ export function Header() { ) : ( + + {item.title} + {item.children.map((child) => child.children.length === 0 ? ( + + {item.title} + {item.children.map((child) => child.children.length === 0 ? ( +
+ +
); } diff --git a/packages/ui/src/components/Organisms/PageContent/BlockCta.stories.ts b/packages/ui/src/components/Organisms/PageContent/BlockCta.stories.ts new file mode 100644 index 000000000..420a4a375 --- /dev/null +++ b/packages/ui/src/components/Organisms/PageContent/BlockCta.stories.ts @@ -0,0 +1,36 @@ +import { CtaIconPosition, Url } from '@custom/schema'; +import { Meta, StoryObj } from '@storybook/react'; + +import { BlockCta } from './BlockCta'; + +export default { + component: BlockCta, +} satisfies Meta; + +export const BlockCtaDefault = { + args: { + text: 'Support center', + url: 'https://example.com' as Url, + openInNewTab: false, + icon: 'ARROW', + iconPosition: CtaIconPosition.After, + }, +} satisfies StoryObj; + +export const BlockCtaStoryIconLeft = { + args: { + text: 'Support center', + url: 'https://example.com' as Url, + openInNewTab: false, + icon: 'ARROW', + iconPosition: CtaIconPosition.Before, + }, +} satisfies StoryObj; + +export const BlockCtaStoryNoIcon = { + args: { + text: 'Support center', + url: 'https://example.com' as Url, + openInNewTab: false, + }, +} satisfies StoryObj; diff --git a/packages/ui/src/components/Organisms/PageContent/BlockCta.tsx b/packages/ui/src/components/Organisms/PageContent/BlockCta.tsx index a652f30b5..cdf574951 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockCta.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockCta.tsx @@ -1,7 +1,43 @@ -import { BlockCtaFragment } from '@custom/schema'; +import { + BlockCtaFragment, + CtaIconPosition, + CtaIconType, + Link, + Url, +} from '@custom/schema'; +import clsx from 'clsx'; import React from 'react'; export function BlockCta(props: BlockCtaFragment) { - console.log(props); - return
; + return ( + + {props.text} + {!!props.icon && props.icon === CtaIconType.Arrow && } + + ); } + +const ArrowRightIcon = () => ( + + + +); diff --git a/packages/ui/src/components/Organisms/PageContent/BlockHorizontalSeparator.stories.ts b/packages/ui/src/components/Organisms/PageContent/BlockHorizontalSeparator.stories.ts new file mode 100644 index 000000000..006c3158f --- /dev/null +++ b/packages/ui/src/components/Organisms/PageContent/BlockHorizontalSeparator.stories.ts @@ -0,0 +1,11 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { BlockHorizontalSeparator } from './BlockHorizontalSeparator'; + +export default { + component: BlockHorizontalSeparator, +} satisfies Meta; + +export const HorizontalSeparator = { + args: {}, +} satisfies StoryObj; diff --git a/packages/ui/src/components/Organisms/PageContent/BlockHorizontalSeparator.tsx b/packages/ui/src/components/Organisms/PageContent/BlockHorizontalSeparator.tsx new file mode 100644 index 000000000..6a3922e8a --- /dev/null +++ b/packages/ui/src/components/Organisms/PageContent/BlockHorizontalSeparator.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export function BlockHorizontalSeparator() { + return
; +} diff --git a/packages/ui/src/components/Organisms/PageContent/BlockMarkup.tsx b/packages/ui/src/components/Organisms/PageContent/BlockMarkup.tsx index ae4ef62f9..48bf07428 100644 --- a/packages/ui/src/components/Organisms/PageContent/BlockMarkup.tsx +++ b/packages/ui/src/components/Organisms/PageContent/BlockMarkup.tsx @@ -16,10 +16,7 @@ export function BlockMarkup(props: BlockMarkupFragment) {
; case 'BlockQuote': return
; + case 'BlockHorizontalSeparator': + return ; default: throw new UnreachableCaseError(block); } diff --git a/packages/ui/src/components/Organisms/UserProfile.stories.tsx b/packages/ui/src/components/Organisms/UserProfile.stories.tsx new file mode 100644 index 000000000..71d24f28e --- /dev/null +++ b/packages/ui/src/components/Organisms/UserProfile.stories.tsx @@ -0,0 +1,54 @@ +import { + CurrentUserQuery, + OperationExecutor, + OperationResult, +} from '@custom/schema'; +import { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +import { UserProfile } from './UserProfile'; + +type CurrentUserExecutor = ( + id: typeof CurrentUserQuery, +) => Promise>; + +export default { + title: 'Components/Organisms/User Profile', + render: (args) => { + return ( + + + + ); + }, +} satisfies Meta<{ exec: CurrentUserExecutor }>; + +type UserProfileStory = StoryObj<{ exec: CurrentUserExecutor }>; + +export const Loading = { + args: { + exec: () => new Promise>(() => {}), + }, +} satisfies UserProfileStory; + +export const Error = { + args: { + exec: () => + new Promise>(() => { + throw 'Authentication error.'; + }), + }, +} satisfies UserProfileStory; + +export const Authenticated = { + args: { + exec: async () => ({ + currentUser: { + id: '1', + name: 'Jane Doe', + email: 'jane@example.com', + memberFor: '1 year', + }, + }), + }, +} satisfies UserProfileStory; diff --git a/packages/ui/src/components/Organisms/UserProfile.tsx b/packages/ui/src/components/Organisms/UserProfile.tsx new file mode 100644 index 000000000..86bd1df10 --- /dev/null +++ b/packages/ui/src/components/Organisms/UserProfile.tsx @@ -0,0 +1,51 @@ +import { CurrentUserQuery } from '@custom/schema'; +import React from 'react'; +import { useIntl } from 'react-intl'; + +import { useOperation } from '../../utils/operation'; + +export function UserProfile() { + const intl = useIntl(); + const { data, isLoading, error } = useOperation(CurrentUserQuery); + return ( +
+
+
+ {!isLoading && !error && data?.currentUser ? ( + <> +

+ {data?.currentUser?.name} +

+

+ {intl.formatMessage( + { defaultMessage: 'Email: {email}', id: 'uPNlBw' }, + { + email: data?.currentUser?.email, + }, + )} +

+

+ {intl.formatMessage( + { + defaultMessage: 'Member for {member_for}', + id: 'OZlBcy', + }, + { + member_for: data?.currentUser?.memberFor, + }, + )} +

+ + ) : ( +

+ {intl.formatMessage({ + defaultMessage: 'Sign in to view your profile.', + id: 'K99M9A', + })} +

+ )} +
+
+
+ ); +} diff --git a/packages/ui/src/components/Routes/UserProfile.tsx b/packages/ui/src/components/Routes/UserProfile.tsx new file mode 100644 index 000000000..5ade53004 --- /dev/null +++ b/packages/ui/src/components/Routes/UserProfile.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { PageTransition } from '../Molecules/PageTransition'; +import { UserProfile as UserProfileOrganism } from '../Organisms/UserProfile'; + +export function UserProfile() { + return ( + + + + ); +} diff --git a/packages/ui/src/tailwind.config.cjs b/packages/ui/src/tailwind.config.cjs index bbdaea5bf..c6f9477ec 100644 --- a/packages/ui/src/tailwind.config.cjs +++ b/packages/ui/src/tailwind.config.cjs @@ -1,9 +1,61 @@ /** @type {import('tailwindcss').Config} */ -const theme = require('./stylingAssets.json'); +const stylingAssets = require('./stylingAssets.json'); module.exports = { content: ['./src/**/*.{tsx, mdx}'], - ...theme, + theme: { + extend: { + typography: ({ theme }) => ({ + DEFAULT: { + css: [ + { + 'a, p a': {}, + 'ul, ol': { + fontSize: '1.125rem', + lineHeight: '1.688rem', + }, + ol: {}, + 'ul>li::marker, ol>li::marker': {}, + strong: { + color: theme('colors.gray.900'), + fontWeight: '700', + }, + '.prose p': { + color: theme('colors.gray.500'), + fontSize: '1.125rem', + lineHeight: '1.688rem', + }, + '.prose a, .prose p a': { + color: theme('colors.blue.600'), + fontWeight: '400', + }, + '.prose em': { + color: theme('colors.gray.900'), + }, + 'prose marker': { + fontWeight: '700', + }, + blockquote: {}, + '.prose blockquote p': { + fontWeight: '700', + color: '#111928', + }, + cite: {}, + 'h1, h2, h3, h4, h5, h6': {}, + '.prose h1': {}, + '.prose h2': { + fontWeight: '700', + color: theme('colors.gray.900'), + }, + '.prose h3': {}, + '.prose h4': {}, + }, + ], + }, + }), + }, + ...stylingAssets.theme, + }, plugins: [ require('@tailwindcss/forms'), require('@tailwindcss/aspect-ratio'), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05006d49f..dd8d3883b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: devDependencies: '@commitlint/cli': specifier: ^18.4.3 - version: 18.4.3(@types/node@18.19.31)(typescript@5.3.3) + version: 18.4.3(@types/node@20.11.17)(typescript@5.3.3) '@commitlint/config-conventional': specifier: ^18.4.3 version: 18.4.3 @@ -64,13 +64,13 @@ importers: version: 5.3.3 vitest: specifier: ^1.1.1 - version: 1.1.1(@types/node@18.19.31) + version: 1.1.1(@types/node@20.11.17) apps/cms: dependencies: '@amazeelabs/gatsby-source-silverback': specifier: '*' - version: 1.14.0(@types/node@18.19.31)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.3)(typescript@5.4.4) + version: 1.14.0(@types/node@20.11.17)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.3)(typescript@5.4.4) '@custom/custom': specifier: workspace:* version: link:../../packages/drupal/custom @@ -279,7 +279,7 @@ importers: version: 1.0.1 '@amazeelabs/gatsby-source-silverback': specifier: ^1.14.0 - version: 1.14.0(@types/node@18.19.31)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.1)(typescript@4.9.5) + version: 1.14.0(@types/node@20.11.17)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.1)(typescript@4.9.5) '@amazeelabs/publisher': specifier: ^2.4.17 version: 2.4.17(@types/react@18.2.46)(react@18.2.0)(typescript@4.9.5) @@ -301,6 +301,21 @@ importers: '@custom/ui': specifier: workspace:* version: link:../../packages/ui + '@gatsbyjs/reach-router': + specifier: ^2.0.1 + version: 2.0.1(react-dom@18.2.0)(react@18.2.0) + '@netlify/plugin-gatsby': + specifier: ^3.8.1 + version: 3.8.1(@gatsbyjs/reach-router@2.0.1)(common-tags@1.8.2) + '@netlify/plugin-nextjs': + specifier: ^4.41.3 + version: 4.41.3 + babel-loader: + specifier: ^9.1.3 + version: 9.1.3(@babel/core@7.24.4)(webpack@5.91.0) + body-parser: + specifier: ^1.20.2 + version: 1.20.2 gatsby: specifier: ^5.13.1 version: 5.13.1(babel-eslint@10.1.0)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) @@ -337,9 +352,18 @@ importers: mime-types: specifier: ^2.1.35 version: 2.1.35 + multer: + specifier: 1.4.5-lts.1 + version: 1.4.5-lts.1 netlify-cli: specifier: ^17.21.1 - version: 17.21.1(@types/node@18.19.31) + version: 17.21.1(@types/node@20.11.17) + next: + specifier: ^14.2.3 + version: 14.2.3(@babel/core@7.24.4)(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0) + next-auth: + specifier: ^4.24.7 + version: 4.24.7(next@14.2.3)(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -373,7 +397,7 @@ importers: version: 2.0.3 vitest: specifier: ^1.1.1 - version: 1.1.1(@types/node@18.19.31)(happy-dom@12.10.3) + version: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) packages/drupal/custom: {} @@ -520,6 +544,9 @@ importers: '@custom/schema': specifier: workspace:* version: link:../schema + '@faker-js/faker': + specifier: ^8.4.1 + version: 8.4.1 '@headlessui/react': specifier: ^1.7.17 version: 1.7.17(react-dom@18.2.0)(react@18.2.0) @@ -541,6 +568,9 @@ importers: hast-util-select: specifier: ^5.0.5 version: 5.0.5 + next-auth: + specifier: ^4.24.7 + version: 4.24.7(next@14.2.3)(react-dom@18.2.0)(react@18.2.0) query-string: specifier: ^9.0.0 version: 9.0.0 @@ -601,7 +631,7 @@ importers: version: 8.0.0-alpha.14(jest@29.7.0)(vitest@1.1.1) '@storybook/test-runner': specifier: ^0.16.0 - version: 0.16.0(@types/node@18.19.31) + version: 0.16.0(@types/node@20.11.17) '@swc/cli': specifier: ^0.1.63 version: 0.1.63(@swc/core@1.3.102) @@ -694,7 +724,7 @@ importers: version: 5.3.3 vite: specifier: ^5.0.10 - version: 5.0.10(@types/node@18.19.31) + version: 5.0.10(@types/node@20.11.17) vite-imagetools: specifier: ^6.2.9 version: 6.2.9 @@ -703,7 +733,7 @@ importers: version: 1.0.3 vitest: specifier: ^1.1.1 - version: 1.1.1(@types/node@18.19.31)(happy-dom@12.10.3) + version: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) tests/e2e: devDependencies: @@ -996,7 +1026,7 @@ packages: - utf-8-validate dev: false - /@amazeelabs/gatsby-source-silverback@1.14.0(@types/node@18.19.31)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.1)(typescript@4.9.5): + /@amazeelabs/gatsby-source-silverback@1.14.0(@types/node@20.11.17)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.1)(typescript@4.9.5): resolution: {integrity: sha512-tIL4lPx7mQDBH5XiouXgTEhOIXF/oKDss0OYbHJEbxXVofv4IDifZcZZO1Hw9oWmrTSaJhYoC2Bdm+2kdvxf6g==} peerDependencies: gatsby-plugin-sharp: ^5.13.1 @@ -1006,7 +1036,7 @@ packages: gatsby-graphql-source-toolkit: 2.0.4(gatsby@5.13.1) gatsby-plugin-sharp: 5.13.1(gatsby@5.13.1)(graphql@16.8.1) graphql: 16.8.1 - graphql-config: 5.0.3(@types/node@18.19.31)(graphql@16.8.1)(typescript@4.9.5) + graphql-config: 5.0.3(@types/node@20.11.17)(graphql@16.8.1)(typescript@4.9.5) isomorphic-fetch: 3.0.0 lodash-es: 4.17.21 node-fetch: 3.3.2 @@ -1021,7 +1051,7 @@ packages: - utf-8-validate dev: false - /@amazeelabs/gatsby-source-silverback@1.14.0(@types/node@18.19.31)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.3)(typescript@5.4.4): + /@amazeelabs/gatsby-source-silverback@1.14.0(@types/node@20.11.17)(gatsby-plugin-sharp@5.13.1)(gatsby@5.13.3)(typescript@5.4.4): resolution: {integrity: sha512-tIL4lPx7mQDBH5XiouXgTEhOIXF/oKDss0OYbHJEbxXVofv4IDifZcZZO1Hw9oWmrTSaJhYoC2Bdm+2kdvxf6g==} peerDependencies: gatsby-plugin-sharp: ^5.13.1 @@ -1031,7 +1061,7 @@ packages: gatsby-graphql-source-toolkit: 2.0.4(gatsby@5.13.3) gatsby-plugin-sharp: 5.13.1(gatsby@5.13.3)(graphql@16.8.1) graphql: 16.8.1 - graphql-config: 5.0.3(@types/node@18.19.31)(graphql@16.8.1)(typescript@5.4.4) + graphql-config: 5.0.3(@types/node@20.11.17)(graphql@16.8.1)(typescript@5.4.4) isomorphic-fetch: 3.0.0 lodash-es: 4.17.21 node-fetch: 3.3.2 @@ -2687,14 +2717,14 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - /@commitlint/cli@18.4.3(@types/node@18.19.31)(typescript@5.3.3): + /@commitlint/cli@18.4.3(@types/node@20.11.17)(typescript@5.3.3): resolution: {integrity: sha512-zop98yfB3A6NveYAZ3P1Mb6bIXuCeWgnUfVNkH4yhIMQpQfzFwseadazOuSn0OOfTt0lWuFauehpm9GcqM5lww==} engines: {node: '>=v18'} hasBin: true dependencies: '@commitlint/format': 18.6.1 '@commitlint/lint': 18.6.1 - '@commitlint/load': 18.6.1(@types/node@18.19.31)(typescript@5.3.3) + '@commitlint/load': 18.6.1(@types/node@20.11.17)(typescript@5.3.3) '@commitlint/read': 18.6.1 '@commitlint/types': 18.6.1 execa: 5.1.1 @@ -2765,7 +2795,7 @@ packages: '@commitlint/types': 18.6.1 dev: true - /@commitlint/load@18.6.1(@types/node@18.19.31)(typescript@5.3.3): + /@commitlint/load@18.6.1(@types/node@20.11.17)(typescript@5.3.3): resolution: {integrity: sha512-p26x8734tSXUHoAw0ERIiHyW4RaI4Bj99D8YgUlVV9SedLf8hlWAfyIFhHRIhfPngLlCe0QYOdRKYFt8gy56TA==} engines: {node: '>=v18'} dependencies: @@ -2775,7 +2805,7 @@ packages: '@commitlint/types': 18.6.1 chalk: 4.1.2 cosmiconfig: 8.3.6(typescript@5.3.3) - cosmiconfig-typescript-loader: 5.0.0(@types/node@18.19.31)(cosmiconfig@8.3.6)(typescript@5.3.3) + cosmiconfig-typescript-loader: 5.0.0(@types/node@20.11.17)(cosmiconfig@8.3.6)(typescript@5.3.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -4099,6 +4129,11 @@ packages: transitivePeerDependencies: - supports-color + /@faker-js/faker@8.4.1: + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + dev: false + /@fal-works/esbuild-plugin-global-externals@2.1.2: resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==} dev: true @@ -4720,7 +4755,7 @@ packages: transitivePeerDependencies: - '@types/node' - /@graphql-tools/executor-http@1.0.9(@types/node@18.19.31)(graphql@16.8.1): + /@graphql-tools/executor-http@1.0.9(@types/node@20.11.17)(graphql@16.8.1): resolution: {integrity: sha512-+NXaZd2MWbbrWHqU4EhXcrDbogeiCDmEbrAN+rMn4Nu2okDjn2MTFDbTIab87oEubQCH4Te1wDkWPKrzXup7+Q==} engines: {node: '>=16.0.0'} peerDependencies: @@ -4731,7 +4766,7 @@ packages: '@whatwg-node/fetch': 0.9.17 extract-files: 11.0.0 graphql: 16.8.1 - meros: 1.3.0(@types/node@18.19.31) + meros: 1.3.0(@types/node@20.11.17) tslib: 2.6.2 value-or-promise: 1.0.12 transitivePeerDependencies: @@ -5044,7 +5079,7 @@ packages: - encoding - utf-8-validate - /@graphql-tools/url-loader@8.0.2(@types/node@18.19.31)(graphql@16.8.1): + /@graphql-tools/url-loader@8.0.2(@types/node@20.11.17)(graphql@16.8.1): resolution: {integrity: sha512-1dKp2K8UuFn7DFo1qX5c1cyazQv2h2ICwA9esHblEqCYrgf69Nk8N7SODmsfWg94OEaI74IqMoM12t7eIGwFzQ==} engines: {node: '>=16.0.0'} peerDependencies: @@ -5053,7 +5088,7 @@ packages: '@ardatan/sync-fetch': 0.0.1 '@graphql-tools/delegate': 10.0.4(graphql@16.8.1) '@graphql-tools/executor-graphql-ws': 1.1.2(graphql@16.8.1) - '@graphql-tools/executor-http': 1.0.9(@types/node@18.19.31)(graphql@16.8.1) + '@graphql-tools/executor-http': 1.0.9(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/executor-legacy-ws': 1.0.6(graphql@16.8.1) '@graphql-tools/utils': 10.1.2(graphql@16.8.1) '@graphql-tools/wrap': 10.0.5(graphql@16.8.1) @@ -5412,6 +5447,10 @@ packages: resolution: {integrity: sha512-tWZNBIS1CoekcwlMuyG2mr0a1Wo5lb5lEHwwWvZo+5GLgr3e9LLDTtmgtCWEwBpXMkxn9D+2W9j2FY6eZQq0tA==} dev: false + /@ioredis/commands@1.2.0: + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + dev: false + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -5683,7 +5722,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.3.3) typescript: 5.3.3 - vite: 5.0.10(@types/node@18.19.31) + vite: 5.0.10(@types/node@20.11.17) dev: true /@jridgewell/gen-mapping@0.3.5: @@ -5884,7 +5923,7 @@ packages: detect-libc: 2.0.3 https-proxy-agent: 5.0.1 make-dir: 3.1.0 - node-fetch: 2.6.12 + node-fetch: 2.7.0 nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 @@ -5902,7 +5941,7 @@ packages: detect-libc: 2.0.3 https-proxy-agent: 5.0.1(supports-color@9.4.0) make-dir: 3.1.0 - node-fetch: 2.6.12 + node-fetch: 2.7.0 nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 @@ -6006,6 +6045,11 @@ packages: resolution: {integrity: sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==} dev: false + /@netlify/blobs@2.2.0: + resolution: {integrity: sha512-j2C0+IvWj9CLNGPoiA7ETquMFDExZTrv4CarjfE6Au0eY3zlinnnTVae7DE+VQFK+U0CDM/O0VvelNy1QbsdwQ==} + engines: {node: ^14.16.0 || >=16.0.0} + dev: false + /@netlify/blobs@6.5.0: resolution: {integrity: sha512-wRFlNnL/Qv3WNLZd3OT/YYqF1zb6iPSo8T31sl9ccL1ahBxW1fBqKgF4b1XL7Z+6mRIkatvcsVPkWBcO+oJMNA==} engines: {node: ^14.16.0 || >=16.0.0} @@ -6032,7 +6076,7 @@ packages: yargs: 17.7.2 dev: false - /@netlify/build@29.36.6(@opentelemetry/api@1.8.0)(@types/node@18.19.31): + /@netlify/build@29.36.6(@opentelemetry/api@1.8.0)(@types/node@20.11.17): resolution: {integrity: sha512-crNoY5Vr7tAodBfYdz8weM+NTw5q6W6ArkowNw6QhKXa4iRXT5MY6H0c2ztsge9o5gAYs55bDhBpKiPcZlzDlA==} engines: {node: ^14.16.0 || >=16.0.0} hasBin: true @@ -6097,9 +6141,9 @@ packages: strip-ansi: 7.1.0 supports-color: 9.4.0 terminal-link: 3.0.0 - ts-node: 10.9.2(@types/node@18.19.31)(typescript@5.4.4) + ts-node: 10.9.2(@types/node@20.11.17)(typescript@5.4.4) typescript: 5.4.4 - uuid: 9.0.0 + uuid: 9.0.1 yargs: 17.7.2 transitivePeerDependencies: - '@swc/core' @@ -6180,7 +6224,7 @@ packages: semver: 7.6.0 tmp-promise: 3.0.3 urlpattern-polyfill: 8.0.2 - uuid: 9.0.0 + uuid: 9.0.1 transitivePeerDependencies: - encoding - supports-color @@ -6212,7 +6256,7 @@ packages: semver: 7.6.0 tmp-promise: 3.0.3 urlpattern-polyfill: 8.0.2 - uuid: 9.0.0 + uuid: 9.0.1 transitivePeerDependencies: - encoding - supports-color @@ -6222,6 +6266,214 @@ packages: resolution: {integrity: sha512-3sJzP1DmzMZppkAZpUixdHA4ryiKD1NSpLMRViYStE9Oe10rZPSnM8yl6A90vTBqCYvbAF5S7W9oPf2ucrCCIQ==} dev: true + /@netlify/esbuild-android-64@0.14.39: + resolution: {integrity: sha512-azq+lsvjRsKLap8ubIwSJXGyknUACqYu5h98Fvyoh40Qk4QXIVKl16JIJ4s+B7jy2k9qblEc5c4nxdDA3aGbVA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-android-arm64@0.14.39: + resolution: {integrity: sha512-WhIP7ePq4qMC1sxoaeB9SsJqSW6uzW7XDj/IuWl1l9r94nwxywU1sYdVLaF2mZr15njviazYjVr8x1d+ipwL3w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-darwin-64@0.14.39: + resolution: {integrity: sha512-eF4GvLYiDxtcyjFT55+h+8c8A2HltjeMezCqkt3AQSgOdu1nhlvwbBhIdg2dyM6gKEaEm5hBtTbicEDSwsLodA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-darwin-arm64@0.14.39: + resolution: {integrity: sha512-b7rtnX/VtYwNbUCxs3eulrCWJ+u2YvqDcXiIV1ka+od+N0fTx+4RrVkVp1lha9L0wEJYK9J7UWZOMLMyd1ynRg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-freebsd-64@0.14.39: + resolution: {integrity: sha512-XtusxDJt2hUKUdggbTFolMx0kJL2zEa4STI7YwpB+ukEWoW5rODZjiLZbqqYLcjDH8k4YwHaMxs103L8eButEQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-freebsd-arm64@0.14.39: + resolution: {integrity: sha512-A9XZKai+k6kfndCtN6Dh2usT28V0+OGxzFdZsANONPQiEUTrGZCgwcHWiVlVn7SeAwPR1tKZreTnvrfj8cj7hA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-32@0.14.39: + resolution: {integrity: sha512-ZQnqk/82YRvINY+aF+LlGfRZ19c5mH0jaxsO046GpIOPx6PcXHG8JJ2lg+vLJVe4zFPohxzabcYpwFuT4cg/GA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-64@0.14.39: + resolution: {integrity: sha512-IQtswVw7GAKNX/3yV390wSfSXvMWy0d5cw8csAffwBk9gupftY2lzepK4Cn6uD/aqLt3Iku33FbHop/2nPGfQA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-arm64@0.14.39: + resolution: {integrity: sha512-4Jie4QV6pWWuGN7TAshNMGbdTA9+VbRkv3rPIxhgK5gBfmsAV1yRKsumE4Y77J0AZNRiOriyoec4zc1qkmI3zg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-arm@0.14.39: + resolution: {integrity: sha512-QdOzQniOed0Bz1cTC9TMMwvtAqKayYv66H4edJlbvElC81yJZF/c9XhmYWJ6P5g4nkChZubQ5RcQwTLmrFGexg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-mips64le@0.14.39: + resolution: {integrity: sha512-Htozxr95tw4tSd86YNbCLs1eoYQzNu/cHpzFIkuJoztZueUhl8XpRvBdob7n3kEjW1gitLWAIn8XUwSt+aJ1Tg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-ppc64le@0.14.39: + resolution: {integrity: sha512-tFy0ufWIdjeuk1rPHee00TZlhr9OSF00Ufb4ROFyt2ArKuMSkWRJuDgx6MtZcAnCIN4cybo/xWl3MKTM+scnww==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-riscv64@0.14.39: + resolution: {integrity: sha512-ZzfKvwIxL7wQnYbVFpyNW0wotnLoKageUEM57RbjekesJoNQnqUR6Usm+LDZoB8iRsI58VX1IxnstP0cX8vOHw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-linux-s390x@0.14.39: + resolution: {integrity: sha512-yjC0mFwnuMRoh0WcF0h71MF71ytZBFEQQTRdgiGT0+gbC4UApBqnTkJdLx32RscBKi9skbMChiJ748hDJou6FA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-netbsd-64@0.14.39: + resolution: {integrity: sha512-mIq4znOoz3YfTVdv3sIWfR4Zx5JgMnT4srlhC5KYVHibhxvyDdin5txldYXmR4Zv4dZd6DSuWFsn441aUegHeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-openbsd-64@0.14.39: + resolution: {integrity: sha512-+t6QdzJCngH19hV7ClpFAeFDI2ko/HNcFbiNwaXTMVLB3hWi1sJtn+fzZck5HfzN4qsajAVqZq4nwX69SSt25A==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-sunos-64@0.14.39: + resolution: {integrity: sha512-HLfXG6i2p3wyyyWHeeP4ShGDJ1zRMnf9YLJLe2ezv2KqvcKw/Un/m/FBuDW1p13oSUO7ShISMzgc1dw1GGBEOQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-windows-32@0.14.39: + resolution: {integrity: sha512-ZpSQcKbVSCU3ln7mHpsL/5dWsUqCNdTnC5YAArnaOwdrlIunrsbo5j4MOZRRcGExb2uvTc/rb+D3mlGb8j1rkA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-windows-64@0.14.39: + resolution: {integrity: sha512-I3gCdO8+6IDhT4Y1ZmV4o2Gg0oELv7N4kCcE4kqclz10fWHNjf19HQNHyBJe0AWnFV5ZfT154VVD31dqgwpgFw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild-windows-arm64@0.14.39: + resolution: {integrity: sha512-WX52W8U1lsfWcz6NWoSpDs57lgiiMHN23seq8G2bvxzGS/tvYD3dxVLLW5UPoKSnFDyVQT7b6Zkt6AkBten1yQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@netlify/esbuild@0.14.39: + resolution: {integrity: sha512-C3xpwdT2xw6SnSb+hLQoxjtikAKiz6BjQjzlIaysHDpGbmIcmUHZ/X+dyLtCqAvf15WNK5GSBZYOlpgcOE0WZA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@netlify/esbuild-android-64': 0.14.39 + '@netlify/esbuild-android-arm64': 0.14.39 + '@netlify/esbuild-darwin-64': 0.14.39 + '@netlify/esbuild-darwin-arm64': 0.14.39 + '@netlify/esbuild-freebsd-64': 0.14.39 + '@netlify/esbuild-freebsd-arm64': 0.14.39 + '@netlify/esbuild-linux-32': 0.14.39 + '@netlify/esbuild-linux-64': 0.14.39 + '@netlify/esbuild-linux-arm': 0.14.39 + '@netlify/esbuild-linux-arm64': 0.14.39 + '@netlify/esbuild-linux-mips64le': 0.14.39 + '@netlify/esbuild-linux-ppc64le': 0.14.39 + '@netlify/esbuild-linux-riscv64': 0.14.39 + '@netlify/esbuild-linux-s390x': 0.14.39 + '@netlify/esbuild-netbsd-64': 0.14.39 + '@netlify/esbuild-openbsd-64': 0.14.39 + '@netlify/esbuild-sunos-64': 0.14.39 + '@netlify/esbuild-windows-32': 0.14.39 + '@netlify/esbuild-windows-64': 0.14.39 + '@netlify/esbuild-windows-arm64': 0.14.39 + dev: false + /@netlify/framework-info@9.8.11: resolution: {integrity: sha512-8NuvzQQVeU36PRilWqijiIWmjy6JZcqbKooGQ4bNgH/26YNdS+tN5gOWGWVYnRHgdmBUCycyYrM5h1Srwnq3hQ==} engines: {node: ^14.14.0 || >=16.0.0} @@ -6250,6 +6502,13 @@ packages: - supports-color dev: false + /@netlify/functions@1.6.0: + resolution: {integrity: sha512-6G92AlcpFrQG72XU8YH8pg94eDnq7+Q0YJhb8x4qNpdGsvuzvrfHWBmqFGp/Yshmv4wex9lpsTRZOocdrA2erQ==} + engines: {node: '>=14.0.0'} + dependencies: + is-promise: 4.0.0 + dev: false + /@netlify/functions@2.6.0: resolution: {integrity: sha512-vU20tij0fb4nRGACqb+5SQvKd50JYyTyEhQetCMHdakcJFzjLDivvRR16u1G2Oy4A7xNAtGJF1uz8reeOtTVcQ==} engines: {node: '>=14.0.0'} @@ -6267,6 +6526,36 @@ packages: path-exists: 5.0.0 dev: false + /@netlify/ipx@1.4.6: + resolution: {integrity: sha512-rnKR2LXhtnflitPX9CQIv+XSrNlYIqGsV54xrXifhbtHHjCjCw/lixsi8qwAXqEIgZBC9b4Y7prhHqRtC4oIjw==} + dependencies: + '@netlify/functions': 2.6.0 + etag: 1.8.1 + fs-extra: 11.2.0 + ipx: 1.3.1 + micromatch: 4.0.5 + mkdirp: 3.0.1 + murmurhash: 2.0.1 + node-fetch: 2.7.0 + ufo: 1.5.3 + unstorage: 1.9.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/kv' + - encoding + - idb-keyval + - supports-color + - uWebSockets.js + dev: false + /@netlify/local-functions-proxy-darwin-arm64@1.1.1: resolution: {integrity: sha512-lphJ9qqZ3glnKWEqlemU1LMqXxtJ/tKf7VzakqqyjigwLscXSZSb6fupSjQfd4tR1xqxA76ylws/2HDhc/gs+Q==} cpu: [arm64] @@ -6410,6 +6699,95 @@ packages: '@opentelemetry/api': 1.8.0 dev: false + /@netlify/plugin-gatsby@3.8.1(@gatsbyjs/reach-router@2.0.1)(common-tags@1.8.2): + resolution: {integrity: sha512-yKSw6gWZNL2P1MzMoG5akGj+MmHJWEmRg/Xlx5zyXJ2kIVJ5EUoSu92lBHCXyOoLovCfUkhKFNCYBuBCAQoaiA==} + engines: {node: '>=14.17.0'} + peerDependencies: + '@gatsbyjs/reach-router': '*' + common-tags: ^1.8.2 + dependencies: + '@gatsbyjs/reach-router': 2.0.1(react-dom@18.2.0)(react@18.2.0) + '@netlify/functions': 1.6.0 + '@netlify/ipx': 1.4.6 + abortcontroller-polyfill: 1.7.5 + chalk: 4.1.2 + co-body: 6.1.0 + common-tags: 1.8.2 + cookie: 0.6.0 + etag: 1.8.1 + fs-extra: 10.1.0 + linkfs: 2.1.0 + multer: 1.4.5-lts.1 + node-fetch: 2.7.0 + node-stream-zip: 1.15.0 + pathe: 0.3.9 + pretty-bytes: 5.6.0 + semver: 7.6.0 + statuses: 2.0.1 + uuid: 9.0.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/kv' + - encoding + - idb-keyval + - supports-color + - uWebSockets.js + dev: false + + /@netlify/plugin-nextjs@4.41.3: + resolution: {integrity: sha512-l8TB61u7A1ZF22QpoyZtresSUsHOJGP9DatECnqlNab3lG8id1kz9Pso+nZVOznWOm98o7w51k2+TIf52x+DBQ==} + engines: {node: '>=12.0.0'} + dependencies: + '@netlify/blobs': 2.2.0 + '@netlify/esbuild': 0.14.39 + '@netlify/functions': 1.6.0 + '@netlify/ipx': 1.4.6 + '@vercel/node-bridge': 2.2.2 + chalk: 4.1.2 + chokidar: 3.6.0 + destr: 1.2.2 + execa: 5.1.1 + follow-redirects: 1.15.6 + fs-extra: 10.1.0 + globby: 11.1.0 + merge-stream: 2.0.0 + moize: 6.1.6 + node-fetch: 2.7.0 + node-stream-zip: 1.15.0 + outdent: 0.8.0 + p-limit: 3.1.0 + pathe: 0.3.9 + pretty-bytes: 5.6.0 + regexp-tree: 0.1.27 + semver: 7.6.0 + slash: 3.0.0 + tiny-glob: 0.2.9 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/kv' + - debug + - encoding + - idb-keyval + - supports-color + - uWebSockets.js + dev: false + /@netlify/plugins-list@6.77.0: resolution: {integrity: sha512-czL3FH61hFhhVQydRj2xjIwLVYHDNskMhRib7dUfOQrUHifqLlUFKp03NwBD87G9BFKXUYGWZMEUU+JjIpVc9w==} engines: {node: ^14.14.0 || >=16.0.0} @@ -6566,6 +6944,91 @@ packages: - supports-color dev: false + /@next/env@14.2.3: + resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} + dev: false + + /@next/swc-darwin-arm64@14.2.3: + resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-darwin-x64@14.2.3: + resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-gnu@14.2.3: + resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-musl@14.2.3: + resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-gnu@14.2.3: + resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-musl@14.2.3: + resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-arm64-msvc@14.2.3: + resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-ia32-msvc@14.2.3: + resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-x64-msvc@14.2.3: + resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} dependencies: @@ -6699,7 +7162,7 @@ packages: '@octokit/request-error': 3.0.3 '@octokit/types': 9.3.2 is-plain-object: 5.0.0 - node-fetch: 2.6.12 + node-fetch: 2.7.0 universal-user-agent: 6.0.1 transitivePeerDependencies: - encoding @@ -6738,6 +7201,10 @@ packages: engines: {node: '>=8.0.0'} dev: false + /@panva/hkdf@1.1.1: + resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} + dev: false + /@parcel/bundler-default@2.8.3(@parcel/core@2.8.3): resolution: {integrity: sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg==} engines: {node: '>= 12.0.0', parcel: ^2.8.3} @@ -8352,7 +8819,7 @@ packages: magic-string: 0.30.9 rollup: 3.29.4 typescript: 5.3.3 - vite: 5.0.10(@types/node@18.19.31) + vite: 5.0.10(@types/node@20.11.17) transitivePeerDependencies: - encoding - supports-color @@ -8861,7 +9328,7 @@ packages: react: 18.2.0 react-docgen: 7.0.3 react-dom: 18.2.0(react@18.2.0) - vite: 5.0.10(@types/node@18.19.31) + vite: 5.0.10(@types/node@20.11.17) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -8935,7 +9402,7 @@ packages: - supports-color dev: true - /@storybook/test-runner@0.16.0(@types/node@18.19.31): + /@storybook/test-runner@0.16.0(@types/node@20.11.17): resolution: {integrity: sha512-LDmNbKFoEDW/VS9o6KR8e1r5MnbCc5ZojUfi5yqLdq80gFD7BvilgKgV0lUh/xWHryzoy+Ids5LYgrPJZmU2dQ==} engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true @@ -8955,7 +9422,7 @@ packages: commander: 9.5.0 expect-playwright: 0.8.0 glob: 10.3.12 - jest: 29.7.0(@types/node@18.19.31) + jest: 29.7.0(@types/node@20.11.17) jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-junit: 16.0.0 @@ -9293,6 +9760,13 @@ packages: legacy-swc-helpers: /@swc/helpers@0.4.14 tslib: 2.6.2 + /@swc/helpers@0.5.5: + resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + dependencies: + '@swc/counter': 0.1.3 + tslib: 2.6.2 + dev: false + /@swc/jest@0.2.36(@swc/core@1.4.13): resolution: {integrity: sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==} engines: {npm: '>= 7.0.0'} @@ -9413,10 +9887,10 @@ packages: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - jest: 29.7.0(@types/node@18.19.31) + jest: 29.7.0(@types/node@20.11.17) lodash: 4.17.21 redent: 3.0.0 - vitest: 1.1.1(@types/node@18.19.31)(happy-dom@12.10.3) + vitest: 1.1.1(@types/node@20.11.17)(happy-dom@12.10.3) dev: true /@testing-library/react@14.1.2(react-dom@18.2.0)(react@18.2.0): @@ -10374,6 +10848,7 @@ packages: typescript: 5.4.4 transitivePeerDependencies: - supports-color + dev: false /@typescript-eslint/parser@6.17.0(eslint@7.0.0)(typescript@5.3.3): resolution: {integrity: sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==} @@ -10687,6 +11162,7 @@ packages: typescript: 5.4.4 transitivePeerDependencies: - supports-color + dev: false /@typescript-eslint/typescript-estree@6.17.0(typescript@5.3.3): resolution: {integrity: sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==} @@ -11006,6 +11482,10 @@ packages: - supports-color dev: false + /@vercel/node-bridge@2.2.2: + resolution: {integrity: sha512-haGBC8noyA5BfjCRXRH+VIkHCDVW5iD5UX24P2nOdilwUxI4qWsattS/co8QBGq64XsNLRAMdM5pQUE3zxkF9Q==} + dev: false + /@vercel/webpack-asset-relocator-loader@1.7.3: resolution: {integrity: sha512-vizrI18v8Lcb1PmNNUBz7yxPxxXoOeuaVEjTG9MjvDrphjiSxFZrRJ5tIghk+qdLFRCXI5HBCshgobftbmrC5g==} dependencies: @@ -11044,7 +11524,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 5.0.10(@types/node@18.19.31) + vite: 5.0.10(@types/node@20.11.17) transitivePeerDependencies: - supports-color dev: true @@ -12001,6 +12481,7 @@ packages: /aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} + requiresBuild: true dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 @@ -12790,6 +13271,19 @@ packages: schema-utils: 2.7.1 webpack: 5.91.0 + /babel-loader@9.1.3(@babel/core@7.24.4)(webpack@5.91.0): + resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: '>=5' + dependencies: + '@babel/core': 7.24.4 + find-cache-dir: 4.0.0 + schema-utils: 4.2.0 + webpack: 5.91.0 + dev: false + /babel-plugin-add-module-exports@1.0.4: resolution: {integrity: sha512-g+8yxHUZ60RcyaUpfNzy56OtWW+x9cyEe9j+CranqLiqbju2yf/Cy6ZtYK40EZxtrdHllzlVZgLmcOUCTlJ7Jg==} @@ -13805,6 +14299,7 @@ packages: /clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} + requiresBuild: true /clean-stack@4.2.0: resolution: {integrity: sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==} @@ -13968,6 +14463,20 @@ packages: engines: {node: '>=6'} dev: false + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + + /co-body@6.1.0: + resolution: {integrity: sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==} + dependencies: + inflation: 2.1.0 + qs: 6.12.0 + raw-body: 2.5.2 + type-is: 1.6.18 + dev: false + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -14425,7 +14934,7 @@ packages: object-assign: 4.1.1 vary: 1.1.2 - /cosmiconfig-typescript-loader@5.0.0(@types/node@18.19.31)(cosmiconfig@8.3.6)(typescript@5.3.3): + /cosmiconfig-typescript-loader@5.0.0(@types/node@20.11.17)(cosmiconfig@8.3.6)(typescript@5.3.3): resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} engines: {node: '>=v16'} peerDependencies: @@ -14433,7 +14942,7 @@ packages: cosmiconfig: '>=8.2' typescript: '>=4' dependencies: - '@types/node': 18.19.31 + '@types/node': 20.11.17 cosmiconfig: 8.3.6(typescript@5.3.3) jiti: 1.21.0 typescript: 5.3.3 @@ -14559,7 +15068,7 @@ packages: dependencies: '@babel/runtime': 7.24.4 - /create-jest@29.7.0(@types/node@18.19.31): + /create-jest@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -14568,7 +15077,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.19.31) + jest-config: 29.7.0(@types/node@20.11.17) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -15935,6 +16444,11 @@ packages: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} dev: false + /denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + dev: false + /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -15956,6 +16470,10 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + /destr@1.2.2: + resolution: {integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==} + dev: false + /destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} dev: false @@ -16438,6 +16956,7 @@ packages: /encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + requiresBuild: true dependencies: iconv-lite: 0.6.3 @@ -17095,7 +17614,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.4.4) + '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5) debug: 3.2.7 eslint: 7.32.0 eslint-import-resolver-node: 0.3.9 @@ -17202,7 +17721,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.4.4) + '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@4.9.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -18467,6 +18986,14 @@ packages: make-dir: 3.1.0 pkg-dir: 4.2.0 + /find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + dev: false + /find-file-up@0.1.3: resolution: {integrity: sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A==} engines: {node: '>=0.10.0'} @@ -18829,7 +19356,6 @@ packages: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - dev: true /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} @@ -20422,6 +20948,10 @@ packages: dependencies: define-properties: 1.2.1 + /globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: false + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -20456,6 +20986,10 @@ packages: unicorn-magic: 0.1.0 dev: true + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: false + /gonzales-pe@4.3.0: resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} engines: {node: '>=0.6.0'} @@ -20576,7 +21110,7 @@ packages: - typescript - utf-8-validate - /graphql-config@5.0.3(@types/node@18.19.31)(graphql@16.8.1)(typescript@4.9.5): + /graphql-config@5.0.3(@types/node@20.11.17)(graphql@16.8.1)(typescript@4.9.5): resolution: {integrity: sha512-BNGZaoxIBkv9yy6Y7omvsaBUHOzfFcII3UN++tpH8MGOKFPFkCPZuwx09ggANMt8FgyWP1Od8SWPmrUEZca4NQ==} engines: {node: '>= 16.0.0'} peerDependencies: @@ -20590,7 +21124,7 @@ packages: '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/load': 8.0.2(graphql@16.8.1) '@graphql-tools/merge': 9.0.3(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.2(@types/node@18.19.31)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.2(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/utils': 10.1.2(graphql@16.8.1) cosmiconfig: 8.3.6(typescript@4.9.5) graphql: 16.8.1 @@ -20606,7 +21140,7 @@ packages: - utf-8-validate dev: false - /graphql-config@5.0.3(@types/node@18.19.31)(graphql@16.8.1)(typescript@5.4.4): + /graphql-config@5.0.3(@types/node@20.11.17)(graphql@16.8.1)(typescript@5.4.4): resolution: {integrity: sha512-BNGZaoxIBkv9yy6Y7omvsaBUHOzfFcII3UN++tpH8MGOKFPFkCPZuwx09ggANMt8FgyWP1Od8SWPmrUEZca4NQ==} engines: {node: '>= 16.0.0'} peerDependencies: @@ -20620,7 +21154,7 @@ packages: '@graphql-tools/json-file-loader': 8.0.1(graphql@16.8.1) '@graphql-tools/load': 8.0.2(graphql@16.8.1) '@graphql-tools/merge': 9.0.3(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.2(@types/node@18.19.31)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.2(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/utils': 10.1.2(graphql@16.8.1) cosmiconfig: 8.3.6(typescript@5.4.4) graphql: 16.8.1 @@ -21441,6 +21975,11 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + /image-meta@0.1.1: + resolution: {integrity: sha512-+oXiHwOEPr1IE5zY0tcBLED/CYcre15J4nwL50x3o0jxWqEkyjrusiKP3YSU+tr9fvJp33ZcP5Gpj2295g3aEw==} + engines: {node: '>=10.18.0'} + dev: false + /image-meta@0.2.0: resolution: {integrity: sha512-ZBGjl0ZMEMeOC3Ns0wUF/5UdUmr3qQhBSCniT0LxOgGGIRHiNFOkMtIHB7EOznRU47V2AxPgiVP+s+0/UCU0Hg==} dev: false @@ -21522,6 +22061,11 @@ packages: dev: false optional: true + /inflation@2.1.0: + resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==} + engines: {node: '>= 0.8.0'} + dev: false + /inflection@1.13.4: resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==} engines: {'0': node >= 0.4.0} @@ -21647,6 +22191,23 @@ packages: dependencies: loose-envify: 1.4.0 + /ioredis@5.4.1: + resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} + engines: {node: '>=12.22.0'} + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.4 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /ip-address@9.0.5: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} @@ -21670,6 +22231,26 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + /ipx@1.3.1: + resolution: {integrity: sha512-hWRLXdMDOz2q81T2x9lowFtAGO3E5b2HtC8xOOBTrlnxygHNaVrZqJ5c1P3T7tDkC3oCocYRRz0VBffvJKeQlw==} + hasBin: true + dependencies: + '@fastify/accept-negotiator': 1.1.0 + consola: 3.2.3 + defu: 6.1.4 + destr: 2.0.3 + etag: 1.8.1 + image-meta: 0.1.1 + listhen: 1.7.2 + node-fetch-native: 1.6.4 + pathe: 1.1.2 + sharp: 0.33.1 + ufo: 1.5.3 + xss: 1.0.15 + transitivePeerDependencies: + - uWebSockets.js + dev: false + /ipx@2.1.0(@netlify/blobs@7.0.1): resolution: {integrity: sha512-AVnPGXJ8L41vjd11Z4akIF2yd14636Klxul3tBySxHA6PKfCOQPxBDkCFK5zcWh0z/keR6toh1eg8qzdBVUgdA==} hasBin: true @@ -22096,7 +22677,6 @@ packages: /is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - dev: true /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} @@ -22480,7 +23060,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@18.19.31): + /jest-cli@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -22494,10 +23074,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.31) + create-jest: 29.7.0(@types/node@20.11.17) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.19.31) + jest-config: 29.7.0(@types/node@20.11.17) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -22548,6 +23128,46 @@ packages: - supports-color dev: true + /jest-config@29.7.0(@types/node@20.11.17): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.24.4 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.11.17 + babel-jest: 29.7.0(@babel/core@7.24.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + /jest-diff@29.7.0: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -22686,7 +23306,7 @@ packages: jest-runner: ^29.3.1 dependencies: expect-playwright: 0.8.0 - jest: 29.7.0(@types/node@18.19.31) + jest: 29.7.0(@types/node@20.11.17) jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-process-manager: 0.4.0 @@ -22897,7 +23517,7 @@ packages: dependencies: ansi-escapes: 6.2.1 chalk: 5.3.0 - jest: 29.7.0(@types/node@18.19.31) + jest: 29.7.0(@types/node@20.11.17) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -22945,7 +23565,7 @@ packages: supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@18.19.31): + /jest@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -22958,7 +23578,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.19.31) + jest-cli: 29.7.0(@types/node@20.11.17) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -22979,6 +23599,10 @@ packages: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 + /jose@4.15.5: + resolution: {integrity: sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==} + dev: false + /jose@5.2.4: resolution: {integrity: sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==} dev: true @@ -23528,6 +24152,10 @@ packages: /lodash.deburr@4.1.0: resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: false + /lodash.every@4.6.0: resolution: {integrity: sha512-isF82d+65/sNvQ3aaQAW7LLHnnTxSN/2fm4rhYyuufLzA4VtHz6y6S5vFwe6PQVr2xdqUOyxBbTNKDpnmeu50w==} @@ -23549,6 +24177,10 @@ packages: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: false + /lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + dev: false + /lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} dev: false @@ -24285,7 +24917,7 @@ packages: dependencies: '@types/node': 18.0.0 - /meros@1.3.0(@types/node@18.19.31): + /meros@1.3.0(@types/node@20.11.17): resolution: {integrity: sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==} engines: {node: '>=13'} peerDependencies: @@ -24294,7 +24926,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 18.19.31 + '@types/node': 20.11.17 dev: false /methods@1.1.2: @@ -24805,6 +25437,12 @@ packages: engines: {node: '>=10'} hasBin: true + /mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + dev: false + /mlly@1.6.1: resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} dependencies: @@ -24922,6 +25560,10 @@ packages: uid-safe: 2.1.5 dev: false + /murmurhash@2.0.1: + resolution: {integrity: sha512-5vQEh3y+DG/lMPM0mCGPDnyV8chYg/g7rl6v3Gd8WMF9S429ox3Xk8qrk174kWhG767KQMqqxLD1WnGd77hiew==} + dev: false + /mute-stream@0.0.7: resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==} dev: false @@ -25009,7 +25651,7 @@ packages: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} dev: false - /netlify-cli@17.21.1(@types/node@18.19.31): + /netlify-cli@17.21.1(@types/node@20.11.17): resolution: {integrity: sha512-B8QveV55h2dFCTnk5LInVW1MiXPINTQ61IkEtih15CVYpvVSQy+he8M6hdpucq83VqaF/phaJkb3Si2ligOxxw==} engines: {node: '>=18.14.0'} hasBin: true @@ -25018,7 +25660,7 @@ packages: '@bugsnag/js': 7.20.2 '@fastify/static': 6.10.2 '@netlify/blobs': 7.0.1 - '@netlify/build': 29.36.6(@opentelemetry/api@1.8.0)(@types/node@18.19.31) + '@netlify/build': 29.36.6(@opentelemetry/api@1.8.0)(@types/node@20.11.17) '@netlify/build-info': 7.13.2 '@netlify/config': 20.12.1 '@netlify/edge-bundler': 11.3.0 @@ -25210,9 +25852,77 @@ packages: qs: 6.12.0 dev: false + /next-auth@4.24.7(next@14.2.3)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==} + peerDependencies: + next: ^12.2.5 || ^13 || ^14 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 + react-dom: ^17.0.2 || ^18 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@babel/runtime': 7.24.4 + '@panva/hkdf': 1.1.1 + cookie: 0.5.0 + jose: 4.15.5 + next: 14.2.3(@babel/core@7.24.4)(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0) + oauth: 0.9.15 + openid-client: 5.6.5 + preact: 10.21.0 + preact-render-to-string: 5.2.6(preact@10.21.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + uuid: 8.3.2 + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + /next@14.2.3(@babel/core@7.24.4)(@opentelemetry/api@1.8.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + sass: + optional: true + dependencies: + '@next/env': 14.2.3 + '@opentelemetry/api': 1.8.0 + '@swc/helpers': 0.5.5 + busboy: 1.6.0 + caniuse-lite: 1.0.30001608 + graceful-fs: 4.2.11 + postcss: 8.4.31 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.24.4)(react@18.2.0) + optionalDependencies: + '@next/swc-darwin-arm64': 14.2.3 + '@next/swc-darwin-x64': 14.2.3 + '@next/swc-linux-arm64-gnu': 14.2.3 + '@next/swc-linux-arm64-musl': 14.2.3 + '@next/swc-linux-x64-gnu': 14.2.3 + '@next/swc-linux-x64-musl': 14.2.3 + '@next/swc-win32-arm64-msvc': 14.2.3 + '@next/swc-win32-ia32-msvc': 14.2.3 + '@next/swc-win32-x64-msvc': 14.2.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + /no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: @@ -25567,6 +26277,10 @@ packages: ufo: 1.5.3 dev: true + /oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -25580,6 +26294,11 @@ packages: kind-of: 3.2.2 dev: false + /object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + dev: false + /object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -25674,6 +26393,11 @@ packages: /ohash@1.1.3: resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} + /oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + dev: false + /ol-mapbox-style@8.2.1: resolution: {integrity: sha512-3kBBuZC627vDL8vnUdfVbCbfkhkcZj2kXPHQcuLhC4JJEA+XkEVEtEde8x8+AZctRbHwBkSiubTPaRukgLxIRw==} dependencies: @@ -25760,6 +26484,15 @@ packages: is-docker: 2.2.1 is-wsl: 2.2.0 + /openid-client@5.6.5: + resolution: {integrity: sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==} + dependencies: + jose: 4.15.5 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.3 + dev: false + /opentracing@0.14.7: resolution: {integrity: sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q==} engines: {node: '>=0.10'} @@ -25840,6 +26573,10 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + /outdent@0.8.0: + resolution: {integrity: sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==} + dev: false + /p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -25903,6 +26640,7 @@ packages: /p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + requiresBuild: true dependencies: yocto-queue: 1.0.0 @@ -25953,6 +26691,7 @@ packages: /p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} + requiresBuild: true dependencies: aggregate-error: 3.1.0 @@ -26290,6 +27029,10 @@ packages: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} + /pathe@0.3.9: + resolution: {integrity: sha512-6Y6s0vT112P3jD8dGfuS6r+lpa0qqNrLyHPOwvXMnyNTQaYiwgau2DP3aNDsR13xqtGj7rrPo+jFUATpU6/s+g==} + dev: false + /pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -27281,6 +28024,15 @@ packages: quote-unquote: 1.0.0 dev: false + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: false + /postcss@8.4.32: resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} engines: {node: ^10 || ^12 || >=14} @@ -27298,6 +28050,19 @@ packages: picocolors: 1.0.0 source-map-js: 1.2.0 + /preact-render-to-string@5.2.6(preact@10.21.0): + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.21.0 + pretty-format: 3.8.0 + dev: false + + /preact@10.21.0: + resolution: {integrity: sha512-aQAIxtzWEwH8ou+OovWVSVNlFImL7xUCwJX3YMqA3U8iKCNC34999fFOnWjYNsylgfPgMexpbk7WYOLtKr/mxg==} + dev: false + /prebuild-install@7.1.2: resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} @@ -27415,6 +28180,10 @@ packages: ansi-styles: 5.2.0 react-is: 18.2.0 + /pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + /pretty-hrtime@1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} @@ -28685,6 +29454,18 @@ packages: indent-string: 4.0.0 strip-indent: 3.0.0 + /redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + dev: false + + /redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + dependencies: + redis-errors: 1.2.0 + dev: false + /redux-devtools-extension@2.13.9(redux@4.2.1): resolution: {integrity: sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==} deprecated: Package moved to @redux-devtools/extension. @@ -28767,6 +29548,11 @@ packages: safe-regex: 1.1.0 dev: false + /regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + dev: false + /regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -29444,6 +30230,16 @@ packages: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) + /schema-utils@4.2.0: + resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} + engines: {node: '>= 12.13.0'} + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + ajv-keywords: 5.1.0(ajv@8.12.0) + dev: false + /scroll-into-view-if-needed@2.2.31: resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} dependencies: @@ -30299,6 +31095,7 @@ packages: /sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + requiresBuild: true /sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} @@ -30358,6 +31155,10 @@ packages: /stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + /standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + dev: false + /start-server-and-test@2.0.3: resolution: {integrity: sha512-QsVObjfjFZKJE6CS6bSKNwWZCKBG6975/jKRPPGFfFh+yOQglSeGXiNWjzgQNXdphcBI9nXbyso9tPfX4YAUhg==} engines: {node: '>=16'} @@ -30743,6 +31544,24 @@ packages: dependencies: inline-style-parser: 0.1.1 + /styled-jsx@5.1.1(@babel/core@7.24.4)(react@18.2.0): + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/core': 7.24.4 + client-only: 0.0.1 + react: 18.2.0 + dev: false + /stylehacks@5.1.1(postcss@8.4.38): resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} engines: {node: ^10 || ^12 || >=14.0} @@ -31247,6 +32066,13 @@ packages: resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} dev: true + /tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: false + /tiny-invariant@1.0.6: resolution: {integrity: sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==} dev: false @@ -31507,7 +32333,7 @@ packages: resolution: {integrity: sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==} dev: true - /ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.4): + /ts-node@10.9.2(@types/node@20.11.17)(typescript@5.4.4): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -31526,7 +32352,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.31 + '@types/node': 20.11.17 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -31625,6 +32451,7 @@ packages: dependencies: tslib: 1.14.1 typescript: 5.4.4 + dev: false /tsx@4.7.1: resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} @@ -32209,6 +33036,60 @@ packages: - uWebSockets.js dev: false + /unstorage@1.9.0: + resolution: {integrity: sha512-VpD8ZEYc/le8DZCrny3bnqKE4ZjioQxBRnWE+j5sGNvziPjeDlaS1NaFFHzl/kkXaO3r7UaF8MGQrs14+1B4pQ==} + peerDependencies: + '@azure/app-configuration': ^1.4.1 + '@azure/cosmos': ^3.17.3 + '@azure/data-tables': ^13.2.2 + '@azure/identity': ^3.2.3 + '@azure/keyvault-secrets': ^4.7.0 + '@azure/storage-blob': ^12.14.0 + '@capacitor/preferences': ^5.0.0 + '@planetscale/database': ^1.8.0 + '@upstash/redis': ^1.22.0 + '@vercel/kv': ^0.2.2 + idb-keyval: ^6.2.1 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/kv': + optional: true + idb-keyval: + optional: true + dependencies: + anymatch: 3.1.3 + chokidar: 3.6.0 + destr: 2.0.3 + h3: 1.11.1 + ioredis: 5.4.1 + listhen: 1.7.2 + lru-cache: 10.2.0 + mri: 1.2.0 + node-fetch-native: 1.6.4 + ofetch: 1.3.4 + ufo: 1.5.3 + transitivePeerDependencies: + - supports-color + - uWebSockets.js + dev: false + /untildify@3.0.3: resolution: {integrity: sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==} engines: {node: '>=4'} @@ -32459,7 +33340,6 @@ packages: /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - dev: true /uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} @@ -32619,7 +33499,7 @@ packages: - terser dev: true - /vite-node@1.1.1(@types/node@18.19.31): + /vite-node@1.1.1(@types/node@20.11.17): resolution: {integrity: sha512-2bGE5w4jvym5v8llF6Gu1oBrmImoNSs4WmRVcavnG2me6+8UQntTqLiAMFyiAobp+ZXhj5ZFhI7SmLiFr/jrow==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -32628,7 +33508,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.2.8(@types/node@18.19.31) + vite: 5.2.8(@types/node@20.11.17) transitivePeerDependencies: - '@types/node' - less @@ -32691,7 +33571,7 @@ packages: fsevents: 2.3.3 dev: true - /vite@5.0.10(@types/node@18.19.31): + /vite@5.0.10(@types/node@20.11.17): resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -32719,7 +33599,7 @@ packages: terser: optional: true dependencies: - '@types/node': 18.19.31 + '@types/node': 20.11.17 esbuild: 0.19.12 postcss: 8.4.32 rollup: 4.14.1 @@ -32797,6 +33677,7 @@ packages: rollup: 4.14.1 optionalDependencies: fsevents: 2.3.3 + optional: true /vite@5.2.8(@types/node@20.11.17): resolution: {integrity: sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==} @@ -33026,7 +33907,7 @@ packages: - terser dev: true - /vitest@1.1.1(@types/node@18.19.31): + /vitest@1.1.1(@types/node@20.11.17): resolution: {integrity: sha512-Ry2qs4UOu/KjpXVfOCfQkTnwSXYGrqTbBZxw6reIYEFjSy1QUARRg5pxiI5BEXy+kBVntxUYNMlq4Co+2vD3fQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -33051,7 +33932,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 18.19.31 + '@types/node': 20.11.17 '@vitest/expect': 1.1.1 '@vitest/runner': 1.1.1 '@vitest/snapshot': 1.1.1 @@ -33070,8 +33951,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.6.0 tinypool: 0.8.3 - vite: 5.2.8(@types/node@18.19.31) - vite-node: 1.1.1(@types/node@18.19.31) + vite: 5.2.8(@types/node@20.11.17) + vite-node: 1.1.1(@types/node@20.11.17) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -33083,7 +33964,7 @@ packages: - terser dev: true - /vitest@1.1.1(@types/node@18.19.31)(happy-dom@12.10.3): + /vitest@1.1.1(@types/node@20.11.17)(happy-dom@12.10.3): resolution: {integrity: sha512-Ry2qs4UOu/KjpXVfOCfQkTnwSXYGrqTbBZxw6reIYEFjSy1QUARRg5pxiI5BEXy+kBVntxUYNMlq4Co+2vD3fQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -33108,7 +33989,7 @@ packages: jsdom: optional: true dependencies: - '@types/node': 18.19.31 + '@types/node': 20.11.17 '@vitest/expect': 1.1.1 '@vitest/runner': 1.1.1 '@vitest/snapshot': 1.1.1 @@ -33128,8 +34009,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.6.0 tinypool: 0.8.3 - vite: 5.2.8(@types/node@18.19.31) - vite-node: 1.1.1(@types/node@18.19.31) + vite: 5.2.8(@types/node@20.11.17) + vite-node: 1.1.1(@types/node@20.11.17) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/tests/e2e/specs/drupal/blocks.spec.ts b/tests/e2e/specs/drupal/blocks.spec.ts index f924e79d5..2432ac6b7 100644 --- a/tests/e2e/specs/drupal/blocks.spec.ts +++ b/tests/e2e/specs/drupal/blocks.spec.ts @@ -22,6 +22,9 @@ test('All blocks are rendered', async ({ page }) => { page.locator('a:text("link")[href="/en/architecture"]'), ).toHaveCount(1); + // Horizontal separator. + await expect(page.locator('hr')).toHaveCount(1); + // Image and ImageWithText block await expect( page.locator( @@ -65,6 +68,11 @@ test('All blocks are rendered', async ({ page }) => { page.locator('blockquote img[alt="The silverback"]'), ).toHaveCount(1); + // CTA blocks + await expect(page.locator('a:text("Internal CTA")')).toHaveCount(1); + await expect(page.locator('a:text("External CTA")')).toHaveCount(1); + await expect(page.locator('a:text("CTA with link to media")')).toHaveCount(1); + // Form await expect( page.locator('.silverback-iframe iframe').last(), diff --git a/tests/schema/specs/blocks.spec.ts b/tests/schema/specs/blocks.spec.ts index 7b0940115..2ef1ea02b 100644 --- a/tests/schema/specs/blocks.spec.ts +++ b/tests/schema/specs/blocks.spec.ts @@ -52,6 +52,25 @@ test('Blocks', async () => { url text openInNewTab + icon + iconPosition + } + ... on BlockImageWithText { + image { + __typename + } + textContent { + __typename + markup + } + } + ... on BlockQuote { + quote + author + role + image { + __typename + } } ... on BlockImageWithText { image { @@ -71,6 +90,9 @@ test('Blocks', async () => { __typename } } + ... on BlockHorizontalSeparator { + __typename + } } } { @@ -106,6 +128,9 @@ test('Blocks', async () => {

A standalone paragraph with markup and link

", }, + { + "__typename": "BlockHorizontalSeparator", + }, { "__typename": "BlockMedia", "caption": "Media image", @@ -182,18 +207,24 @@ test('Blocks', async () => { }, { "__typename": "BlockCta", + "icon": null, + "iconPosition": null, "openInNewTab": null, "text": "Internal CTA", "url": "/en/drupal", }, { "__typename": "BlockCta", + "icon": "ARROW", + "iconPosition": null, "openInNewTab": true, "text": "External CTA", "url": "https://www.google.com", }, { "__typename": "BlockCta", + "icon": "ARROW", + "iconPosition": "BEFORE", "openInNewTab": null, "text": "CTA with link to media", "url": "/media/[numeric]", @@ -249,6 +280,8 @@ test('Blocks', async () => { }, { "__typename": "BlockCta", + "icon": null, + "iconPosition": null, "openInNewTab": null, "text": null, "url": null,