Adds Redux and React-Intl to storybook (#35094)
This commit is contained in:
		@@ -11,6 +11,7 @@ const config: StorybookConfig = {
 | 
			
		||||
    name: '@storybook/react-vite',
 | 
			
		||||
    options: {},
 | 
			
		||||
  },
 | 
			
		||||
  staticDirs: ['./static'],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default config;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
import type { Preview } from '@storybook/react-vite';
 | 
			
		||||
 | 
			
		||||
// If you want to run the dark theme during development,
 | 
			
		||||
// you can change the below to `/application.scss`
 | 
			
		||||
import '../app/javascript/styles/mastodon-light.scss';
 | 
			
		||||
 | 
			
		||||
const preview: Preview = {
 | 
			
		||||
  // Auto-generate docs: https://storybook.js.org/docs/writing-docs/autodocs
 | 
			
		||||
  tags: ['autodocs'],
 | 
			
		||||
  parameters: {
 | 
			
		||||
    layout: 'centered',
 | 
			
		||||
 | 
			
		||||
    controls: {
 | 
			
		||||
      matchers: {
 | 
			
		||||
        color: /(background|color)$/i,
 | 
			
		||||
        date: /Date$/i,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    a11y: {
 | 
			
		||||
      // 'todo' - show a11y violations in the test UI only
 | 
			
		||||
      // 'error' - fail CI on a11y violations
 | 
			
		||||
      // 'off' - skip a11y checks entirely
 | 
			
		||||
      test: 'todo',
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default preview;
 | 
			
		||||
							
								
								
									
										136
									
								
								.storybook/preview.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								.storybook/preview.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
 | 
			
		||||
import { IntlProvider } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import { configureStore } from '@reduxjs/toolkit';
 | 
			
		||||
import { Provider } from 'react-redux';
 | 
			
		||||
 | 
			
		||||
import type { Preview } from '@storybook/react-vite';
 | 
			
		||||
import { http, passthrough } from 'msw';
 | 
			
		||||
import { initialize, mswLoader } from 'msw-storybook-addon';
 | 
			
		||||
 | 
			
		||||
import type { LocaleData } from '@/mastodon/locales';
 | 
			
		||||
import { reducerWithInitialState, rootReducer } from '@/mastodon/reducers';
 | 
			
		||||
import { defaultMiddleware } from '@/mastodon/store/store';
 | 
			
		||||
 | 
			
		||||
// If you want to run the dark theme during development,
 | 
			
		||||
// you can change the below to `/application.scss`
 | 
			
		||||
import '../app/javascript/styles/mastodon-light.scss';
 | 
			
		||||
 | 
			
		||||
const localeFiles = import.meta.glob('@/mastodon/locales/*.json', {
 | 
			
		||||
  query: { as: 'json' },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Initialize MSW
 | 
			
		||||
initialize();
 | 
			
		||||
 | 
			
		||||
const preview: Preview = {
 | 
			
		||||
  // Auto-generate docs: https://storybook.js.org/docs/writing-docs/autodocs
 | 
			
		||||
  tags: ['autodocs'],
 | 
			
		||||
  globalTypes: {
 | 
			
		||||
    locale: {
 | 
			
		||||
      description: 'Locale for the story',
 | 
			
		||||
      toolbar: {
 | 
			
		||||
        title: 'Locale',
 | 
			
		||||
        icon: 'globe',
 | 
			
		||||
        items: Object.keys(localeFiles).map((path) =>
 | 
			
		||||
          path.replace('/mastodon/locales/', '').replace('.json', ''),
 | 
			
		||||
        ),
 | 
			
		||||
        dynamicTitle: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  initialGlobals: {
 | 
			
		||||
    locale: 'en',
 | 
			
		||||
  },
 | 
			
		||||
  decorators: [
 | 
			
		||||
    (Story, { parameters }) => {
 | 
			
		||||
      const { state = {} } = parameters;
 | 
			
		||||
      let reducer = rootReducer;
 | 
			
		||||
      if (typeof state === 'object' && state) {
 | 
			
		||||
        reducer = reducerWithInitialState(state as Record<string, unknown>);
 | 
			
		||||
      }
 | 
			
		||||
      const store = configureStore({
 | 
			
		||||
        reducer,
 | 
			
		||||
        middleware(getDefaultMiddleware) {
 | 
			
		||||
          return getDefaultMiddleware(defaultMiddleware);
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
      return (
 | 
			
		||||
        <Provider store={store}>
 | 
			
		||||
          <Story />
 | 
			
		||||
        </Provider>
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    (Story, { globals }) => {
 | 
			
		||||
      const currentLocale = (globals.locale as string) || 'en';
 | 
			
		||||
      const [messages, setMessages] = useState<
 | 
			
		||||
        Record<string, Record<string, string>>
 | 
			
		||||
      >({});
 | 
			
		||||
      const currentLocaleData = messages[currentLocale];
 | 
			
		||||
 | 
			
		||||
      useEffect(() => {
 | 
			
		||||
        async function loadLocaleData() {
 | 
			
		||||
          const { default: localeFile } = (await import(
 | 
			
		||||
            `@/mastodon/locales/${currentLocale}.json`
 | 
			
		||||
          )) as { default: LocaleData['messages'] };
 | 
			
		||||
          setMessages((prevLocales) => ({
 | 
			
		||||
            ...prevLocales,
 | 
			
		||||
            [currentLocale]: localeFile,
 | 
			
		||||
          }));
 | 
			
		||||
        }
 | 
			
		||||
        if (!currentLocaleData) {
 | 
			
		||||
          void loadLocaleData();
 | 
			
		||||
        }
 | 
			
		||||
      }, [currentLocale, currentLocaleData]);
 | 
			
		||||
 | 
			
		||||
      return (
 | 
			
		||||
        <IntlProvider
 | 
			
		||||
          locale={currentLocale}
 | 
			
		||||
          messages={currentLocaleData}
 | 
			
		||||
          textComponent='span'
 | 
			
		||||
        >
 | 
			
		||||
          <Story />
 | 
			
		||||
        </IntlProvider>
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  loaders: [mswLoader],
 | 
			
		||||
  parameters: {
 | 
			
		||||
    layout: 'centered',
 | 
			
		||||
 | 
			
		||||
    controls: {
 | 
			
		||||
      matchers: {
 | 
			
		||||
        color: /(background|color)$/i,
 | 
			
		||||
        date: /Date$/i,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    a11y: {
 | 
			
		||||
      // 'todo' - show a11y violations in the test UI only
 | 
			
		||||
      // 'error' - fail CI on a11y violations
 | 
			
		||||
      // 'off' - skip a11y checks entirely
 | 
			
		||||
      test: 'todo',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    state: {},
 | 
			
		||||
 | 
			
		||||
    // Force docs to use an iframe as it breaks MSW handlers.
 | 
			
		||||
    // See: https://github.com/mswjs/msw-storybook-addon/issues/83
 | 
			
		||||
    docs: {
 | 
			
		||||
      story: {
 | 
			
		||||
        inline: false,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    msw: {
 | 
			
		||||
      handlers: [
 | 
			
		||||
        http.get('/index.json', passthrough),
 | 
			
		||||
        http.get('/packs-dev/*', passthrough),
 | 
			
		||||
        http.get('/sounds/*', passthrough),
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default preview;
 | 
			
		||||
							
								
								
									
										344
									
								
								.storybook/static/mockServiceWorker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										344
									
								
								.storybook/static/mockServiceWorker.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,344 @@
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Mock Service Worker.
 | 
			
		||||
 * @see https://github.com/mswjs/msw
 | 
			
		||||
 * - Please do NOT modify this file.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const PACKAGE_VERSION = '2.10.2'
 | 
			
		||||
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
 | 
			
		||||
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
 | 
			
		||||
const activeClientIds = new Set()
 | 
			
		||||
 | 
			
		||||
addEventListener('install', function () {
 | 
			
		||||
  self.skipWaiting()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
addEventListener('activate', function (event) {
 | 
			
		||||
  event.waitUntil(self.clients.claim())
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
addEventListener('message', async function (event) {
 | 
			
		||||
  const clientId = Reflect.get(event.source || {}, 'id')
 | 
			
		||||
 | 
			
		||||
  if (!clientId || !self.clients) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const client = await self.clients.get(clientId)
 | 
			
		||||
 | 
			
		||||
  if (!client) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const allClients = await self.clients.matchAll({
 | 
			
		||||
    type: 'window',
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  switch (event.data) {
 | 
			
		||||
    case 'KEEPALIVE_REQUEST': {
 | 
			
		||||
      sendToClient(client, {
 | 
			
		||||
        type: 'KEEPALIVE_RESPONSE',
 | 
			
		||||
      })
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'INTEGRITY_CHECK_REQUEST': {
 | 
			
		||||
      sendToClient(client, {
 | 
			
		||||
        type: 'INTEGRITY_CHECK_RESPONSE',
 | 
			
		||||
        payload: {
 | 
			
		||||
          packageVersion: PACKAGE_VERSION,
 | 
			
		||||
          checksum: INTEGRITY_CHECKSUM,
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'MOCK_ACTIVATE': {
 | 
			
		||||
      activeClientIds.add(clientId)
 | 
			
		||||
 | 
			
		||||
      sendToClient(client, {
 | 
			
		||||
        type: 'MOCKING_ENABLED',
 | 
			
		||||
        payload: {
 | 
			
		||||
          client: {
 | 
			
		||||
            id: client.id,
 | 
			
		||||
            frameType: client.frameType,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'MOCK_DEACTIVATE': {
 | 
			
		||||
      activeClientIds.delete(clientId)
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'CLIENT_CLOSED': {
 | 
			
		||||
      activeClientIds.delete(clientId)
 | 
			
		||||
 | 
			
		||||
      const remainingClients = allClients.filter((client) => {
 | 
			
		||||
        return client.id !== clientId
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      // Unregister itself when there are no more clients
 | 
			
		||||
      if (remainingClients.length === 0) {
 | 
			
		||||
        self.registration.unregister()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
addEventListener('fetch', function (event) {
 | 
			
		||||
  // Bypass navigation requests.
 | 
			
		||||
  if (event.request.mode === 'navigate') {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Opening the DevTools triggers the "only-if-cached" request
 | 
			
		||||
  // that cannot be handled by the worker. Bypass such requests.
 | 
			
		||||
  if (
 | 
			
		||||
    event.request.cache === 'only-if-cached' &&
 | 
			
		||||
    event.request.mode !== 'same-origin'
 | 
			
		||||
  ) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Bypass all requests when there are no active clients.
 | 
			
		||||
  // Prevents the self-unregistered worked from handling requests
 | 
			
		||||
  // after it's been deleted (still remains active until the next reload).
 | 
			
		||||
  if (activeClientIds.size === 0) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const requestId = crypto.randomUUID()
 | 
			
		||||
  event.respondWith(handleRequest(event, requestId))
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {FetchEvent} event
 | 
			
		||||
 * @param {string} requestId
 | 
			
		||||
 */
 | 
			
		||||
async function handleRequest(event, requestId) {
 | 
			
		||||
  const client = await resolveMainClient(event)
 | 
			
		||||
  const requestCloneForEvents = event.request.clone()
 | 
			
		||||
  const response = await getResponse(event, client, requestId)
 | 
			
		||||
 | 
			
		||||
  // Send back the response clone for the "response:*" life-cycle events.
 | 
			
		||||
  // Ensure MSW is active and ready to handle the message, otherwise
 | 
			
		||||
  // this message will pend indefinitely.
 | 
			
		||||
  if (client && activeClientIds.has(client.id)) {
 | 
			
		||||
    const serializedRequest = await serializeRequest(requestCloneForEvents)
 | 
			
		||||
 | 
			
		||||
    // Clone the response so both the client and the library could consume it.
 | 
			
		||||
    const responseClone = response.clone()
 | 
			
		||||
 | 
			
		||||
    sendToClient(
 | 
			
		||||
      client,
 | 
			
		||||
      {
 | 
			
		||||
        type: 'RESPONSE',
 | 
			
		||||
        payload: {
 | 
			
		||||
          isMockedResponse: IS_MOCKED_RESPONSE in response,
 | 
			
		||||
          request: {
 | 
			
		||||
            id: requestId,
 | 
			
		||||
            ...serializedRequest,
 | 
			
		||||
          },
 | 
			
		||||
          response: {
 | 
			
		||||
            type: responseClone.type,
 | 
			
		||||
            status: responseClone.status,
 | 
			
		||||
            statusText: responseClone.statusText,
 | 
			
		||||
            headers: Object.fromEntries(responseClone.headers.entries()),
 | 
			
		||||
            body: responseClone.body,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      responseClone.body ? [serializedRequest.body, responseClone.body] : [],
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return response
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Resolve the main client for the given event.
 | 
			
		||||
 * Client that issues a request doesn't necessarily equal the client
 | 
			
		||||
 * that registered the worker. It's with the latter the worker should
 | 
			
		||||
 * communicate with during the response resolving phase.
 | 
			
		||||
 * @param {FetchEvent} event
 | 
			
		||||
 * @returns {Promise<Client | undefined>}
 | 
			
		||||
 */
 | 
			
		||||
async function resolveMainClient(event) {
 | 
			
		||||
  const client = await self.clients.get(event.clientId)
 | 
			
		||||
 | 
			
		||||
  if (activeClientIds.has(event.clientId)) {
 | 
			
		||||
    return client
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (client?.frameType === 'top-level') {
 | 
			
		||||
    return client
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const allClients = await self.clients.matchAll({
 | 
			
		||||
    type: 'window',
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return allClients
 | 
			
		||||
    .filter((client) => {
 | 
			
		||||
      // Get only those clients that are currently visible.
 | 
			
		||||
      return client.visibilityState === 'visible'
 | 
			
		||||
    })
 | 
			
		||||
    .find((client) => {
 | 
			
		||||
      // Find the client ID that's recorded in the
 | 
			
		||||
      // set of clients that have registered the worker.
 | 
			
		||||
      return activeClientIds.has(client.id)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {FetchEvent} event
 | 
			
		||||
 * @param {Client | undefined} client
 | 
			
		||||
 * @param {string} requestId
 | 
			
		||||
 * @returns {Promise<Response>}
 | 
			
		||||
 */
 | 
			
		||||
async function getResponse(event, client, requestId) {
 | 
			
		||||
  // Clone the request because it might've been already used
 | 
			
		||||
  // (i.e. its body has been read and sent to the client).
 | 
			
		||||
  const requestClone = event.request.clone()
 | 
			
		||||
 | 
			
		||||
  function passthrough() {
 | 
			
		||||
    // Cast the request headers to a new Headers instance
 | 
			
		||||
    // so the headers can be manipulated with.
 | 
			
		||||
    const headers = new Headers(requestClone.headers)
 | 
			
		||||
 | 
			
		||||
    // Remove the "accept" header value that marked this request as passthrough.
 | 
			
		||||
    // This prevents request alteration and also keeps it compliant with the
 | 
			
		||||
    // user-defined CORS policies.
 | 
			
		||||
    const acceptHeader = headers.get('accept')
 | 
			
		||||
    if (acceptHeader) {
 | 
			
		||||
      const values = acceptHeader.split(',').map((value) => value.trim())
 | 
			
		||||
      const filteredValues = values.filter(
 | 
			
		||||
        (value) => value !== 'msw/passthrough',
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      if (filteredValues.length > 0) {
 | 
			
		||||
        headers.set('accept', filteredValues.join(', '))
 | 
			
		||||
      } else {
 | 
			
		||||
        headers.delete('accept')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return fetch(requestClone, { headers })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Bypass mocking when the client is not active.
 | 
			
		||||
  if (!client) {
 | 
			
		||||
    return passthrough()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Bypass initial page load requests (i.e. static assets).
 | 
			
		||||
  // The absence of the immediate/parent client in the map of the active clients
 | 
			
		||||
  // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
 | 
			
		||||
  // and is not ready to handle requests.
 | 
			
		||||
  if (!activeClientIds.has(client.id)) {
 | 
			
		||||
    return passthrough()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Notify the client that a request has been intercepted.
 | 
			
		||||
  const serializedRequest = await serializeRequest(event.request)
 | 
			
		||||
  const clientMessage = await sendToClient(
 | 
			
		||||
    client,
 | 
			
		||||
    {
 | 
			
		||||
      type: 'REQUEST',
 | 
			
		||||
      payload: {
 | 
			
		||||
        id: requestId,
 | 
			
		||||
        ...serializedRequest,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    [serializedRequest.body],
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  switch (clientMessage.type) {
 | 
			
		||||
    case 'MOCK_RESPONSE': {
 | 
			
		||||
      return respondWithMock(clientMessage.data)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'PASSTHROUGH': {
 | 
			
		||||
      return passthrough()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return passthrough()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {Client} client
 | 
			
		||||
 * @param {any} message
 | 
			
		||||
 * @param {Array<Transferable>} transferrables
 | 
			
		||||
 * @returns {Promise<any>}
 | 
			
		||||
 */
 | 
			
		||||
function sendToClient(client, message, transferrables = []) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    const channel = new MessageChannel()
 | 
			
		||||
 | 
			
		||||
    channel.port1.onmessage = (event) => {
 | 
			
		||||
      if (event.data && event.data.error) {
 | 
			
		||||
        return reject(event.data.error)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      resolve(event.data)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    client.postMessage(message, [
 | 
			
		||||
      channel.port2,
 | 
			
		||||
      ...transferrables.filter(Boolean),
 | 
			
		||||
    ])
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {Response} response
 | 
			
		||||
 * @returns {Response}
 | 
			
		||||
 */
 | 
			
		||||
function respondWithMock(response) {
 | 
			
		||||
  // Setting response status code to 0 is a no-op.
 | 
			
		||||
  // However, when responding with a "Response.error()", the produced Response
 | 
			
		||||
  // instance will have status code set to 0. Since it's not possible to create
 | 
			
		||||
  // a Response instance with status code 0, handle that use-case separately.
 | 
			
		||||
  if (response.status === 0) {
 | 
			
		||||
    return Response.error()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const mockedResponse = new Response(response.body, response)
 | 
			
		||||
 | 
			
		||||
  Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
 | 
			
		||||
    value: true,
 | 
			
		||||
    enumerable: true,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  return mockedResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {Request} request
 | 
			
		||||
 */
 | 
			
		||||
async function serializeRequest(request) {
 | 
			
		||||
  return {
 | 
			
		||||
    url: request.url,
 | 
			
		||||
    mode: request.mode,
 | 
			
		||||
    method: request.method,
 | 
			
		||||
    headers: Object.fromEntries(request.headers.entries()),
 | 
			
		||||
    cache: request.cache,
 | 
			
		||||
    credentials: request.credentials,
 | 
			
		||||
    destination: request.destination,
 | 
			
		||||
    integrity: request.integrity,
 | 
			
		||||
    redirect: request.redirect,
 | 
			
		||||
    referrer: request.referrer,
 | 
			
		||||
    referrerPolicy: request.referrerPolicy,
 | 
			
		||||
    body: await request.arrayBuffer(),
 | 
			
		||||
    keepalive: request.keepalive,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -36,7 +36,8 @@ export default meta;
 | 
			
		||||
type Story = StoryObj<typeof meta>;
 | 
			
		||||
 | 
			
		||||
const buttonTest: Story['play'] = async ({ args, canvas, userEvent }) => {
 | 
			
		||||
  await userEvent.click(canvas.getByRole('button'));
 | 
			
		||||
  const button = await canvas.findByRole('button');
 | 
			
		||||
  await userEvent.click(button);
 | 
			
		||||
  await expect(args.onClick).toHaveBeenCalled();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -45,7 +46,8 @@ const disabledButtonTest: Story['play'] = async ({
 | 
			
		||||
  canvas,
 | 
			
		||||
  userEvent,
 | 
			
		||||
}) => {
 | 
			
		||||
  await userEvent.click(canvas.getByRole('button'));
 | 
			
		||||
  const button = await canvas.findByRole('button');
 | 
			
		||||
  await userEvent.click(button);
 | 
			
		||||
  await expect(args.onClick).not.toHaveBeenCalled();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Record as ImmutableRecord } from 'immutable';
 | 
			
		||||
import { Record as ImmutableRecord, mergeDeep } from 'immutable';
 | 
			
		||||
 | 
			
		||||
import { loadingBarReducer } from 'react-redux-loading-bar';
 | 
			
		||||
import { combineReducers } from 'redux-immutable';
 | 
			
		||||
@@ -98,6 +98,15 @@ const initialRootState = Object.fromEntries(
 | 
			
		||||
 | 
			
		||||
const RootStateRecord = ImmutableRecord(initialRootState, 'RootState');
 | 
			
		||||
 | 
			
		||||
const rootReducer = combineReducers(reducers, RootStateRecord);
 | 
			
		||||
export const rootReducer = combineReducers(reducers, RootStateRecord);
 | 
			
		||||
 | 
			
		||||
export { rootReducer };
 | 
			
		||||
export function reducerWithInitialState(
 | 
			
		||||
  stateOverrides: Record<string, unknown> = {},
 | 
			
		||||
) {
 | 
			
		||||
  const initialStateRecord = mergeDeep(initialRootState, stateOverrides);
 | 
			
		||||
  const PatchedRootStateRecord = ImmutableRecord(
 | 
			
		||||
    initialStateRecord,
 | 
			
		||||
    'RootState',
 | 
			
		||||
  );
 | 
			
		||||
  return combineReducers(reducers, PatchedRootStateRecord);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,24 +6,26 @@ import { errorsMiddleware } from './middlewares/errors';
 | 
			
		||||
import { loadingBarMiddleware } from './middlewares/loading_bar';
 | 
			
		||||
import { soundsMiddleware } from './middlewares/sounds';
 | 
			
		||||
 | 
			
		||||
export const defaultMiddleware = {
 | 
			
		||||
  // In development, Redux Toolkit enables 2 default middlewares to detect
 | 
			
		||||
  // common issues with states. Unfortunately, our use of ImmutableJS for state
 | 
			
		||||
  // triggers both, so lets disable them until our state is fully refactored
 | 
			
		||||
 | 
			
		||||
  // https://redux-toolkit.js.org/api/serializabilityMiddleware
 | 
			
		||||
  // This checks recursively that every values in the state are serializable in JSON
 | 
			
		||||
  // Which is not the case, as we use ImmutableJS structures, but also File objects
 | 
			
		||||
  serializableCheck: false,
 | 
			
		||||
 | 
			
		||||
  // https://redux-toolkit.js.org/api/immutabilityMiddleware
 | 
			
		||||
  // This checks recursively if every value in the state is immutable (ie, a JS primitive type)
 | 
			
		||||
  // But this is not the case, as our Root State is an ImmutableJS map, which is an object
 | 
			
		||||
  immutableCheck: false,
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export const store = configureStore({
 | 
			
		||||
  reducer: rootReducer,
 | 
			
		||||
  middleware: (getDefaultMiddleware) =>
 | 
			
		||||
    getDefaultMiddleware({
 | 
			
		||||
      // In development, Redux Toolkit enables 2 default middlewares to detect
 | 
			
		||||
      // common issues with states. Unfortunately, our use of ImmutableJS for state
 | 
			
		||||
      // triggers both, so lets disable them until our state is fully refactored
 | 
			
		||||
 | 
			
		||||
      // https://redux-toolkit.js.org/api/serializabilityMiddleware
 | 
			
		||||
      // This checks recursively that every values in the state are serializable in JSON
 | 
			
		||||
      // Which is not the case, as we use ImmutableJS structures, but also File objects
 | 
			
		||||
      serializableCheck: false,
 | 
			
		||||
 | 
			
		||||
      // https://redux-toolkit.js.org/api/immutabilityMiddleware
 | 
			
		||||
      // This checks recursively if every value in the state is immutable (ie, a JS primitive type)
 | 
			
		||||
      // But this is not the case, as our Root State is an ImmutableJS map, which is an object
 | 
			
		||||
      immutableCheck: false,
 | 
			
		||||
    })
 | 
			
		||||
    getDefaultMiddleware(defaultMiddleware)
 | 
			
		||||
      .concat(
 | 
			
		||||
        loadingBarMiddleware({
 | 
			
		||||
          promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'],
 | 
			
		||||
 
 | 
			
		||||
@@ -258,7 +258,7 @@ export default tseslint.config([
 | 
			
		||||
            'app/javascript/**/*.stories.tsx',
 | 
			
		||||
            'app/javascript/**/*.test.ts',
 | 
			
		||||
            'app/javascript/**/*.test.tsx',
 | 
			
		||||
            '.storybook/**/*.ts',
 | 
			
		||||
            '.storybook/**/*',
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
@@ -406,7 +406,7 @@ export default tseslint.config([
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    files: ['**/*.stories.ts', '**/*.stories.tsx', '.storybook/**/*.ts'],
 | 
			
		||||
    files: ['**/*.stories.ts', '**/*.stories.tsx', '.storybook/*'],
 | 
			
		||||
    rules: {
 | 
			
		||||
      'import/no-default-export': 'off',
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -172,6 +172,8 @@
 | 
			
		||||
    "globals": "^16.0.0",
 | 
			
		||||
    "husky": "^9.0.11",
 | 
			
		||||
    "lint-staged": "^16.0.0",
 | 
			
		||||
    "msw": "^2.10.2",
 | 
			
		||||
    "msw-storybook-addon": "^2.0.5",
 | 
			
		||||
    "playwright": "^1.52.0",
 | 
			
		||||
    "prettier": "^3.3.3",
 | 
			
		||||
    "react-test-renderer": "^18.2.0",
 | 
			
		||||
@@ -202,5 +204,10 @@
 | 
			
		||||
    "react-router-dom": {
 | 
			
		||||
      "optional": true
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "msw": {
 | 
			
		||||
    "workerDirectory": [
 | 
			
		||||
      ".storybook/static"
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										355
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										355
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -1189,6 +1189,34 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@bundled-es-modules/cookie@npm:^2.0.1":
 | 
			
		||||
  version: 2.0.1
 | 
			
		||||
  resolution: "@bundled-es-modules/cookie@npm:2.0.1"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    cookie: "npm:^0.7.2"
 | 
			
		||||
  checksum: 10c0/dfac5e36127e827c5557b8577f17a8aa94c057baff6d38555917927b99da0ecf0b1357e7fedadc8853ecdbd4a8a7fa1f5e64111b2a656612f4a36376f5bdbe8d
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@bundled-es-modules/statuses@npm:^1.0.1":
 | 
			
		||||
  version: 1.0.1
 | 
			
		||||
  resolution: "@bundled-es-modules/statuses@npm:1.0.1"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    statuses: "npm:^2.0.1"
 | 
			
		||||
  checksum: 10c0/c1a8ede3efa8da61ccda4b98e773582a9733edfbeeee569d4630785f8e018766202edb190a754a3ec7a7f6bd738e857829affc2fdb676b6dab4db1bb44e62785
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@bundled-es-modules/tough-cookie@npm:^0.1.6":
 | 
			
		||||
  version: 0.1.6
 | 
			
		||||
  resolution: "@bundled-es-modules/tough-cookie@npm:0.1.6"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/tough-cookie": "npm:^4.0.5"
 | 
			
		||||
    tough-cookie: "npm:^4.1.4"
 | 
			
		||||
  checksum: 10c0/28bcac878bff6b34719ba3aa8341e9924772ee55de5487680ebe784981ec9fccb70ed5d46f563e2404855a04de606f9e56aa4202842d4f5835bc04a4fe820571
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@csstools/cascade-layer-name-parser@npm:^2.0.5":
 | 
			
		||||
  version: 2.0.5
 | 
			
		||||
  resolution: "@csstools/cascade-layer-name-parser@npm:2.0.5"
 | 
			
		||||
@@ -2412,6 +2440,61 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@inquirer/confirm@npm:^5.0.0":
 | 
			
		||||
  version: 5.1.12
 | 
			
		||||
  resolution: "@inquirer/confirm@npm:5.1.12"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@inquirer/core": "npm:^10.1.13"
 | 
			
		||||
    "@inquirer/type": "npm:^3.0.7"
 | 
			
		||||
  peerDependencies:
 | 
			
		||||
    "@types/node": ">=18"
 | 
			
		||||
  peerDependenciesMeta:
 | 
			
		||||
    "@types/node":
 | 
			
		||||
      optional: true
 | 
			
		||||
  checksum: 10c0/581aedfe8ce45e177fb4470a12f874f5162a4396636bf4140edc5812ffc8ed0d1fa7e9bbc3a7af618203089a084f489e0b32112947eedc6930a766fad992449e
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@inquirer/core@npm:^10.1.13":
 | 
			
		||||
  version: 10.1.13
 | 
			
		||||
  resolution: "@inquirer/core@npm:10.1.13"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@inquirer/figures": "npm:^1.0.12"
 | 
			
		||||
    "@inquirer/type": "npm:^3.0.7"
 | 
			
		||||
    ansi-escapes: "npm:^4.3.2"
 | 
			
		||||
    cli-width: "npm:^4.1.0"
 | 
			
		||||
    mute-stream: "npm:^2.0.0"
 | 
			
		||||
    signal-exit: "npm:^4.1.0"
 | 
			
		||||
    wrap-ansi: "npm:^6.2.0"
 | 
			
		||||
    yoctocolors-cjs: "npm:^2.1.2"
 | 
			
		||||
  peerDependencies:
 | 
			
		||||
    "@types/node": ">=18"
 | 
			
		||||
  peerDependenciesMeta:
 | 
			
		||||
    "@types/node":
 | 
			
		||||
      optional: true
 | 
			
		||||
  checksum: 10c0/919208a31307297d5a07a44b9ebe69a999ce1470b31a2e1b5a04538bc36624d2053808cd6c677637a61690af09bdbdd635bd7031b64e3dd86c5b18df3ca7c3f9
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@inquirer/figures@npm:^1.0.12":
 | 
			
		||||
  version: 1.0.12
 | 
			
		||||
  resolution: "@inquirer/figures@npm:1.0.12"
 | 
			
		||||
  checksum: 10c0/08694288bdf9aa474571ca94272113a5ac443229519ce71447eba9eb7d5a2007901bdc3e92216d929a69746dcbac29683886c20e67b7864a7c7f6c59b99d3269
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@inquirer/type@npm:^3.0.7":
 | 
			
		||||
  version: 3.0.7
 | 
			
		||||
  resolution: "@inquirer/type@npm:3.0.7"
 | 
			
		||||
  peerDependencies:
 | 
			
		||||
    "@types/node": ">=18"
 | 
			
		||||
  peerDependenciesMeta:
 | 
			
		||||
    "@types/node":
 | 
			
		||||
      optional: true
 | 
			
		||||
  checksum: 10c0/bbaa33c274a10f70d3a587264e1db6dbfcd8c1458d595c54870d1d5b3fc113ab5063203ec12a098485bb9e2fcef1a87d8c6ecd2a6d44ddc575f5c4715379be5e
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@ioredis/commands@npm:^1.1.1":
 | 
			
		||||
  version: 1.2.0
 | 
			
		||||
  resolution: "@ioredis/commands@npm:1.2.0"
 | 
			
		||||
@@ -2608,6 +2691,8 @@ __metadata:
 | 
			
		||||
    lint-staged: "npm:^16.0.0"
 | 
			
		||||
    lodash: "npm:^4.17.21"
 | 
			
		||||
    marky: "npm:^1.2.5"
 | 
			
		||||
    msw: "npm:^2.10.2"
 | 
			
		||||
    msw-storybook-addon: "npm:^2.0.5"
 | 
			
		||||
    path-complete-extname: "npm:^1.0.0"
 | 
			
		||||
    playwright: "npm:^1.52.0"
 | 
			
		||||
    postcss-preset-env: "npm:^10.1.5"
 | 
			
		||||
@@ -2721,6 +2806,20 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@mswjs/interceptors@npm:^0.39.1":
 | 
			
		||||
  version: 0.39.2
 | 
			
		||||
  resolution: "@mswjs/interceptors@npm:0.39.2"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@open-draft/deferred-promise": "npm:^2.2.0"
 | 
			
		||||
    "@open-draft/logger": "npm:^0.3.0"
 | 
			
		||||
    "@open-draft/until": "npm:^2.0.0"
 | 
			
		||||
    is-node-process: "npm:^1.2.0"
 | 
			
		||||
    outvariant: "npm:^1.4.3"
 | 
			
		||||
    strict-event-emitter: "npm:^0.5.1"
 | 
			
		||||
  checksum: 10c0/5698e33930a6b6e7cc78cf762291be60c91c6348faa22750acc41ef41528e7891e74541ccfb668ba470d964233fd2121c44d0224a2917eedeba2459cf0b78ca2
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@napi-rs/wasm-runtime@npm:^0.2.7":
 | 
			
		||||
  version: 0.2.7
 | 
			
		||||
  resolution: "@napi-rs/wasm-runtime@npm:0.2.7"
 | 
			
		||||
@@ -2781,6 +2880,30 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@open-draft/deferred-promise@npm:^2.2.0":
 | 
			
		||||
  version: 2.2.0
 | 
			
		||||
  resolution: "@open-draft/deferred-promise@npm:2.2.0"
 | 
			
		||||
  checksum: 10c0/eafc1b1d0fc8edb5e1c753c5e0f3293410b40dde2f92688211a54806d4136887051f39b98c1950370be258483deac9dfd17cf8b96557553765198ef2547e4549
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@open-draft/logger@npm:^0.3.0":
 | 
			
		||||
  version: 0.3.0
 | 
			
		||||
  resolution: "@open-draft/logger@npm:0.3.0"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    is-node-process: "npm:^1.2.0"
 | 
			
		||||
    outvariant: "npm:^1.4.0"
 | 
			
		||||
  checksum: 10c0/90010647b22e9693c16258f4f9adb034824d1771d3baa313057b9a37797f571181005bc50415a934eaf7c891d90ff71dcd7a9d5048b0b6bb438f31bef2c7c5c1
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@open-draft/until@npm:^2.0.0, @open-draft/until@npm:^2.1.0":
 | 
			
		||||
  version: 2.1.0
 | 
			
		||||
  resolution: "@open-draft/until@npm:2.1.0"
 | 
			
		||||
  checksum: 10c0/61d3f99718dd86bb393fee2d7a785f961dcaf12f2055f0c693b27f4d0cd5f7a03d498a6d9289773b117590d794a43cd129366fd8e99222e4832f67b1653d54cf
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@opentelemetry/api@npm:^1.4.0":
 | 
			
		||||
  version: 1.6.0
 | 
			
		||||
  resolution: "@opentelemetry/api@npm:1.6.0"
 | 
			
		||||
@@ -3786,6 +3909,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@types/cookie@npm:^0.6.0":
 | 
			
		||||
  version: 0.6.0
 | 
			
		||||
  resolution: "@types/cookie@npm:0.6.0"
 | 
			
		||||
  checksum: 10c0/5b326bd0188120fb32c0be086b141b1481fec9941b76ad537f9110e10d61ee2636beac145463319c71e4be67a17e85b81ca9e13ceb6e3bb63b93d16824d6c149
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@types/cors@npm:^2.8.16":
 | 
			
		||||
  version: 2.8.18
 | 
			
		||||
  resolution: "@types/cors@npm:2.8.18"
 | 
			
		||||
@@ -4201,6 +4331,20 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@types/statuses@npm:^2.0.4":
 | 
			
		||||
  version: 2.0.6
 | 
			
		||||
  resolution: "@types/statuses@npm:2.0.6"
 | 
			
		||||
  checksum: 10c0/dd88c220b0e2c6315686289525fd61472d2204d2e4bef4941acfb76bda01d3066f749ac74782aab5b537a45314fcd7d6261eefa40b6ec872691f5803adaa608d
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@types/tough-cookie@npm:^4.0.5":
 | 
			
		||||
  version: 4.0.5
 | 
			
		||||
  resolution: "@types/tough-cookie@npm:4.0.5"
 | 
			
		||||
  checksum: 10c0/68c6921721a3dcb40451543db2174a145ef915bc8bcbe7ad4e59194a0238e776e782b896c7a59f4b93ac6acefca9161fccb31d1ce3b3445cb6faa467297fb473
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@types/trusted-types@npm:^2.0.2":
 | 
			
		||||
  version: 2.0.3
 | 
			
		||||
  resolution: "@types/trusted-types@npm:2.0.3"
 | 
			
		||||
@@ -4882,6 +5026,15 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"ansi-escapes@npm:^4.3.2":
 | 
			
		||||
  version: 4.3.2
 | 
			
		||||
  resolution: "ansi-escapes@npm:4.3.2"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    type-fest: "npm:^0.21.3"
 | 
			
		||||
  checksum: 10c0/da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"ansi-escapes@npm:^7.0.0":
 | 
			
		||||
  version: 7.0.0
 | 
			
		||||
  resolution: "ansi-escapes@npm:7.0.0"
 | 
			
		||||
@@ -5658,6 +5811,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"cli-width@npm:^4.1.0":
 | 
			
		||||
  version: 4.1.0
 | 
			
		||||
  resolution: "cli-width@npm:4.1.0"
 | 
			
		||||
  checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"cliui@npm:^8.0.1":
 | 
			
		||||
  version: 8.0.1
 | 
			
		||||
  resolution: "cliui@npm:8.0.1"
 | 
			
		||||
@@ -5808,6 +5968,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"cookie@npm:^0.7.2":
 | 
			
		||||
  version: 0.7.2
 | 
			
		||||
  resolution: "cookie@npm:0.7.2"
 | 
			
		||||
  checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"core-js-compat@npm:^3.40.0":
 | 
			
		||||
  version: 3.41.0
 | 
			
		||||
  resolution: "core-js-compat@npm:3.41.0"
 | 
			
		||||
@@ -7712,6 +7879,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"graphql@npm:^16.8.1":
 | 
			
		||||
  version: 16.11.0
 | 
			
		||||
  resolution: "graphql@npm:16.11.0"
 | 
			
		||||
  checksum: 10c0/124da7860a2292e9acf2fed0c71fc0f6a9b9ca865d390d112bdd563c1f474357141501c12891f4164fe984315764736ad67f705219c62f7580681d431a85db88
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"has-bigints@npm:^1.0.2":
 | 
			
		||||
  version: 1.0.2
 | 
			
		||||
  resolution: "has-bigints@npm:1.0.2"
 | 
			
		||||
@@ -7769,6 +7943,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"headers-polyfill@npm:^4.0.2":
 | 
			
		||||
  version: 4.0.3
 | 
			
		||||
  resolution: "headers-polyfill@npm:4.0.3"
 | 
			
		||||
  checksum: 10c0/53e85b2c6385f8d411945fb890c5369f1469ce8aa32a6e8d28196df38568148de640c81cf88cbc7c67767103dd9acba48f4f891982da63178fc6e34560022afe
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"help-me@npm:^5.0.0":
 | 
			
		||||
  version: 5.0.0
 | 
			
		||||
  resolution: "help-me@npm:5.0.0"
 | 
			
		||||
@@ -8263,6 +8444,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"is-node-process@npm:^1.0.1, is-node-process@npm:^1.2.0":
 | 
			
		||||
  version: 1.2.0
 | 
			
		||||
  resolution: "is-node-process@npm:1.2.0"
 | 
			
		||||
  checksum: 10c0/5b24fda6776d00e42431d7bcd86bce81cb0b6cabeb944142fe7b077a54ada2e155066ad06dbe790abdb397884bdc3151e04a9707b8cd185099efbc79780573ed
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"is-number-object@npm:^1.1.1":
 | 
			
		||||
  version: 1.1.1
 | 
			
		||||
  resolution: "is-number-object@npm:1.1.1"
 | 
			
		||||
@@ -9343,6 +9531,57 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"msw-storybook-addon@npm:^2.0.5":
 | 
			
		||||
  version: 2.0.5
 | 
			
		||||
  resolution: "msw-storybook-addon@npm:2.0.5"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    is-node-process: "npm:^1.0.1"
 | 
			
		||||
  peerDependencies:
 | 
			
		||||
    msw: ^2.0.0
 | 
			
		||||
  checksum: 10c0/3f26fd4a8a6b1b4da165a8940eca4da2e175a69036a1c85c07ec1952fbb595252db689c4380d8f88ec1cfaa66a6696e90ef0c26b2d1bf17c30092b81247d1d40
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"msw@npm:^2.10.2":
 | 
			
		||||
  version: 2.10.2
 | 
			
		||||
  resolution: "msw@npm:2.10.2"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@bundled-es-modules/cookie": "npm:^2.0.1"
 | 
			
		||||
    "@bundled-es-modules/statuses": "npm:^1.0.1"
 | 
			
		||||
    "@bundled-es-modules/tough-cookie": "npm:^0.1.6"
 | 
			
		||||
    "@inquirer/confirm": "npm:^5.0.0"
 | 
			
		||||
    "@mswjs/interceptors": "npm:^0.39.1"
 | 
			
		||||
    "@open-draft/deferred-promise": "npm:^2.2.0"
 | 
			
		||||
    "@open-draft/until": "npm:^2.1.0"
 | 
			
		||||
    "@types/cookie": "npm:^0.6.0"
 | 
			
		||||
    "@types/statuses": "npm:^2.0.4"
 | 
			
		||||
    graphql: "npm:^16.8.1"
 | 
			
		||||
    headers-polyfill: "npm:^4.0.2"
 | 
			
		||||
    is-node-process: "npm:^1.2.0"
 | 
			
		||||
    outvariant: "npm:^1.4.3"
 | 
			
		||||
    path-to-regexp: "npm:^6.3.0"
 | 
			
		||||
    picocolors: "npm:^1.1.1"
 | 
			
		||||
    strict-event-emitter: "npm:^0.5.1"
 | 
			
		||||
    type-fest: "npm:^4.26.1"
 | 
			
		||||
    yargs: "npm:^17.7.2"
 | 
			
		||||
  peerDependencies:
 | 
			
		||||
    typescript: ">= 4.8.x"
 | 
			
		||||
  peerDependenciesMeta:
 | 
			
		||||
    typescript:
 | 
			
		||||
      optional: true
 | 
			
		||||
  bin:
 | 
			
		||||
    msw: cli/index.js
 | 
			
		||||
  checksum: 10c0/fb44961e17e12864b4764b4c015f6ce7c907081f8dcd237ecd635eab00b787847406fbd36a2bcf2ef4c21114a3610ac03c7f93f3080f509a69b0c1c5285fd683
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"mute-stream@npm:^2.0.0":
 | 
			
		||||
  version: 2.0.0
 | 
			
		||||
  resolution: "mute-stream@npm:2.0.0"
 | 
			
		||||
  checksum: 10c0/2cf48a2087175c60c8dcdbc619908b49c07f7adcfc37d29236b0c5c612d6204f789104c98cc44d38acab7b3c96f4a3ec2cfdc4934d0738d876dbefa2a12c69f4
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"nano-spawn@npm:^1.0.0":
 | 
			
		||||
  version: 1.0.1
 | 
			
		||||
  resolution: "nano-spawn@npm:1.0.1"
 | 
			
		||||
@@ -9625,6 +9864,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"outvariant@npm:^1.4.0, outvariant@npm:^1.4.3":
 | 
			
		||||
  version: 1.4.3
 | 
			
		||||
  resolution: "outvariant@npm:1.4.3"
 | 
			
		||||
  checksum: 10c0/5976ca7740349cb8c71bd3382e2a762b1aeca6f33dc984d9d896acdf3c61f78c3afcf1bfe9cc633a7b3c4b295ec94d292048f83ea2b2594fae4496656eba992c
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"own-keys@npm:^1.0.1":
 | 
			
		||||
  version: 1.0.1
 | 
			
		||||
  resolution: "own-keys@npm:1.0.1"
 | 
			
		||||
@@ -9778,6 +10024,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"path-to-regexp@npm:^6.3.0":
 | 
			
		||||
  version: 6.3.0
 | 
			
		||||
  resolution: "path-to-regexp@npm:6.3.0"
 | 
			
		||||
  checksum: 10c0/73b67f4638b41cde56254e6354e46ae3a2ebc08279583f6af3d96fe4664fc75788f74ed0d18ca44fa4a98491b69434f9eee73b97bb5314bd1b5adb700f5c18d6
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"path-type@npm:^4.0.0":
 | 
			
		||||
  version: 4.0.0
 | 
			
		||||
  resolution: "path-type@npm:4.0.0"
 | 
			
		||||
@@ -10580,6 +10833,15 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"psl@npm:^1.1.33":
 | 
			
		||||
  version: 1.15.0
 | 
			
		||||
  resolution: "psl@npm:1.15.0"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    punycode: "npm:^2.3.1"
 | 
			
		||||
  checksum: 10c0/d8d45a99e4ca62ca12ac3c373e63d80d2368d38892daa40cfddaa1eb908be98cd549ac059783ef3a56cfd96d57ae8e2fd9ae53d1378d90d42bc661ff924e102a
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"pump@npm:^3.0.0":
 | 
			
		||||
  version: 3.0.0
 | 
			
		||||
  resolution: "pump@npm:3.0.0"
 | 
			
		||||
@@ -10597,7 +10859,7 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"punycode@npm:^2.1.0, punycode@npm:^2.3.0, punycode@npm:^2.3.1":
 | 
			
		||||
"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.0, punycode@npm:^2.3.1":
 | 
			
		||||
  version: 2.3.1
 | 
			
		||||
  resolution: "punycode@npm:2.3.1"
 | 
			
		||||
  checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
 | 
			
		||||
@@ -10613,6 +10875,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"querystringify@npm:^2.1.1":
 | 
			
		||||
  version: 2.2.0
 | 
			
		||||
  resolution: "querystringify@npm:2.2.0"
 | 
			
		||||
  checksum: 10c0/3258bc3dbdf322ff2663619afe5947c7926a6ef5fb78ad7d384602974c467fadfc8272af44f5eb8cddd0d011aae8fabf3a929a8eee4b86edcc0a21e6bd10f9aa
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"queue-microtask@npm:^1.2.2":
 | 
			
		||||
  version: 1.2.3
 | 
			
		||||
  resolution: "queue-microtask@npm:1.2.3"
 | 
			
		||||
@@ -11307,6 +11576,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"requires-port@npm:^1.0.0":
 | 
			
		||||
  version: 1.0.0
 | 
			
		||||
  resolution: "requires-port@npm:1.0.0"
 | 
			
		||||
  checksum: 10c0/b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"reselect@npm:^5.1.0":
 | 
			
		||||
  version: 5.1.0
 | 
			
		||||
  resolution: "reselect@npm:5.1.0"
 | 
			
		||||
@@ -12147,6 +12423,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"statuses@npm:^2.0.1":
 | 
			
		||||
  version: 2.0.2
 | 
			
		||||
  resolution: "statuses@npm:2.0.2"
 | 
			
		||||
  checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"std-env@npm:^3.9.0":
 | 
			
		||||
  version: 3.9.0
 | 
			
		||||
  resolution: "std-env@npm:3.9.0"
 | 
			
		||||
@@ -12187,6 +12470,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"strict-event-emitter@npm:^0.5.1":
 | 
			
		||||
  version: 0.5.1
 | 
			
		||||
  resolution: "strict-event-emitter@npm:0.5.1"
 | 
			
		||||
  checksum: 10c0/f5228a6e6b6393c57f52f62e673cfe3be3294b35d6f7842fc24b172ae0a6e6c209fa83241d0e433fc267c503bc2f4ffdbe41a9990ff8ffd5ac425ec0489417f7
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"string-argv@npm:^0.3.2":
 | 
			
		||||
  version: 0.3.2
 | 
			
		||||
  resolution: "string-argv@npm:0.3.2"
 | 
			
		||||
@@ -12820,6 +13110,18 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"tough-cookie@npm:^4.1.4":
 | 
			
		||||
  version: 4.1.4
 | 
			
		||||
  resolution: "tough-cookie@npm:4.1.4"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    psl: "npm:^1.1.33"
 | 
			
		||||
    punycode: "npm:^2.1.1"
 | 
			
		||||
    universalify: "npm:^0.2.0"
 | 
			
		||||
    url-parse: "npm:^1.5.3"
 | 
			
		||||
  checksum: 10c0/aca7ff96054f367d53d1e813e62ceb7dd2eda25d7752058a74d64b7266fd07be75908f3753a32ccf866a2f997604b414cfb1916d6e7f69bc64d9d9939b0d6c45
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"tough-cookie@npm:^5.1.1":
 | 
			
		||||
  version: 5.1.2
 | 
			
		||||
  resolution: "tough-cookie@npm:5.1.2"
 | 
			
		||||
@@ -12956,6 +13258,20 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"type-fest@npm:^0.21.3":
 | 
			
		||||
  version: 0.21.3
 | 
			
		||||
  resolution: "type-fest@npm:0.21.3"
 | 
			
		||||
  checksum: 10c0/902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"type-fest@npm:^4.26.1":
 | 
			
		||||
  version: 4.41.0
 | 
			
		||||
  resolution: "type-fest@npm:4.41.0"
 | 
			
		||||
  checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"type-is@npm:~1.6.18":
 | 
			
		||||
  version: 1.6.18
 | 
			
		||||
  resolution: "type-is@npm:1.6.18"
 | 
			
		||||
@@ -13173,6 +13489,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"universalify@npm:^0.2.0":
 | 
			
		||||
  version: 0.2.0
 | 
			
		||||
  resolution: "universalify@npm:0.2.0"
 | 
			
		||||
  checksum: 10c0/cedbe4d4ca3967edf24c0800cfc161c5a15e240dac28e3ce575c689abc11f2c81ccc6532c8752af3b40f9120fb5e454abecd359e164f4f6aa44c29cd37e194fe
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"universalify@npm:^2.0.0":
 | 
			
		||||
  version: 2.0.1
 | 
			
		||||
  resolution: "universalify@npm:2.0.1"
 | 
			
		||||
@@ -13281,6 +13604,16 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"url-parse@npm:^1.5.3":
 | 
			
		||||
  version: 1.5.10
 | 
			
		||||
  resolution: "url-parse@npm:1.5.10"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    querystringify: "npm:^2.1.1"
 | 
			
		||||
    requires-port: "npm:^1.0.0"
 | 
			
		||||
  checksum: 10c0/bd5aa9389f896974beb851c112f63b466505a04b4807cea2e5a3b7092f6fbb75316f0491ea84e44f66fed55f1b440df5195d7e3a8203f64fcefa19d182f5be87
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"use-composed-ref@npm:^1.3.0":
 | 
			
		||||
  version: 1.3.0
 | 
			
		||||
  resolution: "use-composed-ref@npm:1.3.0"
 | 
			
		||||
@@ -14043,6 +14376,17 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"wrap-ansi@npm:^6.2.0":
 | 
			
		||||
  version: 6.2.0
 | 
			
		||||
  resolution: "wrap-ansi@npm:6.2.0"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    ansi-styles: "npm:^4.0.0"
 | 
			
		||||
    string-width: "npm:^4.1.0"
 | 
			
		||||
    strip-ansi: "npm:^6.0.0"
 | 
			
		||||
  checksum: 10c0/baad244e6e33335ea24e86e51868fe6823626e3a3c88d9a6674642afff1d34d9a154c917e74af8d845fd25d170c4ea9cf69a47133c3f3656e1252b3d462d9f6c
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"wrap-ansi@npm:^8.1.0":
 | 
			
		||||
  version: 8.1.0
 | 
			
		||||
  resolution: "wrap-ansi@npm:8.1.0"
 | 
			
		||||
@@ -14162,7 +14506,7 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"yargs@npm:^17.5.1":
 | 
			
		||||
"yargs@npm:^17.5.1, yargs@npm:^17.7.2":
 | 
			
		||||
  version: 17.7.2
 | 
			
		||||
  resolution: "yargs@npm:17.7.2"
 | 
			
		||||
  dependencies:
 | 
			
		||||
@@ -14184,6 +14528,13 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"yoctocolors-cjs@npm:^2.1.2":
 | 
			
		||||
  version: 2.1.2
 | 
			
		||||
  resolution: "yoctocolors-cjs@npm:2.1.2"
 | 
			
		||||
  checksum: 10c0/a0e36eb88fea2c7981eab22d1ba45e15d8d268626e6c4143305e2c1628fa17ebfaa40cd306161a8ce04c0a60ee0262058eab12567493d5eb1409780853454c6f
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"zlibjs@npm:^0.3.1":
 | 
			
		||||
  version: 0.3.1
 | 
			
		||||
  resolution: "zlibjs@npm:0.3.1"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user