) => executionContext.waitUntil(p);
+ const [cache, session] = await Promise.all([
+ caches.open('hydrogen'),
+ HydrogenSession.init(request, [env.SESSION_SECRET]),
+ ]);
+
+ /**
+ * Create Hydrogen's Storefront client.
+ */
+ const { storefront } = createStorefrontClient({
+ cache,
+ waitUntil,
+ i18n: { language: 'EN', country: 'US' },
+ publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
+ privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
+ storeDomain: env.PUBLIC_STORE_DOMAIN,
+ storefrontId: env.PUBLIC_STOREFRONT_ID,
+ storefrontHeaders: getStorefrontHeaders(request),
+ });
+
+ /**
+ * Create a Remix request handler and pass
+ * Hydrogen's Storefront client to the loader context.
+ */
+ const handleRequest = createRequestHandler({
+ build: remixBuild,
+ mode: process.env.NODE_ENV,
+ getLoadContext: () => ({ session, storefront, env, waitUntil }),
+ });
+
+ const response = await handleRequest(request);
+
+ if (response.status === 404) {
+ /**
+ * Check for redirects only when there's a 404 from the app.
+ * If the redirect doesn't exist, then `storefrontRedirect`
+ * will pass through the 404 response.
+ */
+ return storefrontRedirect({ request, response, storefront });
+ }
+
+ return response;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error(error);
+ return new Response('An unexpected error occurred', { status: 500 });
+ }
+ },
+};
+
+/**
+ * This is a custom session implementation for your Hydrogen shop.
+ * Feel free to customize it to your needs, add helper methods, or
+ * swap out the cookie-based implementation with something else!
+ */
+export class HydrogenSession {
+ constructor(private sessionStorage: SessionStorage, private session: Session) {}
+
+ static async init(request: Request, secrets: string[]) {
+ const storage = createCookieSessionStorage({
+ cookie: {
+ name: 'session',
+ httpOnly: true,
+ path: '/',
+ sameSite: 'lax',
+ secrets,
+ },
+ });
+
+ const session = await storage.getSession(request.headers.get('Cookie'));
+
+ return new this(storage, session);
+ }
+
+ has(key: string) {
+ return this.session.has(key);
+ }
+
+ get(key: string) {
+ return this.session.get(key);
+ }
+
+ destroy() {
+ return this.sessionStorage.destroySession(this.session);
+ }
+
+ flash(key: string, value: any) {
+ this.session.flash(key, value);
+ }
+
+ unset(key: string) {
+ this.session.unset(key);
+ }
+
+ set(key: string, value: any) {
+ this.session.set(key, value);
+ }
+
+ commit() {
+ return this.sessionStorage.commitSession(this.session);
+ }
+}
diff --git a/packages/e2e-tests/test-applications/remix-hydrogen/tests/behaviour-client.test.ts b/packages/e2e-tests/test-applications/remix-hydrogen/tests/behaviour-client.test.ts
new file mode 100644
index 000000000000..8e2f78a1fb67
--- /dev/null
+++ b/packages/e2e-tests/test-applications/remix-hydrogen/tests/behaviour-client.test.ts
@@ -0,0 +1,208 @@
+import { test, expect } from '@playwright/test';
+import axios, { AxiosError } from 'axios';
+
+const EVENT_POLLING_TIMEOUT = 60_000;
+
+const authToken = process.env.E2E_TEST_AUTH_TOKEN;
+const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG;
+const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT;
+
+test('Sends a client-side exception to Sentry', async ({ page }) => {
+ await page.goto('/');
+
+ const exceptionButton = page.locator('id=exception-button');
+ await exceptionButton.click();
+
+ const exceptionIdHandle = await page.waitForFunction(() => window.capturedExceptionId);
+ const exceptionEventId = await exceptionIdHandle.jsonValue();
+
+ console.log(`Polling for error eventId: ${exceptionEventId}`);
+
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+});
+
+test('Sends a pageload transaction to Sentry', async ({ page }) => {
+ await page.goto('/');
+
+ const recordedTransactionsHandle = await page.waitForFunction(() => {
+ if (window.recordedTransactions && window.recordedTransactions?.length >= 1) {
+ return window.recordedTransactions;
+ } else {
+ return undefined;
+ }
+ });
+ const recordedTransactionEventIds = await recordedTransactionsHandle.jsonValue();
+
+ if (recordedTransactionEventIds === undefined) {
+ throw new Error("Application didn't record any transaction event IDs.");
+ }
+
+ let hadPageLoadTransaction = false;
+
+ console.log(`Polling for transaction eventIds: ${JSON.stringify(recordedTransactionEventIds)}`);
+
+ await Promise.all(
+ recordedTransactionEventIds.map(async transactionEventId => {
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+
+ if (response.data.contexts.trace.op === 'pageload') {
+ hadPageLoadTransaction = true;
+ }
+
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+ }),
+ );
+
+ expect(hadPageLoadTransaction).toBe(true);
+});
+
+test('Sends a navigation transaction to Sentry', async ({ page }) => {
+ await page.goto('/');
+
+ // Give pageload transaction time to finish
+ await page.waitForTimeout(4000);
+
+ const linkElement = page.locator('id=navigation');
+ await linkElement.click();
+
+ const recordedTransactionsHandle = await page.waitForFunction(() => {
+ if (window.recordedTransactions && window.recordedTransactions?.length >= 2) {
+ return window.recordedTransactions;
+ } else {
+ return undefined;
+ }
+ });
+ const recordedTransactionEventIds = await recordedTransactionsHandle.jsonValue();
+
+ if (recordedTransactionEventIds === undefined) {
+ throw new Error("Application didn't record any transaction event IDs.");
+ }
+
+ let hadPageNavigationTransaction = false;
+
+ console.log(`Polling for transaction eventIds: ${JSON.stringify(recordedTransactionEventIds)}`);
+
+ await Promise.all(
+ recordedTransactionEventIds.map(async transactionEventId => {
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+
+ if (response.data.contexts.trace.op === 'navigation') {
+ hadPageNavigationTransaction = true;
+ }
+
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+ }),
+ );
+
+ expect(hadPageNavigationTransaction).toBe(true);
+});
+
+test('Sends a client-side ErrorBoundary exception to Sentry', async ({ page }) => {
+ await page.goto('/client-error');
+
+ const exceptionIdHandle = await page.waitForSelector('#event-id');
+ const exceptionEventId = await exceptionIdHandle.textContent();
+
+ console.log(`Polling for error eventId: ${exceptionEventId}`);
+
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+});
diff --git a/packages/e2e-tests/test-applications/remix-hydrogen/tests/behaviour-server.test.ts b/packages/e2e-tests/test-applications/remix-hydrogen/tests/behaviour-server.test.ts
new file mode 100644
index 000000000000..7b0ff8a67374
--- /dev/null
+++ b/packages/e2e-tests/test-applications/remix-hydrogen/tests/behaviour-server.test.ts
@@ -0,0 +1,44 @@
+import { test, expect } from '@playwright/test';
+import axios, { AxiosError } from 'axios';
+
+const EVENT_POLLING_TIMEOUT = 60_000;
+
+const authToken = process.env.E2E_TEST_AUTH_TOKEN;
+const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG;
+const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT;
+
+test('Sends a server-side captured error to Sentry', async ({ page }) => {
+ await page.goto('/server-error');
+
+ const exceptionIdHandle = await page.waitForSelector('#event-id');
+ const exceptionEventId = await exceptionIdHandle.textContent();
+
+ console.log(`Polling for error eventId: ${exceptionEventId}`);
+
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+});
diff --git a/packages/e2e-tests/test-applications/remix-hydrogen/tsconfig.json b/packages/e2e-tests/test-applications/remix-hydrogen/tsconfig.json
new file mode 100644
index 000000000000..dcd7c7237a90
--- /dev/null
+++ b/packages/e2e-tests/test-applications/remix-hydrogen/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"],
+ "compilerOptions": {
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "jsx": "react-jsx",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "module": "ES2022",
+ "target": "ES2022",
+ "strict": true,
+ "allowJs": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "baseUrl": ".",
+ "types": ["@shopify/oxygen-workers-types"],
+ "paths": {
+ "~/*": ["app/*"]
+ },
+ "noEmit": true
+ }
+}
diff --git a/packages/remix/package.json b/packages/remix/package.json
index 19eec703b3e9..32ca8615a2be 100644
--- a/packages/remix/package.json
+++ b/packages/remix/package.json
@@ -34,11 +34,13 @@
"@sentry/types": "7.81.1",
"@sentry/utils": "7.81.1",
"glob": "^10.3.4",
+ "is-ip": "^3.1.0",
+ "tslib": "^2.4.1 || ^1.9.3",
"yargs": "^17.6.0"
},
"devDependencies": {
- "@remix-run/node": "^1.4.3",
- "@remix-run/react": "^1.4.3",
+ "@remix-run/node": "^1.19.3",
+ "@remix-run/react": "^1.19.3",
"@types/express": "^4.17.14"
},
"peerDependencies": {
diff --git a/packages/remix/src/client/errors.tsx b/packages/remix/src/client/errors.tsx
index a6afefbc0ef7..a36d0050777d 100644
--- a/packages/remix/src/client/errors.tsx
+++ b/packages/remix/src/client/errors.tsx
@@ -1,6 +1,7 @@
import { captureException } from '@sentry/core';
-import { isNodeEnv, isString } from '@sentry/utils';
+import { isString } from '@sentry/utils';
+import { isBrowser } from '../utils/futureFlags';
import { isRouteErrorResponse } from '../utils/vendor/response';
import type { ErrorResponse } from '../utils/vendor/types';
@@ -12,7 +13,7 @@ import type { ErrorResponse } from '../utils/vendor/types';
*/
export function captureRemixErrorBoundaryError(error: unknown): string | undefined {
let eventId: string | undefined;
- const isClientSideRuntimeError = !isNodeEnv() && error instanceof Error;
+ const isClientSideRuntimeError = isBrowser() && error instanceof Error;
const isRemixErrorResponse = isRouteErrorResponse(error);
// Server-side errors apart from `ErrorResponse`s also appear here without their stacktraces.
// So, we only capture:
diff --git a/packages/remix/src/client/performance.tsx b/packages/remix/src/client/performance.tsx
index 97c455a80a94..6fa4e4393a42 100644
--- a/packages/remix/src/client/performance.tsx
+++ b/packages/remix/src/client/performance.tsx
@@ -1,10 +1,10 @@
import type { ErrorBoundaryProps } from '@sentry/react';
import { WINDOW, withErrorBoundary } from '@sentry/react';
import type { Transaction, TransactionContext } from '@sentry/types';
-import { isNodeEnv, logger } from '@sentry/utils';
+import { logger } from '@sentry/utils';
import * as React from 'react';
-import { getFutureFlagsBrowser, readRemixVersionFromLoader } from '../utils/futureFlags';
+import { getFutureFlagsBrowser, isBrowser, readRemixVersionFromLoader } from '../utils/futureFlags';
const DEFAULT_TAGS = {
'routing.instrumentation': 'remix-router',
@@ -109,7 +109,7 @@ export function withSentry, R extends React.Co
// Early return when any of the required functions is not available.
if (!_useEffect || !_useLocation || !_useMatches || !_customStartTransaction) {
__DEBUG_BUILD__ &&
- !isNodeEnv() &&
+ isBrowser() &&
logger.warn('Remix SDK was unable to wrap your root because of one or more missing parameters.');
// @ts-expect-error Setting more specific React Component typing for `R` generic above
diff --git a/packages/remix/src/index.client.tsx b/packages/remix/src/index.client.tsx
index 64951a3f10cd..fa9f596f3db6 100644
--- a/packages/remix/src/index.client.tsx
+++ b/packages/remix/src/index.client.tsx
@@ -1,11 +1,25 @@
/* eslint-disable import/export */
-import { configureScope, init as reactInit } from '@sentry/react';
+import { init as reactInit } from '@sentry/react';
import { buildMetadata } from './utils/metadata';
import type { RemixOptions } from './utils/remixOptions';
export { remixRouterInstrumentation, withSentry } from './client/performance';
export { captureRemixErrorBoundaryError } from './client/errors';
export * from '@sentry/react';
+import type { ServerRuntimeClientOptions } from '@sentry/core';
+import { configureScope, getCurrentHub, getIntegrationsToSetup, initAndBind, ServerRuntimeClient } from '@sentry/core';
+import { createStackParser, logger, nodeStackLineParser, stackParserFromStackParserOptions } from '@sentry/utils';
+
+import { makeWorkerTransport } from './worker/transport';
+
+export { captureRemixServerException } from './utils/instrumentServer';
+export { ErrorBoundary, withErrorBoundary } from '@sentry/react';
+const nodeStackParser = createStackParser(nodeStackLineParser());
+
+function sdkAlreadyInitialized(): boolean {
+ const hub = getCurrentHub();
+ return !!hub.getClient();
+}
export function init(options: RemixOptions): void {
buildMetadata(options, ['remix', 'react']);
@@ -17,3 +31,27 @@ export function init(options: RemixOptions): void {
scope.setTag('runtime', 'browser');
});
}
+
+/** Initializes Sentry Remix SDK on Worker Environments. */
+export function workerInit(options: RemixOptions): void {
+ buildMetadata(options, ['remix', 'worker']);
+
+ if (sdkAlreadyInitialized()) {
+ __DEBUG_BUILD__ && logger.log('SDK already initialized');
+
+ return;
+ }
+
+ const clientOptions: ServerRuntimeClientOptions = {
+ ...options,
+ stackParser: stackParserFromStackParserOptions(options.stackParser || nodeStackParser),
+ integrations: getIntegrationsToSetup(options),
+ transport: options.transport || makeWorkerTransport,
+ };
+
+ initAndBind(ServerRuntimeClient, clientOptions);
+
+ configureScope(scope => {
+ scope.setTag('runtime', 'worker');
+ });
+}
diff --git a/packages/remix/src/utils/futureFlags.ts b/packages/remix/src/utils/futureFlags.ts
index 03d03270a73e..91afbf658ea3 100644
--- a/packages/remix/src/utils/futureFlags.ts
+++ b/packages/remix/src/utils/futureFlags.ts
@@ -1,4 +1,4 @@
-import { GLOBAL_OBJ } from '@sentry/utils';
+import { GLOBAL_OBJ, isNodeEnv } from '@sentry/utils';
import type { FutureConfig, ServerBuild } from './vendor/types';
@@ -65,3 +65,15 @@ export function readRemixVersionFromLoader(): number | undefined {
return window.__remixContext?.state?.loaderData?.root?.remixVersion;
}
+
+/**
+ * Check if we are in the browser
+ * Checking the existence of document instead of window
+ * See:https://remix.run/docs/en/1.19.3/pages/gotchas#typeof-window-checks
+ *
+ * @returns True if we are in the browser
+ */
+export function isBrowser(): boolean {
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-restricted-globals
+ return typeof document !== 'undefined' && !isNodeEnv();
+}
diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts
index 935e24124b49..c027fd4986f6 100644
--- a/packages/remix/src/utils/instrumentServer.ts
+++ b/packages/remix/src/utils/instrumentServer.ts
@@ -1,19 +1,23 @@
/* eslint-disable max-lines */
-import { getActiveTransaction, hasTracingEnabled, runWithAsyncContext } from '@sentry/core';
-import type { Hub } from '@sentry/node';
-import { captureException, getCurrentHub } from '@sentry/node';
+import type { Hub } from '@sentry/core';
+import {
+ captureException,
+ getActiveTransaction,
+ getCurrentHub,
+ hasTracingEnabled,
+ runWithAsyncContext,
+} from '@sentry/core';
import type { Transaction, TransactionSource, WrappedFunction } from '@sentry/types';
import {
addExceptionMechanism,
dynamicSamplingContextToSentryBaggageHeader,
fill,
- isNodeEnv,
loadModule,
logger,
tracingContextFromHeaders,
} from '@sentry/utils';
-import { getFutureFlagsServer, getRemixVersionFromBuild } from './futureFlags';
+import { getFutureFlagsServer, getRemixVersionFromBuild, isBrowser } from './futureFlags';
import {
extractData,
getRequestMatch,
@@ -236,7 +240,7 @@ function getTraceAndBaggage(): { sentryTrace?: string; sentryBaggage?: string }
const transaction = getActiveTransaction();
const currentScope = getCurrentHub().getScope();
- if (isNodeEnv() && hasTracingEnabled()) {
+ if (!isBrowser() && hasTracingEnabled()) {
const span = currentScope.getSpan();
if (span && transaction) {
diff --git a/packages/remix/src/utils/vendor/getIpAddress.ts b/packages/remix/src/utils/vendor/getIpAddress.ts
index d63e31779aac..52fef0db1f7f 100644
--- a/packages/remix/src/utils/vendor/getIpAddress.ts
+++ b/packages/remix/src/utils/vendor/getIpAddress.ts
@@ -23,8 +23,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
-import { isIP } from 'net';
-
+import isIP from 'is-ip';
/**
* Get the IP address of the client sending a request.
*
diff --git a/packages/remix/src/utils/vendor/types.ts b/packages/remix/src/utils/vendor/types.ts
index faaa7e5f6f60..da5c2a19b595 100644
--- a/packages/remix/src/utils/vendor/types.ts
+++ b/packages/remix/src/utils/vendor/types.ts
@@ -202,7 +202,7 @@ export interface DataFunction {
}
export interface ReactRouterDomPkg {
- matchRoutes: (routes: ServerRoute[], pathname: string) => RouteMatch[] | null;
+ matchRoutes: (routes: any[], pathname: string) => RouteMatch[] | null;
}
// Taken from Remix Implementation
diff --git a/packages/remix/src/worker/transport.ts b/packages/remix/src/worker/transport.ts
new file mode 100644
index 000000000000..810a828214d3
--- /dev/null
+++ b/packages/remix/src/worker/transport.ts
@@ -0,0 +1,41 @@
+import { createTransport } from '@sentry/core';
+import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types';
+
+export type WorkersTransportOptions = BaseTransportOptions & {
+ headers?: Record;
+ context?: Record;
+ fetcher?: typeof fetch;
+};
+
+/**
+ * Creates a Transport that uses the Cloudflare Workers' or Shopify Oxygen's fetch API to send events to Sentry.
+ */
+export function makeWorkerTransport(options: WorkersTransportOptions): Transport {
+ function makeRequest(request: TransportRequest): PromiseLike {
+ const requestOptions: RequestInit = {
+ body: request.body,
+ method: 'POST',
+ headers: options.headers,
+ };
+
+ const fetchRequest = fetch(options.url, requestOptions).then(response => {
+ return {
+ statusCode: response.status,
+ headers: {
+ 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),
+ 'retry-after': response.headers.get('Retry-After'),
+ },
+ };
+ });
+
+ // If we're in a Cloudflare Worker, wait for the fetch to complete
+ // before returning. This ensures that the Worker doesn't shut down
+ if (options.context && options.context.waitUntil) {
+ options.context.waitUntil(fetchRequest);
+ }
+
+ return fetchRequest;
+ }
+
+ return createTransport(options, makeRequest);
+}
diff --git a/yarn.lock b/yarn.lock
index d32acb0b89ae..d1e6f43f1900 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4740,85 +4740,91 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
-"@remix-run/node@^1.4.3":
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/@remix-run/node/-/node-1.5.1.tgz#1c367d4035baaef8f0ea66962a826456d62f0030"
- integrity sha512-yl4bd1nl7MiJp4tI3+4ygObeMU3txM4Uo09IdHLRa4NMdBQnacUJ47kqCahny01MerC2JL2d9NPjdVPwRCRZvQ==
- dependencies:
- "@remix-run/server-runtime" "1.5.1"
- "@remix-run/web-fetch" "^4.1.3"
- "@remix-run/web-file" "^3.0.2"
- "@remix-run/web-stream" "^1.0.3"
+"@remix-run/node@^1.19.3":
+ version "1.19.3"
+ resolved "https://registry.yarnpkg.com/@remix-run/node/-/node-1.19.3.tgz#d27e2f742fc45379525cb3fca466a883ca06d6c9"
+ integrity sha512-z5qrVL65xLXIUpU4mkR4MKlMeKARLepgHAk4W5YY3IBXOreRqOGUC70POViYmY7x38c2Ia1NwqL80H+0h7jbMw==
+ dependencies:
+ "@remix-run/server-runtime" "1.19.3"
+ "@remix-run/web-fetch" "^4.3.6"
+ "@remix-run/web-file" "^3.0.3"
+ "@remix-run/web-stream" "^1.0.4"
"@web3-storage/multipart-parser" "^1.0.0"
abort-controller "^3.0.0"
cookie-signature "^1.1.0"
source-map-support "^0.5.21"
stream-slice "^0.1.2"
-"@remix-run/react@^1.4.3":
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/@remix-run/react/-/react-1.5.1.tgz#372e5e80f3f10a638b0567c4e03307dfb0a28dc0"
- integrity sha512-p4t6tC/WyPeLW7DO4g7ZSyH9EpWO37c4wD2np3rDwtv3WtsTZ70bU/+NOWE9nv74mH8i1C50eJ3/OR+8Ll8UbA==
+"@remix-run/react@^1.19.3":
+ version "1.19.3"
+ resolved "https://registry.yarnpkg.com/@remix-run/react/-/react-1.19.3.tgz#00efcc583bf05b434566e56381d51df86575d8b0"
+ integrity sha512-iP37MZ+oG1n4kv4rX77pKT/knra51lNwKo5tinPPF0SuNJhF3+XjWo5nwEjvisKTXLZ/OHeicinhgX2JHHdDvA==
dependencies:
- history "^5.3.0"
- react-router-dom "^6.2.2"
+ "@remix-run/router" "1.7.2"
+ react-router-dom "6.14.2"
"@remix-run/router@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.2.tgz#1c17eadb2fa77f80a796ad5ea9bf108e6993ef06"
integrity sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==
-"@remix-run/server-runtime@1.5.1":
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/@remix-run/server-runtime/-/server-runtime-1.5.1.tgz#5272b01e6dce109dc10bd68447ceae2d039315b2"
- integrity sha512-FQbCCdW+qzE3wpoCwUKdwcL8yZVYNPiyHS9JS/6r6qmd/yvZfbj44E48wEQ6trbWE2TUiEh/EQqNMyrZWEs4bw==
+"@remix-run/router@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.2.tgz#cba1cf0a04bc04cb66027c51fa600e9cbc388bc8"
+ integrity sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==
+
+"@remix-run/server-runtime@1.19.3":
+ version "1.19.3"
+ resolved "https://registry.yarnpkg.com/@remix-run/server-runtime/-/server-runtime-1.19.3.tgz#206b55337c266c5bc254878f8ff3cd5677cc60fb"
+ integrity sha512-KzQ+htUsKqpBgKE2tWo7kIIGy3MyHP58Io/itUPvV+weDjApwr9tQr9PZDPA3yAY6rAzLax7BU0NMSYCXWFY5A==
dependencies:
- "@types/cookie" "^0.4.0"
+ "@remix-run/router" "1.7.2"
+ "@types/cookie" "^0.4.1"
"@web3-storage/multipart-parser" "^1.0.0"
cookie "^0.4.1"
- jsesc "^3.0.1"
- react-router-dom "^6.2.2"
set-cookie-parser "^2.4.8"
source-map "^0.7.3"
-"@remix-run/web-blob@^3.0.3", "@remix-run/web-blob@^3.0.4":
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/@remix-run/web-blob/-/web-blob-3.0.4.tgz#99c67b9d0fb641bd0c07d267fd218ae5aa4ae5ed"
- integrity sha512-AfegzZvSSDc+LwnXV+SwROTrDtoLiPxeFW+jxgvtDAnkuCX1rrzmVJ6CzqZ1Ai0bVfmJadkG5GxtAfYclpPmgw==
+"@remix-run/web-blob@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@remix-run/web-blob/-/web-blob-3.1.0.tgz#e0c669934c1eb6028960047e57a13ed38bbfb434"
+ integrity sha512-owGzFLbqPH9PlKb8KvpNJ0NO74HWE2euAn61eEiyCXX/oteoVzTVSN8mpLgDjaxBf2btj5/nUllSUgpyd6IH6g==
dependencies:
- "@remix-run/web-stream" "^1.0.0"
+ "@remix-run/web-stream" "^1.1.0"
web-encoding "1.1.5"
-"@remix-run/web-fetch@^4.1.3":
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/@remix-run/web-fetch/-/web-fetch-4.1.3.tgz#8ad3077c1b5bd9fe2a8813d0ad3c84970a495c04"
- integrity sha512-D3KXAEkzhR248mu7wCHReQrMrIo3Y9pDDa7TrlISnsOEvqkfWkJJF+PQWmOIKpOSHAhDg7TCb2tzvW8lc/MfHw==
+"@remix-run/web-fetch@^4.3.6":
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/@remix-run/web-fetch/-/web-fetch-4.4.1.tgz#1ea34e6f1c660a52e7582007917a552f0efdc58b"
+ integrity sha512-xMceEGn2kvfeWS91nHSOhEQHPGgjFnmDVpWFZrbWPVdiTByMZIn421/tdSF6Kd1RsNsY+5Iwt3JFEKZHAcMQHw==
dependencies:
- "@remix-run/web-blob" "^3.0.4"
- "@remix-run/web-form-data" "^3.0.2"
- "@remix-run/web-stream" "^1.0.3"
+ "@remix-run/web-blob" "^3.1.0"
+ "@remix-run/web-file" "^3.1.0"
+ "@remix-run/web-form-data" "^3.1.0"
+ "@remix-run/web-stream" "^1.1.0"
"@web3-storage/multipart-parser" "^1.0.0"
+ abort-controller "^3.0.0"
data-uri-to-buffer "^3.0.1"
mrmime "^1.0.0"
-"@remix-run/web-file@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@remix-run/web-file/-/web-file-3.0.2.tgz#1a6cc0900a1310ede4bc96abad77ac6eb27a2131"
- integrity sha512-eFC93Onh/rZ5kUNpCQersmBtxedGpaXK2/gsUl49BYSGK/DvuPu3l06vmquEDdcPaEuXcsdGP0L7zrmUqrqo4A==
+"@remix-run/web-file@^3.0.3", "@remix-run/web-file@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@remix-run/web-file/-/web-file-3.1.0.tgz#07219021a2910e90231bc30ca1ce693d0e9d3825"
+ integrity sha512-dW2MNGwoiEYhlspOAXFBasmLeYshyAyhIdrlXBi06Duex5tDr3ut2LFKVj7tyHLmn8nnNwFf1BjNbkQpygC2aQ==
dependencies:
- "@remix-run/web-blob" "^3.0.3"
+ "@remix-run/web-blob" "^3.1.0"
-"@remix-run/web-form-data@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@remix-run/web-form-data/-/web-form-data-3.0.2.tgz#733a4c8f8176523b7b60a8bd0dc6704fd4d498f3"
- integrity sha512-F8tm3iB1sPxMpysK6Js7lV3gvLfTNKGmIW38t/e6dtPEB5L1WdbRG1cmLyhsonFc7rT1x1JKdz+2jCtoSdnIUw==
+"@remix-run/web-form-data@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@remix-run/web-form-data/-/web-form-data-3.1.0.tgz#47f9ad8ce8bf1c39ed83eab31e53967fe8e3df6a"
+ integrity sha512-NdeohLMdrb+pHxMQ/Geuzdp0eqPbea+Ieo8M8Jx2lGC6TBHsgHzYcBvr0LyPdPVycNRDEpWpiDdCOdCryo3f9A==
dependencies:
web-encoding "1.1.5"
-"@remix-run/web-stream@^1.0.0", "@remix-run/web-stream@^1.0.3":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@remix-run/web-stream/-/web-stream-1.0.3.tgz#3284a6a45675d1455c4d9c8f31b89225c9006438"
- integrity sha512-wlezlJaA5NF6SsNMiwQnnAW6tnPzQ5I8qk0Y0pSohm0eHKa2FQ1QhEKLVVcDDu02TmkfHgnux0igNfeYhDOXiA==
+"@remix-run/web-stream@^1.0.4", "@remix-run/web-stream@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@remix-run/web-stream/-/web-stream-1.1.0.tgz#b93a8f806c2c22204930837c44d81fdedfde079f"
+ integrity sha512-KRJtwrjRV5Bb+pM7zxcTJkhIqWWSy+MYsIxHK+0m5atcznsf15YwUBWHWulZerV2+vvHH1Lp1DD7pw6qKW8SgA==
dependencies:
web-streams-polyfill "^3.1.1"
@@ -5559,7 +5565,7 @@
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.2.tgz#9bf9d62c838c85a07c92fdf2334c2c14fd9c59a9"
integrity sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==
-"@types/cookie@^0.4.0", "@types/cookie@^0.4.1":
+"@types/cookie@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
@@ -16806,7 +16812,7 @@ history@^4.6.0, history@^4.9.0:
tiny-warning "^1.0.0"
value-equal "^1.0.1"
-history@^5.2.0, history@^5.3.0:
+history@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
@@ -17551,6 +17557,11 @@ ip-regex@^2.1.0:
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+ip-regex@^4.0.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5"
+ integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==
+
ip@1.1.5, ip@^1.1.0, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@@ -17852,6 +17863,13 @@ is-interactive@^2.0.0:
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90"
integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==
+is-ip@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8"
+ integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==
+ dependencies:
+ ip-regex "^4.0.0"
+
is-lambda@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
@@ -19010,11 +19028,6 @@ jsesc@^2.5.1:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
-jsesc@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
- integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
-
jsesc@~0.3.x:
version "0.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.3.0.tgz#1bf5ee63b4539fe2e26d0c1e99c240b97a457972"
@@ -25725,21 +25738,27 @@ react-refresh@0.8.3:
dependencies:
"@remix-run/router" "1.0.2"
-"react-router-6@npm:react-router@6.3.0", react-router@6.3.0:
- name react-router-6
+"react-router-6@npm:react-router@6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
dependencies:
history "^5.2.0"
-react-router-dom@^6.2.2:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d"
- integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==
+react-router-dom@6.14.2:
+ version "6.14.2"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.2.tgz#88f520118b91aa60233bd08dbd3fdcaea3a68488"
+ integrity sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==
dependencies:
- history "^5.2.0"
- react-router "6.3.0"
+ "@remix-run/router" "1.7.2"
+ react-router "6.14.2"
+
+react-router@6.14.2:
+ version "6.14.2"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.2.tgz#1f60994d8c369de7b8ba7a78d8f7ec23df76b300"
+ integrity sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==
+ dependencies:
+ "@remix-run/router" "1.7.2"
react@^18.0.0:
version "18.0.0"
@@ -29722,6 +29741,11 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338"
integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==
+"tslib@^2.4.1 || ^1.9.3":
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+ integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
tsutils@^3.17.1, tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"