Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@remix-run/server-runtime

Package Overview
Dependencies
Maintainers
2
Versions
1042
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@remix-run/server-runtime - npm Package Compare versions

Comparing version 0.0.0-nightly-d8bcdc1-20230506 to 0.0.0-nightly-d9061d154-20241010

dist/deprecations.d.ts

15

dist/build.d.ts

@@ -1,2 +0,2 @@

import type { DataFunctionArgs } from "./routeModules";
import type { ActionFunctionArgs, LoaderFunctionArgs } from "./routeModules";
import type { AssetsManifest, EntryContext, FutureConfig } from "./entry";

@@ -9,2 +9,3 @@ import type { ServerRouteManifest } from "./routes";

export interface ServerBuild {
mode: string;
entry: {

@@ -15,8 +16,7 @@ module: ServerEntryModule;

assets: AssetsManifest;
basename?: string;
publicPath: string;
assetsBuildDirectory: string;
future: FutureConfig;
dev?: {
websocketPort: number;
};
isSpaMode: boolean;
}

@@ -27,4 +27,7 @@ export interface HandleDocumentRequestFunction {

export interface HandleDataRequestFunction {
(response: Response, args: DataFunctionArgs): Promise<Response> | Response;
(response: Response, args: LoaderFunctionArgs | ActionFunctionArgs): Promise<Response> | Response;
}
export interface HandleErrorFunction {
(error: unknown, args: LoaderFunctionArgs | ActionFunctionArgs): void;
}
/**

@@ -37,2 +40,4 @@ * A module that serves as the entry point for a Remix app during server

handleDataRequest?: HandleDataRequestFunction;
handleError?: HandleErrorFunction;
streamTimeout?: number;
}
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -19,2 +19,13 @@ * Copyright (c) Remix Software Inc.

/**
* A HTTP cookie.
*
* A Cookie is a logical container for metadata about a HTTP cookie; its name
* and options. But it doesn't contain a value. Instead, it has `parse()` and
* `serialize()` methods that allow a single instance to be reused for
* parsing/encoding multiple different values.
*
* @see https://remix.run/utils/cookies#cookie-api
*/
/**
* Creates a logical container for managing a browser cookie from the server.

@@ -21,0 +32,0 @@ *

@@ -1,5 +0,7 @@

import type { ActionFunction, DataFunctionArgs, LoaderFunction } from "./routeModules";
import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "./routeModules";
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function.
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/

@@ -11,19 +13,19 @@ export interface AppLoadContext {

* Data for a route that was returned from a `loader()`.
*
* Note: This moves to unknown in ReactRouter and eventually likely in Remix
*/
export type AppData = any;
export declare function callRouteActionRR({ loadContext, action, params, request, routeId, }: {
export type AppData = unknown;
export declare function callRouteAction({ loadContext, action, params, request, routeId, singleFetch, }: {
request: Request;
action: ActionFunction;
params: DataFunctionArgs["params"];
params: ActionFunctionArgs["params"];
loadContext: AppLoadContext;
routeId: string;
}): Promise<Response>;
export declare function callRouteLoaderRR({ loadContext, loader, params, request, routeId, }: {
singleFetch: boolean;
}): Promise<{} | Response | null>;
export declare function callRouteLoader({ loadContext, loader, params, request, routeId, singleFetch, }: {
request: Request;
loader: LoaderFunction;
params: DataFunctionArgs["params"];
params: LoaderFunctionArgs["params"];
loadContext: AppLoadContext;
routeId: string;
}): Promise<import("@remix-run/router").UNSAFE_DeferredData | Response>;
singleFetch: boolean;
}): Promise<{} | Response | null>;
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -17,3 +17,14 @@ * Copyright (c) Remix Software Inc.

async function callRouteActionRR({
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
/**
* Data for a route that was returned from a `loader()`.
*/
async function callRouteAction({
loadContext,

@@ -23,6 +34,7 @@ action,

request,
routeId
routeId,
singleFetch
}) {
let result = await action({
request: stripDataParam(stripIndexParam(request)),
request: singleFetch ? stripRoutesParam(stripIndexParam(request)) : stripDataParam(stripIndexParam(request)),
context: loadContext,

@@ -34,5 +46,10 @@ params

}
// Allow naked object returns when single fetch is enabled
if (singleFetch) {
return result;
}
return responses.isResponse(result) ? result : responses.json(result);
}
async function callRouteLoaderRR({
async function callRouteLoader({
loadContext,

@@ -42,6 +59,7 @@ loader,

request,
routeId
routeId,
singleFetch
}) {
let result = await loader({
request: stripDataParam(stripIndexParam(request)),
request: singleFetch ? stripRoutesParam(stripIndexParam(request)) : stripDataParam(stripIndexParam(request)),
context: loadContext,

@@ -59,2 +77,7 @@ params

}
// Allow naked object returns when single fetch is enabled
if (singleFetch) {
return result;
}
return responses.isResponse(result) ? result : responses.json(result);

@@ -81,3 +104,12 @@ }

}
return new Request(url.href, request);
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}

@@ -87,6 +119,29 @@ function stripDataParam(request) {

url.searchParams.delete("_data");
return new Request(url.href, request);
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}
function stripRoutesParam(request) {
let url = new URL(request.url);
url.searchParams.delete("_routes");
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}
exports.callRouteActionRR = callRouteActionRR;
exports.callRouteLoaderRR = callRouteLoaderRR;
exports.callRouteAction = callRouteAction;
exports.callRouteLoader = callRouteLoader;
import type { ServerBuild } from "./build";
export declare function broadcastDevReady(build: ServerBuild, origin?: string): void;
export declare function broadcastDevReady(build: ServerBuild, origin?: string): Promise<void>;
export declare function logDevReady(build: ServerBuild): void;
type DevServerHooks = {
getCriticalCss?: (build: ServerBuild, pathname: string) => Promise<string | undefined>;
processRequestError?: (error: unknown) => void;
};
export declare function setDevServerHooks(devServerHooks: DevServerHooks): void;
export declare function getDevServerHooks(): DevServerHooks | undefined;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -15,6 +15,8 @@ * Copyright (c) Remix Software Inc.

function broadcastDevReady(build, origin) {
origin ?? (origin = process.env.REMIX_DEV_HTTP_ORIGIN);
async function broadcastDevReady(build, origin) {
origin ??= process.env.REMIX_DEV_ORIGIN;
if (!origin) throw Error("Dev server origin not set");
fetch(`${origin}/ping`, {
let url = new URL(origin);
url.pathname = "ping";
let response = await fetch(url.href, {
method: "POST",

@@ -28,4 +30,9 @@ headers: {

}).catch(error => {
console.error(`Could not reach Remix dev server at ${origin}`);
console.error(`Could not reach Remix dev server at ${url}`);
throw error;
});
if (!response.ok) {
console.error(`Could not reach Remix dev server at ${url} (${response.status})`);
throw Error(await response.text());
}
}

@@ -35,4 +42,15 @@ function logDevReady(build) {

}
const globalDevServerHooksKey = "__remix_devServerHooks";
function setDevServerHooks(devServerHooks) {
// @ts-expect-error
globalThis[globalDevServerHooksKey] = devServerHooks;
}
function getDevServerHooks() {
// @ts-expect-error
return globalThis[globalDevServerHooksKey];
}
exports.broadcastDevReady = broadcastDevReady;
exports.getDevServerHooks = getDevServerHooks;
exports.logDevReady = logDevReady;
exports.setDevServerHooks = setDevServerHooks;
import type { StaticHandlerContext } from "@remix-run/router";
import type { SerializedError } from "./errors";
import type { RouteManifest, ServerRouteManifest, EntryRoute } from "./routes";

@@ -7,22 +8,26 @@ import type { RouteModules, EntryRouteModule } from "./routeModules";

routeModules: RouteModules<EntryRouteModule>;
criticalCss?: string;
serverHandoffString?: string;
serverHandoffStream?: ReadableStream<Uint8Array>;
renderMeta?: {
didRenderScripts?: boolean;
streamCache?: Record<number, Promise<void> & {
result?: {
done: boolean;
value: string;
};
error?: unknown;
}>;
};
staticHandlerContext: StaticHandlerContext;
future: FutureConfig;
isSpaMode: boolean;
serializeError(error: Error): SerializedError;
}
type Dev = {
port?: number;
appServerPort?: number;
remixRequestHandlerPath?: string;
rebuildPollIntervalMs?: number;
};
export interface FutureConfig {
unstable_dev: boolean | Dev;
/** @deprecated Use the `postcss` config option instead */
unstable_postcss: boolean;
/** @deprecated Use the `tailwind` config option instead */
unstable_tailwind: boolean;
v2_errorBoundary: boolean;
v2_meta: boolean;
v2_normalizeFormMethod: boolean;
v2_routeConvention: boolean;
v3_fetcherPersist: boolean;
v3_relativeSplatPath: boolean;
v3_throwAbortReason: boolean;
v3_lazyRouteDiscovery: boolean;
v3_singleFetch: boolean;
}

@@ -40,2 +45,1 @@ export interface AssetsManifest {

export declare function createEntryRouteModules(manifest: ServerRouteManifest): RouteModules<EntryRouteModule>;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -44,11 +44,2 @@ import type { StaticHandlerContext } from "@remix-run/router";

*/
/**
* @deprecated in favor of the `ErrorResponse` class in React Router. Please
* enable the `future.v2_errorBoundary` flag to ease your migration to Remix v2.
*/
export interface ThrownResponse<T = any> {
status: number;
statusText: string;
data: T;
}
export declare function sanitizeError<T = unknown>(error: T, serverMode: ServerMode): T | Error;

@@ -55,0 +46,0 @@ export declare function sanitizeErrors(errors: NonNullable<StaticHandlerContext["errors"]>, serverMode: ServerMode): {};

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -60,7 +60,2 @@ * Copyright (c) Remix Software Inc.

/**
* @deprecated in favor of the `ErrorResponse` class in React Router. Please
* enable the `future.v2_errorBoundary` flag to ease your migration to Remix v2.
*/
function sanitizeError(error, serverMode) {

@@ -109,3 +104,10 @@ if (error instanceof Error && serverMode !== mode.ServerMode.Development) {

stack: sanitized.stack,
__type: "Error"
__type: "Error",
// If this is a subclass (i.e., ReferenceError), send up the type so we
// can re-create the same type during hydration. This will only apply
// in dev mode since all production errors are sanitized to normal
// Error instances
...(sanitized.name !== "Error" ? {
__subType: sanitized.name
} : {})
};

@@ -112,0 +114,0 @@ } else {

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -15,2 +15,13 @@ * Copyright (c) Remix Software Inc.

/**
* A HTTP cookie.
*
* A Cookie is a logical container for metadata about a HTTP cookie; its name
* and options. But it doesn't contain a value. Instead, it has `parse()` and
* `serialize()` methods that allow a single instance to be reused for
* parsing/encoding multiple different values.
*
* @see https://remix.run/utils/cookies#cookie-api
*/
/**
* Creates a logical container for managing a browser cookie from the server.

@@ -17,0 +28,0 @@ *

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -13,3 +13,14 @@ * Copyright (c) Remix Software Inc.

async function callRouteActionRR({
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
/**
* Data for a route that was returned from a `loader()`.
*/
async function callRouteAction({
loadContext,

@@ -19,6 +30,7 @@ action,

request,
routeId
routeId,
singleFetch
}) {
let result = await action({
request: stripDataParam(stripIndexParam(request)),
request: singleFetch ? stripRoutesParam(stripIndexParam(request)) : stripDataParam(stripIndexParam(request)),
context: loadContext,

@@ -30,5 +42,10 @@ params

}
// Allow naked object returns when single fetch is enabled
if (singleFetch) {
return result;
}
return isResponse(result) ? result : json(result);
}
async function callRouteLoaderRR({
async function callRouteLoader({
loadContext,

@@ -38,6 +55,7 @@ loader,

request,
routeId
routeId,
singleFetch
}) {
let result = await loader({
request: stripDataParam(stripIndexParam(request)),
request: singleFetch ? stripRoutesParam(stripIndexParam(request)) : stripDataParam(stripIndexParam(request)),
context: loadContext,

@@ -55,2 +73,7 @@ params

}
// Allow naked object returns when single fetch is enabled
if (singleFetch) {
return result;
}
return isResponse(result) ? result : json(result);

@@ -77,3 +100,12 @@ }

}
return new Request(url.href, request);
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}

@@ -83,5 +115,28 @@ function stripDataParam(request) {

url.searchParams.delete("_data");
return new Request(url.href, request);
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}
function stripRoutesParam(request) {
let url = new URL(request.url);
url.searchParams.delete("_routes");
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}
export { callRouteActionRR, callRouteLoaderRR };
export { callRouteAction, callRouteLoader };
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -11,6 +11,8 @@ * Copyright (c) Remix Software Inc.

*/
function broadcastDevReady(build, origin) {
origin ?? (origin = process.env.REMIX_DEV_HTTP_ORIGIN);
async function broadcastDevReady(build, origin) {
origin ??= process.env.REMIX_DEV_ORIGIN;
if (!origin) throw Error("Dev server origin not set");
fetch(`${origin}/ping`, {
let url = new URL(origin);
url.pathname = "ping";
let response = await fetch(url.href, {
method: "POST",

@@ -24,4 +26,9 @@ headers: {

}).catch(error => {
console.error(`Could not reach Remix dev server at ${origin}`);
console.error(`Could not reach Remix dev server at ${url}`);
throw error;
});
if (!response.ok) {
console.error(`Could not reach Remix dev server at ${url} (${response.status})`);
throw Error(await response.text());
}
}

@@ -31,3 +38,12 @@ function logDevReady(build) {

}
const globalDevServerHooksKey = "__remix_devServerHooks";
function setDevServerHooks(devServerHooks) {
// @ts-expect-error
globalThis[globalDevServerHooksKey] = devServerHooks;
}
function getDevServerHooks() {
// @ts-expect-error
return globalThis[globalDevServerHooksKey];
}
export { broadcastDevReady, logDevReady };
export { broadcastDevReady, getDevServerHooks, logDevReady, setDevServerHooks };
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -56,7 +56,2 @@ * Copyright (c) Remix Software Inc.

/**
* @deprecated in favor of the `ErrorResponse` class in React Router. Please
* enable the `future.v2_errorBoundary` flag to ease your migration to Remix v2.
*/
function sanitizeError(error, serverMode) {

@@ -105,3 +100,10 @@ if (error instanceof Error && serverMode !== ServerMode.Development) {

stack: sanitized.stack,
__type: "Error"
__type: "Error",
// If this is a subclass (i.e., ReferenceError), send up the type so we
// can re-create the same type during hydration. This will only apply
// in dev mode since all production errors are sanitized to normal
// Error instances
...(sanitized.name !== "Error" ? {
__subType: sanitized.name
} : {})
};

@@ -108,0 +110,0 @@ } else {

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -13,2 +13,3 @@ * Copyright (c) Remix Software Inc.

// @ts-ignore
function composeUploadHandlers(...handlers) {

@@ -15,0 +16,0 @@ return async part => {

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -13,6 +13,27 @@ * Copyright (c) Remix Software Inc.

function getDocumentHeadersRR(build, context) {
let matches = context.errors ? context.matches.slice(0, context.matches.findIndex(m => context.errors[m.route.id]) + 1) : context.matches;
return matches.reduce((parentHeaders, match) => {
function getDocumentHeaders(build, context) {
let boundaryIdx = context.errors ? context.matches.findIndex(m => context.errors[m.route.id]) : -1;
let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
let errorHeaders;
if (boundaryIdx >= 0) {
// Look for any errorHeaders from the boundary route down, which can be
// identified by the presence of headers but no data
let {
actionHeaders,
actionData,
loaderHeaders,
loaderData
} = context;
context.matches.slice(boundaryIdx).some(match => {
let id = match.route.id;
if (actionHeaders[id] && (!actionData || actionData[id] === undefined)) {
errorHeaders = actionHeaders[id];
} else if (loaderHeaders[id] && loaderData[id] === undefined) {
errorHeaders = loaderHeaders[id];
}
return errorHeaders != null;
});
}
return matches.reduce((parentHeaders, match, idx) => {
let {
id

@@ -23,10 +44,33 @@ } = match.route;

let actionHeaders = context.actionHeaders[id] || new Headers();
// Only expose errorHeaders to the leaf headers() function to
// avoid duplication via parentHeaders
let includeErrorHeaders = errorHeaders != undefined && idx === matches.length - 1;
// Only prepend cookies from errorHeaders at the leaf renderable route
// when it's not the same as loaderHeaders/actionHeaders to avoid
// duplicate cookies
let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
// Use the parent headers for any route without a `headers` export
if (routeModule.headers == null) {
let headers = new Headers(parentHeaders);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);
prependCookies(loaderHeaders, headers);
return headers;
}
let headers = new Headers(routeModule.headers ? typeof routeModule.headers === "function" ? routeModule.headers({
loaderHeaders,
parentHeaders,
actionHeaders
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : undefined
}) : routeModule.headers : undefined);
// Automatically preserve Set-Cookie headers that were set either by the
// loader or by a parent route.
// Automatically preserve Set-Cookie headers from bubbled responses,
// loaders, errors, and parent routes
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);

@@ -48,2 +92,2 @@ prependCookies(loaderHeaders, headers);

export { getDocumentHeadersRR };
export { getDocumentHeaders };
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -13,3 +13,4 @@ * Copyright (c) Remix Software Inc.

export { composeUploadHandlers as unstable_composeUploadHandlers, parseMultipartFormData as unstable_parseMultipartFormData } from './formData.js';
export { defer, json, redirect } from './responses.js';
export { defer, json, redirect, redirectDocument, replace } from './responses.js';
export { SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, data } from './single-fetch.js';
export { createRequestHandler } from './server.js';

@@ -21,2 +22,2 @@ export { createSession, createSessionStorageFactory, isSession } from './sessions.js';

export { MaxPartSizeExceededError } from './upload/errors.js';
export { broadcastDevReady, logDevReady } from './dev.js';
export { broadcastDevReady, logDevReady, setDevServerHooks as unstable_setDevServerHooks } from './dev.js';
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -14,8 +14,8 @@ * Copyright (c) Remix Software Inc.

*/
let ServerMode;
(function (ServerMode) {
let ServerMode = /*#__PURE__*/function (ServerMode) {
ServerMode["Development"] = "development";
ServerMode["Production"] = "production";
ServerMode["Test"] = "test";
})(ServerMode || (ServerMode = {}));
return ServerMode;
}({});
function isServerMode(value) {

@@ -22,0 +22,0 @@ return value === ServerMode.Development || value === ServerMode.Production || value === ServerMode.Test;

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -11,5 +11,8 @@ * Copyright (c) Remix Software Inc.

*/
import { json as json$1, defer as defer$1, redirect as redirect$1 } from '@remix-run/router';
import { json as json$1, defer as defer$1, redirect as redirect$1, replace as replace$1, redirectDocument as redirectDocument$1 } from '@remix-run/router';
import { serializeError } from './errors.js';
// must be a type since this is a subtype of response
// interfaces must conform to the types they extend
/**

@@ -28,3 +31,3 @@ * This is a shortcut for creating `application/json` responses. Converts `data`

*
* @see https://remix.run/docs/utils/defer
* @see https://remix.run/utils/defer
*/

@@ -43,2 +46,23 @@ const defer = (data, init = {}) => {

};
/**
* A redirect response. Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/utils/redirect
*/
const replace = (url, init = 302) => {
return replace$1(url, init);
};
/**
* A redirect response that will force a document reload to the new location.
* Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/utils/redirect
*/
const redirectDocument = (url, init = 302) => {
return redirectDocument$1(url, init);
};
function isDeferredData(value) {

@@ -112,2 +136,2 @@ let deferred = value;

export { createDeferredReadableStream, defer, isDeferredData, isRedirectResponse, isRedirectStatusCode, isResponse, json, redirect };
export { createDeferredReadableStream, defer, isDeferredData, isRedirectResponse, isRedirectStatusCode, isResponse, json, redirect, redirectDocument, replace };
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -13,4 +13,4 @@ * Copyright (c) Remix Software Inc.

function matchServerRoutes(routes, pathname) {
let matches = matchRoutes(routes, pathname);
function matchServerRoutes(routes, pathname, basename) {
let matches = matchRoutes(routes, pathname, basename);
if (!matches) return null;

@@ -17,0 +17,0 @@ return matches.map(match => ({

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -11,4 +11,8 @@ * Copyright (c) Remix Software Inc.

*/
import { callRouteLoaderRR, callRouteActionRR } from './data.js';
import { callRouteLoader, callRouteAction } from './data.js';
// NOTE: make sure to change the Route in remix-react if you change this
// NOTE: make sure to change the EntryRoute in remix-react if you change this
function groupRoutesByParentId(manifest) {

@@ -39,9 +43,11 @@ let routes = {};

return (routesByParentId[parentId] || []).map(route => {
let hasErrorBoundary = future.v2_errorBoundary === true ? route.id === "root" || route.module.ErrorBoundary != null : route.id === "root" || route.module.CatchBoundary != null || route.module.ErrorBoundary != null;
let commonRoute = {
// Always include root due to default boundaries
hasErrorBoundary,
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
id: route.id,
path: route.path,
loader: route.module.loader ? args => callRouteLoaderRR({
loader: route.module.loader ?
// Need to use RR's version here to permit the optional context even
// though we know it'll always be provided in remix
(args, dataStrategyCtx) => callRouteLoader({
request: args.request,

@@ -51,5 +57,6 @@ params: args.params,

loader: route.module.loader,
routeId: route.id
routeId: route.id,
singleFetch: future.v3_singleFetch === true
}) : undefined,
action: route.module.action ? args => callRouteActionRR({
action: route.module.action ? (args, dataStrategyCtx) => callRouteAction({
request: args.request,

@@ -59,3 +66,4 @@ params: args.params,

action: route.module.action,
routeId: route.id
routeId: route.id,
singleFetch: future.v3_singleFetch === true
}) : undefined,

@@ -62,0 +70,0 @@ handle: route.module.handle

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -11,37 +11,148 @@ * Copyright (c) Remix Software Inc.

*/
import { createStaticHandler, UNSAFE_DEFERRED_SYMBOL, isRouteErrorResponse, getStaticContextFromError } from '@remix-run/router';
import { UNSAFE_DEFERRED_SYMBOL, isRouteErrorResponse, json as json$1, UNSAFE_ErrorResponseImpl, getStaticContextFromError, stripBasename, createStaticHandler } from '@remix-run/router';
import { createEntryRouteModules } from './entry.js';
import { serializeError, sanitizeErrors, serializeErrors } from './errors.js';
import { getDocumentHeadersRR } from './headers.js';
import { getDocumentHeaders } from './headers.js';
import invariant from './invariant.js';
import { isServerMode, ServerMode } from './mode.js';
import { ServerMode, isServerMode } from './mode.js';
import { matchServerRoutes } from './routeMatching.js';
import { createRoutes, createStaticHandlerDataRoutes } from './routes.js';
import { isRedirectResponse, createDeferredReadableStream, isResponse, json } from './responses.js';
import { isRedirectResponse, json, createDeferredReadableStream, isResponse } from './responses.js';
import { createServerHandoffString } from './serverHandoff.js';
import { getDevServerHooks } from './dev.js';
import { getSingleFetchRedirect, encodeViaTurboStream, SINGLE_FETCH_REDIRECT_STATUS, singleFetchAction, singleFetchLoaders, SingleFetchRedirectSymbol } from './single-fetch.js';
import { resourceRouteJsonWarning } from './deprecations.js';
const createRequestHandler = (build, mode) => {
function derive(build, mode) {
var _build$future, _build$future2;
let routes = createRoutes(build.routes);
let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);
let serverMode = isServerMode(mode) ? mode : ServerMode.Production;
let staticHandler = createStaticHandler(dataRoutes);
let staticHandler = createStaticHandler(dataRoutes, {
basename: build.basename,
future: {
v7_relativeSplatPath: ((_build$future = build.future) === null || _build$future === void 0 ? void 0 : _build$future.v3_relativeSplatPath) === true,
v7_throwAbortReason: ((_build$future2 = build.future) === null || _build$future2 === void 0 ? void 0 : _build$future2.v3_throwAbortReason) === true
}
});
let errorHandler = build.entry.module.handleError || ((error, {
request
}) => {
if (serverMode !== ServerMode.Test && !request.signal.aborted) {
console.error(
// @ts-expect-error This is "private" from users but intended for internal use
isRouteErrorResponse(error) && error.error ? error.error : error);
}
});
return {
routes,
dataRoutes,
serverMode,
staticHandler,
errorHandler
};
}
const createRequestHandler = (build, mode) => {
let _build;
let routes;
let serverMode;
let staticHandler;
let errorHandler;
return async function requestHandler(request, loadContext = {}) {
_build = typeof build === "function" ? await build() : build;
mode ??= _build.mode;
if (typeof build === "function") {
let derived = derive(_build, mode);
routes = derived.routes;
serverMode = derived.serverMode;
staticHandler = derived.staticHandler;
errorHandler = derived.errorHandler;
} else if (!routes || !serverMode || !staticHandler || !errorHandler) {
let derived = derive(_build, mode);
routes = derived.routes;
serverMode = derived.serverMode;
staticHandler = derived.staticHandler;
errorHandler = derived.errorHandler;
}
let url = new URL(request.url);
let matches = matchServerRoutes(routes, url.pathname);
let params = {};
let handleError = error => {
if (mode === ServerMode.Development) {
var _getDevServerHooks, _getDevServerHooks$pr;
(_getDevServerHooks = getDevServerHooks()) === null || _getDevServerHooks === void 0 ? void 0 : (_getDevServerHooks$pr = _getDevServerHooks.processRequestError) === null || _getDevServerHooks$pr === void 0 ? void 0 : _getDevServerHooks$pr.call(_getDevServerHooks, error);
}
errorHandler(error, {
context: loadContext,
params,
request
});
};
// Manifest request for fog of war
let manifestUrl = `${_build.basename ?? "/"}/__manifest`.replace(/\/+/g, "/");
if (url.pathname === manifestUrl) {
try {
let res = await handleManifestRequest(_build, routes, url);
return res;
} catch (e) {
handleError(e);
return new Response("Unknown Server Error", {
status: 500
});
}
}
let matches = matchServerRoutes(routes, url.pathname, _build.basename);
if (matches && matches.length > 0) {
Object.assign(params, matches[0].params);
}
let response;
if (url.searchParams.has("_data")) {
if (_build.future.v3_singleFetch) {
handleError(new Error("Warning: Single fetch-enabled apps should not be making ?_data requests, " + "this is likely to break in the future"));
}
let routeId = url.searchParams.get("_data");
response = await handleDataRequestRR(serverMode, staticHandler, routeId, request, loadContext);
if (build.entry.module.handleDataRequest) {
let match = matches.find(match => match.route.id == routeId);
response = await build.entry.module.handleDataRequest(response, {
response = await handleDataRequest(serverMode, _build, staticHandler, routeId, request, loadContext, handleError);
if (_build.entry.module.handleDataRequest) {
response = await _build.entry.module.handleDataRequest(response, {
context: loadContext,
params: match ? match.params : {},
params,
request
});
if (isRedirectResponse(response)) {
response = createRemixRedirectResponse(response, _build.basename);
}
}
} else if (matches && matches[matches.length - 1].route.module.default == null) {
response = await handleResourceRequestRR(serverMode, staticHandler, matches.slice(-1)[0].route.id, request, loadContext);
} else if (_build.future.v3_singleFetch && url.pathname.endsWith(".data")) {
let handlerUrl = new URL(request.url);
handlerUrl.pathname = handlerUrl.pathname.replace(/\.data$/, "").replace(/^\/_root$/, "/");
let singleFetchMatches = matchServerRoutes(routes, handlerUrl.pathname, _build.basename);
response = await handleSingleFetchRequest(serverMode, _build, staticHandler, request, handlerUrl, loadContext, handleError);
if (_build.entry.module.handleDataRequest) {
response = await _build.entry.module.handleDataRequest(response, {
context: loadContext,
params: singleFetchMatches ? singleFetchMatches[0].params : {},
request
});
if (isRedirectResponse(response)) {
let result = getSingleFetchRedirect(response.status, response.headers, _build.basename);
if (request.method === "GET") {
result = {
[SingleFetchRedirectSymbol]: result
};
}
let headers = new Headers(response.headers);
headers.set("Content-Type", "text/x-script");
return new Response(encodeViaTurboStream(result, request.signal, _build.entry.module.streamTimeout, serverMode), {
status: SINGLE_FETCH_REDIRECT_STATUS,
headers
});
}
}
} else if (matches && matches[matches.length - 1].route.module.default == null && matches[matches.length - 1].route.module.ErrorBoundary == null) {
response = await handleResourceRequest(serverMode, _build, staticHandler, matches.slice(-1)[0].route.id, request, loadContext, handleError);
} else {
response = await handleDocumentRequestRR(serverMode, build, staticHandler, request, loadContext);
var _getDevServerHooks2, _getDevServerHooks2$g;
let criticalCss = mode === ServerMode.Development ? await ((_getDevServerHooks2 = getDevServerHooks()) === null || _getDevServerHooks2 === void 0 ? void 0 : (_getDevServerHooks2$g = _getDevServerHooks2.getCriticalCss) === null || _getDevServerHooks2$g === void 0 ? void 0 : _getDevServerHooks2$g.call(_getDevServerHooks2, _build, url.pathname)) : undefined;
response = await handleDocumentRequest(serverMode, _build, staticHandler, request, loadContext, handleError, criticalCss);
}

@@ -58,3 +169,25 @@ if (request.method === "HEAD") {

};
async function handleDataRequestRR(serverMode, staticHandler, routeId, request, loadContext) {
async function handleManifestRequest(build, routes, url) {
let patches = {};
if (url.searchParams.has("p")) {
for (let path of url.searchParams.getAll("p")) {
let matches = matchServerRoutes(routes, path, build.basename);
if (matches) {
for (let match of matches) {
let routeId = match.route.id;
patches[routeId] = build.assets.routes[routeId];
}
}
}
return json(patches, {
headers: {
"Cache-Control": "public, max-age=31536000, immutable"
}
}); // Override the TypedResponse stuff from json()
}
return new Response("Invalid Request", {
status: 400
});
}
async function handleDataRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {

@@ -66,16 +199,3 @@ let response = await staticHandler.queryRoute(request, {

if (isRedirectResponse(response)) {
// We don't have any way to prevent a fetch request from following
// redirects. So we use the `X-Remix-Redirect` header to indicate the
// next URL, and then "follow" the redirect manually on the client.
let headers = new Headers(response.headers);
headers.set("X-Remix-Redirect", headers.get("Location"));
headers.set("X-Remix-Status", response.status);
headers.delete("Location");
if (response.headers.get("Set-Cookie") !== null) {
headers.set("X-Remix-Revalidate", "yes");
}
return new Response(null, {
status: 204,
headers
});
return createRemixRedirectResponse(response, build.basename);
}

@@ -88,16 +208,26 @@ if (UNSAFE_DEFERRED_SYMBOL in response) {

headers.set("Content-Type", "text/remix-deferred");
// Mark successful responses with a header so we can identify in-flight
// network errors that are missing this header
headers.set("X-Remix-Response", "yes");
init.headers = headers;
return new Response(body, init);
}
// Mark all successful responses with a header so we can identify in-flight
// network errors that are missing this header
response = safelySetHeader(response, "X-Remix-Response", "yes");
return response;
} catch (error) {
if (isResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
return error;
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
let status = isRouteErrorResponse(error) ? error.status : 500;
let errorInstance = isRouteErrorResponse(error) && error.error ? error.error : error instanceof Error ? error : new Error("Unexpected Server Error");
logServerErrorIfNotAborted(errorInstance, request, serverMode);
return json(serializeError(errorInstance, serverMode), {
status,
if (isRouteErrorResponse(error)) {
handleError(error);
return errorResponseToJson(error, serverMode);
}
let errorInstance = error instanceof Error || error instanceof DOMException ? error : new Error("Unexpected Server Error");
handleError(errorInstance);
return json$1(serializeError(errorInstance, serverMode), {
status: 500,
headers: {

@@ -109,37 +239,36 @@ "X-Remix-Error": "yes"

}
function findParentBoundary(routes, routeId, error) {
// Fall back to the root route if we don't match any routes, since Remix
// has default error/catch boundary handling. This handles the case where
// react-router doesn't have a matching "root" route to assign the error to
// so it returns context.errors = { __shim-error-route__: ErrorResponse }
let route = routes[routeId] || routes["root"];
// Router-thrown ErrorResponses will have the error instance. User-thrown
// Responses will not have an error. The one exception here is internal 404s
// which we handle the same as user-thrown 404s
let isCatch = isRouteErrorResponse(error) && (!error.error || error.status === 404);
if (isCatch && route.module.CatchBoundary || !isCatch && route.module.ErrorBoundary || !route.parentId) {
return route.id;
async function handleSingleFetchRequest(serverMode, build, staticHandler, request, handlerUrl, loadContext, handleError) {
let {
result,
headers,
status
} = request.method !== "GET" ? await singleFetchAction(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError) : await singleFetchLoaders(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError);
// Mark all successful responses with a header so we can identify in-flight
// network errors that are missing this header
let resultHeaders = new Headers(headers);
resultHeaders.set("X-Remix-Response", "yes");
// 304 responses should not have a body
if (status === 304) {
return new Response(null, {
status: 304,
headers: resultHeaders
});
}
return findParentBoundary(routes, route.parentId, error);
}
// Re-generate a remix-friendly context.errors structure. The Router only
// handles generic errors and does not distinguish error versus catch. We
// may have a thrown response tagged to a route that only exports an
// ErrorBoundary or vice versa. So we adjust here and ensure that
// data-loading errors are properly associated with routes that have the right
// type of boundaries.
function differentiateCatchVersusErrorBoundaries(build, context) {
if (!context.errors) {
return;
}
let errors = {};
for (let routeId of Object.keys(context.errors)) {
let error = context.errors[routeId];
let handlingRouteId = findParentBoundary(build.routes, routeId, error);
errors[handlingRouteId] = error;
}
context.errors = errors;
// We use a less-descriptive `text/x-script` here instead of something like
// `text/x-turbo` to enable compression when deployed via Cloudflare. See:
// - https://github.com/remix-run/remix/issues/9884
// - https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/
resultHeaders.set("Content-Type", "text/x-script");
// Note: Deferred data is already just Promises, so we don't have to mess
// `activeDeferreds` or anything :)
return new Response(encodeViaTurboStream(result, request.signal, build.entry.module.streamTimeout, serverMode), {
status: status || 200,
headers: resultHeaders
});
}
async function handleDocumentRequestRR(serverMode, build, staticHandler, request, loadContext) {
async function handleDocumentRequest(serverMode, build, staticHandler, request, loadContext, handleError, criticalCss) {
let context;

@@ -151,3 +280,3 @@ try {

} catch (error) {
logServerErrorIfNotAborted(error, request, serverMode);
handleError(error);
return new Response(null, {

@@ -160,13 +289,31 @@ status: 500

}
let headers = getDocumentHeaders(build, context);
// 304 responses should not have a body or a content-type
if (context.statusCode === 304) {
return new Response(null, {
status: 304,
headers
});
}
// Sanitize errors outside of development environments
if (context.errors) {
Object.values(context.errors).forEach(err => {
// @ts-expect-error `err.error` is "private" from users but intended for internal use
if (!isRouteErrorResponse(err) || err.error) {
handleError(err);
}
});
context.errors = sanitizeErrors(context.errors, serverMode);
}
// Restructure context.errors to the right Catch/Error Boundary
if (build.future.v2_errorBoundary !== true) {
differentiateCatchVersusErrorBoundaries(build, context);
}
let headers = getDocumentHeadersRR(build, context);
// Server UI state to send to the client.
// - When single fetch is enabled, this is streamed down via `serverHandoffStream`
// - Otherwise it's stringified into `serverHandoffString`
let state = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: serializeErrors(context.errors, serverMode)
};
let entryContext = {

@@ -176,12 +323,19 @@ manifest: build.assets,

staticHandlerContext: context,
criticalCss,
serverHandoffString: createServerHandoffString({
state: {
loaderData: context.loaderData,
actionData: context.actionData,
errors: serializeErrors(context.errors, serverMode)
},
basename: build.basename,
criticalCss,
future: build.future,
dev: build.dev
isSpaMode: build.isSpaMode,
...(!build.future.v3_singleFetch ? {
state
} : null)
}),
future: build.future
...(build.future.v3_singleFetch ? {
serverHandoffStream: encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null),
future: build.future,
isSpaMode: build.isSpaMode,
serializeError: err => serializeError(err, serverMode)
};

@@ -192,4 +346,18 @@ let handleDocumentRequestFunction = build.entry.module.default;

} catch (error) {
handleError(error);
let errorForSecondRender = error;
// If they threw a response, unwrap it into an ErrorResponse like we would
// have for a loader/action
if (isResponse(error)) {
try {
let data = await unwrapResponse(error);
errorForSecondRender = new UNSAFE_ErrorResponseImpl(error.status, error.statusText, data);
} catch (e) {
// If we can't unwrap the response - just leave it as-is
}
}
// Get a new StaticHandlerContext that contains the error at the right boundary
context = getStaticContextFromError(staticHandler.dataRoutes, context, error);
context = getStaticContextFromError(staticHandler.dataRoutes, context, errorForSecondRender);

@@ -201,8 +369,11 @@ // Sanitize errors outside of development environments

// Restructure context.errors to the right Catch/Error Boundary
if (build.future.v2_errorBoundary !== true) {
differentiateCatchVersusErrorBoundaries(build, context);
}
// Update entryContext for the second render pass
// Get a new entryContext for the second render pass
// Server UI state to send to the client.
// - When single fetch is enabled, this is streamed down via `serverHandoffStream`
// - Otherwise it's stringified into `serverHandoffString`
let state = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: serializeErrors(context.errors, serverMode)
};
entryContext = {

@@ -212,9 +383,13 @@ ...entryContext,

serverHandoffString: createServerHandoffString({
state: {
loaderData: context.loaderData,
actionData: context.actionData,
errors: serializeErrors(context.errors, serverMode)
},
future: build.future
})
basename: build.basename,
future: build.future,
isSpaMode: build.isSpaMode,
...(!build.future.v3_singleFetch ? {
state
} : null)
}),
...(build.future.v3_singleFetch ? {
serverHandoffStream: encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null)
};

@@ -224,3 +399,3 @@ try {

} catch (error) {
logServerErrorIfNotAborted(error, request, serverMode);
handleError(error);
return returnLastResortErrorResponse(error, serverMode);

@@ -230,3 +405,3 @@ }

}
async function handleResourceRequestRR(serverMode, staticHandler, routeId, request, loadContext) {
async function handleResourceRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {

@@ -240,3 +415,12 @@ // Note we keep the routeId here to align with the Remix handling of

});
// callRouteLoader/callRouteAction always return responses
if (typeof response === "object" && response !== null) {
invariant(!(UNSAFE_DEFERRED_SYMBOL in response), `You cannot return a \`defer()\` response from a Resource Route. Did you ` + `forget to export a default UI component from the "${routeId}" route?`);
}
if (build.future.v3_singleFetch && !isResponse(response)) {
console.warn(resourceRouteJsonWarning(request.method === "GET" ? "loader" : "action", routeId));
response = json(response);
}
// callRouteLoader/callRouteAction always return responses (w/o single fetch).
// With single fetch, users should always be Responses from resource routes
invariant(isResponse(response), "Expected a Response to be returned from queryRoute");

@@ -248,13 +432,25 @@ return response;

// match identically to what Remix returns
error.headers.set("X-Remix-Catch", "yes");
return error;
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
logServerErrorIfNotAborted(error, request, serverMode);
if (isRouteErrorResponse(error)) {
if (error) {
handleError(error);
}
return errorResponseToJson(error, serverMode);
}
handleError(error);
return returnLastResortErrorResponse(error, serverMode);
}
}
function logServerErrorIfNotAborted(error, request, serverMode) {
if (serverMode !== ServerMode.Test && !request.signal.aborted) {
console.error(error);
}
function errorResponseToJson(errorResponse, serverMode) {
return json$1(serializeError(
// @ts-expect-error This is "private" from users but intended for internal use
errorResponse.error || new Error("Unexpected Server Error"), serverMode), {
status: errorResponse.status,
statusText: errorResponse.statusText,
headers: {
"X-Remix-Error": "yes"
}
});
}

@@ -275,3 +471,42 @@ function returnLastResortErrorResponse(error, serverMode) {

}
function unwrapResponse(response) {
let contentType = response.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
return contentType && /\bapplication\/json\b/.test(contentType) ? response.body == null ? null : response.json() : response.text();
}
function createRemixRedirectResponse(response, basename) {
// We don't have any way to prevent a fetch request from following
// redirects. So we use the `X-Remix-Redirect` header to indicate the
// next URL, and then "follow" the redirect manually on the client.
let headers = new Headers(response.headers);
let redirectUrl = headers.get("Location");
headers.set("X-Remix-Redirect", basename ? stripBasename(redirectUrl, basename) || redirectUrl : redirectUrl);
headers.set("X-Remix-Status", String(response.status));
headers.delete("Location");
if (response.headers.get("Set-Cookie") !== null) {
headers.set("X-Remix-Revalidate", "yes");
}
return new Response(null, {
status: 204,
headers
});
}
export { createRequestHandler, differentiateCatchVersusErrorBoundaries };
// Anytime we are setting a header on a `Response` created in the loader/action,
// we have to so it in this manner since in an `undici` world, if the `Response`
// came directly from a `fetch` call, the headers are immutable will throw if
// we try to set a new header. This is a sort of shallow clone of the `Response`
// so we can safely set our own header.
function safelySetHeader(response, name, value) {
let headers = new Headers(response.headers);
headers.set(name, value);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
duplex: response.body ? "half" : undefined
});
}
export { createRequestHandler };
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -18,2 +18,8 @@ * Copyright (c) Remix Software Inc.

/**
* Session persists data across HTTP requests.
*
* @see https://remix.run/utils/sessions#session-api
*/
function flash(name) {

@@ -81,2 +87,12 @@ return `__flash_${name}__`;

/**
* SessionIdStorageStrategy is designed to allow anyone to easily build their
* own SessionStorage using `createSessionStorage(strategy)`.
*
* This strategy describes a common scenario where the session id is stored in
* a cookie but the actual session data is stored elsewhere, usually in a
* database or on disk. A set of create, read, update, and delete operations
* are provided for managing the session data.
*/
/**
* Creates a SessionStorage object using a SessionIdStorageStrategy.

@@ -109,6 +125,7 @@ *

} = session;
let expires = (options === null || options === void 0 ? void 0 : options.maxAge) != null ? new Date(Date.now() + options.maxAge * 1000) : (options === null || options === void 0 ? void 0 : options.expires) != null ? options.expires : cookie.expires;
if (id) {
await updateData(id, data, cookie.expires);
await updateData(id, data, expires);
} else {
id = await createData(data, cookie.expires);
id = await createData(data, expires);
}

@@ -121,2 +138,3 @@ return cookie.serialize(id, options);

...options,
maxAge: undefined,
expires: new Date(0)

@@ -123,0 +141,0 @@ });

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -44,2 +44,3 @@ * Copyright (c) Remix Software Inc.

...options,
maxAge: undefined,
expires: new Date(0)

@@ -46,0 +47,0 @@ });

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -23,3 +23,2 @@ * Copyright (c) Remix Software Inc.

} = {}) => {
let uniqueId = 0;
let map = new Map();

@@ -29,3 +28,3 @@ return createSessionStorage({

async createData(data, expires) {
let id = (++uniqueId).toString();
let id = Math.random().toString(36).substring(2, 10);
map.set(id, {

@@ -32,0 +31,0 @@ data,

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -17,2 +17,3 @@ * Copyright (c) Remix Software Inc.

// @ts-ignore
function composeUploadHandlers(...handlers) {

@@ -19,0 +20,0 @@ return async part => {

import type { StaticHandlerContext } from "@remix-run/router";
import type { ServerBuild } from "./build";
export declare function getDocumentHeadersRR(build: ServerBuild, context: StaticHandlerContext): Headers;
export declare function getDocumentHeaders(build: ServerBuild, context: StaticHandlerContext): Headers;
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -17,6 +17,27 @@ * Copyright (c) Remix Software Inc.

function getDocumentHeadersRR(build, context) {
let matches = context.errors ? context.matches.slice(0, context.matches.findIndex(m => context.errors[m.route.id]) + 1) : context.matches;
return matches.reduce((parentHeaders, match) => {
function getDocumentHeaders(build, context) {
let boundaryIdx = context.errors ? context.matches.findIndex(m => context.errors[m.route.id]) : -1;
let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
let errorHeaders;
if (boundaryIdx >= 0) {
// Look for any errorHeaders from the boundary route down, which can be
// identified by the presence of headers but no data
let {
actionHeaders,
actionData,
loaderHeaders,
loaderData
} = context;
context.matches.slice(boundaryIdx).some(match => {
let id = match.route.id;
if (actionHeaders[id] && (!actionData || actionData[id] === undefined)) {
errorHeaders = actionHeaders[id];
} else if (loaderHeaders[id] && loaderData[id] === undefined) {
errorHeaders = loaderHeaders[id];
}
return errorHeaders != null;
});
}
return matches.reduce((parentHeaders, match, idx) => {
let {
id

@@ -27,10 +48,33 @@ } = match.route;

let actionHeaders = context.actionHeaders[id] || new Headers();
// Only expose errorHeaders to the leaf headers() function to
// avoid duplication via parentHeaders
let includeErrorHeaders = errorHeaders != undefined && idx === matches.length - 1;
// Only prepend cookies from errorHeaders at the leaf renderable route
// when it's not the same as loaderHeaders/actionHeaders to avoid
// duplicate cookies
let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
// Use the parent headers for any route without a `headers` export
if (routeModule.headers == null) {
let headers = new Headers(parentHeaders);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);
prependCookies(loaderHeaders, headers);
return headers;
}
let headers = new Headers(routeModule.headers ? typeof routeModule.headers === "function" ? routeModule.headers({
loaderHeaders,
parentHeaders,
actionHeaders
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : undefined
}) : routeModule.headers : undefined);
// Automatically preserve Set-Cookie headers that were set either by the
// loader or by a parent route.
// Automatically preserve Set-Cookie headers from bubbled responses,
// loaders, errors, and parent routes
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);

@@ -52,2 +96,2 @@ prependCookies(loaderHeaders, headers);

exports.getDocumentHeadersRR = getDocumentHeadersRR;
exports.getDocumentHeaders = getDocumentHeaders;
export { createCookieFactory, isCookie } from "./cookies";
export { composeUploadHandlers as unstable_composeUploadHandlers, parseMultipartFormData as unstable_parseMultipartFormData, } from "./formData";
export { defer, json, redirect } from "./responses";
export { defer, json, redirect, redirectDocument, replace } from "./responses";
export { SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, data, } from "./single-fetch";
export type { SingleFetchResult as UNSAFE_SingleFetchResult, SingleFetchResults as UNSAFE_SingleFetchResults, } from "./single-fetch";
export { createRequestHandler } from "./server";

@@ -10,4 +12,5 @@ export { createSession, createSessionStorageFactory, isSession, } from "./sessions";

export { MaxPartSizeExceededError } from "./upload/errors";
export { broadcastDevReady, logDevReady } from "./dev";
export { broadcastDevReady, logDevReady, setDevServerHooks as unstable_setDevServerHooks, } from "./dev";
export type { CreateCookieFunction, CreateCookieSessionStorageFunction, CreateMemorySessionStorageFunction, CreateRequestHandlerFunction, CreateSessionFunction, CreateSessionStorageFunction, IsCookieFunction, IsSessionFunction, JsonFunction, RedirectFunction, } from "./interface";
export type { ActionArgs, ActionFunction, AppData, AppLoadContext, Cookie, CookieOptions, CookieParseOptions, CookieSerializeOptions, CookieSignatureOptions, DataFunctionArgs, EntryContext, ErrorBoundaryComponent, FlashSessionData, HandleDataRequestFunction, HandleDocumentRequestFunction, HeadersFunction, HtmlLinkDescriptor, HtmlMetaDescriptor, LinkDescriptor, LinksFunction, LoaderArgs, LoaderFunction, MemoryUploadHandlerFilterArgs, MemoryUploadHandlerOptions, MetaDescriptor, MetaFunction, PageLinkDescriptor, RequestHandler, RouteComponent, RouteHandle, SerializeFrom, ServerBuild, ServerEntryModule, V2_ServerRuntimeMetaArgs, V2_ServerRuntimeMetaDescriptor, V2_ServerRuntimeMetaFunction, Session, SessionData, SessionIdStorageStrategy, SessionStorage, SignFunction, TypedDeferredData, TypedResponse, UnsignFunction, UploadHandler, UploadHandlerPart, } from "./reexport";
export type { Future } from "./future";
export type { ActionFunction, ActionFunctionArgs, AppLoadContext, Cookie, CookieOptions, CookieParseOptions, CookieSerializeOptions, CookieSignatureOptions, DataFunctionArgs, EntryContext, ErrorResponse, FlashSessionData, HandleDataRequestFunction, HandleDocumentRequestFunction, HeadersArgs, HeadersFunction, HtmlLinkDescriptor, LinkDescriptor, LinksFunction, LoaderFunction, LoaderFunctionArgs, MemoryUploadHandlerFilterArgs, MemoryUploadHandlerOptions, HandleErrorFunction, PageLinkDescriptor, RequestHandler, SerializeFrom, ServerBuild, ServerEntryModule, ServerRuntimeMetaArgs, ServerRuntimeMetaDescriptor, ServerRuntimeMetaFunction, Session, SessionData, SessionIdStorageStrategy, SessionStorage, SignFunction, TypedDeferredData, TypedResponse, UnsignFunction, UploadHandler, UploadHandlerPart, } from "./reexport";
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -18,2 +18,3 @@ * Copyright (c) Remix Software Inc.

var responses = require('./responses.js');
var singleFetch = require('./single-fetch.js');
var server = require('./server.js');

@@ -36,2 +37,6 @@ var sessions = require('./sessions.js');

exports.redirect = responses.redirect;
exports.redirectDocument = responses.redirectDocument;
exports.replace = responses.replace;
exports.UNSAFE_SingleFetchRedirectSymbol = singleFetch.SingleFetchRedirectSymbol;
exports.data = singleFetch.data;
exports.createRequestHandler = server.createRequestHandler;

@@ -47,1 +52,2 @@ exports.createSession = sessions.createSession;

exports.logDevReady = dev.logDevReady;
exports.unstable_setDevServerHooks = dev.setDevServerHooks;
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -94,16 +94,7 @@ type Primitive = null | undefined | string | number | boolean | symbol | bigint;

*/
export type HtmlLinkDescriptor = ((HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
export type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
imageSizes?: never;
})) & {
});
export interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> {
/**
* @deprecated Use `imageSrcSet` instead.
*/
imagesrcset?: string;
/**
* @deprecated Use `imageSizes` instead.
*/
imagesizes?: string;
};
export interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "imagesrcset" | "imagesizes" | "as" | "color" | "title"> {
/**
* The absolute path of the page to prefetch.

@@ -110,0 +101,0 @@ */

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -18,12 +18,13 @@ * Copyright (c) Remix Software Inc.

*/
exports.ServerMode = void 0;
(function (ServerMode) {
let ServerMode = /*#__PURE__*/function (ServerMode) {
ServerMode["Development"] = "development";
ServerMode["Production"] = "production";
ServerMode["Test"] = "test";
})(exports.ServerMode || (exports.ServerMode = {}));
return ServerMode;
}({});
function isServerMode(value) {
return value === exports.ServerMode.Development || value === exports.ServerMode.Production || value === exports.ServerMode.Test;
return value === ServerMode.Development || value === ServerMode.Production || value === ServerMode.Test;
}
exports.ServerMode = ServerMode;
exports.isServerMode = isServerMode;

@@ -1,2 +0,4 @@

export type { HandleDataRequestFunction, HandleDocumentRequestFunction, ServerBuild, ServerEntryModule, } from "./build";
export type { ErrorResponse } from "@remix-run/router";
export type { HandleDataRequestFunction, HandleDocumentRequestFunction, HandleErrorFunction, ServerBuild, ServerEntryModule, } from "./build";
export type { Future } from "./future";
export type { UploadHandlerPart, UploadHandler } from "./formData";

@@ -6,9 +8,9 @@ export type { MemoryUploadHandlerOptions, MemoryUploadHandlerFilterArgs, } from "./upload/memoryUploadHandler";

export type { SignFunction, UnsignFunction } from "./crypto";
export type { AppLoadContext, AppData } from "./data";
export type { AppLoadContext } from "./data";
export type { EntryContext } from "./entry";
export type { HtmlLinkDescriptor, LinkDescriptor, PageLinkDescriptor, } from "./links";
export type { TypedDeferredData, TypedResponse } from "./responses";
export type { ActionArgs, ActionFunction, DataFunctionArgs, ErrorBoundaryComponent, HeadersFunction, HtmlMetaDescriptor, LinksFunction, LoaderArgs, LoaderFunction, MetaDescriptor, MetaFunction, RouteComponent, RouteHandle, V2_ServerRuntimeMetaArgs, V2_ServerRuntimeMetaDescriptor, V2_ServerRuntimeMetaFunction, } from "./routeModules";
export type { ActionFunction, ActionFunctionArgs, DataFunctionArgs, HeadersArgs, HeadersFunction, LinksFunction, LoaderFunction, LoaderFunctionArgs, ServerRuntimeMetaArgs, ServerRuntimeMetaDescriptor, ServerRuntimeMetaFunction, } from "./routeModules";
export type { SerializeFrom } from "./serialize";
export type { RequestHandler } from "./server";
export type { Session, SessionData, SessionIdStorageStrategy, SessionStorage, FlashSessionData, } from "./sessions";

@@ -9,4 +9,4 @@ import { type UNSAFE_DeferredData as DeferredData } from "@remix-run/router";

export type DeferFunction = <Data extends Record<string, unknown>>(data: Data, init?: number | ResponseInit) => TypedDeferredData<Data>;
export type JsonFunction = <Data extends unknown>(data: Data, init?: number | ResponseInit) => TypedResponse<Data>;
export type TypedResponse<T extends unknown = unknown> = Omit<Response, "json"> & {
export type JsonFunction = <Data>(data: Data, init?: number | ResponseInit) => TypedResponse<Data>;
export type TypedResponse<T = unknown> = Omit<Response, "json"> & {
json(): Promise<T>;

@@ -24,3 +24,3 @@ };

*
* @see https://remix.run/docs/utils/defer
* @see https://remix.run/utils/defer
*/

@@ -36,2 +36,17 @@ export declare const defer: DeferFunction;

export declare const redirect: RedirectFunction;
/**
* A redirect response. Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/utils/redirect
*/
export declare const replace: RedirectFunction;
/**
* A redirect response that will force a document reload to the new location.
* Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/utils/redirect
*/
export declare const redirectDocument: RedirectFunction;
export declare function isDeferredData(value: any): value is DeferredData;

@@ -38,0 +53,0 @@ export declare function isResponse(value: any): value is Response;

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -18,2 +18,5 @@ * Copyright (c) Remix Software Inc.

// must be a type since this is a subtype of response
// interfaces must conform to the types they extend
/**

@@ -32,3 +35,3 @@ * This is a shortcut for creating `application/json` responses. Converts `data`

*
* @see https://remix.run/docs/utils/defer
* @see https://remix.run/utils/defer
*/

@@ -47,2 +50,23 @@ const defer = (data, init = {}) => {

};
/**
* A redirect response. Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/utils/redirect
*/
const replace = (url, init = 302) => {
return router.replace(url, init);
};
/**
* A redirect response that will force a document reload to the new location.
* Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/utils/redirect
*/
const redirectDocument = (url, init = 302) => {
return router.redirectDocument(url, init);
};
function isDeferredData(value) {

@@ -124,1 +148,3 @@ let deferred = value;

exports.redirect = redirect;
exports.redirectDocument = redirectDocument;
exports.replace = replace;

@@ -8,2 +8,2 @@ import type { Params } from "@remix-run/router";

}
export declare function matchServerRoutes(routes: ServerRoute[], pathname: string): RouteMatch<ServerRoute>[] | null;
export declare function matchServerRoutes(routes: ServerRoute[], pathname: string, basename?: string): RouteMatch<ServerRoute>[] | null;
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -17,4 +17,4 @@ * Copyright (c) Remix Software Inc.

function matchServerRoutes(routes, pathname) {
let matches = router.matchRoutes(routes, pathname);
function matchServerRoutes(routes, pathname, basename) {
let matches = router.matchRoutes(routes, pathname, basename);
if (!matches) return null;

@@ -21,0 +21,0 @@ return matches.map(match => ({

@@ -1,52 +0,67 @@

import type { AgnosticRouteMatch, Location, Params, RouterState } from "@remix-run/router";
import type { ComponentType } from "react";
import type { AppLoadContext, AppData } from "./data";
import type { ActionFunction as RRActionFunction, ActionFunctionArgs as RRActionFunctionArgs, AgnosticRouteMatch, LoaderFunction as RRLoaderFunction, LoaderFunctionArgs as RRLoaderFunctionArgs, Location, Params } from "@remix-run/router";
import type { AppData, AppLoadContext } from "./data";
import type { LinkDescriptor } from "./links";
import type { SerializeFrom } from "./serialize";
type RouteData = RouterState["loaderData"];
export interface RouteModules<RouteModule> {
[routeId: string]: RouteModule;
[routeId: string]: RouteModule | undefined;
}
/**
* The arguments passed to ActionFunction and LoaderFunction.
*
* Note this is almost identical to React Router's version but over there the
* context is optional since it's only there during static handler invocations.
* Keeping Remix's own definition for now so it can differentiate between
* client/server
* @deprecated Use `LoaderFunctionArgs`/`ActionFunctionArgs` instead
*/
export interface DataFunctionArgs {
request: Request;
export type DataFunctionArgs = RRActionFunctionArgs<AppLoadContext> & RRLoaderFunctionArgs<AppLoadContext> & {
context: AppLoadContext;
params: Params;
}
export type LoaderArgs = DataFunctionArgs;
export type ActionArgs = DataFunctionArgs;
};
/**
* A function that handles data mutations for a route.
* A function that handles data mutations for a route on the server
*/
export interface ActionFunction {
(args: DataFunctionArgs): Promise<Response> | Response | Promise<AppData> | AppData;
}
export type ActionFunction = (args: ActionFunctionArgs) => ReturnType<RRActionFunction>;
/**
* A React component that is rendered when the server throws a Response.
*
* @deprecated Please enable the v2_errorBoundary flag
* Arguments passed to a route `action` function
*/
export type CatchBoundaryComponent = ComponentType;
export type ActionFunctionArgs = RRActionFunctionArgs<AppLoadContext> & {
context: AppLoadContext;
};
/**
* A React component that is rendered when there is an error on a route.
*
* @deprecated Please enable the v2_errorBoundary flag
* A function that handles data mutations for a route on the client
* @private Public API is exported from @remix-run/react
*/
export type ErrorBoundaryComponent = ComponentType<{
error: Error;
}>;
type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<RRActionFunction>;
/**
* V2 version of the ErrorBoundary that eliminates the distinction between
* Error and Catch Boundaries and behaves like RR 6.4 errorElement and captures
* errors with useRouteError()
* Arguments passed to a route `clientAction` function
* @private Public API is exported from @remix-run/react
*/
export type V2_ErrorBoundaryComponent = ComponentType;
export type ClientActionFunctionArgs = RRActionFunctionArgs<undefined> & {
serverAction: <T = AppData>() => Promise<SerializeFrom<T>>;
};
/**
* A function that loads data for a route on the server
*/
export type LoaderFunction = (args: LoaderFunctionArgs) => ReturnType<RRLoaderFunction>;
/**
* Arguments passed to a route `loader` function
*/
export type LoaderFunctionArgs = RRLoaderFunctionArgs<AppLoadContext> & {
context: AppLoadContext;
};
/**
* A function that loads data for a route on the client
* @private Public API is exported from @remix-run/react
*/
type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<RRLoaderFunction>) & {
hydrate?: boolean;
};
/**
* Arguments passed to a route `clientLoader` function
* @private Public API is exported from @remix-run/react
*/
export type ClientLoaderFunctionArgs = RRLoaderFunctionArgs<undefined> & {
serverLoader: <T = AppData>() => Promise<SerializeFrom<T>>;
};
export type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
/**
* A function that returns HTTP headers to be used for a route. These headers

@@ -56,7 +71,3 @@ * will be merged with (and take precedence over) headers from parent routes.

export interface HeadersFunction {
(args: {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
}): Headers | HeadersInit;
(args: HeadersArgs): Headers | HeadersInit;
}

@@ -71,14 +82,10 @@ /**

/**
* A function that loads data for a route.
*/
export interface LoaderFunction {
(args: DataFunctionArgs): Promise<Response> | Response | Promise<AppData> | AppData;
}
/**
* A function that returns an object of name + content pairs to use for
* `<meta>` tags for a route. These tags will be merged with (and take
* precedence over) tags from parent routes.
* A function that returns an array of data objects to use for rendering
* metadata HTML tags in a route. These tags are not rendered on descendant
* routes in the route hierarchy. In other words, they will only be rendered on
* the route in which they are exported.
*
* @param Loader - Loader for this meta function's route
* @param ParentsLoaders - Mapping from a parent's route filepath to that route's loader
* @param Loader - The type of the current route's loader function
* @param MatchLoaders - Mapping from a parent route's filepath to its loader
* function type
*

@@ -121,10 +128,10 @@ * Note that parent route filepaths are relative to the `app/` directory.

* "routes/sales/customers": CustomersLoader,
* }> = ({ data, parentsData }) => {
* }> = ({ data, matches }) => {
* const { name } = data
* // ^? string
* const { customerCount } = parentsData["routes/sales/customers"]
* const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
* // ^? number
* const { salesCount } = parentsData["routes/sales"]
* const { salesCount } = matches.find((match) => match.id === "routes/sales").data
* // ^? number
* const { hello } = parentsData["root"]
* const { hello } = matches.find((match) => match.id === "root").data
* // ^? "world"

@@ -134,48 +141,25 @@ * }

*/
export interface V1_MetaFunction<Loader extends LoaderFunction | unknown = unknown, ParentsLoaders extends Record<string, LoaderFunction> = {}> {
(args: {
data: Loader extends LoaderFunction ? SerializeFrom<Loader> : AppData;
parentsData: {
[k in keyof ParentsLoaders]: SerializeFrom<ParentsLoaders[k]>;
} & RouteData;
params: Params;
location: Location;
}): HtmlMetaDescriptor;
export interface ServerRuntimeMetaFunction<Loader extends LoaderFunction | unknown = unknown, ParentsLoaders extends Record<string, LoaderFunction | unknown> = Record<string, unknown>> {
(args: ServerRuntimeMetaArgs<Loader, ParentsLoaders>): ServerRuntimeMetaDescriptor[];
}
export type MetaFunction<Loader extends LoaderFunction | unknown = unknown, ParentsLoaders extends Record<string, LoaderFunction> = {}> = V1_MetaFunction<Loader, ParentsLoaders>;
interface V2_ServerRuntimeMetaMatch<RouteId extends string = string, Loader extends LoaderFunction | unknown = unknown> {
interface ServerRuntimeMetaMatch<RouteId extends string = string, Loader extends LoaderFunction | unknown = unknown> {
id: RouteId;
pathname: AgnosticRouteMatch["pathname"];
data: Loader extends LoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: unknown;
handle?: RouteHandle;
params: AgnosticRouteMatch["params"];
meta: V2_ServerRuntimeMetaDescriptor[];
meta: ServerRuntimeMetaDescriptor[];
error?: unknown;
}
type V2_ServerRuntimeMetaMatches<MatchLoaders extends Record<string, unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: V2_ServerRuntimeMetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
type ServerRuntimeMetaMatches<MatchLoaders extends Record<string, LoaderFunction | unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: ServerRuntimeMetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
}[keyof MatchLoaders]>;
export interface V2_ServerRuntimeMetaArgs<Loader extends LoaderFunction | unknown = unknown, MatchLoaders extends Record<string, unknown> = Record<string, unknown>> {
export interface ServerRuntimeMetaArgs<Loader extends LoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | unknown> = Record<string, unknown>> {
data: (Loader extends LoaderFunction ? SerializeFrom<Loader> : AppData) | undefined;
params: Params;
location: Location;
matches: V2_ServerRuntimeMetaMatches<MatchLoaders>;
matches: ServerRuntimeMetaMatches<MatchLoaders>;
error?: unknown;
}
export interface V2_ServerRuntimeMetaFunction<Loader extends LoaderFunction | unknown = unknown, ParentsLoaders extends Record<string, LoaderFunction> = {}> {
(args: V2_ServerRuntimeMetaArgs<Loader, ParentsLoaders>): V2_ServerRuntimeMetaDescriptor[];
}
/**
* A name/content pair used to render `<meta>` tags in a meta function for a
* route. The value can be either a string, which will render a single `<meta>`
* tag, or an array of strings that will render multiple tags with the same
* `name` attribute.
*/
export interface V1_HtmlMetaDescriptor {
charset?: "utf-8";
charSet?: "utf-8";
title?: string;
[name: string]: null | string | undefined | Record<string, string> | Array<Record<string, string> | string>;
}
export type HtmlMetaDescriptor = V1_HtmlMetaDescriptor;
export type MetaDescriptor = HtmlMetaDescriptor;
export type V2_ServerRuntimeMetaDescriptor = {
export type ServerRuntimeMetaDescriptor = {
charSet: "utf-8";

@@ -210,16 +194,15 @@ } | {

/**
* A React component that is rendered for a route.
*/
export type RouteComponent = ComponentType<{}>;
/**
* An arbitrary object that is associated with a route.
*/
export type RouteHandle = any;
export type RouteHandle = unknown;
export interface EntryRouteModule {
CatchBoundary?: CatchBoundaryComponent;
ErrorBoundary?: ErrorBoundaryComponent | V2_ErrorBoundaryComponent;
default: RouteComponent;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: any;
HydrateFallback?: any;
Layout?: any;
default: any;
handle?: RouteHandle;
links?: LinksFunction;
meta?: MetaFunction | HtmlMetaDescriptor;
meta?: ServerRuntimeMetaFunction;
}

@@ -226,0 +209,0 @@ export interface ServerRouteModule extends EntryRouteModule {

@@ -18,5 +18,7 @@ import type { AgnosticDataRouteObject } from "@remix-run/router";

hasLoader: boolean;
hasCatchBoundary: boolean;
hasClientAction: boolean;
hasClientLoader: boolean;
hasErrorBoundary: boolean;
imports?: string[];
css?: string[];
module: string;

@@ -23,0 +25,0 @@ parentId?: string;

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -17,2 +17,6 @@ * Copyright (c) Remix Software Inc.

// NOTE: make sure to change the Route in remix-react if you change this
// NOTE: make sure to change the EntryRoute in remix-react if you change this
function groupRoutesByParentId(manifest) {

@@ -43,9 +47,11 @@ let routes = {};

return (routesByParentId[parentId] || []).map(route => {
let hasErrorBoundary = future.v2_errorBoundary === true ? route.id === "root" || route.module.ErrorBoundary != null : route.id === "root" || route.module.CatchBoundary != null || route.module.ErrorBoundary != null;
let commonRoute = {
// Always include root due to default boundaries
hasErrorBoundary,
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
id: route.id,
path: route.path,
loader: route.module.loader ? args => data.callRouteLoaderRR({
loader: route.module.loader ?
// Need to use RR's version here to permit the optional context even
// though we know it'll always be provided in remix
(args, dataStrategyCtx) => data.callRouteLoader({
request: args.request,

@@ -55,5 +61,6 @@ params: args.params,

loader: route.module.loader,
routeId: route.id
routeId: route.id,
singleFetch: future.v3_singleFetch === true
}) : undefined,
action: route.module.action ? args => data.callRouteActionRR({
action: route.module.action ? (args, dataStrategyCtx) => data.callRouteAction({
request: args.request,

@@ -63,3 +70,4 @@ params: args.params,

action: route.module.action,
routeId: route.id
routeId: route.id,
singleFetch: future.v3_singleFetch === true
}) : undefined,

@@ -66,0 +74,0 @@ handle: route.module.handle

@@ -1,28 +0,13 @@

import type { AppData } from "./data";
import type { Jsonify } from "./jsonify";
import type { TypedDeferredData, TypedResponse } from "./responses";
type JsonPrimitive = string | number | boolean | String | Number | Boolean | null;
type NonJsonPrimitive = undefined | Function | symbol;
type IsAny<T> = 0 extends 1 & T ? true : false;
type Serialize<T> = IsAny<T> extends true ? any : T extends TypedDeferredData<infer U> ? SerializeDeferred<U> : T extends JsonPrimitive ? T : T extends NonJsonPrimitive ? never : T extends {
toJSON(): infer U;
} ? U : T extends [] ? [] : T extends [unknown, ...unknown[]] ? SerializeTuple<T> : T extends ReadonlyArray<infer U> ? (U extends NonJsonPrimitive ? null : Serialize<U>)[] : T extends object ? SerializeObject<UndefinedToOptional<T>> : never;
/** JSON serialize [tuples](https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types) */
type SerializeTuple<T extends [unknown, ...unknown[]]> = {
[k in keyof T]: T[k] extends NonJsonPrimitive ? null : Serialize<T[k]>;
};
/** JSON serialize objects (not including arrays) and classes */
type SerializeObject<T extends object> = {
[k in keyof T as T[k] extends NonJsonPrimitive ? never : k]: Serialize<T[k]>;
};
type SerializeDeferred<T extends Record<string, unknown>> = {
[k in keyof T as T[k] extends Promise<unknown> ? k : T[k] extends NonJsonPrimitive ? never : k]: T[k] extends Promise<infer U> ? Promise<Serialize<U>> extends never ? "wtf" : Promise<Serialize<U>> : Serialize<T[k]> extends never ? k : Serialize<T[k]>;
};
type UndefinedToOptional<T extends object> = {
[k in keyof T as undefined extends T[k] ? never : k]: T[k];
} & {
[k in keyof T as undefined extends T[k] ? k : never]?: Exclude<T[k], undefined>;
};
type ArbitraryFunction = (...args: any[]) => unknown;
import type { ClientActionFunctionArgs, ClientLoaderFunctionArgs } from "./routeModules";
import { type SerializeFrom as SingleFetch_SerializeFrom } from "./single-fetch";
import type { Future } from "./future";
type SingleFetchEnabled = Future extends {
v3_singleFetch: infer T extends boolean;
} ? T : false;
/**
* Infer JSON serialized data type returned by a loader or action.
* Infer JSON serialized data type returned by a loader or action, while
* avoiding deserialization if the input type if it's a clientLoader or
* clientAction that returns a non-Response
*

@@ -32,3 +17,15 @@ * For example:

*/
export type SerializeFrom<T extends AppData | ArbitraryFunction> = Serialize<T extends (...args: any[]) => infer Output ? Awaited<Output> extends TypedResponse<infer U> ? U : Awaited<Output> : Awaited<T>>;
export type SerializeFrom<T> = SingleFetchEnabled extends true ? SingleFetch_SerializeFrom<T> : T extends (...args: any[]) => infer Output ? Parameters<T> extends [ClientLoaderFunctionArgs | ClientActionFunctionArgs] ? SerializeClient<Awaited<Output>> : Serialize<Awaited<Output>> : Jsonify<Awaited<T>>;
type SerializeClient<Output> = Output extends TypedDeferredData<infer U> ? {
[K in keyof U as K extends symbol ? never : Promise<any> extends U[K] ? K : never]: DeferValueClient<U[K]>;
} & {
[K in keyof U as Promise<any> extends U[K] ? never : K]: U[K];
} : Output extends TypedResponse<infer U> ? Jsonify<U> : Awaited<Output>;
type DeferValueClient<T> = T extends undefined ? undefined : T extends Promise<unknown> ? Promise<Awaited<T>> : T;
type Serialize<Output> = Output extends TypedDeferredData<infer U> ? {
[K in keyof U as K extends symbol ? never : Promise<any> extends U[K] ? K : never]: DeferValue<U[K]>;
} & Jsonify<{
[K in keyof U as Promise<any> extends U[K] ? never : K]: U[K];
}> : Output extends TypedResponse<infer U> ? Jsonify<U> : Jsonify<Output>;
type DeferValue<T> = T extends undefined ? undefined : T extends Promise<unknown> ? Promise<Jsonify<Awaited<T>>> : Jsonify<T>;
export {};

@@ -1,7 +0,5 @@

import type { StaticHandlerContext } from "@remix-run/router";
import type { AppLoadContext } from "./data";
import type { ServerBuild } from "./build";
export type RequestHandler = (request: Request, loadContext?: AppLoadContext) => Promise<Response>;
export type CreateRequestHandlerFunction = (build: ServerBuild, mode?: string) => RequestHandler;
export type CreateRequestHandlerFunction = (build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>), mode?: string) => RequestHandler;
export declare const createRequestHandler: CreateRequestHandlerFunction;
export declare function differentiateCatchVersusErrorBoundaries(build: ServerBuild, context: StaticHandlerContext): void;
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -25,27 +25,138 @@ * Copyright (c) Remix Software Inc.

var serverHandoff = require('./serverHandoff.js');
var dev = require('./dev.js');
var singleFetch = require('./single-fetch.js');
var deprecations = require('./deprecations.js');
const createRequestHandler = (build, mode$1) => {
function derive(build, mode$1) {
var _build$future, _build$future2;
let routes$1 = routes.createRoutes(build.routes);
let dataRoutes = routes.createStaticHandlerDataRoutes(build.routes, build.future);
let serverMode = mode.isServerMode(mode$1) ? mode$1 : mode.ServerMode.Production;
let staticHandler = router.createStaticHandler(dataRoutes);
let staticHandler = router.createStaticHandler(dataRoutes, {
basename: build.basename,
future: {
v7_relativeSplatPath: ((_build$future = build.future) === null || _build$future === void 0 ? void 0 : _build$future.v3_relativeSplatPath) === true,
v7_throwAbortReason: ((_build$future2 = build.future) === null || _build$future2 === void 0 ? void 0 : _build$future2.v3_throwAbortReason) === true
}
});
let errorHandler = build.entry.module.handleError || ((error, {
request
}) => {
if (serverMode !== mode.ServerMode.Test && !request.signal.aborted) {
console.error(
// @ts-expect-error This is "private" from users but intended for internal use
router.isRouteErrorResponse(error) && error.error ? error.error : error);
}
});
return {
routes: routes$1,
dataRoutes,
serverMode,
staticHandler,
errorHandler
};
}
const createRequestHandler = (build, mode$1) => {
let _build;
let routes;
let serverMode;
let staticHandler;
let errorHandler;
return async function requestHandler(request, loadContext = {}) {
_build = typeof build === "function" ? await build() : build;
mode$1 ??= _build.mode;
if (typeof build === "function") {
let derived = derive(_build, mode$1);
routes = derived.routes;
serverMode = derived.serverMode;
staticHandler = derived.staticHandler;
errorHandler = derived.errorHandler;
} else if (!routes || !serverMode || !staticHandler || !errorHandler) {
let derived = derive(_build, mode$1);
routes = derived.routes;
serverMode = derived.serverMode;
staticHandler = derived.staticHandler;
errorHandler = derived.errorHandler;
}
let url = new URL(request.url);
let matches = routeMatching.matchServerRoutes(routes$1, url.pathname);
let params = {};
let handleError = error => {
if (mode$1 === mode.ServerMode.Development) {
var _getDevServerHooks, _getDevServerHooks$pr;
(_getDevServerHooks = dev.getDevServerHooks()) === null || _getDevServerHooks === void 0 ? void 0 : (_getDevServerHooks$pr = _getDevServerHooks.processRequestError) === null || _getDevServerHooks$pr === void 0 ? void 0 : _getDevServerHooks$pr.call(_getDevServerHooks, error);
}
errorHandler(error, {
context: loadContext,
params,
request
});
};
// Manifest request for fog of war
let manifestUrl = `${_build.basename ?? "/"}/__manifest`.replace(/\/+/g, "/");
if (url.pathname === manifestUrl) {
try {
let res = await handleManifestRequest(_build, routes, url);
return res;
} catch (e) {
handleError(e);
return new Response("Unknown Server Error", {
status: 500
});
}
}
let matches = routeMatching.matchServerRoutes(routes, url.pathname, _build.basename);
if (matches && matches.length > 0) {
Object.assign(params, matches[0].params);
}
let response;
if (url.searchParams.has("_data")) {
if (_build.future.v3_singleFetch) {
handleError(new Error("Warning: Single fetch-enabled apps should not be making ?_data requests, " + "this is likely to break in the future"));
}
let routeId = url.searchParams.get("_data");
response = await handleDataRequestRR(serverMode, staticHandler, routeId, request, loadContext);
if (build.entry.module.handleDataRequest) {
let match = matches.find(match => match.route.id == routeId);
response = await build.entry.module.handleDataRequest(response, {
response = await handleDataRequest(serverMode, _build, staticHandler, routeId, request, loadContext, handleError);
if (_build.entry.module.handleDataRequest) {
response = await _build.entry.module.handleDataRequest(response, {
context: loadContext,
params: match ? match.params : {},
params,
request
});
if (responses.isRedirectResponse(response)) {
response = createRemixRedirectResponse(response, _build.basename);
}
}
} else if (matches && matches[matches.length - 1].route.module.default == null) {
response = await handleResourceRequestRR(serverMode, staticHandler, matches.slice(-1)[0].route.id, request, loadContext);
} else if (_build.future.v3_singleFetch && url.pathname.endsWith(".data")) {
let handlerUrl = new URL(request.url);
handlerUrl.pathname = handlerUrl.pathname.replace(/\.data$/, "").replace(/^\/_root$/, "/");
let singleFetchMatches = routeMatching.matchServerRoutes(routes, handlerUrl.pathname, _build.basename);
response = await handleSingleFetchRequest(serverMode, _build, staticHandler, request, handlerUrl, loadContext, handleError);
if (_build.entry.module.handleDataRequest) {
response = await _build.entry.module.handleDataRequest(response, {
context: loadContext,
params: singleFetchMatches ? singleFetchMatches[0].params : {},
request
});
if (responses.isRedirectResponse(response)) {
let result = singleFetch.getSingleFetchRedirect(response.status, response.headers, _build.basename);
if (request.method === "GET") {
result = {
[singleFetch.SingleFetchRedirectSymbol]: result
};
}
let headers = new Headers(response.headers);
headers.set("Content-Type", "text/x-script");
return new Response(singleFetch.encodeViaTurboStream(result, request.signal, _build.entry.module.streamTimeout, serverMode), {
status: singleFetch.SINGLE_FETCH_REDIRECT_STATUS,
headers
});
}
}
} else if (matches && matches[matches.length - 1].route.module.default == null && matches[matches.length - 1].route.module.ErrorBoundary == null) {
response = await handleResourceRequest(serverMode, _build, staticHandler, matches.slice(-1)[0].route.id, request, loadContext, handleError);
} else {
response = await handleDocumentRequestRR(serverMode, build, staticHandler, request, loadContext);
var _getDevServerHooks2, _getDevServerHooks2$g;
let criticalCss = mode$1 === mode.ServerMode.Development ? await ((_getDevServerHooks2 = dev.getDevServerHooks()) === null || _getDevServerHooks2 === void 0 ? void 0 : (_getDevServerHooks2$g = _getDevServerHooks2.getCriticalCss) === null || _getDevServerHooks2$g === void 0 ? void 0 : _getDevServerHooks2$g.call(_getDevServerHooks2, _build, url.pathname)) : undefined;
response = await handleDocumentRequest(serverMode, _build, staticHandler, request, loadContext, handleError, criticalCss);
}

@@ -62,3 +173,25 @@ if (request.method === "HEAD") {

};
async function handleDataRequestRR(serverMode, staticHandler, routeId, request, loadContext) {
async function handleManifestRequest(build, routes, url) {
let patches = {};
if (url.searchParams.has("p")) {
for (let path of url.searchParams.getAll("p")) {
let matches = routeMatching.matchServerRoutes(routes, path, build.basename);
if (matches) {
for (let match of matches) {
let routeId = match.route.id;
patches[routeId] = build.assets.routes[routeId];
}
}
}
return responses.json(patches, {
headers: {
"Cache-Control": "public, max-age=31536000, immutable"
}
}); // Override the TypedResponse stuff from json()
}
return new Response("Invalid Request", {
status: 400
});
}
async function handleDataRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {

@@ -70,16 +203,3 @@ let response = await staticHandler.queryRoute(request, {

if (responses.isRedirectResponse(response)) {
// We don't have any way to prevent a fetch request from following
// redirects. So we use the `X-Remix-Redirect` header to indicate the
// next URL, and then "follow" the redirect manually on the client.
let headers = new Headers(response.headers);
headers.set("X-Remix-Redirect", headers.get("Location"));
headers.set("X-Remix-Status", response.status);
headers.delete("Location");
if (response.headers.get("Set-Cookie") !== null) {
headers.set("X-Remix-Revalidate", "yes");
}
return new Response(null, {
status: 204,
headers
});
return createRemixRedirectResponse(response, build.basename);
}

@@ -92,16 +212,26 @@ if (router.UNSAFE_DEFERRED_SYMBOL in response) {

headers.set("Content-Type", "text/remix-deferred");
// Mark successful responses with a header so we can identify in-flight
// network errors that are missing this header
headers.set("X-Remix-Response", "yes");
init.headers = headers;
return new Response(body, init);
}
// Mark all successful responses with a header so we can identify in-flight
// network errors that are missing this header
response = safelySetHeader(response, "X-Remix-Response", "yes");
return response;
} catch (error) {
if (responses.isResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
return error;
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
let status = router.isRouteErrorResponse(error) ? error.status : 500;
let errorInstance = router.isRouteErrorResponse(error) && error.error ? error.error : error instanceof Error ? error : new Error("Unexpected Server Error");
logServerErrorIfNotAborted(errorInstance, request, serverMode);
return responses.json(errors.serializeError(errorInstance, serverMode), {
status,
if (router.isRouteErrorResponse(error)) {
handleError(error);
return errorResponseToJson(error, serverMode);
}
let errorInstance = error instanceof Error || error instanceof DOMException ? error : new Error("Unexpected Server Error");
handleError(errorInstance);
return router.json(errors.serializeError(errorInstance, serverMode), {
status: 500,
headers: {

@@ -113,37 +243,36 @@ "X-Remix-Error": "yes"

}
function findParentBoundary(routes, routeId, error) {
// Fall back to the root route if we don't match any routes, since Remix
// has default error/catch boundary handling. This handles the case where
// react-router doesn't have a matching "root" route to assign the error to
// so it returns context.errors = { __shim-error-route__: ErrorResponse }
let route = routes[routeId] || routes["root"];
// Router-thrown ErrorResponses will have the error instance. User-thrown
// Responses will not have an error. The one exception here is internal 404s
// which we handle the same as user-thrown 404s
let isCatch = router.isRouteErrorResponse(error) && (!error.error || error.status === 404);
if (isCatch && route.module.CatchBoundary || !isCatch && route.module.ErrorBoundary || !route.parentId) {
return route.id;
async function handleSingleFetchRequest(serverMode, build, staticHandler, request, handlerUrl, loadContext, handleError) {
let {
result,
headers,
status
} = request.method !== "GET" ? await singleFetch.singleFetchAction(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError) : await singleFetch.singleFetchLoaders(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError);
// Mark all successful responses with a header so we can identify in-flight
// network errors that are missing this header
let resultHeaders = new Headers(headers);
resultHeaders.set("X-Remix-Response", "yes");
// 304 responses should not have a body
if (status === 304) {
return new Response(null, {
status: 304,
headers: resultHeaders
});
}
return findParentBoundary(routes, route.parentId, error);
}
// Re-generate a remix-friendly context.errors structure. The Router only
// handles generic errors and does not distinguish error versus catch. We
// may have a thrown response tagged to a route that only exports an
// ErrorBoundary or vice versa. So we adjust here and ensure that
// data-loading errors are properly associated with routes that have the right
// type of boundaries.
function differentiateCatchVersusErrorBoundaries(build, context) {
if (!context.errors) {
return;
}
let errors = {};
for (let routeId of Object.keys(context.errors)) {
let error = context.errors[routeId];
let handlingRouteId = findParentBoundary(build.routes, routeId, error);
errors[handlingRouteId] = error;
}
context.errors = errors;
// We use a less-descriptive `text/x-script` here instead of something like
// `text/x-turbo` to enable compression when deployed via Cloudflare. See:
// - https://github.com/remix-run/remix/issues/9884
// - https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/
resultHeaders.set("Content-Type", "text/x-script");
// Note: Deferred data is already just Promises, so we don't have to mess
// `activeDeferreds` or anything :)
return new Response(singleFetch.encodeViaTurboStream(result, request.signal, build.entry.module.streamTimeout, serverMode), {
status: status || 200,
headers: resultHeaders
});
}
async function handleDocumentRequestRR(serverMode, build, staticHandler, request, loadContext) {
async function handleDocumentRequest(serverMode, build, staticHandler, request, loadContext, handleError, criticalCss) {
let context;

@@ -155,3 +284,3 @@ try {

} catch (error) {
logServerErrorIfNotAborted(error, request, serverMode);
handleError(error);
return new Response(null, {

@@ -164,13 +293,31 @@ status: 500

}
let headers$1 = headers.getDocumentHeaders(build, context);
// 304 responses should not have a body or a content-type
if (context.statusCode === 304) {
return new Response(null, {
status: 304,
headers: headers$1
});
}
// Sanitize errors outside of development environments
if (context.errors) {
Object.values(context.errors).forEach(err => {
// @ts-expect-error `err.error` is "private" from users but intended for internal use
if (!router.isRouteErrorResponse(err) || err.error) {
handleError(err);
}
});
context.errors = errors.sanitizeErrors(context.errors, serverMode);
}
// Restructure context.errors to the right Catch/Error Boundary
if (build.future.v2_errorBoundary !== true) {
differentiateCatchVersusErrorBoundaries(build, context);
}
let headers$1 = headers.getDocumentHeadersRR(build, context);
// Server UI state to send to the client.
// - When single fetch is enabled, this is streamed down via `serverHandoffStream`
// - Otherwise it's stringified into `serverHandoffString`
let state = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: errors.serializeErrors(context.errors, serverMode)
};
let entryContext = {

@@ -180,12 +327,19 @@ manifest: build.assets,

staticHandlerContext: context,
criticalCss,
serverHandoffString: serverHandoff.createServerHandoffString({
state: {
loaderData: context.loaderData,
actionData: context.actionData,
errors: errors.serializeErrors(context.errors, serverMode)
},
basename: build.basename,
criticalCss,
future: build.future,
dev: build.dev
isSpaMode: build.isSpaMode,
...(!build.future.v3_singleFetch ? {
state
} : null)
}),
future: build.future
...(build.future.v3_singleFetch ? {
serverHandoffStream: singleFetch.encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null),
future: build.future,
isSpaMode: build.isSpaMode,
serializeError: err => errors.serializeError(err, serverMode)
};

@@ -196,4 +350,18 @@ let handleDocumentRequestFunction = build.entry.module.default;

} catch (error) {
handleError(error);
let errorForSecondRender = error;
// If they threw a response, unwrap it into an ErrorResponse like we would
// have for a loader/action
if (responses.isResponse(error)) {
try {
let data = await unwrapResponse(error);
errorForSecondRender = new router.UNSAFE_ErrorResponseImpl(error.status, error.statusText, data);
} catch (e) {
// If we can't unwrap the response - just leave it as-is
}
}
// Get a new StaticHandlerContext that contains the error at the right boundary
context = router.getStaticContextFromError(staticHandler.dataRoutes, context, error);
context = router.getStaticContextFromError(staticHandler.dataRoutes, context, errorForSecondRender);

@@ -205,8 +373,11 @@ // Sanitize errors outside of development environments

// Restructure context.errors to the right Catch/Error Boundary
if (build.future.v2_errorBoundary !== true) {
differentiateCatchVersusErrorBoundaries(build, context);
}
// Update entryContext for the second render pass
// Get a new entryContext for the second render pass
// Server UI state to send to the client.
// - When single fetch is enabled, this is streamed down via `serverHandoffStream`
// - Otherwise it's stringified into `serverHandoffString`
let state = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: errors.serializeErrors(context.errors, serverMode)
};
entryContext = {

@@ -216,9 +387,13 @@ ...entryContext,

serverHandoffString: serverHandoff.createServerHandoffString({
state: {
loaderData: context.loaderData,
actionData: context.actionData,
errors: errors.serializeErrors(context.errors, serverMode)
},
future: build.future
})
basename: build.basename,
future: build.future,
isSpaMode: build.isSpaMode,
...(!build.future.v3_singleFetch ? {
state
} : null)
}),
...(build.future.v3_singleFetch ? {
serverHandoffStream: singleFetch.encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null)
};

@@ -228,3 +403,3 @@ try {

} catch (error) {
logServerErrorIfNotAborted(error, request, serverMode);
handleError(error);
return returnLastResortErrorResponse(error, serverMode);

@@ -234,3 +409,3 @@ }

}
async function handleResourceRequestRR(serverMode, staticHandler, routeId, request, loadContext) {
async function handleResourceRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {

@@ -244,3 +419,12 @@ // Note we keep the routeId here to align with the Remix handling of

});
// callRouteLoader/callRouteAction always return responses
if (typeof response === "object" && response !== null) {
invariant["default"](!(router.UNSAFE_DEFERRED_SYMBOL in response), `You cannot return a \`defer()\` response from a Resource Route. Did you ` + `forget to export a default UI component from the "${routeId}" route?`);
}
if (build.future.v3_singleFetch && !responses.isResponse(response)) {
console.warn(deprecations.resourceRouteJsonWarning(request.method === "GET" ? "loader" : "action", routeId));
response = responses.json(response);
}
// callRouteLoader/callRouteAction always return responses (w/o single fetch).
// With single fetch, users should always be Responses from resource routes
invariant["default"](responses.isResponse(response), "Expected a Response to be returned from queryRoute");

@@ -252,13 +436,25 @@ return response;

// match identically to what Remix returns
error.headers.set("X-Remix-Catch", "yes");
return error;
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
logServerErrorIfNotAborted(error, request, serverMode);
if (router.isRouteErrorResponse(error)) {
if (error) {
handleError(error);
}
return errorResponseToJson(error, serverMode);
}
handleError(error);
return returnLastResortErrorResponse(error, serverMode);
}
}
function logServerErrorIfNotAborted(error, request, serverMode) {
if (serverMode !== mode.ServerMode.Test && !request.signal.aborted) {
console.error(error);
}
function errorResponseToJson(errorResponse, serverMode) {
return router.json(errors.serializeError(
// @ts-expect-error This is "private" from users but intended for internal use
errorResponse.error || new Error("Unexpected Server Error"), serverMode), {
status: errorResponse.status,
statusText: errorResponse.statusText,
headers: {
"X-Remix-Error": "yes"
}
});
}

@@ -279,4 +475,42 @@ function returnLastResortErrorResponse(error, serverMode) {

}
function unwrapResponse(response) {
let contentType = response.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
return contentType && /\bapplication\/json\b/.test(contentType) ? response.body == null ? null : response.json() : response.text();
}
function createRemixRedirectResponse(response, basename) {
// We don't have any way to prevent a fetch request from following
// redirects. So we use the `X-Remix-Redirect` header to indicate the
// next URL, and then "follow" the redirect manually on the client.
let headers = new Headers(response.headers);
let redirectUrl = headers.get("Location");
headers.set("X-Remix-Redirect", basename ? router.stripBasename(redirectUrl, basename) || redirectUrl : redirectUrl);
headers.set("X-Remix-Status", String(response.status));
headers.delete("Location");
if (response.headers.get("Set-Cookie") !== null) {
headers.set("X-Remix-Revalidate", "yes");
}
return new Response(null, {
status: 204,
headers
});
}
// Anytime we are setting a header on a `Response` created in the loader/action,
// we have to so it in this manner since in an `undici` world, if the `Response`
// came directly from a `fetch` call, the headers are immutable will throw if
// we try to set a new header. This is a sort of shallow clone of the `Response`
// so we can safely set our own header.
function safelySetHeader(response, name, value) {
let headers = new Headers(response.headers);
headers.set(name, value);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
duplex: response.body ? "half" : undefined
});
}
exports.createRequestHandler = createRequestHandler;
exports.differentiateCatchVersusErrorBoundaries = differentiateCatchVersusErrorBoundaries;

@@ -5,8 +5,8 @@ import type { HydrationState } from "@remix-run/router";

export declare function createServerHandoffString<T>(serverHandoff: {
state: ValidateShape<T, HydrationState>;
state?: ValidateShape<T, HydrationState>;
criticalCss?: string;
basename: string | undefined;
future: FutureConfig;
dev?: {
websocketPort: number;
};
isSpaMode: boolean;
}): string;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -86,3 +86,3 @@ import type { CookieParseOptions, CookieSerializeOptions } from "cookie";

*/
getSession(cookieHeader?: string | null, options?: CookieParseOptions): Promise<Session<Data, FlashData>>;
getSession: (cookieHeader?: string | null, options?: CookieParseOptions) => Promise<Session<Data, FlashData>>;
/**

@@ -92,3 +92,3 @@ * Stores all data in the Session and returns the Set-Cookie header to be

*/
commitSession(session: Session<Data, FlashData>, options?: CookieSerializeOptions): Promise<string>;
commitSession: (session: Session<Data, FlashData>, options?: CookieSerializeOptions) => Promise<string>;
/**

@@ -98,3 +98,3 @@ * Deletes all data associated with the Session and returns the Set-Cookie

*/
destroySession(session: Session<Data, FlashData>, options?: CookieSerializeOptions): Promise<string>;
destroySession: (session: Session<Data, FlashData>, options?: CookieSerializeOptions) => Promise<string>;
}

@@ -101,0 +101,0 @@ /**

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -22,2 +22,8 @@ * Copyright (c) Remix Software Inc.

/**
* Session persists data across HTTP requests.
*
* @see https://remix.run/utils/sessions#session-api
*/
function flash(name) {

@@ -85,2 +91,12 @@ return `__flash_${name}__`;

/**
* SessionIdStorageStrategy is designed to allow anyone to easily build their
* own SessionStorage using `createSessionStorage(strategy)`.
*
* This strategy describes a common scenario where the session id is stored in
* a cookie but the actual session data is stored elsewhere, usually in a
* database or on disk. A set of create, read, update, and delete operations
* are provided for managing the session data.
*/
/**
* Creates a SessionStorage object using a SessionIdStorageStrategy.

@@ -113,6 +129,7 @@ *

} = session;
let expires = (options === null || options === void 0 ? void 0 : options.maxAge) != null ? new Date(Date.now() + options.maxAge * 1000) : (options === null || options === void 0 ? void 0 : options.expires) != null ? options.expires : cookie.expires;
if (id) {
await updateData(id, data, cookie.expires);
await updateData(id, data, expires);
} else {
id = await createData(data, cookie.expires);
id = await createData(data, expires);
}

@@ -125,2 +142,3 @@ return cookie.serialize(id, options);

...options,
maxAge: undefined,
expires: new Date(0)

@@ -127,0 +145,0 @@ });

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -48,2 +48,3 @@ * Copyright (c) Remix Software Inc.

...options,
maxAge: undefined,
expires: new Date(0)

@@ -50,0 +51,0 @@ });

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -27,3 +27,2 @@ * Copyright (c) Remix Software Inc.

} = {}) => {
let uniqueId = 0;
let map = new Map();

@@ -33,3 +32,3 @@ return createSessionStorage({

async createData(data, expires) {
let id = (++uniqueId).toString();
let id = Math.random().toString(36).substring(2, 10);
map.set(id, {

@@ -36,0 +35,0 @@ data,

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -1,2 +0,2 @@

import type { UploadHandler } from "@remix-run/server-runtime";
import type { UploadHandler } from "../formData";
export type MemoryUploadHandlerFilterArgs = {

@@ -3,0 +3,0 @@ filename?: string;

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/server-runtime v0.0.0-nightly-d8bcdc1-20230506
* @remix-run/server-runtime v0.0.0-nightly-d9061d154-20241010
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

MIT License
Copyright (c) Remix Software Inc. 2020-2021
Copyright (c) Shopify Inc. 2022-2023
Copyright (c) Shopify Inc. 2022-2024

@@ -6,0 +6,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"name": "@remix-run/server-runtime",
"version": "0.0.0-nightly-d8bcdc1-20230506",
"version": "0.0.0-nightly-d9061d154-20241010",
"description": "Server runtime for Remix",

@@ -19,16 +19,24 @@ "bugs": {

"dependencies": {
"@remix-run/router": "1.6.0",
"@remix-run/router": "1.20.0-pre.0",
"@types/cookie": "^0.6.0",
"@web3-storage/multipart-parser": "^1.0.0",
"cookie": "^0.4.1",
"cookie": "^0.6.0",
"set-cookie-parser": "^2.4.8",
"source-map": "^0.7.3"
"source-map": "^0.7.3",
"turbo-stream": "2.4.0"
},
"devDependencies": {
"@remix-run/web-file": "^3.0.2",
"@types/cookie": "^0.4.0",
"@types/react": "^18.0.15",
"@types/set-cookie-parser": "^2.4.1"
"@types/set-cookie-parser": "^2.4.1",
"typescript": "^5.1.6"
},
"peerDependencies": {
"typescript": "^5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"engines": {
"node": ">=14"
"node": ">=18.0.0"
},

@@ -40,3 +48,6 @@ "files": [

"README.md"
]
}
],
"scripts": {
"tsc": "tsc"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc