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-2483ce5-20221019 to 0.0.0-nightly-24d1b2186-20240830

dist/deprecations.d.ts

18

dist/build.d.ts

@@ -1,4 +0,5 @@

import type { DataFunctionArgs } from "./routeModules";
import type { EntryContext, AssetsManifest } from "./entry";
import type { ActionFunctionArgs, LoaderFunctionArgs } from "./routeModules";
import type { AssetsManifest, EntryContext, FutureConfig } from "./entry";
import type { ServerRouteManifest } from "./routes";
import type { AppLoadContext } from "./data";
/**

@@ -8,2 +9,3 @@ * The output of the compiler for the server build.

export interface ServerBuild {
mode: string;
entry: {

@@ -14,11 +16,17 @@ module: ServerEntryModule;

assets: AssetsManifest;
basename?: string;
publicPath: string;
assetsBuildDirectory: string;
future: FutureConfig;
isSpaMode: boolean;
}
export interface HandleDocumentRequestFunction {
(request: Request, responseStatusCode: number, responseHeaders: Headers, context: EntryContext): Promise<Response> | Response;
(request: Request, responseStatusCode: number, responseHeaders: Headers, context: EntryContext, loadContext: AppLoadContext): Promise<Response> | Response;
}
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;
}
/**

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

handleDataRequest?: HandleDataRequestFunction;
handleError?: HandleErrorFunction;
streamTimeout?: number;
}

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

}
export declare type CookieOptions = CookieParseOptions & CookieSerializeOptions & CookieSignatureOptions;
export type CookieOptions = CookieParseOptions & CookieSerializeOptions & CookieSignatureOptions;
/**

@@ -25,3 +25,3 @@ * A HTTP cookie.

*
* @see https://remix.run/api/remix#cookie-api
* @see https://remix.run/utils/cookies#cookie-api
*/

@@ -55,7 +55,7 @@ export interface Cookie {

}
export declare type CreateCookieFunction = (name: string, cookieOptions?: CookieOptions) => Cookie;
export type CreateCookieFunction = (name: string, cookieOptions?: CookieOptions) => Cookie;
/**
* Creates a logical container for managing a browser cookie from the server.
*
* @see https://remix.run/api/remix#createcookie
* @see https://remix.run/utils/cookies#createcookie
*/

@@ -66,8 +66,8 @@ export declare const createCookieFactory: ({ sign, unsign, }: {

}) => CreateCookieFunction;
export declare type IsCookieFunction = (object: any) => object is Cookie;
export type IsCookieFunction = (object: any) => object is Cookie;
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/api/remix#iscookie
* @see https://remix.run/utils/cookies#iscookie
*/
export declare const isCookie: IsCookieFunction;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -19,5 +19,16 @@ * 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.
*
* @see https://remix.run/api/remix#createcookie
* @see https://remix.run/utils/cookies#createcookie
*/

@@ -29,6 +40,5 @@ const createCookieFactory = ({

let {
secrets,
secrets = [],
...options
} = {
secrets: [],
path: "/",

@@ -43,7 +53,5 @@ sameSite: "lax",

},
get isSigned() {
return secrets.length > 0;
},
get expires() {

@@ -53,6 +61,6 @@ // Max-Age takes precedence over Expires

},
async parse(cookieHeader, parseOptions) {
if (!cookieHeader) return null;
let cookies = cookie.parse(cookieHeader, { ...options,
let cookies = cookie.parse(cookieHeader, {
...options,
...parseOptions

@@ -62,16 +70,14 @@ });

},
async serialize(value, serializeOptions) {
return cookie.serialize(name, value === "" ? "" : await encodeCookieValue(sign, value, secrets), { ...options,
return cookie.serialize(name, value === "" ? "" : await encodeCookieValue(sign, value, secrets), {
...options,
...serializeOptions
});
}
};
};
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/api/remix#iscookie
* @see https://remix.run/utils/cookies#iscookie
*/

@@ -81,13 +87,9 @@ const isCookie = object => {

};
async function encodeCookieValue(sign, value, secrets) {
let encoded = encodeData(value);
if (secrets.length > 0) {
encoded = await sign(encoded, secrets[0]);
}
return encoded;
}
async function decodeCookieValue(unsign, value, secrets) {

@@ -97,3 +99,2 @@ if (secrets.length > 0) {

let unsignedValue = await unsign(value, secret);
if (unsignedValue !== false) {

@@ -103,13 +104,9 @@ return decodeData(unsignedValue);

}
return null;
}
return decodeData(value);
}
function encodeData(value) {
return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
}
function decodeData(value) {

@@ -121,5 +118,5 @@ try {

}
} // See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.escape.js
}
// See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.escape.js
function myEscape(value) {

@@ -130,6 +127,4 @@ let str = value.toString();

let chr, code;
while (index < str.length) {
chr = str.charAt(index++);
if (/[\w*+\-./@]/.exec(chr)) {

@@ -139,3 +134,2 @@ result += chr;

code = chr.charCodeAt(0);
if (code < 256) {

@@ -148,15 +142,11 @@ result += "%" + hex(code, 2);

}
return result;
}
function hex(code, length) {
let result = code.toString(16);
while (result.length < length) result = "0" + result;
return result;
} // See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js
}
// See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js
function myUnescape(value) {

@@ -167,10 +157,7 @@ let str = value.toString();

let chr, part;
while (index < str.length) {
chr = str.charAt(index++);
if (chr === "%") {
if (str.charAt(index) === "u") {
part = str.slice(index + 1, index + 5);
if (/^[\da-f]{4}$/i.exec(part)) {

@@ -183,3 +170,2 @@ result += String.fromCharCode(parseInt(part, 16));

part = str.slice(index, index + 2);
if (/^[\da-f]{2}$/i.exec(part)) {

@@ -192,9 +178,6 @@ result += String.fromCharCode(parseInt(part, 16));

}
result += chr;
}
return result;
}
function warnOnceAboutExpiresCookie(name, expires) {

@@ -201,0 +184,0 @@ warnings.warnOnce(!expires, `The "${name}" cookie has an "expires" property set. ` + `This will cause the expires value to not be updated when the session is committed. ` + `Instead, you should set the expires value when serializing the cookie. ` + `You can use \`commitSession(session, { expires })\` if using a session storage object, ` + `or \`cookie.serialize("value", { expires })\` if you're using the cookie directly.`);

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

export declare type SignFunction = (value: string, secret: string) => Promise<string>;
export declare type UnsignFunction = (cookie: string, secret: string) => Promise<string | false>;
export type SignFunction = (value: string, secret: string) => Promise<string>;
export type UnsignFunction = (cookie: string, secret: string) => Promise<string | false>;

@@ -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
*/

@@ -12,17 +14,18 @@ export interface AppLoadContext {

*/
export declare type AppData = any;
export declare function callRouteAction({ loadContext, routeId, action, params, request, }: {
export type AppData = unknown;
export declare function callRouteAction({ loadContext, action, params, request, routeId, singleFetch, }: {
request: Request;
action: ActionFunction;
params: ActionFunctionArgs["params"];
loadContext: AppLoadContext;
routeId: string;
action?: ActionFunction;
params: DataFunctionArgs["params"];
singleFetch: boolean;
}): Promise<{} | Response | null>;
export declare function callRouteLoader({ loadContext, loader, params, request, routeId, singleFetch, }: {
request: Request;
}): Promise<Response>;
export declare function callRouteLoader({ loadContext, routeId, loader, params, request, }: {
request: Request;
loader: LoaderFunction;
params: LoaderFunctionArgs["params"];
loadContext: AppLoadContext;
routeId: string;
loader?: LoaderFunction;
params: DataFunctionArgs["params"];
loadContext: AppLoadContext;
}): Promise<Response>;
export declare function extractData(response: Response): Promise<unknown>;
singleFetch: boolean;
}): Promise<{} | Response | null>;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

/**
* 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,
routeId,
action,
params,
request
request,
routeId,
singleFetch
}) {
if (!action) {
let response = new Response(null, {
status: 405
});
response.headers.set("X-Remix-Catch", "yes");
return response;
}
let result;
try {
result = await action({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
} catch (error) {
if (!responses.isResponse(error)) {
throw error;
}
if (!responses.isRedirectResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
}
result = error;
}
let result = await action({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
if (result === undefined) {

@@ -57,2 +46,6 @@ throw new Error(`You defined an action for route "${routeId}" but didn't return ` + `anything from your \`action\` function. Please return a value or \`null\`.`);

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

@@ -62,38 +55,35 @@ }

loadContext,
routeId,
loader,
params,
request
request,
routeId,
singleFetch
}) {
if (!loader) {
throw new Error(`You made a ${request.method} request to ${request.url} but did not provide ` + `a default component or \`loader\` for route "${routeId}", ` + `so there is no way to handle the request.`);
let result = await loader({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
if (result === undefined) {
throw new Error(`You defined a loader for route "${routeId}" but didn't return ` + `anything from your \`loader\` function. Please return a value or \`null\`.`);
}
let result;
try {
result = await loader({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
} catch (error) {
if (!responses.isResponse(error)) {
throw error;
if (responses.isDeferredData(result)) {
if (result.init && responses.isRedirectStatusCode(result.init.status || 200)) {
return responses.redirect(new Headers(result.init.headers).get("Location"), result.init);
}
if (!responses.isRedirectResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
}
result = error;
return result;
}
if (result === undefined) {
throw new Error(`You defined a loader for route "${routeId}" but didn't return ` + `anything from your \`loader\` function. Please return a value or \`null\`.`);
// Allow naked object returns when single fetch is enabled
if (singleFetch) {
return result;
}
return responses.isResponse(result) ? result : responses.json(result);
}
// TODO: Document these search params better
// and stop stripping these in V2. These break
// support for running in a SW and also expose
// valuable info to data funcs that is being asked
// for such as "is this a data request?".
function stripIndexParam(request) {

@@ -104,3 +94,2 @@ let url = new URL(request.url);

let indexValuesToKeep = [];
for (let indexValue of indexValues) {

@@ -111,33 +100,32 @@ if (indexValue) {

}
for (let toKeep of indexValuesToKeep) {
url.searchParams.append("index", toKeep);
}
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 stripDataParam(request) {
let url = new URL(request.url);
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 extractData(response) {
let contentType = response.headers.get("Content-Type");
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return response.json();
} // What other data types do we need to handle here? What other kinds of
// responses are people going to be returning from their loaders?
// - application/x-www-form-urlencoded ?
// - multipart/form-data ?
// - binary (audio/video) ?
return response.text();
}
exports.callRouteAction = callRouteAction;
exports.callRouteLoader = callRouteLoader;
exports.extractData = extractData;

@@ -1,15 +0,33 @@

import type { AppState } from "./errors";
import type { RouteManifest, ServerRouteManifest, EntryRoute, ServerRoute } from "./routes";
import type { RouteData } from "./routeData";
import type { RouteMatch } from "./routeMatching";
import type { StaticHandlerContext } from "@remix-run/router";
import type { SerializedError } from "./errors";
import type { RouteManifest, ServerRouteManifest, EntryRoute } from "./routes";
import type { RouteModules, EntryRouteModule } from "./routeModules";
export interface EntryContext {
appState: AppState;
manifest: AssetsManifest;
matches: RouteMatch<EntryRoute>[];
routeData: RouteData;
actionData?: RouteData;
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;
}
export interface FutureConfig {
v3_fetcherPersist: boolean;
v3_relativeSplatPath: boolean;
v3_throwAbortReason: boolean;
unstable_lazyRouteDiscovery: boolean;
unstable_singleFetch: boolean;
}
export interface AssetsManifest {

@@ -23,4 +41,4 @@ entry: {

version: string;
hmrRuntime?: string;
}
export declare function createEntryMatches(matches: RouteMatch<ServerRoute>[], routes: RouteManifest<EntryRoute>): RouteMatch<EntryRoute>[];
export declare function createEntryRouteModules(manifest: ServerRouteManifest): RouteModules<EntryRouteModule>;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

function createEntryMatches(matches, routes) {
return matches.map(match => ({
params: match.params,
pathname: match.pathname,
route: routes[match.route.id]
}));
}
function createEntryRouteModules(manifest) {

@@ -30,3 +23,2 @@ return Object.keys(manifest).reduce((memo, routeId) => {

exports.createEntryMatches = createEntryMatches;
exports.createEntryRouteModules = createEntryRouteModules;

@@ -0,1 +1,3 @@

import type { StaticHandlerContext } from "@remix-run/router";
import { ServerMode } from "./mode";
/**

@@ -42,20 +44,9 @@ * This thing probably warrants some explanation.

*/
export interface AppState {
error?: SerializedError;
catch?: ThrownResponse;
catchBoundaryRouteId: string | null;
loaderBoundaryRouteId: string | null;
renderBoundaryRouteId: string | null;
trackBoundaries: boolean;
trackCatchBoundaries: boolean;
}
export interface ThrownResponse<T = any> {
status: number;
statusText: string;
data: T;
}
export declare type SerializedError = {
export declare function sanitizeError<T = unknown>(error: T, serverMode: ServerMode): T | Error;
export declare function sanitizeErrors(errors: NonNullable<StaticHandlerContext["errors"]>, serverMode: ServerMode): {};
export type SerializedError = {
message: string;
stack?: string;
};
export declare function serializeError(error: Error): Promise<SerializedError>;
export declare function serializeError(error: Error, serverMode: ServerMode): SerializedError;
export declare function serializeErrors(errors: StaticHandlerContext["errors"], serverMode: ServerMode): StaticHandlerContext["errors"];
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

var router = require('@remix-run/router');
var mode = require('./mode.js');
/**

@@ -57,11 +60,65 @@ * This thing probably warrants some explanation.

*/
function sanitizeError(error, serverMode) {
if (error instanceof Error && serverMode !== mode.ServerMode.Development) {
let sanitized = new Error("Unexpected Server Error");
sanitized.stack = undefined;
return sanitized;
}
return error;
}
function sanitizeErrors(errors, serverMode) {
return Object.entries(errors).reduce((acc, [routeId, error]) => {
return Object.assign(acc, {
[routeId]: sanitizeError(error, serverMode)
});
}, {});
}
// must be type alias due to inference issues on interfaces
// https://github.com/microsoft/TypeScript/issues/15300
async function serializeError(error) {
function serializeError(error, serverMode) {
let sanitized = sanitizeError(error, serverMode);
return {
message: error.message,
stack: error.stack
message: sanitized.message,
stack: sanitized.stack
};
}
function serializeErrors(errors, serverMode) {
if (!errors) return null;
let entries = Object.entries(errors);
let serialized = {};
for (let [key, val] of entries) {
// Hey you! If you change this, please change the corresponding logic in
// deserializeErrors in remix-react/errors.ts :)
if (router.isRouteErrorResponse(val)) {
serialized[key] = {
...val,
__type: "RouteErrorResponse"
};
} else if (val instanceof Error) {
let sanitized = sanitizeError(val, serverMode);
serialized[key] = {
message: sanitized.message,
stack: sanitized.stack,
__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
} : {})
};
} else {
serialized[key] = val;
}
}
return serialized;
}
exports.sanitizeError = sanitizeError;
exports.sanitizeErrors = sanitizeErrors;
exports.serializeError = serializeError;
exports.serializeErrors = serializeErrors;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -15,5 +15,16 @@ * 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.
*
* @see https://remix.run/api/remix#createcookie
* @see https://remix.run/utils/cookies#createcookie
*/

@@ -25,6 +36,5 @@ const createCookieFactory = ({

let {
secrets,
secrets = [],
...options
} = {
secrets: [],
path: "/",

@@ -39,7 +49,5 @@ sameSite: "lax",

},
get isSigned() {
return secrets.length > 0;
},
get expires() {

@@ -49,6 +57,6 @@ // Max-Age takes precedence over Expires

},
async parse(cookieHeader, parseOptions) {
if (!cookieHeader) return null;
let cookies = parse(cookieHeader, { ...options,
let cookies = parse(cookieHeader, {
...options,
...parseOptions

@@ -58,16 +66,14 @@ });

},
async serialize(value, serializeOptions) {
return serialize(name, value === "" ? "" : await encodeCookieValue(sign, value, secrets), { ...options,
return serialize(name, value === "" ? "" : await encodeCookieValue(sign, value, secrets), {
...options,
...serializeOptions
});
}
};
};
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/api/remix#iscookie
* @see https://remix.run/utils/cookies#iscookie
*/

@@ -77,13 +83,9 @@ const isCookie = object => {

};
async function encodeCookieValue(sign, value, secrets) {
let encoded = encodeData(value);
if (secrets.length > 0) {
encoded = await sign(encoded, secrets[0]);
}
return encoded;
}
async function decodeCookieValue(unsign, value, secrets) {

@@ -93,3 +95,2 @@ if (secrets.length > 0) {

let unsignedValue = await unsign(value, secret);
if (unsignedValue !== false) {

@@ -99,13 +100,9 @@ return decodeData(unsignedValue);

}
return null;
}
return decodeData(value);
}
function encodeData(value) {
return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
}
function decodeData(value) {

@@ -117,5 +114,5 @@ try {

}
} // See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.escape.js
}
// See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.escape.js
function myEscape(value) {

@@ -126,6 +123,4 @@ let str = value.toString();

let chr, code;
while (index < str.length) {
chr = str.charAt(index++);
if (/[\w*+\-./@]/.exec(chr)) {

@@ -135,3 +130,2 @@ result += chr;

code = chr.charCodeAt(0);
if (code < 256) {

@@ -144,15 +138,11 @@ result += "%" + hex(code, 2);

}
return result;
}
function hex(code, length) {
let result = code.toString(16);
while (result.length < length) result = "0" + result;
return result;
} // See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js
}
// See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js
function myUnescape(value) {

@@ -163,10 +153,7 @@ let str = value.toString();

let chr, part;
while (index < str.length) {
chr = str.charAt(index++);
if (chr === "%") {
if (str.charAt(index) === "u") {
part = str.slice(index + 1, index + 5);
if (/^[\da-f]{4}$/i.exec(part)) {

@@ -179,3 +166,2 @@ result += String.fromCharCode(parseInt(part, 16));

part = str.slice(index, index + 2);
if (/^[\da-f]{2}$/i.exec(part)) {

@@ -188,9 +174,6 @@ result += String.fromCharCode(parseInt(part, 16));

}
result += chr;
}
return result;
}
function warnOnceAboutExpiresCookie(name, expires) {

@@ -197,0 +180,0 @@ warnOnce(!expires, `The "${name}" cookie has an "expires" property set. ` + `This will cause the expires value to not be updated when the session is committed. ` + `Instead, you should set the expires value when serializing the cookie. ` + `You can use \`commitSession(session, { expires })\` if using a session storage object, ` + `or \`cookie.serialize("value", { expires })\` if you're using the cookie directly.`);

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -11,39 +11,28 @@ * Copyright (c) Remix Software Inc.

*/
import { isResponse, isRedirectResponse, json } from './responses.js';
import { isResponse, json, isDeferredData, isRedirectStatusCode, redirect } from './responses.js';
/**
* 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,
routeId,
action,
params,
request
request,
routeId,
singleFetch
}) {
if (!action) {
let response = new Response(null, {
status: 405
});
response.headers.set("X-Remix-Catch", "yes");
return response;
}
let result;
try {
result = await action({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
} catch (error) {
if (!isResponse(error)) {
throw error;
}
if (!isRedirectResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
}
result = error;
}
let result = await action({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
if (result === undefined) {

@@ -53,2 +42,6 @@ throw new Error(`You defined an action for route "${routeId}" but didn't return ` + `anything from your \`action\` function. Please return a value or \`null\`.`);

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

@@ -58,38 +51,35 @@ }

loadContext,
routeId,
loader,
params,
request
request,
routeId,
singleFetch
}) {
if (!loader) {
throw new Error(`You made a ${request.method} request to ${request.url} but did not provide ` + `a default component or \`loader\` for route "${routeId}", ` + `so there is no way to handle the request.`);
let result = await loader({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
if (result === undefined) {
throw new Error(`You defined a loader for route "${routeId}" but didn't return ` + `anything from your \`loader\` function. Please return a value or \`null\`.`);
}
let result;
try {
result = await loader({
request: stripDataParam(stripIndexParam(request)),
context: loadContext,
params
});
} catch (error) {
if (!isResponse(error)) {
throw error;
if (isDeferredData(result)) {
if (result.init && isRedirectStatusCode(result.init.status || 200)) {
return redirect(new Headers(result.init.headers).get("Location"), result.init);
}
if (!isRedirectResponse(error)) {
error.headers.set("X-Remix-Catch", "yes");
}
result = error;
return result;
}
if (result === undefined) {
throw new Error(`You defined a loader for route "${routeId}" but didn't return ` + `anything from your \`loader\` function. Please return a value or \`null\`.`);
// Allow naked object returns when single fetch is enabled
if (singleFetch) {
return result;
}
return isResponse(result) ? result : json(result);
}
// TODO: Document these search params better
// and stop stripping these in V2. These break
// support for running in a SW and also expose
// valuable info to data funcs that is being asked
// for such as "is this a data request?".
function stripIndexParam(request) {

@@ -100,3 +90,2 @@ let url = new URL(request.url);

let indexValuesToKeep = [];
for (let indexValue of indexValues) {

@@ -107,31 +96,31 @@ if (indexValue) {

}
for (let toKeep of indexValuesToKeep) {
url.searchParams.append("index", toKeep);
}
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 stripDataParam(request) {
let url = new URL(request.url);
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 extractData(response) {
let contentType = response.headers.get("Content-Type");
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return response.json();
} // What other data types do we need to handle here? What other kinds of
// responses are people going to be returning from their loaders?
// - application/x-www-form-urlencoded ?
// - multipart/form-data ?
// - binary (audio/video) ?
return response.text();
}
export { callRouteAction, callRouteLoader, extractData };
export { callRouteAction, callRouteLoader };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -11,9 +11,2 @@ * Copyright (c) Remix Software Inc.

*/
function createEntryMatches(matches, routes) {
return matches.map(match => ({
params: match.params,
pathname: match.pathname,
route: routes[match.route.id]
}));
}
function createEntryRouteModules(manifest) {

@@ -26,2 +19,2 @@ return Object.keys(manifest).reduce((memo, routeId) => {

export { createEntryMatches, createEntryRouteModules };
export { createEntryRouteModules };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*/
import { isRouteErrorResponse } from '@remix-run/router';
import { ServerMode } from './mode.js';
/**

@@ -53,11 +56,62 @@ * This thing probably warrants some explanation.

*/
function sanitizeError(error, serverMode) {
if (error instanceof Error && serverMode !== ServerMode.Development) {
let sanitized = new Error("Unexpected Server Error");
sanitized.stack = undefined;
return sanitized;
}
return error;
}
function sanitizeErrors(errors, serverMode) {
return Object.entries(errors).reduce((acc, [routeId, error]) => {
return Object.assign(acc, {
[routeId]: sanitizeError(error, serverMode)
});
}, {});
}
// must be type alias due to inference issues on interfaces
// https://github.com/microsoft/TypeScript/issues/15300
async function serializeError(error) {
function serializeError(error, serverMode) {
let sanitized = sanitizeError(error, serverMode);
return {
message: error.message,
stack: error.stack
message: sanitized.message,
stack: sanitized.stack
};
}
function serializeErrors(errors, serverMode) {
if (!errors) return null;
let entries = Object.entries(errors);
let serialized = {};
for (let [key, val] of entries) {
// Hey you! If you change this, please change the corresponding logic in
// deserializeErrors in remix-react/errors.ts :)
if (isRouteErrorResponse(val)) {
serialized[key] = {
...val,
__type: "RouteErrorResponse"
};
} else if (val instanceof Error) {
let sanitized = sanitizeError(val, serverMode);
serialized[key] = {
message: sanitized.message,
stack: sanitized.stack,
__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
} : {})
};
} else {
serialized[key] = val;
}
}
return serialized;
}
export { serializeError };
export { sanitizeError, sanitizeErrors, serializeError, serializeErrors };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

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

@@ -18,3 +19,2 @@ return async part => {

let value = await handler(part);
if (typeof value !== "undefined" && value !== null) {

@@ -24,6 +24,6 @@ return value;

}
return undefined;
};
}
/**

@@ -33,19 +33,14 @@ * Allows you to handle multipart forms (file uploads) for your app.

* TODO: Update this comment
* @see https://remix.run/api/remix#parsemultipartformdata-node
* @see https://remix.run/utils/parse-multipart-form-data
*/
async function parseMultipartFormData(request, uploadHandler) {
let contentType = request.headers.get("Content-Type") || "";
let [type, boundary] = contentType.split(/\s*;\s*boundary=/);
if (!request.body || !boundary || type !== "multipart/form-data") {
throw new TypeError("Could not parse content as FormData.");
}
let formData = new FormData();
let parts = streamMultipart(request.body, boundary);
for await (let part of parts) {
if (part.done) break;
if (typeof part.filename === "string") {

@@ -56,5 +51,3 @@ // only pass basename as the multipart/form-data spec recommends

}
let value = await uploadHandler(part);
if (typeof value !== "undefined" && value !== null) {

@@ -64,3 +57,2 @@ formData.append(part.name, value);

}
return formData;

@@ -67,0 +59,0 @@ }

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

function getDocumentHeaders(build, matches, routeLoaderResponses, actionResponse) {
return matches.reduce((parentHeaders, match, index) => {
let routeModule = build.routes[match.route.id].module;
let routeLoaderResponse = routeLoaderResponses[match.route.id];
let loaderHeaders = routeLoaderResponse ? routeLoaderResponse.headers : new Headers();
let actionHeaders = actionResponse ? actionResponse.headers : new Headers();
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
} = match.route;
let routeModule = build.routes[id].module;
let loaderHeaders = context.loaderHeaders[id] || new Headers();
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
}) : routeModule.headers : undefined); // Automatically preserve Set-Cookie headers that were set either by the
// loader or by a parent route.
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : undefined
}) : routeModule.headers : undefined);
// Automatically preserve Set-Cookie headers from bubbled responses,
// loaders, errors, and parent routes
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);

@@ -33,6 +81,4 @@ prependCookies(loaderHeaders, headers);

}
function prependCookies(parentHeaders, childHeaders) {
let parentSetCookieString = parentHeaders.get("Set-Cookie");
if (parentSetCookieString) {

@@ -39,0 +85,0 @@ let cookies = splitCookiesString(parentSetCookieString);

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

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

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

export { MaxPartSizeExceededError } from './upload/errors.js';
export { broadcastDevReady, logDevReady, setDevServerHooks as unstable_setDevServerHooks } from './dev.js';
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
// We've chosen to inline the utility here to reduce the number of npm dependencies we have,
// slightly decrease the code size compared the original package and make it esm compatible.
const ESCAPE_LOOKUP = {

@@ -17,0 +19,0 @@ "&": "\\u0026",

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -14,10 +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) {

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

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*/
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

@@ -19,42 +22,50 @@ // interfaces must conform to the types they extend

*
* @see https://remix.run/api/remix#json
* @see https://remix.run/utils/json
*/
const json = (data, init = {}) => {
let responseInit = typeof init === "number" ? {
status: init
} : init;
let headers = new Headers(responseInit.headers);
if (!headers.has("Content-Type")) {
headers.set("Content-Type", "application/json; charset=utf-8");
}
return new Response(JSON.stringify(data), { ...responseInit,
headers
});
return json$1(data, init);
};
/**
* This is a shortcut for creating Remix deferred responses
*
* @see https://remix.run/utils/defer
*/
const defer = (data, init = {}) => {
return defer$1(data, init);
};
/**
* A redirect response. Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/api/remix#redirect
* @see https://remix.run/utils/redirect
*/
const redirect = (url, init = 302) => {
let responseInit = init;
return redirect$1(url, init);
};
if (typeof responseInit === "number") {
responseInit = {
status: responseInit
};
} else if (typeof responseInit.status === "undefined") {
responseInit.status = 302;
}
/**
* 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);
};
let headers = new Headers(responseInit.headers);
headers.set("Location", url);
return new Response(null, { ...responseInit,
headers
});
/**
* 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) {
let deferred = value;
return deferred && typeof deferred === "object" && typeof deferred.data === "object" && typeof deferred.subscribe === "function" && typeof deferred.cancel === "function" && typeof deferred.resolveData === "function";
}
function isResponse(value) {

@@ -64,9 +75,62 @@ return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";

const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
function isRedirectStatusCode(statusCode) {
return redirectStatusCodes.has(statusCode);
}
function isRedirectResponse(response) {
return redirectStatusCodes.has(response.status);
return isRedirectStatusCode(response.status);
}
function isCatchResponse(response) {
return response.headers.get("X-Remix-Catch") != null;
function isTrackedPromise(value) {
return value != null && typeof value.then === "function" && value._tracked === true;
}
export { isCatchResponse, isRedirectResponse, isResponse, json, redirect };
// TODO: Figure out why ReadableStream types are borked sooooooo badly
// in this file. Probably related to our TS configurations and configs
// bleeding into each other.
const DEFERRED_VALUE_PLACEHOLDER_PREFIX = "__deferred_promise:";
function createDeferredReadableStream(deferredData, signal, serverMode) {
let encoder = new TextEncoder();
let stream = new ReadableStream({
async start(controller) {
let criticalData = {};
let preresolvedKeys = [];
for (let [key, value] of Object.entries(deferredData.data)) {
if (isTrackedPromise(value)) {
criticalData[key] = `${DEFERRED_VALUE_PLACEHOLDER_PREFIX}${key}`;
if (typeof value._data !== "undefined" || typeof value._error !== "undefined") {
preresolvedKeys.push(key);
}
} else {
criticalData[key] = value;
}
}
// Send the critical data
controller.enqueue(encoder.encode(JSON.stringify(criticalData) + "\n\n"));
for (let preresolvedKey of preresolvedKeys) {
enqueueTrackedPromise(controller, encoder, preresolvedKey, deferredData.data[preresolvedKey], serverMode);
}
let unsubscribe = deferredData.subscribe((aborted, settledKey) => {
if (settledKey) {
enqueueTrackedPromise(controller, encoder, settledKey, deferredData.data[settledKey], serverMode);
}
});
await deferredData.resolveData(signal);
unsubscribe();
controller.close();
}
});
return stream;
}
function enqueueTrackedPromise(controller, encoder, settledKey, promise, serverMode) {
if ("_error" in promise) {
controller.enqueue(encoder.encode("error:" + JSON.stringify({
[settledKey]: promise._error instanceof Error ? serializeError(promise._error, serverMode) : promise._error
}) + "\n\n"));
} else {
controller.enqueue(encoder.encode("data:" + JSON.stringify({
[settledKey]: promise._data ?? null
}) + "\n\n"));
}
}
export { createDeferredReadableStream, defer, isDeferredData, isRedirectResponse, isRedirectStatusCode, isResponse, json, redirect, redirectDocument, replace };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*/
import { matchRoutes } from 'react-router-dom';
import { matchRoutes } from '@remix-run/router';
// TODO: export/import from react-router-dom
function matchServerRoutes(routes, pathname) {
let matches = matchRoutes(routes, pathname);
function matchServerRoutes(routes, pathname, basename) {
let matches = matchRoutes(routes, pathname, basename);
if (!matches) return null;

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

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -11,9 +11,70 @@ * Copyright (c) Remix Software Inc.

*/
// TODO: RRR - Change import to @remix-run/router
function createRoutes(manifest, parentId) {
return Object.entries(manifest).filter(([, route]) => route.parentId === parentId).map(([id, route]) => ({ ...route,
children: createRoutes(manifest, id)
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) {
let routes = {};
Object.values(manifest).forEach(route => {
let parentId = route.parentId || "";
if (!routes[parentId]) {
routes[parentId] = [];
}
routes[parentId].push(route);
});
return routes;
}
// Create a map of routes by parentId to use recursively instead of
// repeatedly filtering the manifest.
function createRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
return (routesByParentId[parentId] || []).map(route => ({
...route,
children: createRoutes(manifest, route.id, routesByParentId)
}));
} // Convert the Remix ServerManifest into DataRouteObject's for use with
}
export { createRoutes };
// Convert the Remix ServerManifest into DataRouteObject's for use with
// createStaticHandler
function createStaticHandlerDataRoutes(manifest, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
return (routesByParentId[parentId] || []).map(route => {
let commonRoute = {
// Always include root due to default boundaries
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
id: route.id,
path: route.path,
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,
params: args.params,
loadContext: args.context,
loader: route.module.loader,
routeId: route.id,
singleFetch: future.unstable_singleFetch === true
}) : undefined,
action: route.module.action ? (args, dataStrategyCtx) => callRouteAction({
request: args.request,
params: args.params,
loadContext: args.context,
action: route.module.action,
routeId: route.id,
singleFetch: future.unstable_singleFetch === true
}) : undefined,
handle: route.module.handle
};
return route.index ? {
index: true,
...commonRoute
} : {
caseSensitive: route.caseSensitive,
children: createStaticHandlerDataRoutes(manifest, future, route.id, routesByParentId),
...commonRoute
};
});
}
export { createRoutes, createStaticHandlerDataRoutes };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -11,69 +11,149 @@ * Copyright (c) Remix Software Inc.

*/
import { callRouteAction, callRouteLoader, extractData } from './data.js';
import { createEntryRouteModules, createEntryMatches } from './entry.js';
import { serializeError } from './errors.js';
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 { getDocumentHeaders } from './headers.js';
import { isServerMode, ServerMode } from './mode.js';
import invariant from './invariant.js';
import { ServerMode, isServerMode } from './mode.js';
import { matchServerRoutes } from './routeMatching.js';
import { createRoutes } from './routes.js';
import { isRedirectResponse, isCatchResponse, json } from './responses.js';
import { createRoutes, createStaticHandlerDataRoutes } from './routes.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';
// TODO: RRR - Change import to @remix-run/router
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, {
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")) {
let responsePromise = handleDataRequest({
request: // We need to clone the request here instead of the call to the new
// handler otherwise the first handler will lock the body for the other.
// Cloning here allows the new handler to be the stream reader and delegate
// chunks back to this cloned request.
request,
loadContext,
matches: matches,
serverMode
});
if (_build.future.unstable_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 responsePromise;
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.params,
params,
request
});
if (isRedirectResponse(response)) {
response = createRemixRedirectResponse(response, _build.basename);
}
}
} else if (matches && matches[matches.length - 1].route.module.default == null) {
let responsePromise = handleResourceRequest({
request: // We need to clone the request here instead of the call to the new
// handler otherwise the first handler will lock the body for the other.
// Cloning here allows the new handler to be the stream reader and delegate
// chunks back to this cloned request.
request,
loadContext,
matches,
serverMode
});
response = await responsePromise;
} else if (_build.future.unstable_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 handleDocumentRequest({
build,
loadContext,
matches,
request,
routes,
serverMode
});
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);
}
if (request.method === "HEAD") {

@@ -86,346 +166,210 @@ return new Response(null, {

}
return response;
};
};
async function handleDataRequest({
loadContext,
matches,
request,
serverMode
}) {
if (!isValidRequestMethod(request)) {
return errorBoundaryError(new Error(`Invalid request method "${request.method}"`), 405);
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()
}
let url = new URL(request.url);
if (!matches) {
return errorBoundaryError(new Error(`No route matches URL "${url.pathname}"`), 404);
}
let response;
let match;
return new Response("Invalid Request", {
status: 400
});
}
async function handleDataRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {
if (isActionRequest(request)) {
match = getRequestMatch(url, matches);
response = await callRouteAction({
loadContext,
action: match.route.module.action,
routeId: match.route.id,
params: match.params,
request: request
});
} else {
let routeId = url.searchParams.get("_data");
if (!routeId) {
return errorBoundaryError(new Error(`Missing route id in ?_data`), 403);
}
let tempMatch = matches.find(match => match.route.id === routeId);
if (!tempMatch) {
return errorBoundaryError(new Error(`Route "${routeId}" does not match URL "${url.pathname}"`), 403);
}
match = tempMatch;
response = await callRouteLoader({
loadContext,
loader: match.route.module.loader,
routeId: match.route.id,
params: match.params,
request
});
}
let response = await staticHandler.queryRoute(request, {
routeId,
requestContext: loadContext
});
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.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);
}
if (UNSAFE_DEFERRED_SYMBOL in response) {
let deferredData = response[UNSAFE_DEFERRED_SYMBOL];
let body = createDeferredReadableStream(deferredData, request.signal, serverMode);
let init = deferredData.init || {};
let headers = new Headers(init.headers);
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 (serverMode !== ServerMode.Test) {
console.error(error);
if (isResponse(error)) {
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
if (serverMode === ServerMode.Development && error instanceof Error) {
return errorBoundaryError(error, 500);
if (isRouteErrorResponse(error)) {
handleError(error);
return errorResponseToJson(error, serverMode);
}
return errorBoundaryError(new Error("Unexpected Server Error"), 500);
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: {
"X-Remix-Error": "yes"
}
});
}
}
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);
async function handleDocumentRequest({
build,
loadContext,
matches,
request,
routes,
serverMode
}) {
let url = new URL(request.url);
let appState = {
trackBoundaries: true,
trackCatchBoundaries: true,
catchBoundaryRouteId: null,
renderBoundaryRouteId: null,
loaderBoundaryRouteId: null,
error: undefined,
catch: undefined
};
// 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");
if (!isValidRequestMethod(request)) {
matches = null;
appState.trackCatchBoundaries = false;
appState.catch = {
data: null,
status: 405,
statusText: "Method Not Allowed"
};
} else if (!matches) {
appState.trackCatchBoundaries = false;
appState.catch = {
data: null,
status: 404,
statusText: "Not Found"
};
}
// 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");
let actionStatus;
let actionData;
let actionMatch;
let actionResponse;
if (matches && isActionRequest(request)) {
actionMatch = getRequestMatch(url, matches);
try {
actionResponse = await callRouteAction({
loadContext,
action: actionMatch.route.module.action,
routeId: actionMatch.route.id,
params: actionMatch.params,
request: request
});
if (isRedirectResponse(actionResponse)) {
return actionResponse;
}
actionStatus = {
status: actionResponse.status,
statusText: actionResponse.statusText
};
if (isCatchResponse(actionResponse)) {
appState.catchBoundaryRouteId = getDeepestRouteIdWithBoundary(matches, "CatchBoundary");
appState.trackCatchBoundaries = false;
appState.catch = { ...actionStatus,
data: await extractData(actionResponse)
};
} else {
actionData = {
[actionMatch.route.id]: await extractData(actionResponse)
};
}
} catch (error) {
appState.loaderBoundaryRouteId = getDeepestRouteIdWithBoundary(matches, "ErrorBoundary");
appState.trackBoundaries = false;
appState.error = await serializeError(error);
if (serverMode !== ServerMode.Test) {
console.error(`There was an error running the action for route ${actionMatch.route.id}`);
}
}
// 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 handleDocumentRequest(serverMode, build, staticHandler, request, loadContext, handleError, criticalCss) {
let context;
try {
context = await staticHandler.query(request, {
requestContext: loadContext
});
} catch (error) {
handleError(error);
return new Response(null, {
status: 500
});
}
let routeModules = createEntryRouteModules(build.routes);
let matchesToLoad = matches || []; // get rid of the action, we don't want to call it's loader either
// because we'll be rendering the error/catch boundary, if you can get
// access to the loader data in the error/catch boundary then how the heck
// is it supposed to deal with thrown responses and/or errors in the loader?
if (appState.catch) {
matchesToLoad = getMatchesUpToDeepestBoundary(matchesToLoad, "CatchBoundary").slice(0, -1);
} else if (appState.error) {
matchesToLoad = getMatchesUpToDeepestBoundary(matchesToLoad, "ErrorBoundary").slice(0, -1);
if (isResponse(context)) {
return context;
}
let headers = getDocumentHeaders(build, context);
let loaderRequest = new Request(request.url, {
body: null,
headers: request.headers,
method: request.method,
redirect: request.redirect,
signal: request.signal
});
let routeLoaderResults = await Promise.allSettled(matchesToLoad.map(match => match.route.module.loader ? callRouteLoader({
loadContext,
loader: match.route.module.loader,
routeId: match.route.id,
params: match.params,
request: loaderRequest
}) : Promise.resolve(undefined))); // Store the state of the action. We will use this to determine later
// what catch or error boundary should be rendered under cases where
// actions don't throw but loaders do, actions throw and parent loaders
// also throw, etc.
let actionCatch = appState.catch;
let actionError = appState.error;
let actionCatchBoundaryRouteId = appState.catchBoundaryRouteId;
let actionLoaderBoundaryRouteId = appState.loaderBoundaryRouteId; // Reset the app error and catch state to propagate the loader states
// from the results into the app state.
appState.catch = undefined;
appState.error = undefined;
let routeLoaderResponses = {};
let loaderStatusCodes = [];
let routeData = {};
for (let index = 0; index < matchesToLoad.length; index++) {
let match = matchesToLoad[index];
let result = routeLoaderResults[index];
let error = result.status === "rejected" ? result.reason : undefined;
let response = result.status === "fulfilled" ? result.value : undefined;
let isRedirect = response ? isRedirectResponse(response) : false;
let isCatch = response ? isCatchResponse(response) : false; // If a parent loader has already caught or error'd, bail because
// we don't need any more child data.
if (appState.catch || appState.error) {
break;
} // If there is a response and it's a redirect, do it unless there
// is an action error or catch state, those action boundary states
// take precedence over loader sates, this means if a loader redirects
// after an action catches or errors we won't follow it, and instead
// render the boundary caused by the action.
if (!actionCatch && !actionError && response && isRedirect) {
return response;
} // Track the boundary ID's for the loaders
if (match.route.module.CatchBoundary) {
appState.catchBoundaryRouteId = match.route.id;
}
if (match.route.module.ErrorBoundary) {
appState.loaderBoundaryRouteId = match.route.id;
}
if (error) {
loaderStatusCodes.push(500);
appState.trackBoundaries = false;
appState.error = await serializeError(error);
if (serverMode !== ServerMode.Test) {
console.error(`There was an error running the data loader for route ${match.route.id}`);
// 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);
}
break;
} else if (response) {
routeLoaderResponses[match.route.id] = response;
loaderStatusCodes.push(response.status);
if (isCatch) {
// If it's a catch response, store it in app state, and bail
appState.trackCatchBoundaries = false;
appState.catch = {
data: await extractData(response),
status: response.status,
statusText: response.statusText
};
break;
} else {
// Extract and store the loader data
routeData[match.route.id] = await extractData(response);
}
}
} // If there was not a loader catch or error state triggered reset the
// boundaries as they are probably deeper in the tree if the action
// initially triggered a boundary as that match would not exist in the
// matches to load.
if (!appState.catch) {
appState.catchBoundaryRouteId = actionCatchBoundaryRouteId;
});
context.errors = sanitizeErrors(context.errors, serverMode);
}
if (!appState.error) {
appState.loaderBoundaryRouteId = actionLoaderBoundaryRouteId;
} // If there was an action error or catch, we will reset the state to the
// initial values, otherwise we will use whatever came out of the loaders.
appState.catch = actionCatch || appState.catch;
appState.error = actionError || appState.error;
let renderableMatches = getRenderableMatches(matches, appState);
if (!renderableMatches) {
renderableMatches = [];
let root = routes[0];
if (root !== null && root !== void 0 && root.module.CatchBoundary) {
appState.catchBoundaryRouteId = "root";
renderableMatches.push({
params: {},
pathname: "",
route: routes[0]
});
}
} // Handle responses with a non-200 status code. The first loader with a
// non-200 status code determines the status code for the whole response.
let notOkResponse = actionStatus && actionStatus.status !== 200 ? actionStatus.status : loaderStatusCodes.find(status => status !== 200);
let responseStatusCode = appState.error ? 500 : typeof notOkResponse === "number" ? notOkResponse : appState.catch ? appState.catch.status : 200;
let responseHeaders = getDocumentHeaders(build, renderableMatches, routeLoaderResponses, actionResponse);
let entryMatches = createEntryMatches(renderableMatches, build.assets.routes);
let serverHandoff = {
actionData,
appState: appState,
matches: entryMatches,
routeData
// 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 = { ...serverHandoff,
let entryContext = {
manifest: build.assets,
routeModules,
serverHandoffString: createServerHandoffString(serverHandoff)
routeModules: createEntryRouteModules(build.routes),
staticHandlerContext: context,
criticalCss,
serverHandoffString: createServerHandoffString({
basename: build.basename,
criticalCss,
future: build.future,
isSpaMode: build.isSpaMode,
...(!build.future.unstable_singleFetch ? {
state
} : null)
}),
...(build.future.unstable_singleFetch ? {
serverHandoffStream: encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null),
future: build.future,
isSpaMode: build.isSpaMode,
serializeError: err => serializeError(err, serverMode)
};
let handleDocumentRequest = build.entry.module.default;
let handleDocumentRequestFunction = build.entry.module.default;
try {
return await handleDocumentRequest(request, responseStatusCode, responseHeaders, entryContext);
return await handleDocumentRequestFunction(request, context.statusCode, headers, entryContext, loadContext);
} catch (error) {
responseStatusCode = 500; // Go again, this time with the componentDidCatch emulation. As it rendered
// last time we mutated `componentDidCatch.routeId` for the last rendered
// route, now we know where to render the error boundary (feels a little
// hacky but that's how hooks work). This tells the emulator to stop
// tracking the `routeId` as we render because we already have an error to
// render.
handleError(error);
let errorForSecondRender = error;
appState.trackBoundaries = false;
appState.error = await serializeError(error);
entryContext.serverHandoffString = createServerHandoffString(serverHandoff);
// 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, errorForSecondRender);
// Sanitize errors outside of development environments
if (context.errors) {
context.errors = sanitizeErrors(context.errors, serverMode);
}
// 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 = {
...entryContext,
staticHandlerContext: context,
serverHandoffString: createServerHandoffString({
basename: build.basename,
future: build.future,
isSpaMode: build.isSpaMode,
...(!build.future.unstable_singleFetch ? {
state
} : null)
}),
...(build.future.unstable_singleFetch ? {
serverHandoffStream: encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null)
};
try {
return await handleDocumentRequest(request, responseStatusCode, responseHeaders, entryContext);
return await handleDocumentRequestFunction(request, context.statusCode, headers, entryContext, loadContext);
} catch (error) {
handleError(error);
return returnLastResortErrorResponse(error, serverMode);

@@ -435,53 +379,46 @@ }

}
async function handleResourceRequest({
loadContext,
matches,
request,
serverMode
}) {
let match = matches.slice(-1)[0];
async function handleResourceRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {
if (isActionRequest(request)) {
return await callRouteAction({
loadContext,
action: match.route.module.action,
routeId: match.route.id,
params: match.params,
request
});
} else {
return await callRouteLoader({
loadContext,
loader: match.route.module.loader,
routeId: match.route.id,
params: match.params,
request
});
// Note we keep the routeId here to align with the Remix handling of
// resource routes which doesn't take ?index into account and just takes
// the leaf match
let response = await staticHandler.queryRoute(request, {
routeId,
requestContext: loadContext
});
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.unstable_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");
return response;
} catch (error) {
if (isResponse(error)) {
// Note: Not functionally required but ensures that our response headers
// match identically to what Remix returns
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
if (isRouteErrorResponse(error)) {
if (error) {
handleError(error);
}
return errorResponseToJson(error, serverMode);
}
handleError(error);
return returnLastResortErrorResponse(error, serverMode);
}
}
const validActionMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
function isActionRequest({
method
}) {
return validActionMethods.has(method.toUpperCase());
}
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
function isValidRequestMethod({
method
}) {
return validRequestMethods.has(method.toUpperCase());
}
async function errorBoundaryError(error, status) {
return json(await serializeError(error), {
status,
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: {

@@ -492,78 +429,9 @@ "X-Remix-Error": "yes"

}
function isIndexRequestUrl(url) {
// only use bare `?index` params without a value
// ✅ /foo?index
// ✅ /foo?index&index=123
// ✅ /foo?index=123&index
// ❌ /foo?index=123
return url.searchParams.getAll("index").some(param => param === "");
}
function getRequestMatch(url, matches) {
let match = matches.slice(-1)[0];
if (!isIndexRequestUrl(url) && match.route.id.endsWith("/index")) {
return matches.slice(-2)[0];
}
return match;
}
function getDeepestRouteIdWithBoundary(matches, key) {
let matched = getMatchesUpToDeepestBoundary(matches, key).slice(-1)[0];
return matched ? matched.route.id : null;
}
function getMatchesUpToDeepestBoundary(matches, key) {
let deepestBoundaryIndex = -1;
matches.forEach((match, index) => {
if (match.route.module[key]) {
deepestBoundaryIndex = index;
}
});
if (deepestBoundaryIndex === -1) {
// no route error boundaries, don't need to call any loaders
return [];
}
return matches.slice(0, deepestBoundaryIndex + 1);
} // This prevents `<Outlet/>` from rendering anything below where the error threw
// TODO: maybe do this in <RemixErrorBoundary + context>
function getRenderableMatches(matches, appState) {
if (!matches) {
return null;
} // no error, no worries
if (!appState.catch && !appState.error) {
return matches;
}
let lastRenderableIndex = -1;
matches.forEach((match, index) => {
let id = match.route.id;
if (appState.renderBoundaryRouteId === id || appState.loaderBoundaryRouteId === id || appState.catchBoundaryRouteId === id) {
lastRenderableIndex = index;
}
});
return matches.slice(0, lastRenderableIndex + 1);
}
function returnLastResortErrorResponse(error, serverMode) {
if (serverMode !== ServerMode.Test) {
console.error(error);
}
let message = "Unexpected Server Error";
if (serverMode !== ServerMode.Production) {
message += `\n\n${String(error)}`;
} // Good grief folks, get your act together 😂!
}
// Good grief folks, get your act together 😂!
return new Response(message, {

@@ -576,3 +444,42 @@ status: 500,

}
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
});
}
// 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-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

// TODO: Remove Promises from serialization
function createServerHandoffString(serverHandoff) {

@@ -15,0 +16,0 @@ // Uses faster alternative of jsesc to escape data returned from the loaders.

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

/**
* Session persists data across HTTP requests.
*
* @see https://remix.run/utils/sessions#session-api
*/
function flash(name) {
return `__flash_${name}__`;
}
/**

@@ -29,3 +34,3 @@ * Creates a new Session object.

*
* @see https://remix.run/api/remix#createsession
* @see https://remix.run/utils/sessions#createsession
*/

@@ -38,15 +43,11 @@ const createSession = (initialData = {}, id = "") => {

},
get data() {
return Object.fromEntries(map);
},
has(name) {
return map.has(name) || map.has(flash(name));
},
get(name) {
if (map.has(name)) return map.get(name);
let flashName = flash(name);
if (map.has(flashName)) {

@@ -57,25 +58,19 @@ let value = map.get(flashName);

}
return undefined;
},
set(name, value) {
map.set(name, value);
},
flash(name, value) {
map.set(flash(name), value);
},
unset(name) {
map.delete(name);
}
};
};
/**
* Returns true if an object is a Remix session.
*
* @see https://remix.run/api/remix#issession
* @see https://remix.run/utils/sessions#issession
*/

@@ -85,2 +80,3 @@ const isSession = object => {

};
/**

@@ -95,2 +91,12 @@ * SessionStorage stores session data between HTTP requests and knows how to

/**
* 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.

@@ -101,3 +107,3 @@ *

*
* @see https://remix.run/api/remix#createsessionstorage
* @see https://remix.run/utils/sessions#createsessionstorage
*/

@@ -119,3 +125,2 @@ const createSessionStorageFactory = createCookie => ({

},
async commitSession(session, options) {

@@ -126,25 +131,24 @@ let {

} = 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);
}
return cookie.serialize(id, options);
},
async destroySession(session, options) {
await deleteData(session.id);
return cookie.serialize("", { ...options,
return cookie.serialize("", {
...options,
maxAge: undefined,
expires: new Date(0)
});
}
};
};
function warnOnceAboutSigningSessionCookie(cookie) {
warnOnce(cookie.isSigned, `The "${cookie.name}" cookie is not signed, but session cookies should be ` + `signed to prevent tampering on the client before they are sent back to the ` + `server. See https://remix.run/api/remix#signing-cookies ` + `for more information.`);
warnOnce(cookie.isSigned, `The "${cookie.name}" cookie is not signed, but session cookies should be ` + `signed to prevent tampering on the client before they are sent back to the ` + `server. See https://remix.run/utils/cookies#signing-cookies ` + `for more information.`);
}
export { createSession, createSessionStorageFactory, isSession, warnOnceAboutSigningSessionCookie };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*
* @see https://remix.run/api/remix#createcookiesessionstorage
* @see https://remix.run/utils/sessions#createcookiesessionstorage
*/

@@ -35,19 +35,16 @@ const createCookieSessionStorageFactory = createCookie => ({

},
async commitSession(session, options) {
let serializedCookie = await cookie.serialize(session.data, options);
if (serializedCookie.length > 4096) {
throw new Error("Cookie length will exceed browser maximum. Length: " + serializedCookie.length);
}
return serializedCookie;
},
async destroySession(_session, options) {
return cookie.serialize("", { ...options,
return cookie.serialize("", {
...options,
maxAge: undefined,
expires: new Date(0)
});
}
};

@@ -54,0 +51,0 @@ };

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*
* @see https://remix.run/api/remix#creatememorysessionstorage
* @see https://remix.run/utils/sessions#creatememorysessionstorage
*/

@@ -24,9 +24,7 @@ const createMemorySessionStorageFactory = createSessionStorage => ({

} = {}) => {
let uniqueId = 0;
let map = new Map();
return createSessionStorage({
cookie,
async createData(data, expires) {
let id = (++uniqueId).toString();
let id = Math.random().toString(36).substring(2, 10);
map.set(id, {

@@ -38,3 +36,2 @@ data,

},
async readData(id) {

@@ -46,14 +43,11 @@ if (map.has(id)) {

} = map.get(id);
if (!expires || expires > new Date()) {
return data;
} // Remove expired session data.
}
// Remove expired session data.
if (expires) map.delete(id);
}
return null;
},
async updateData(id, data, expires) {

@@ -65,7 +59,5 @@ map.set(id, {

},
async deleteData(id) {
map.delete(id);
}
});

@@ -72,0 +64,0 @@ };

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

}
}
export { MaxPartSizeExceededError };
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -30,16 +30,11 @@ * Copyright (c) Remix Software Inc.

}
let size = 0;
let chunks = [];
for await (let chunk of data) {
size += chunk.byteLength;
if (size > maxPartSize) {
throw new MaxPartSizeExceededError(name, maxPartSize);
}
chunks.push(chunk);
}
if (typeof filename === "string") {

@@ -50,3 +45,2 @@ return new File(chunks, filename, {

}
return await new Blob(chunks, {

@@ -53,0 +47,0 @@ type: contentType

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

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

export declare type UploadHandlerPart = {
export type UploadHandlerPart = {
name: string;

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

};
export declare type UploadHandler = (part: UploadHandlerPart) => Promise<File | string | null | undefined>;
export type UploadHandler = (part: UploadHandlerPart) => Promise<File | string | null | undefined>;
export declare function composeUploadHandlers(...handlers: UploadHandler[]): UploadHandler;

@@ -14,4 +14,4 @@ /**

* TODO: Update this comment
* @see https://remix.run/api/remix#parsemultipartformdata-node
* @see https://remix.run/utils/parse-multipart-form-data
*/
export declare function parseMultipartFormData(request: Request, uploadHandler: UploadHandler): Promise<FormData>;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

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

@@ -22,3 +23,2 @@ return async part => {

let value = await handler(part);
if (typeof value !== "undefined" && value !== null) {

@@ -28,6 +28,6 @@ return value;

}
return undefined;
};
}
/**

@@ -37,19 +37,14 @@ * Allows you to handle multipart forms (file uploads) for your app.

* TODO: Update this comment
* @see https://remix.run/api/remix#parsemultipartformdata-node
* @see https://remix.run/utils/parse-multipart-form-data
*/
async function parseMultipartFormData(request, uploadHandler) {
let contentType = request.headers.get("Content-Type") || "";
let [type, boundary] = contentType.split(/\s*;\s*boundary=/);
if (!request.body || !boundary || type !== "multipart/form-data") {
throw new TypeError("Could not parse content as FormData.");
}
let formData = new FormData();
let parts = multipartParser.streamMultipart(request.body, boundary);
for await (let part of parts) {
if (part.done) break;
if (typeof part.filename === "string") {

@@ -60,5 +55,3 @@ // only pass basename as the multipart/form-data spec recommends

}
let value = await uploadHandler(part);
if (typeof value !== "undefined" && value !== null) {

@@ -68,3 +61,2 @@ formData.append(part.name, value);

}
return formData;

@@ -71,0 +63,0 @@ }

@@ -0,4 +1,3 @@

import type { StaticHandlerContext } from "@remix-run/router";
import type { ServerBuild } from "./build";
import type { ServerRoute } from "./routes";
import type { RouteMatch } from "./routeMatching";
export declare function getDocumentHeaders(build: ServerBuild, matches: RouteMatch<ServerRoute>[], routeLoaderResponses: Record<string, Response>, actionResponse?: Response): Headers;
export declare function getDocumentHeaders(build: ServerBuild, context: StaticHandlerContext): Headers;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

function getDocumentHeaders(build, matches, routeLoaderResponses, actionResponse) {
return matches.reduce((parentHeaders, match, index) => {
let routeModule = build.routes[match.route.id].module;
let routeLoaderResponse = routeLoaderResponses[match.route.id];
let loaderHeaders = routeLoaderResponse ? routeLoaderResponse.headers : new Headers();
let actionHeaders = actionResponse ? actionResponse.headers : new Headers();
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
} = match.route;
let routeModule = build.routes[id].module;
let loaderHeaders = context.loaderHeaders[id] || new Headers();
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
}) : routeModule.headers : undefined); // Automatically preserve Set-Cookie headers that were set either by the
// loader or by a parent route.
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : undefined
}) : routeModule.headers : undefined);
// Automatically preserve Set-Cookie headers from bubbled responses,
// loaders, errors, and parent routes
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);

@@ -37,6 +85,4 @@ prependCookies(loaderHeaders, headers);

}
function prependCookies(parentHeaders, childHeaders) {
let parentSetCookieString = parentHeaders.get("Set-Cookie");
if (parentSetCookieString) {

@@ -43,0 +89,0 @@ let cookies = setCookieParser.splitCookiesString(parentSetCookieString);

export { createCookieFactory, isCookie } from "./cookies";
export { composeUploadHandlers as unstable_composeUploadHandlers, parseMultipartFormData as unstable_parseMultipartFormData, } from "./formData";
export { json, redirect } from "./responses";
export { defer, json, redirect, redirectDocument, replace } from "./responses";
export { SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, data as unstable_data, } from "./single-fetch";
export type { SingleFetchResult as UNSAFE_SingleFetchResult, SingleFetchResults as UNSAFE_SingleFetchResults, } from "./single-fetch";
export { createRequestHandler } from "./server";
export { createSession, isSession, createSessionStorageFactory, } from "./sessions";
export { createSession, createSessionStorageFactory, isSession, } from "./sessions";
export { createCookieSessionStorageFactory } from "./sessions/cookieStorage";

@@ -10,3 +12,5 @@ export { createMemorySessionStorageFactory } from "./sessions/memoryStorage";

export { MaxPartSizeExceededError } from "./upload/errors";
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, HandleDataRequestFunction, HandleDocumentRequestFunction, HeadersFunction, HtmlLinkDescriptor, HtmlMetaDescriptor, LinkDescriptor, LinksFunction, LoaderArgs, LoaderFunction, MemoryUploadHandlerFilterArgs, MemoryUploadHandlerOptions, MetaDescriptor, MetaFunction, PageLinkDescriptor, RequestHandler, RouteComponent, RouteHandle, SerializeFrom, ServerBuild, ServerEntryModule, Session, SessionData, SessionIdStorageStrategy, SessionStorage, SignFunction, TypedResponse, UnsignFunction, UploadHandlerPart, UploadHandler, } 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-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -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');

@@ -25,2 +26,3 @@ var sessions = require('./sessions.js');

var errors = require('./upload/errors.js');
var dev = require('./dev.js');

@@ -33,4 +35,9 @@

exports.unstable_parseMultipartFormData = formData.parseMultipartFormData;
exports.defer = responses.defer;
exports.json = responses.json;
exports.redirect = responses.redirect;
exports.redirectDocument = responses.redirectDocument;
exports.replace = responses.replace;
exports.UNSAFE_SingleFetchRedirectSymbol = singleFetch.SingleFetchRedirectSymbol;
exports.unstable_data = singleFetch.data;
exports.createRequestHandler = server.createRequestHandler;

@@ -44,1 +51,4 @@ exports.createSession = sessions.createSession;

exports.MaxPartSizeExceededError = errors.MaxPartSizeExceededError;
exports.broadcastDevReady = dev.broadcastDevReady;
exports.logDevReady = dev.logDevReady;
exports.unstable_setDevServerHooks = dev.setDevServerHooks;

@@ -1,3 +0,3 @@

declare type Primitive = null | undefined | string | number | boolean | symbol | bigint;
declare type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
interface HtmlLinkProps {

@@ -94,16 +94,7 @@ /**

*/
export declare 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.

@@ -113,3 +104,3 @@ */

}
export declare type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor;
export type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
// We've chosen to inline the utility here to reduce the number of npm dependencies we have,
// slightly decrease the code size compared the original package and make it esm compatible.
const ESCAPE_LOOKUP = {

@@ -21,0 +23,0 @@ "&": "\\u0026",

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -18,14 +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,3 @@

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 { UploadHandlerPart, UploadHandler } from "./formData";

@@ -6,9 +7,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 { TypedResponse } from "./responses";
export type { ActionArgs, ActionFunction, DataFunctionArgs, ErrorBoundaryComponent, HeadersFunction, HtmlMetaDescriptor, LinksFunction, LoaderArgs, LoaderFunction, MetaDescriptor, MetaFunction, RouteComponent, RouteHandle, } from "./routeModules";
export type { TypedDeferredData, TypedResponse } from "./responses";
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, } from "./sessions";
export type { Session, SessionData, SessionIdStorageStrategy, SessionStorage, FlashSessionData, } from "./sessions";

@@ -1,3 +0,11 @@

export declare type JsonFunction = <Data extends unknown>(data: Data, init?: number | ResponseInit) => TypedResponse<Data>;
export declare type TypedResponse<T extends unknown = unknown> = Response & {
import { type UNSAFE_DeferredData as DeferredData } from "@remix-run/router";
import type { ServerMode } from "./mode";
declare const typedDeferredDataBrand: unique symbol;
export type TypedDeferredData<Data extends Record<string, unknown>> = Pick<DeferredData, "init"> & {
data: Data;
readonly [typedDeferredDataBrand]: "TypedDeferredData";
};
export type DeferFunction = <Data extends Record<string, unknown>>(data: Data, init?: number | ResponseInit) => TypedDeferredData<Data>;
export type JsonFunction = <Data>(data: Data, init?: number | ResponseInit) => TypedResponse<Data>;
export type TypedResponse<T = unknown> = Omit<Response, "json"> & {
json(): Promise<T>;

@@ -9,15 +17,39 @@ };

*
* @see https://remix.run/api/remix#json
* @see https://remix.run/utils/json
*/
export declare const json: JsonFunction;
export declare type RedirectFunction = (url: string, init?: number | ResponseInit) => TypedResponse<never>;
/**
* This is a shortcut for creating Remix deferred responses
*
* @see https://remix.run/utils/defer
*/
export declare const defer: DeferFunction;
export type RedirectFunction = (url: string, init?: number | ResponseInit) => TypedResponse<never>;
/**
* A redirect response. Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/api/remix#redirect
* @see https://remix.run/utils/redirect
*/
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;
export declare function isResponse(value: any): value is Response;
export declare function isRedirectStatusCode(statusCode: number): boolean;
export declare function isRedirectResponse(response: Response): boolean;
export declare function isCatchResponse(response: Response): boolean;
export declare function createDeferredReadableStream(deferredData: DeferredData, signal: AbortSignal, serverMode: ServerMode): any;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

var router = require('@remix-run/router');
var errors = require('./errors.js');
// must be a type since this is a subtype of response

@@ -23,42 +26,50 @@ // interfaces must conform to the types they extend

*
* @see https://remix.run/api/remix#json
* @see https://remix.run/utils/json
*/
const json = (data, init = {}) => {
let responseInit = typeof init === "number" ? {
status: init
} : init;
let headers = new Headers(responseInit.headers);
if (!headers.has("Content-Type")) {
headers.set("Content-Type", "application/json; charset=utf-8");
}
return new Response(JSON.stringify(data), { ...responseInit,
headers
});
return router.json(data, init);
};
/**
* This is a shortcut for creating Remix deferred responses
*
* @see https://remix.run/utils/defer
*/
const defer = (data, init = {}) => {
return router.defer(data, init);
};
/**
* A redirect response. Sets the status code and the `Location` header.
* Defaults to "302 Found".
*
* @see https://remix.run/api/remix#redirect
* @see https://remix.run/utils/redirect
*/
const redirect = (url, init = 302) => {
let responseInit = init;
return router.redirect(url, init);
};
if (typeof responseInit === "number") {
responseInit = {
status: responseInit
};
} else if (typeof responseInit.status === "undefined") {
responseInit.status = 302;
}
/**
* 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);
};
let headers = new Headers(responseInit.headers);
headers.set("Location", url);
return new Response(null, { ...responseInit,
headers
});
/**
* 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) {
let deferred = value;
return deferred && typeof deferred === "object" && typeof deferred.data === "object" && typeof deferred.subscribe === "function" && typeof deferred.cancel === "function" && typeof deferred.resolveData === "function";
}
function isResponse(value) {

@@ -68,13 +79,71 @@ return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";

const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
function isRedirectStatusCode(statusCode) {
return redirectStatusCodes.has(statusCode);
}
function isRedirectResponse(response) {
return redirectStatusCodes.has(response.status);
return isRedirectStatusCode(response.status);
}
function isCatchResponse(response) {
return response.headers.get("X-Remix-Catch") != null;
function isTrackedPromise(value) {
return value != null && typeof value.then === "function" && value._tracked === true;
}
exports.isCatchResponse = isCatchResponse;
// TODO: Figure out why ReadableStream types are borked sooooooo badly
// in this file. Probably related to our TS configurations and configs
// bleeding into each other.
const DEFERRED_VALUE_PLACEHOLDER_PREFIX = "__deferred_promise:";
function createDeferredReadableStream(deferredData, signal, serverMode) {
let encoder = new TextEncoder();
let stream = new ReadableStream({
async start(controller) {
let criticalData = {};
let preresolvedKeys = [];
for (let [key, value] of Object.entries(deferredData.data)) {
if (isTrackedPromise(value)) {
criticalData[key] = `${DEFERRED_VALUE_PLACEHOLDER_PREFIX}${key}`;
if (typeof value._data !== "undefined" || typeof value._error !== "undefined") {
preresolvedKeys.push(key);
}
} else {
criticalData[key] = value;
}
}
// Send the critical data
controller.enqueue(encoder.encode(JSON.stringify(criticalData) + "\n\n"));
for (let preresolvedKey of preresolvedKeys) {
enqueueTrackedPromise(controller, encoder, preresolvedKey, deferredData.data[preresolvedKey], serverMode);
}
let unsubscribe = deferredData.subscribe((aborted, settledKey) => {
if (settledKey) {
enqueueTrackedPromise(controller, encoder, settledKey, deferredData.data[settledKey], serverMode);
}
});
await deferredData.resolveData(signal);
unsubscribe();
controller.close();
}
});
return stream;
}
function enqueueTrackedPromise(controller, encoder, settledKey, promise, serverMode) {
if ("_error" in promise) {
controller.enqueue(encoder.encode("error:" + JSON.stringify({
[settledKey]: promise._error instanceof Error ? errors.serializeError(promise._error, serverMode) : promise._error
}) + "\n\n"));
} else {
controller.enqueue(encoder.encode("data:" + JSON.stringify({
[settledKey]: promise._data ?? null
}) + "\n\n"));
}
}
exports.createDeferredReadableStream = createDeferredReadableStream;
exports.defer = defer;
exports.isDeferredData = isDeferredData;
exports.isRedirectResponse = isRedirectResponse;
exports.isRedirectStatusCode = isRedirectStatusCode;
exports.isResponse = isResponse;
exports.json = json;
exports.redirect = redirect;
exports.redirectDocument = redirectDocument;
exports.replace = replace;

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

import type { Params } from "react-router";
import type { Params } from "@remix-run/router";
import type { ServerRoute } from "./routes";

@@ -8,2 +8,2 @@ export interface RouteMatch<Route> {

}
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-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

var reactRouterDom = require('react-router-dom');
var router = require('@remix-run/router');
// TODO: export/import from react-router-dom
function matchServerRoutes(routes, pathname) {
let matches = reactRouterDom.matchRoutes(routes, pathname);
function matchServerRoutes(routes, pathname, basename) {
let matches = router.matchRoutes(routes, pathname, basename);
if (!matches) return null;

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

@@ -1,38 +0,67 @@

import type { Location } from "history";
import type { ComponentType } from "react";
import type { Params } from "react-router-dom";
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 { RouteData } from "./routeData";
import type { SerializeFrom } from "./serialize";
export interface RouteModules<RouteModule> {
[routeId: string]: RouteModule;
[routeId: string]: RouteModule | undefined;
}
/**
* The arguments passed to ActionFunction and LoaderFunction.
* @deprecated Use `LoaderFunctionArgs`/`ActionFunctionArgs` instead
*/
export interface DataFunctionArgs {
request: Request;
export type DataFunctionArgs = RRActionFunctionArgs<AppLoadContext> & RRLoaderFunctionArgs<AppLoadContext> & {
context: AppLoadContext;
params: Params;
}
export declare type LoaderArgs = DataFunctionArgs;
export declare 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.
* Arguments passed to a route `action` function
*/
export declare type CatchBoundaryComponent = ComponentType<{}>;
export type ActionFunctionArgs = RRActionFunctionArgs<AppLoadContext> & {
context: AppLoadContext;
};
/**
* A React component that is rendered when there is an error on a route.
* A function that handles data mutations for a route on the client
* @private Public API is exported from @remix-run/react
*/
export declare type ErrorBoundaryComponent = ComponentType<{
error: Error;
}>;
type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<RRActionFunction>;
/**
* Arguments passed to a route `clientAction` function
* @private Public API is exported from @remix-run/react
*/
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

@@ -42,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;
}

@@ -57,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
*

@@ -107,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"

@@ -120,40 +141,67 @@ * }

*/
export interface 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[];
}
/**
* 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 HtmlMetaDescriptor {
charset?: "utf-8";
charSet?: "utf-8";
title?: string;
[name: string]: null | string | undefined | Record<string, string> | Array<Record<string, string> | string>;
interface ServerRuntimeMetaMatch<RouteId extends string = string, Loader extends LoaderFunction | unknown = unknown> {
id: RouteId;
pathname: AgnosticRouteMatch["pathname"];
data: Loader extends LoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: RouteHandle;
params: AgnosticRouteMatch["params"];
meta: ServerRuntimeMetaDescriptor[];
error?: unknown;
}
export declare type MetaDescriptor = HtmlMetaDescriptor;
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 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: ServerRuntimeMetaMatches<MatchLoaders>;
error?: unknown;
}
export type ServerRuntimeMetaDescriptor = {
charSet: "utf-8";
} | {
title: string;
} | {
name: string;
content: string;
} | {
property: string;
content: string;
} | {
httpEquiv: string;
content: string;
} | {
"script:ld+json": LdJsonObject;
} | {
tagName: "meta" | "link";
[name: string]: string;
} | {
[name: string]: unknown;
};
type LdJsonObject = {
[Key in string]: LdJsonValue;
} & {
[Key in string]?: LdJsonValue | undefined;
};
type LdJsonArray = LdJsonValue[] | readonly LdJsonValue[];
type LdJsonPrimitive = string | number | boolean | null;
type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray;
/**
* A React component that is rendered for a route.
*/
export declare type RouteComponent = ComponentType<{}>;
/**
* An arbitrary object that is associated with a route.
*/
export declare type RouteHandle = any;
export type RouteHandle = unknown;
export interface EntryRouteModule {
CatchBoundary?: CatchBoundaryComponent;
ErrorBoundary?: ErrorBoundaryComponent;
default: RouteComponent;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: any;
HydrateFallback?: any;
Layout?: any;
default: any;
handle?: RouteHandle;
links?: LinksFunction;
meta?: MetaFunction | HtmlMetaDescriptor;
meta?: ServerRuntimeMetaFunction;
}

@@ -167,1 +215,2 @@ export interface ServerRouteModule extends EntryRouteModule {

}
export {};

@@ -1,3 +0,3 @@

import type { AgnosticDataRouteObject } from "./router";
import type { AppLoadContext } from "./data";
import type { AgnosticDataRouteObject } from "@remix-run/router";
import type { FutureConfig } from "./entry";
import type { ServerRouteModule } from "./routeModules";

@@ -7,4 +7,4 @@ export interface RouteManifest<Route> {

}
export declare type ServerRouteManifest = RouteManifest<Omit<ServerRoute, "children">>;
interface Route {
export type ServerRouteManifest = RouteManifest<Omit<ServerRoute, "children">>;
export interface Route {
index?: boolean;

@@ -19,6 +19,9 @@ caseSensitive?: boolean;

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

@@ -29,4 +32,3 @@ export interface ServerRoute extends Route {

}
export declare function createRoutes(manifest: ServerRouteManifest, parentId?: string): ServerRoute[];
export declare function createStaticHandlerDataRoutes(manifest: ServerRouteManifest, loadContext: AppLoadContext, parentId?: string): AgnosticDataRouteObject[];
export {};
export declare function createRoutes(manifest: ServerRouteManifest, parentId?: string, routesByParentId?: Record<string, Omit<ServerRoute, "children">[]>): ServerRoute[];
export declare function createStaticHandlerDataRoutes(manifest: ServerRouteManifest, future: FutureConfig, parentId?: string, routesByParentId?: Record<string, Omit<ServerRoute, "children">[]>): AgnosticDataRouteObject[];
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -15,9 +15,71 @@ * Copyright (c) Remix Software Inc.

// TODO: RRR - Change import to @remix-run/router
function createRoutes(manifest, parentId) {
return Object.entries(manifest).filter(([, route]) => route.parentId === parentId).map(([id, route]) => ({ ...route,
children: createRoutes(manifest, id)
var data = require('./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) {
let routes = {};
Object.values(manifest).forEach(route => {
let parentId = route.parentId || "";
if (!routes[parentId]) {
routes[parentId] = [];
}
routes[parentId].push(route);
});
return routes;
}
// Create a map of routes by parentId to use recursively instead of
// repeatedly filtering the manifest.
function createRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
return (routesByParentId[parentId] || []).map(route => ({
...route,
children: createRoutes(manifest, route.id, routesByParentId)
}));
} // Convert the Remix ServerManifest into DataRouteObject's for use with
}
// Convert the Remix ServerManifest into DataRouteObject's for use with
// createStaticHandler
function createStaticHandlerDataRoutes(manifest, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
return (routesByParentId[parentId] || []).map(route => {
let commonRoute = {
// Always include root due to default boundaries
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
id: route.id,
path: route.path,
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,
params: args.params,
loadContext: args.context,
loader: route.module.loader,
routeId: route.id,
singleFetch: future.unstable_singleFetch === true
}) : undefined,
action: route.module.action ? (args, dataStrategyCtx) => data.callRouteAction({
request: args.request,
params: args.params,
loadContext: args.context,
action: route.module.action,
routeId: route.id,
singleFetch: future.unstable_singleFetch === true
}) : undefined,
handle: route.module.handle
};
return route.index ? {
index: true,
...commonRoute
} : {
caseSensitive: route.caseSensitive,
children: createStaticHandlerDataRoutes(manifest, future, route.id, routesByParentId),
...commonRoute
};
});
}
exports.createRoutes = createRoutes;
exports.createStaticHandlerDataRoutes = createStaticHandlerDataRoutes;

@@ -1,25 +0,13 @@

import type { AppData } from "./data";
import type { TypedResponse } from "./responses";
declare type JsonPrimitive = string | number | boolean | String | Number | Boolean | null;
declare type NonJsonPrimitive = undefined | Function | symbol;
declare type IsAny<T> = 0 extends 1 & T ? true : false;
declare type Serialize<T> = IsAny<T> extends true ? any : 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) */
declare 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 */
declare type SerializeObject<T extends object> = {
[k in keyof T as T[k] extends NonJsonPrimitive ? never : k]: Serialize<T[k]>;
};
declare 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>;
};
declare type ArbitraryFunction = (...args: any[]) => unknown;
import type { Jsonify } from "./jsonify";
import type { TypedDeferredData, TypedResponse } from "./responses";
import type { ClientActionFunctionArgs, ClientLoaderFunctionArgs } from "./routeModules";
import { type SerializeFrom as SingleFetch_SerializeFrom } from "./single-fetch";
import type { Future } from "./future";
type SingleFetchEnabled = Future extends {
unstable_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
*

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

*/
export declare 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 ? T extends (...args: []) => unknown ? SingleFetch_SerializeFrom<T> : never : 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 {};
import type { AppLoadContext } from "./data";
import type { ServerBuild } from "./build";
export declare type RequestHandler = (request: Request, loadContext?: AppLoadContext) => Promise<Response>;
export declare type CreateRequestHandlerFunction = (build: ServerBuild, mode?: string) => RequestHandler;
export type RequestHandler = (request: Request, loadContext?: AppLoadContext) => Promise<Response>;
export type CreateRequestHandlerFunction = (build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>), mode?: string) => RequestHandler;
export declare const createRequestHandler: CreateRequestHandlerFunction;
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

var data = require('./data.js');
var router = require('@remix-run/router');
var entry = require('./entry.js');
var errors = require('./errors.js');
var headers = require('./headers.js');
var invariant = require('./invariant.js');
var mode = require('./mode.js');

@@ -25,60 +26,139 @@ var routeMatching = require('./routeMatching.js');

var serverHandoff = require('./serverHandoff.js');
var dev = require('./dev.js');
var singleFetch = require('./single-fetch.js');
var deprecations = require('./deprecations.js');
// TODO: RRR - Change import to @remix-run/router
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, {
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")) {
let responsePromise = handleDataRequest({
request: // We need to clone the request here instead of the call to the new
// handler otherwise the first handler will lock the body for the other.
// Cloning here allows the new handler to be the stream reader and delegate
// chunks back to this cloned request.
request,
loadContext,
matches: matches,
serverMode
});
if (_build.future.unstable_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 responsePromise;
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.params,
params,
request
});
if (responses.isRedirectResponse(response)) {
response = createRemixRedirectResponse(response, _build.basename);
}
}
} else if (matches && matches[matches.length - 1].route.module.default == null) {
let responsePromise = handleResourceRequest({
request: // We need to clone the request here instead of the call to the new
// handler otherwise the first handler will lock the body for the other.
// Cloning here allows the new handler to be the stream reader and delegate
// chunks back to this cloned request.
request,
loadContext,
matches,
serverMode
});
response = await responsePromise;
} else if (_build.future.unstable_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 handleDocumentRequest({
build,
loadContext,
matches,
request,
routes: routes$1,
serverMode
});
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);
}
if (request.method === "HEAD") {

@@ -91,346 +171,210 @@ return new Response(null, {

}
return response;
};
};
async function handleDataRequest({
loadContext,
matches,
request,
serverMode
}) {
if (!isValidRequestMethod(request)) {
return errorBoundaryError(new Error(`Invalid request method "${request.method}"`), 405);
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()
}
let url = new URL(request.url);
if (!matches) {
return errorBoundaryError(new Error(`No route matches URL "${url.pathname}"`), 404);
}
let response;
let match;
return new Response("Invalid Request", {
status: 400
});
}
async function handleDataRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {
if (isActionRequest(request)) {
match = getRequestMatch(url, matches);
response = await data.callRouteAction({
loadContext,
action: match.route.module.action,
routeId: match.route.id,
params: match.params,
request: request
});
} else {
let routeId = url.searchParams.get("_data");
if (!routeId) {
return errorBoundaryError(new Error(`Missing route id in ?_data`), 403);
}
let tempMatch = matches.find(match => match.route.id === routeId);
if (!tempMatch) {
return errorBoundaryError(new Error(`Route "${routeId}" does not match URL "${url.pathname}"`), 403);
}
match = tempMatch;
response = await data.callRouteLoader({
loadContext,
loader: match.route.module.loader,
routeId: match.route.id,
params: match.params,
request
});
}
let response = await staticHandler.queryRoute(request, {
routeId,
requestContext: loadContext
});
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.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);
}
if (router.UNSAFE_DEFERRED_SYMBOL in response) {
let deferredData = response[router.UNSAFE_DEFERRED_SYMBOL];
let body = responses.createDeferredReadableStream(deferredData, request.signal, serverMode);
let init = deferredData.init || {};
let headers = new Headers(init.headers);
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 (serverMode !== mode.ServerMode.Test) {
console.error(error);
if (responses.isResponse(error)) {
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
if (serverMode === mode.ServerMode.Development && error instanceof Error) {
return errorBoundaryError(error, 500);
if (router.isRouteErrorResponse(error)) {
handleError(error);
return errorResponseToJson(error, serverMode);
}
return errorBoundaryError(new Error("Unexpected Server Error"), 500);
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: {
"X-Remix-Error": "yes"
}
});
}
}
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);
async function handleDocumentRequest({
build,
loadContext,
matches,
request,
routes,
serverMode
}) {
let url = new URL(request.url);
let appState = {
trackBoundaries: true,
trackCatchBoundaries: true,
catchBoundaryRouteId: null,
renderBoundaryRouteId: null,
loaderBoundaryRouteId: null,
error: undefined,
catch: undefined
};
// 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");
if (!isValidRequestMethod(request)) {
matches = null;
appState.trackCatchBoundaries = false;
appState.catch = {
data: null,
status: 405,
statusText: "Method Not Allowed"
};
} else if (!matches) {
appState.trackCatchBoundaries = false;
appState.catch = {
data: null,
status: 404,
statusText: "Not Found"
};
}
// 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");
let actionStatus;
let actionData;
let actionMatch;
let actionResponse;
if (matches && isActionRequest(request)) {
actionMatch = getRequestMatch(url, matches);
try {
actionResponse = await data.callRouteAction({
loadContext,
action: actionMatch.route.module.action,
routeId: actionMatch.route.id,
params: actionMatch.params,
request: request
});
if (responses.isRedirectResponse(actionResponse)) {
return actionResponse;
}
actionStatus = {
status: actionResponse.status,
statusText: actionResponse.statusText
};
if (responses.isCatchResponse(actionResponse)) {
appState.catchBoundaryRouteId = getDeepestRouteIdWithBoundary(matches, "CatchBoundary");
appState.trackCatchBoundaries = false;
appState.catch = { ...actionStatus,
data: await data.extractData(actionResponse)
};
} else {
actionData = {
[actionMatch.route.id]: await data.extractData(actionResponse)
};
}
} catch (error) {
appState.loaderBoundaryRouteId = getDeepestRouteIdWithBoundary(matches, "ErrorBoundary");
appState.trackBoundaries = false;
appState.error = await errors.serializeError(error);
if (serverMode !== mode.ServerMode.Test) {
console.error(`There was an error running the action for route ${actionMatch.route.id}`);
}
}
// 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 handleDocumentRequest(serverMode, build, staticHandler, request, loadContext, handleError, criticalCss) {
let context;
try {
context = await staticHandler.query(request, {
requestContext: loadContext
});
} catch (error) {
handleError(error);
return new Response(null, {
status: 500
});
}
let routeModules = entry.createEntryRouteModules(build.routes);
let matchesToLoad = matches || []; // get rid of the action, we don't want to call it's loader either
// because we'll be rendering the error/catch boundary, if you can get
// access to the loader data in the error/catch boundary then how the heck
// is it supposed to deal with thrown responses and/or errors in the loader?
if (appState.catch) {
matchesToLoad = getMatchesUpToDeepestBoundary(matchesToLoad, "CatchBoundary").slice(0, -1);
} else if (appState.error) {
matchesToLoad = getMatchesUpToDeepestBoundary(matchesToLoad, "ErrorBoundary").slice(0, -1);
if (responses.isResponse(context)) {
return context;
}
let headers$1 = headers.getDocumentHeaders(build, context);
let loaderRequest = new Request(request.url, {
body: null,
headers: request.headers,
method: request.method,
redirect: request.redirect,
signal: request.signal
});
let routeLoaderResults = await Promise.allSettled(matchesToLoad.map(match => match.route.module.loader ? data.callRouteLoader({
loadContext,
loader: match.route.module.loader,
routeId: match.route.id,
params: match.params,
request: loaderRequest
}) : Promise.resolve(undefined))); // Store the state of the action. We will use this to determine later
// what catch or error boundary should be rendered under cases where
// actions don't throw but loaders do, actions throw and parent loaders
// also throw, etc.
let actionCatch = appState.catch;
let actionError = appState.error;
let actionCatchBoundaryRouteId = appState.catchBoundaryRouteId;
let actionLoaderBoundaryRouteId = appState.loaderBoundaryRouteId; // Reset the app error and catch state to propagate the loader states
// from the results into the app state.
appState.catch = undefined;
appState.error = undefined;
let routeLoaderResponses = {};
let loaderStatusCodes = [];
let routeData = {};
for (let index = 0; index < matchesToLoad.length; index++) {
let match = matchesToLoad[index];
let result = routeLoaderResults[index];
let error = result.status === "rejected" ? result.reason : undefined;
let response = result.status === "fulfilled" ? result.value : undefined;
let isRedirect = response ? responses.isRedirectResponse(response) : false;
let isCatch = response ? responses.isCatchResponse(response) : false; // If a parent loader has already caught or error'd, bail because
// we don't need any more child data.
if (appState.catch || appState.error) {
break;
} // If there is a response and it's a redirect, do it unless there
// is an action error or catch state, those action boundary states
// take precedence over loader sates, this means if a loader redirects
// after an action catches or errors we won't follow it, and instead
// render the boundary caused by the action.
if (!actionCatch && !actionError && response && isRedirect) {
return response;
} // Track the boundary ID's for the loaders
if (match.route.module.CatchBoundary) {
appState.catchBoundaryRouteId = match.route.id;
}
if (match.route.module.ErrorBoundary) {
appState.loaderBoundaryRouteId = match.route.id;
}
if (error) {
loaderStatusCodes.push(500);
appState.trackBoundaries = false;
appState.error = await errors.serializeError(error);
if (serverMode !== mode.ServerMode.Test) {
console.error(`There was an error running the data loader for route ${match.route.id}`);
// 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);
}
break;
} else if (response) {
routeLoaderResponses[match.route.id] = response;
loaderStatusCodes.push(response.status);
if (isCatch) {
// If it's a catch response, store it in app state, and bail
appState.trackCatchBoundaries = false;
appState.catch = {
data: await data.extractData(response),
status: response.status,
statusText: response.statusText
};
break;
} else {
// Extract and store the loader data
routeData[match.route.id] = await data.extractData(response);
}
}
} // If there was not a loader catch or error state triggered reset the
// boundaries as they are probably deeper in the tree if the action
// initially triggered a boundary as that match would not exist in the
// matches to load.
if (!appState.catch) {
appState.catchBoundaryRouteId = actionCatchBoundaryRouteId;
});
context.errors = errors.sanitizeErrors(context.errors, serverMode);
}
if (!appState.error) {
appState.loaderBoundaryRouteId = actionLoaderBoundaryRouteId;
} // If there was an action error or catch, we will reset the state to the
// initial values, otherwise we will use whatever came out of the loaders.
appState.catch = actionCatch || appState.catch;
appState.error = actionError || appState.error;
let renderableMatches = getRenderableMatches(matches, appState);
if (!renderableMatches) {
renderableMatches = [];
let root = routes[0];
if (root !== null && root !== void 0 && root.module.CatchBoundary) {
appState.catchBoundaryRouteId = "root";
renderableMatches.push({
params: {},
pathname: "",
route: routes[0]
});
}
} // Handle responses with a non-200 status code. The first loader with a
// non-200 status code determines the status code for the whole response.
let notOkResponse = actionStatus && actionStatus.status !== 200 ? actionStatus.status : loaderStatusCodes.find(status => status !== 200);
let responseStatusCode = appState.error ? 500 : typeof notOkResponse === "number" ? notOkResponse : appState.catch ? appState.catch.status : 200;
let responseHeaders = headers.getDocumentHeaders(build, renderableMatches, routeLoaderResponses, actionResponse);
let entryMatches = entry.createEntryMatches(renderableMatches, build.assets.routes);
let serverHandoff$1 = {
actionData,
appState: appState,
matches: entryMatches,
routeData
// 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 = { ...serverHandoff$1,
let entryContext = {
manifest: build.assets,
routeModules,
serverHandoffString: serverHandoff.createServerHandoffString(serverHandoff$1)
routeModules: entry.createEntryRouteModules(build.routes),
staticHandlerContext: context,
criticalCss,
serverHandoffString: serverHandoff.createServerHandoffString({
basename: build.basename,
criticalCss,
future: build.future,
isSpaMode: build.isSpaMode,
...(!build.future.unstable_singleFetch ? {
state
} : null)
}),
...(build.future.unstable_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)
};
let handleDocumentRequest = build.entry.module.default;
let handleDocumentRequestFunction = build.entry.module.default;
try {
return await handleDocumentRequest(request, responseStatusCode, responseHeaders, entryContext);
return await handleDocumentRequestFunction(request, context.statusCode, headers$1, entryContext, loadContext);
} catch (error) {
responseStatusCode = 500; // Go again, this time with the componentDidCatch emulation. As it rendered
// last time we mutated `componentDidCatch.routeId` for the last rendered
// route, now we know where to render the error boundary (feels a little
// hacky but that's how hooks work). This tells the emulator to stop
// tracking the `routeId` as we render because we already have an error to
// render.
handleError(error);
let errorForSecondRender = error;
appState.trackBoundaries = false;
appState.error = await errors.serializeError(error);
entryContext.serverHandoffString = serverHandoff.createServerHandoffString(serverHandoff$1);
// 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, errorForSecondRender);
// Sanitize errors outside of development environments
if (context.errors) {
context.errors = errors.sanitizeErrors(context.errors, serverMode);
}
// 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 = {
...entryContext,
staticHandlerContext: context,
serverHandoffString: serverHandoff.createServerHandoffString({
basename: build.basename,
future: build.future,
isSpaMode: build.isSpaMode,
...(!build.future.unstable_singleFetch ? {
state
} : null)
}),
...(build.future.unstable_singleFetch ? {
serverHandoffStream: singleFetch.encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
renderMeta: {}
} : null)
};
try {
return await handleDocumentRequest(request, responseStatusCode, responseHeaders, entryContext);
return await handleDocumentRequestFunction(request, context.statusCode, headers$1, entryContext, loadContext);
} catch (error) {
handleError(error);
return returnLastResortErrorResponse(error, serverMode);

@@ -440,53 +384,46 @@ }

}
async function handleResourceRequest({
loadContext,
matches,
request,
serverMode
}) {
let match = matches.slice(-1)[0];
async function handleResourceRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {
if (isActionRequest(request)) {
return await data.callRouteAction({
loadContext,
action: match.route.module.action,
routeId: match.route.id,
params: match.params,
request
});
} else {
return await data.callRouteLoader({
loadContext,
loader: match.route.module.loader,
routeId: match.route.id,
params: match.params,
request
});
// Note we keep the routeId here to align with the Remix handling of
// resource routes which doesn't take ?index into account and just takes
// the leaf match
let response = await staticHandler.queryRoute(request, {
routeId,
requestContext: loadContext
});
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.unstable_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");
return response;
} catch (error) {
if (responses.isResponse(error)) {
// Note: Not functionally required but ensures that our response headers
// match identically to what Remix returns
let response = safelySetHeader(error, "X-Remix-Catch", "yes");
return response;
}
if (router.isRouteErrorResponse(error)) {
if (error) {
handleError(error);
}
return errorResponseToJson(error, serverMode);
}
handleError(error);
return returnLastResortErrorResponse(error, serverMode);
}
}
const validActionMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
function isActionRequest({
method
}) {
return validActionMethods.has(method.toUpperCase());
}
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
function isValidRequestMethod({
method
}) {
return validRequestMethods.has(method.toUpperCase());
}
async function errorBoundaryError(error, status) {
return responses.json(await errors.serializeError(error), {
status,
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: {

@@ -497,78 +434,9 @@ "X-Remix-Error": "yes"

}
function isIndexRequestUrl(url) {
// only use bare `?index` params without a value
// ✅ /foo?index
// ✅ /foo?index&index=123
// ✅ /foo?index=123&index
// ❌ /foo?index=123
return url.searchParams.getAll("index").some(param => param === "");
}
function getRequestMatch(url, matches) {
let match = matches.slice(-1)[0];
if (!isIndexRequestUrl(url) && match.route.id.endsWith("/index")) {
return matches.slice(-2)[0];
}
return match;
}
function getDeepestRouteIdWithBoundary(matches, key) {
let matched = getMatchesUpToDeepestBoundary(matches, key).slice(-1)[0];
return matched ? matched.route.id : null;
}
function getMatchesUpToDeepestBoundary(matches, key) {
let deepestBoundaryIndex = -1;
matches.forEach((match, index) => {
if (match.route.module[key]) {
deepestBoundaryIndex = index;
}
});
if (deepestBoundaryIndex === -1) {
// no route error boundaries, don't need to call any loaders
return [];
}
return matches.slice(0, deepestBoundaryIndex + 1);
} // This prevents `<Outlet/>` from rendering anything below where the error threw
// TODO: maybe do this in <RemixErrorBoundary + context>
function getRenderableMatches(matches, appState) {
if (!matches) {
return null;
} // no error, no worries
if (!appState.catch && !appState.error) {
return matches;
}
let lastRenderableIndex = -1;
matches.forEach((match, index) => {
let id = match.route.id;
if (appState.renderBoundaryRouteId === id || appState.loaderBoundaryRouteId === id || appState.catchBoundaryRouteId === id) {
lastRenderableIndex = index;
}
});
return matches.slice(0, lastRenderableIndex + 1);
}
function returnLastResortErrorResponse(error, serverMode) {
if (serverMode !== mode.ServerMode.Test) {
console.error(error);
}
let message = "Unexpected Server Error";
if (serverMode !== mode.ServerMode.Production) {
message += `\n\n${String(error)}`;
} // Good grief folks, get your act together 😂!
}
// Good grief folks, get your act together 😂!
return new Response(message, {

@@ -581,3 +449,42 @@ status: 500,

}
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;

@@ -1,1 +0,11 @@

export declare function createServerHandoffString(serverHandoff: any): string;
import type { HydrationState } from "@remix-run/router";
import type { FutureConfig } from "./entry";
type ValidateShape<T, Shape> = T extends Shape ? Exclude<keyof T, keyof Shape> extends never ? T : never : never;
export declare function createServerHandoffString<T>(serverHandoff: {
state?: ValidateShape<T, HydrationState>;
criticalCss?: string;
basename: string | undefined;
future: FutureConfig;
isSpaMode: boolean;
}): string;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

// TODO: Remove Promises from serialization
function createServerHandoffString(serverHandoff) {

@@ -19,0 +20,0 @@ // Uses faster alternative of jsesc to escape data returned from the loaders.

@@ -12,5 +12,5 @@ import type { CookieParseOptions, CookieSerializeOptions } from "cookie";

*
* @see https://remix.run/api/remix#session-api
* @see https://remix.run/utils/sessions#session-api
*/
export interface Session {
export interface Session<Data = SessionData, FlashData = Data> {
/**

@@ -29,3 +29,3 @@ * A unique identifier for this session.

*/
readonly data: SessionData;
readonly data: FlashSessionData<Data, FlashData>;
/**

@@ -35,11 +35,11 @@ * Returns `true` if the session has a value for the given `name`, `false`

*/
has(name: string): boolean;
has(name: (keyof Data | keyof FlashData) & string): boolean;
/**
* Returns the value for the given `name` in this session.
*/
get(name: string): any;
get<Key extends (keyof Data | keyof FlashData) & string>(name: Key): (Key extends keyof Data ? Data[Key] : undefined) | (Key extends keyof FlashData ? FlashData[Key] : undefined) | undefined;
/**
* Sets a value in the session for the given `name`.
*/
set(name: string, value: any): void;
set<Key extends keyof Data & string>(name: Key, value: Data[Key]): void;
/**

@@ -49,9 +49,13 @@ * Sets a value in the session that is only valid until the next `get()`.

*/
flash(name: string, value: any): void;
flash<Key extends keyof FlashData & string>(name: Key, value: FlashData[Key]): void;
/**
* Removes a value from the session.
*/
unset(name: string): void;
unset(name: keyof Data & string): void;
}
export declare type CreateSessionFunction = (initialData?: SessionData, id?: string) => Session;
export type FlashSessionData<Data, FlashData> = Partial<Data & {
[Key in keyof FlashData as FlashDataKey<Key & string>]: FlashData[Key];
}>;
type FlashDataKey<Key extends string> = `__flash_${Key}__`;
export type CreateSessionFunction = <Data = SessionData, FlashData = Data>(initialData?: Data, id?: string) => Session<Data, FlashData>;
/**

@@ -63,10 +67,10 @@ * Creates a new Session object.

*
* @see https://remix.run/api/remix#createsession
* @see https://remix.run/utils/sessions#createsession
*/
export declare const createSession: CreateSessionFunction;
export declare type IsSessionFunction = (object: any) => object is Session;
export type IsSessionFunction = (object: any) => object is Session;
/**
* Returns true if an object is a Remix session.
*
* @see https://remix.run/api/remix#issession
* @see https://remix.run/utils/sessions#issession
*/

@@ -81,3 +85,3 @@ export declare const isSession: IsSessionFunction;

*/
export interface SessionStorage {
export interface SessionStorage<Data = SessionData, FlashData = Data> {
/**

@@ -88,3 +92,3 @@ * Parses a Cookie header from a HTTP request and returns the associated

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

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

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

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

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

@@ -112,3 +116,3 @@ /**

*/
export interface SessionIdStorageStrategy {
export interface SessionIdStorageStrategy<Data = SessionData, FlashData = Data> {
/**

@@ -124,11 +128,11 @@ * The Cookie used to store the session id, or options used to automatically

*/
createData: (data: SessionData, expires?: Date) => Promise<string>;
createData: (data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<string>;
/**
* Returns data for a given session id, or `null` if there isn't any.
*/
readData: (id: string) => Promise<SessionData | null>;
readData: (id: string) => Promise<FlashSessionData<Data, FlashData> | null>;
/**
* Updates data for the given session id.
*/
updateData: (id: string, data: SessionData, expires?: Date) => Promise<void>;
updateData: (id: string, data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<void>;
/**

@@ -139,3 +143,3 @@ * Deletes data for a given session id from the data store.

}
export declare type CreateSessionStorageFunction = (strategy: SessionIdStorageStrategy) => SessionStorage;
export type CreateSessionStorageFunction = <Data = SessionData, FlashData = Data>(strategy: SessionIdStorageStrategy<Data, FlashData>) => SessionStorage<Data, FlashData>;
/**

@@ -147,5 +151,6 @@ * Creates a SessionStorage object using a SessionIdStorageStrategy.

*
* @see https://remix.run/api/remix#createsessionstorage
* @see https://remix.run/utils/sessions#createsessionstorage
*/
export declare const createSessionStorageFactory: (createCookie: CreateCookieFunction) => CreateSessionStorageFunction;
export declare function warnOnceAboutSigningSessionCookie(cookie: Cookie): void;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

/**
* Session persists data across HTTP requests.
*
* @see https://remix.run/utils/sessions#session-api
*/
function flash(name) {
return `__flash_${name}__`;
}
/**

@@ -33,3 +38,3 @@ * Creates a new Session object.

*
* @see https://remix.run/api/remix#createsession
* @see https://remix.run/utils/sessions#createsession
*/

@@ -42,15 +47,11 @@ const createSession = (initialData = {}, id = "") => {

},
get data() {
return Object.fromEntries(map);
},
has(name) {
return map.has(name) || map.has(flash(name));
},
get(name) {
if (map.has(name)) return map.get(name);
let flashName = flash(name);
if (map.has(flashName)) {

@@ -61,25 +62,19 @@ let value = map.get(flashName);

}
return undefined;
},
set(name, value) {
map.set(name, value);
},
flash(name, value) {
map.set(flash(name), value);
},
unset(name) {
map.delete(name);
}
};
};
/**
* Returns true if an object is a Remix session.
*
* @see https://remix.run/api/remix#issession
* @see https://remix.run/utils/sessions#issession
*/

@@ -89,2 +84,3 @@ const isSession = object => {

};
/**

@@ -99,2 +95,12 @@ * SessionStorage stores session data between HTTP requests and knows how to

/**
* 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.

@@ -105,3 +111,3 @@ *

*
* @see https://remix.run/api/remix#createsessionstorage
* @see https://remix.run/utils/sessions#createsessionstorage
*/

@@ -123,3 +129,2 @@ const createSessionStorageFactory = createCookie => ({

},
async commitSession(session, options) {

@@ -130,23 +135,22 @@ let {

} = 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);
}
return cookie.serialize(id, options);
},
async destroySession(session, options) {
await deleteData(session.id);
return cookie.serialize("", { ...options,
return cookie.serialize("", {
...options,
maxAge: undefined,
expires: new Date(0)
});
}
};
};
function warnOnceAboutSigningSessionCookie(cookie) {
warnings.warnOnce(cookie.isSigned, `The "${cookie.name}" cookie is not signed, but session cookies should be ` + `signed to prevent tampering on the client before they are sent back to the ` + `server. See https://remix.run/api/remix#signing-cookies ` + `for more information.`);
warnings.warnOnce(cookie.isSigned, `The "${cookie.name}" cookie is not signed, but session cookies should be ` + `signed to prevent tampering on the client before they are sent back to the ` + `server. See https://remix.run/utils/cookies#signing-cookies ` + `for more information.`);
}

@@ -153,0 +157,0 @@

import type { CreateCookieFunction } from "../cookies";
import type { SessionStorage, SessionIdStorageStrategy } from "../sessions";
import type { SessionStorage, SessionIdStorageStrategy, SessionData } from "../sessions";
interface CookieSessionStorageOptions {

@@ -10,3 +10,3 @@ /**

}
export declare type CreateCookieSessionStorageFunction = (options?: CookieSessionStorageOptions) => SessionStorage;
export type CreateCookieSessionStorageFunction = <Data = SessionData, FlashData = Data>(options?: CookieSessionStorageOptions) => SessionStorage<Data, FlashData>;
/**

@@ -21,5 +21,5 @@ * Creates and returns a SessionStorage object that stores all session data

*
* @see https://remix.run/api/remix#createcookiesessionstorage
* @see https://remix.run/utils/sessions#createcookiesessionstorage
*/
export declare const createCookieSessionStorageFactory: (createCookie: CreateCookieFunction) => CreateCookieSessionStorageFunction;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*
* @see https://remix.run/api/remix#createcookiesessionstorage
* @see https://remix.run/utils/sessions#createcookiesessionstorage
*/

@@ -39,19 +39,16 @@ const createCookieSessionStorageFactory = createCookie => ({

},
async commitSession(session, options) {
let serializedCookie = await cookie.serialize(session.data, options);
if (serializedCookie.length > 4096) {
throw new Error("Cookie length will exceed browser maximum. Length: " + serializedCookie.length);
}
return serializedCookie;
},
async destroySession(_session, options) {
return cookie.serialize("", { ...options,
return cookie.serialize("", {
...options,
maxAge: undefined,
expires: new Date(0)
});
}
};

@@ -58,0 +55,0 @@ };

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

import type { SessionStorage, SessionIdStorageStrategy, CreateSessionStorageFunction } from "../sessions";
import type { SessionData, SessionStorage, SessionIdStorageStrategy, CreateSessionStorageFunction } from "../sessions";
interface MemorySessionStorageOptions {

@@ -9,3 +9,3 @@ /**

}
export declare type CreateMemorySessionStorageFunction = (options?: MemorySessionStorageOptions) => SessionStorage;
export type CreateMemorySessionStorageFunction = <Data = SessionData, FlashData = Data>(options?: MemorySessionStorageOptions) => SessionStorage<Data, FlashData>;
/**

@@ -18,5 +18,5 @@ * Creates and returns a simple in-memory SessionStorage object, mostly useful

*
* @see https://remix.run/api/remix#creatememorysessionstorage
* @see https://remix.run/utils/sessions#creatememorysessionstorage
*/
export declare const createMemorySessionStorageFactory: (createSessionStorage: CreateSessionStorageFunction) => CreateMemorySessionStorageFunction;
export {};
/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

*
* @see https://remix.run/api/remix#creatememorysessionstorage
* @see https://remix.run/utils/sessions#creatememorysessionstorage
*/

@@ -28,9 +28,7 @@ const createMemorySessionStorageFactory = createSessionStorage => ({

} = {}) => {
let uniqueId = 0;
let map = new Map();
return createSessionStorage({
cookie,
async createData(data, expires) {
let id = (++uniqueId).toString();
let id = Math.random().toString(36).substring(2, 10);
map.set(id, {

@@ -42,3 +40,2 @@ data,

},
async readData(id) {

@@ -50,14 +47,11 @@ if (map.has(id)) {

} = map.get(id);
if (!expires || expires > new Date()) {
return data;
} // Remove expired session data.
}
// Remove expired session data.
if (expires) map.delete(id);
}
return null;
},
async updateData(id, data, expires) {

@@ -69,7 +63,5 @@ map.set(id, {

},
async deleteData(id) {
map.delete(id);
}
});

@@ -76,0 +68,0 @@ };

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

}
}
exports.MaxPartSizeExceededError = MaxPartSizeExceededError;

@@ -1,3 +0,3 @@

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

@@ -7,3 +7,3 @@ contentType: string;

};
export declare type MemoryUploadHandlerOptions = {
export type MemoryUploadHandlerOptions = {
/**

@@ -10,0 +10,0 @@ * The maximum upload size allowed. If the size is exceeded an error will be thrown.

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

@@ -34,16 +34,11 @@ * Copyright (c) Remix Software Inc.

}
let size = 0;
let chunks = [];
for await (let chunk of data) {
size += chunk.byteLength;
if (size > maxPartSize) {
throw new errors.MaxPartSizeExceededError(name, maxPartSize);
}
chunks.push(chunk);
}
if (typeof filename === "string") {

@@ -54,3 +49,2 @@ return new File(chunks, filename, {

}
return await new Blob(chunks, {

@@ -57,0 +51,0 @@ type: contentType

/**
* @remix-run/server-runtime v0.0.0-nightly-2483ce5-20221019
* @remix-run/server-runtime v0.0.0-nightly-24d1b2186-20240830
*

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

@@ -1,7 +0,22 @@

Copyright 2021 Remix Software Inc.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Copyright (c) Remix Software Inc. 2020-2021
Copyright (c) Shopify Inc. 2022-2024
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
{
"name": "@remix-run/server-runtime",
"version": "0.0.0-nightly-2483ce5-20221019",
"version": "0.0.0-nightly-24d1b2186-20240830",
"description": "Server runtime for Remix",

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

"dependencies": {
"@types/cookie": "^0.4.0",
"@remix-run/router": "0.0.0-experimental-7d87ffb8c",
"@types/cookie": "^0.6.0",
"@web3-storage/multipart-parser": "^1.0.0",
"cookie": "^0.4.1",
"react-router-dom": "6.3.0",
"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/set-cookie-parser": "^2.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"typescript": "^5.1.6"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
"typescript": "^5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"engines": {
"node": ">=14"
"node": ">=18.0.0"
},

@@ -45,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