Skip to content

chore(remix): Add license text to vendored files #7629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 3 additions & 106 deletions packages/remix/src/utils/instrumentServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,16 @@ import type {
ReactRouterDomPkg,
RemixRequest,
RequestHandler,
RouteMatch,
ServerBuild,
ServerRoute,
ServerRouteManifest,
} from './types';
import { extractData, getRequestMatch, isResponse, json, matchServerRoutes } from './vendor/response';
import { normalizeRemixRequest } from './web-fetch';

// Flag to track if the core request handler is instrumented.
export let isRequestHandlerWrapped = false;

// Taken from Remix Implementation
// https://github.com/remix-run/remix/blob/32300ec6e6e8025602cea63e17a2201989589eab/packages/remix-server-runtime/responses.ts#L60-L77
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isResponse(value: any): value is Response {
return (
value != null &&
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
typeof value.status === 'number' &&
typeof value.statusText === 'string' &&
typeof value.headers === 'object' &&
typeof value.body !== 'undefined'
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
);
}

const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
function isRedirectResponse(response: Response): boolean {
return redirectStatusCodes.has(response.status);
Expand All @@ -58,21 +43,6 @@ function isCatchResponse(response: Response): boolean {
return response.headers.get('X-Remix-Catch') != null;
}

// Based on Remix Implementation
// https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/data.ts#L131-L145
async function extractData(response: Response): Promise<unknown> {
const contentType = response.headers.get('Content-Type');

// Cloning the response to avoid consuming the original body stream
const responseClone = response.clone();

if (contentType && /\bapplication\/json\b/.test(contentType)) {
return responseClone.json();
}

return responseClone.text();
}

async function extractResponseError(response: Response): Promise<unknown> {
const responseData = await extractData(response);

Expand All @@ -94,9 +64,11 @@ async function captureRemixServerException(err: Error, name: string, request: Re
return;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
let normalizedRequest: Record<string, unknown> = request as unknown as any;

try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
normalizedRequest = normalizeRemixRequest(request as unknown as any);
} catch (e) {
__DEBUG_BUILD__ && logger.warn('Failed to normalize Remix request');
Expand Down Expand Up @@ -245,29 +217,6 @@ function getTraceAndBaggage(): { sentryTrace?: string; sentryBaggage?: string }
return {};
}

// https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/responses.ts#L1-L4
export type JsonFunction = <Data>(data: Data, init?: number | ResponseInit) => Response;

/**
* This is a shortcut for creating `application/json` responses. Converts `data`
* to JSON and sets the `Content-Type` header.
*
* @see https://remix.run/api/remix#json
*
* https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/responses.ts#L12-L24
*/
const json: JsonFunction = (data, init = {}) => {
const responseInit = typeof init === 'number' ? { status: init } : init;
const headers = new Headers(responseInit.headers);
if (!headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json; charset=utf-8');
}
return new Response(JSON.stringify(data), {
...responseInit,
headers,
});
};

function makeWrappedRootLoader(origLoader: DataFunction): DataFunction {
return async function (this: unknown, args: DataFunctionArgs): Promise<Response | AppData> {
const res = await origLoader.call(this, args);
Expand Down Expand Up @@ -307,31 +256,6 @@ export function createRoutes(manifest: ServerRouteManifest, parentId?: string):
}));
}

// Remix Implementation:
// https://github.com/remix-run/remix/blob/38e127b1d97485900b9c220d93503de0deb1fc81/packages/remix-server-runtime/routeMatching.ts#L12-L24
//
// Changed so that `matchRoutes` function is passed in.
function matchServerRoutes(
routes: ServerRoute[],
pathname: string,
pkg?: ReactRouterDomPkg,
): RouteMatch<ServerRoute>[] | null {
if (!pkg) {
return null;
}

const matches = pkg.matchRoutes(routes, pathname);
if (!matches) {
return null;
}

return matches.map(match => ({
params: match.params,
pathname: match.pathname,
route: match.route,
}));
}

/**
* Starts a new transaction for the given request to be used by different `RequestHandler` wrappers.
*
Expand Down Expand Up @@ -445,33 +369,6 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui
};
}

// https://github.com/remix-run/remix/blob/97999d02493e8114c39d48b76944069d58526e8d/packages/remix-server-runtime/server.ts#L573-L586
function isIndexRequestUrl(url: URL): boolean {
for (const param of url.searchParams.getAll('index')) {
// only use bare `?index` params without a value
// ✅ /foo?index
// ✅ /foo?index&index=123
// ✅ /foo?index=123&index
// ❌ /foo?index=123
if (param === '') {
return true;
}
}

return false;
}

// https://github.com/remix-run/remix/blob/97999d02493e8114c39d48b76944069d58526e8d/packages/remix-server-runtime/server.ts#L588-L596
function getRequestMatch(url: URL, matches: RouteMatch<ServerRoute>[]): RouteMatch<ServerRoute> {
const match = matches.slice(-1)[0];

if (!isIndexRequestUrl(url) && match.route.id.endsWith('/index')) {
return matches.slice(-2)[0];
}

return match;
}

/**
* Instruments `remix` ServerBuild for performance tracing and error tracking.
*/
Expand Down
8 changes: 8 additions & 0 deletions packages/remix/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable max-lines */
/* eslint-disable @typescript-eslint/ban-types */
// Types vendored from @remix-run/[email protected]:
// https://github.com/remix-run/remix/blob/f3691d51027b93caa3fd2cdfe146d7b62a6eb8f2/packages/remix-server-runtime/server.ts
// Copyright 2021 Remix Software Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import type * as Express from 'express';
import type { Agent } from 'https';
import type { ComponentType } from 'react';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
// Vendored / modified from @sergiodxa/remix-utils

// https://github.com/sergiodxa/remix-utils/blob/02af80e12829a53696bfa8f3c2363975cf59f55e/src/server/get-client-ip-address.ts
// MIT License

// Copyright (c) 2021 Sergio Xalambrí

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import { isIP } from 'net';

Expand Down
126 changes: 126 additions & 0 deletions packages/remix/src/utils/vendor/response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright 2021 Remix Software Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import type { ReactRouterDomPkg, RouteMatch, ServerRoute } from '../types';

/**
* Based on Remix Implementation
*
* https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/data.ts#L131-L145
*/
export async function extractData(response: Response): Promise<unknown> {
const contentType = response.headers.get('Content-Type');

// Cloning the response to avoid consuming the original body stream
const responseClone = response.clone();

if (contentType && /\bapplication\/json\b/.test(contentType)) {
return responseClone.json();
}

return responseClone.text();
}

/**
* Taken from Remix Implementation
*
* https://github.com/remix-run/remix/blob/32300ec6e6e8025602cea63e17a2201989589eab/packages/remix-server-runtime/responses.ts#L60-L77
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isResponse(value: any): value is Response {
return (
value != null &&
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
typeof value.status === 'number' &&
typeof value.statusText === 'string' &&
typeof value.headers === 'object' &&
typeof value.body !== 'undefined'
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
);
}

// https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/responses.ts#L1-L4
export type JsonFunction = <Data>(data: Data, init?: number | ResponseInit) => Response;

/**
* This is a shortcut for creating `application/json` responses. Converts `data`
* to JSON and sets the `Content-Type` header.
*
* @see https://remix.run/api/remix#json
*
* https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/responses.ts#L12-L24
*/
export const json: JsonFunction = (data, init = {}) => {
const responseInit = typeof init === 'number' ? { status: init } : init;
const headers = new Headers(responseInit.headers);
if (!headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json; charset=utf-8');
}
return new Response(JSON.stringify(data), {
...responseInit,
headers,
});
};

/**
* Remix Implementation:
* https://github.com/remix-run/remix/blob/38e127b1d97485900b9c220d93503de0deb1fc81/packages/remix-server-runtime/routeMatching.ts#L12-L24
*
* Changed so that `matchRoutes` function is passed in.
*/
export function matchServerRoutes(
routes: ServerRoute[],
pathname: string,
pkg?: ReactRouterDomPkg,
): RouteMatch<ServerRoute>[] | null {
if (!pkg) {
return null;
}

const matches = pkg.matchRoutes(routes, pathname);
if (!matches) {
return null;
}

return matches.map(match => ({
params: match.params,
pathname: match.pathname,
route: match.route,
}));
}

/**
* https://github.com/remix-run/remix/blob/97999d02493e8114c39d48b76944069d58526e8d/packages/remix-server-runtime/server.ts#L573-L586
*/
export function isIndexRequestUrl(url: URL): boolean {
for (const param of url.searchParams.getAll('index')) {
// only use bare `?index` params without a value
// ✅ /foo?index
// ✅ /foo?index&index=123
// ✅ /foo?index=123&index
// ❌ /foo?index=123
if (param === '') {
return true;
}
}

return false;
}

/**
* https://github.com/remix-run/remix/blob/97999d02493e8114c39d48b76944069d58526e8d/packages/remix-server-runtime/server.ts#L588-L596
*/
export function getRequestMatch(url: URL, matches: RouteMatch<ServerRoute>[]): RouteMatch<ServerRoute> {
const match = matches.slice(-1)[0];

if (!isIndexRequestUrl(url) && match.route.id.endsWith('/index')) {
return matches.slice(-2)[0];
}

return match;
}
28 changes: 26 additions & 2 deletions packages/remix/src/utils/web-fetch.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
// Based on Remix's implementation of Fetch API
// https://github.com/remix-run/web-std-io/tree/main/packages/fetch
// https://github.com/remix-run/web-std-io/blob/d2a003fe92096aaf97ab2a618b74875ccaadc280/packages/fetch/
// The MIT License (MIT)

// Copyright (c) 2016 - 2020 Node Fetch Team

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import { logger } from '@sentry/utils';

import { getClientIPAddress } from './getIpAddress';
import type { RemixRequest } from './types';
import { getClientIPAddress } from './vendor/getIpAddress';

/*
* Symbol extractor utility to be able to access internal fields of Remix requests.
Expand All @@ -17,7 +38,9 @@ const getInternalSymbols = (
} => {
const symbols = Object.getOwnPropertySymbols(request);
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bodyInternalsSymbol: symbols.find(symbol => symbol.toString().includes('Body internals')) as any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
requestInternalsSymbol: symbols.find(symbol => symbol.toString().includes('Request internals')) as any,
};
};
Expand All @@ -42,6 +65,7 @@ export const getSearch = (parsedURL: URL): string => {
* Vendored / modified from:
* https://github.com/remix-run/web-std-io/blob/f715b354c8c5b8edc550c5442dec5712705e25e7/packages/fetch/src/request.js#L259
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const normalizeRemixRequest = (request: RemixRequest): Record<string, any> => {
const { requestInternalsSymbol, bodyInternalsSymbol } = getInternalSymbols(request);

Expand Down
2 changes: 1 addition & 1 deletion packages/remix/test/utils/getIpAddress.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getClientIPAddress } from '../../src/utils/getIpAddress';
import { getClientIPAddress } from '../../src/utils/vendor/getIpAddress';

class Headers {
private _headers: Record<string, string> = {};
Expand Down