| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 |
|
| 11 | import { AsyncLocalStorage } from "node:async_hooks";
|
| 12 | import * as React from "react";
|
| 13 | import { parse, serialize, splitSetCookieString } from "cookie-es";
|
| 14 | import { BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Outlet as Outlet$1, Route, Router, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, UNSAFE_AwaitContextProvider, UNSAFE_WithComponentProps, UNSAFE_WithErrorBoundaryProps, UNSAFE_WithHydrateFallbackProps, unstable_HistoryRouter } from "react-router/internal/react-server-client";
|
| 15 |
|
| 16 | const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i;
|
| 17 |
|
| 18 |
|
| 19 | function invariant$1(value, message) {
|
| 20 | if (value === false || value === null || typeof value === "undefined") throw new Error(message);
|
| 21 | }
|
| 22 | function warning(cond, message) {
|
| 23 | if (!cond) {
|
| 24 | if (typeof console !== "undefined") console.warn(message);
|
| 25 | try {
|
| 26 | throw new Error(message);
|
| 27 | } catch (e) {}
|
| 28 | }
|
| 29 | }
|
| 30 | function createKey$1() {
|
| 31 | return Math.random().toString(36).substring(2, 10);
|
| 32 | }
|
| 33 | |
| 34 | |
| 35 |
|
| 36 | function createLocation(current, to, state = null, key, mask) {
|
| 37 | return {
|
| 38 | pathname: typeof current === "string" ? current : current.pathname,
|
| 39 | search: "",
|
| 40 | hash: "",
|
| 41 | ...typeof to === "string" ? parsePath(to) : to,
|
| 42 | state,
|
| 43 | key: to && to.key || key || createKey$1(),
|
| 44 | mask
|
| 45 | };
|
| 46 | }
|
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 |
|
| 52 | function createPath({ pathname = "/", search = "", hash = "" }) {
|
| 53 | if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search;
|
| 54 | if (hash && hash !== "#") pathname += hash.charAt(0) === "#" ? hash : "#" + hash;
|
| 55 | return pathname;
|
| 56 | }
|
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 |
|
| 62 | function parsePath(path) {
|
| 63 | let parsedPath = {};
|
| 64 | if (path) {
|
| 65 | let hashIndex = path.indexOf("#");
|
| 66 | if (hashIndex >= 0) {
|
| 67 | parsedPath.hash = path.substring(hashIndex);
|
| 68 | path = path.substring(0, hashIndex);
|
| 69 | }
|
| 70 | let searchIndex = path.indexOf("?");
|
| 71 | if (searchIndex >= 0) {
|
| 72 | parsedPath.search = path.substring(searchIndex);
|
| 73 | path = path.substring(0, searchIndex);
|
| 74 | }
|
| 75 | if (path) parsedPath.pathname = path;
|
| 76 | }
|
| 77 | return parsedPath;
|
| 78 | }
|
| 79 |
|
| 80 |
|
| 81 | const UninstrumentedSymbol = Symbol("Uninstrumented");
|
| 82 | function getRouteInstrumentationUpdates(fns, route) {
|
| 83 | let aggregated = {
|
| 84 | lazy: [],
|
| 85 | "lazy.loader": [],
|
| 86 | "lazy.action": [],
|
| 87 | "lazy.middleware": [],
|
| 88 | middleware: [],
|
| 89 | loader: [],
|
| 90 | action: []
|
| 91 | };
|
| 92 | fns.forEach((fn) => fn({
|
| 93 | id: route.id,
|
| 94 | index: route.index,
|
| 95 | path: route.path,
|
| 96 | instrument(i) {
|
| 97 | let keys = Object.keys(aggregated);
|
| 98 | for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
|
| 99 | }
|
| 100 | }));
|
| 101 | let updates = {};
|
| 102 | if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
|
| 103 | let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0);
|
| 104 | if (instrumented) updates.lazy = instrumented;
|
| 105 | }
|
| 106 | if (typeof route.lazy === "object") {
|
| 107 | let lazyObject = route.lazy;
|
| 108 | [
|
| 109 | "middleware",
|
| 110 | "loader",
|
| 111 | "action"
|
| 112 | ].forEach((key) => {
|
| 113 | let lazyFn = lazyObject[key];
|
| 114 | let instrumentations = aggregated[`lazy.${key}`];
|
| 115 | if (typeof lazyFn === "function" && instrumentations.length > 0) {
|
| 116 | let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0);
|
| 117 | if (instrumented) updates.lazy = Object.assign(updates.lazy || {}, { [key]: instrumented });
|
| 118 | }
|
| 119 | });
|
| 120 | }
|
| 121 | ["loader", "action"].forEach((key) => {
|
| 122 | let handler = route[key];
|
| 123 | if (typeof handler === "function" && aggregated[key].length > 0) {
|
| 124 | let original = handler[UninstrumentedSymbol] ?? handler;
|
| 125 | let instrumented = wrapImpl(aggregated[key], original, (...args) => getHandlerInfo(args[0]));
|
| 126 | if (instrumented) {
|
| 127 | if (key === "loader" && original.hydrate === true) instrumented.hydrate = true;
|
| 128 | instrumented[UninstrumentedSymbol] = original;
|
| 129 | updates[key] = instrumented;
|
| 130 | }
|
| 131 | }
|
| 132 | });
|
| 133 | if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => {
|
| 134 | let original = middleware[UninstrumentedSymbol] ?? middleware;
|
| 135 | let instrumented = wrapImpl(aggregated.middleware, original, (...args) => getHandlerInfo(args[0]));
|
| 136 | if (instrumented) {
|
| 137 | instrumented[UninstrumentedSymbol] = original;
|
| 138 | return instrumented;
|
| 139 | }
|
| 140 | return middleware;
|
| 141 | });
|
| 142 | return updates;
|
| 143 | }
|
| 144 | function wrapImpl(impls, handler, getInfo) {
|
| 145 | if (impls.length === 0) return null;
|
| 146 | return async (...args) => {
|
| 147 | let result = await recurseRight(impls, getInfo(...args), () => handler(...args), impls.length - 1);
|
| 148 | if (result.type === "error") throw result.value;
|
| 149 | return result.value;
|
| 150 | };
|
| 151 | }
|
| 152 | async function recurseRight(impls, info, handler, index) {
|
| 153 | let impl = impls[index];
|
| 154 | let result;
|
| 155 | if (!impl) try {
|
| 156 | result = {
|
| 157 | type: "success",
|
| 158 | value: await handler()
|
| 159 | };
|
| 160 | } catch (e) {
|
| 161 | result = {
|
| 162 | type: "error",
|
| 163 | value: e
|
| 164 | };
|
| 165 | }
|
| 166 | else {
|
| 167 | let handlerPromise = void 0;
|
| 168 | let callHandler = async () => {
|
| 169 | if (handlerPromise) console.error("You cannot call instrumented handlers more than once");
|
| 170 | else handlerPromise = recurseRight(impls, info, handler, index - 1);
|
| 171 | result = await handlerPromise;
|
| 172 | invariant$1(result, "Expected a result");
|
| 173 | if (result.type === "error" && result.value instanceof Error) return {
|
| 174 | status: "error",
|
| 175 | error: result.value
|
| 176 | };
|
| 177 | return {
|
| 178 | status: "success",
|
| 179 | error: void 0
|
| 180 | };
|
| 181 | };
|
| 182 | try {
|
| 183 | await impl(callHandler, info);
|
| 184 | } catch (e) {
|
| 185 | console.error("An instrumentation function threw an error:", e);
|
| 186 | }
|
| 187 | if (!handlerPromise) await callHandler();
|
| 188 | await handlerPromise;
|
| 189 | }
|
| 190 | if (result) return result;
|
| 191 | return {
|
| 192 | type: "error",
|
| 193 | value: new Error("No result assigned in instrumentation chain.")
|
| 194 | };
|
| 195 | }
|
| 196 | function getHandlerInfo(args) {
|
| 197 | let { request, context, params, pattern } = args;
|
| 198 | return {
|
| 199 | request: getReadonlyRequest(request),
|
| 200 | params: { ...params },
|
| 201 | pattern,
|
| 202 | context: getReadonlyContext(context)
|
| 203 | };
|
| 204 | }
|
| 205 | function getReadonlyRequest(request) {
|
| 206 | return {
|
| 207 | method: request.method,
|
| 208 | url: request.url,
|
| 209 | headers: { get: (...args) => request.headers.get(...args) }
|
| 210 | };
|
| 211 | }
|
| 212 | function getReadonlyContext(context) {
|
| 213 | return { get: (ctx) => context.get(ctx) };
|
| 214 | }
|
| 215 |
|
| 216 |
|
| 217 | |
| 218 | |
| 219 | |
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | |
| 234 | |
| 235 | |
| 236 | |
| 237 | |
| 238 | |
| 239 | |
| 240 | |
| 241 | |
| 242 | |
| 243 | |
| 244 | |
| 245 | |
| 246 | |
| 247 | |
| 248 | |
| 249 | |
| 250 | |
| 251 | |
| 252 | |
| 253 | |
| 254 | |
| 255 | |
| 256 | |
| 257 | |
| 258 | |
| 259 | |
| 260 | |
| 261 | |
| 262 | |
| 263 | |
| 264 | |
| 265 | |
| 266 | |
| 267 | |
| 268 | |
| 269 | |
| 270 | |
| 271 | |
| 272 | |
| 273 | |
| 274 |
|
| 275 | function createContext(defaultValue) {
|
| 276 | return { defaultValue };
|
| 277 | }
|
| 278 | |
| 279 | |
| 280 | |
| 281 | |
| 282 | |
| 283 | |
| 284 | |
| 285 | |
| 286 | |
| 287 | |
| 288 | |
| 289 | |
| 290 | |
| 291 | |
| 292 | |
| 293 | |
| 294 | |
| 295 | |
| 296 | |
| 297 | |
| 298 | |
| 299 |
|
| 300 | var RouterContextProvider = class {
|
| 301 | #map = /* @__PURE__ */ new Map();
|
| 302 | |
| 303 | |
| 304 | |
| 305 |
|
| 306 | constructor(init) {
|
| 307 | if (init) for (let [context, value] of init) this.set(context, value);
|
| 308 | }
|
| 309 | |
| 310 | |
| 311 | |
| 312 | |
| 313 | |
| 314 | |
| 315 | |
| 316 |
|
| 317 | get(context) {
|
| 318 | if (this.#map.has(context)) return this.#map.get(context);
|
| 319 | if (context.defaultValue !== void 0) return context.defaultValue;
|
| 320 | throw new Error("No value found for context");
|
| 321 | }
|
| 322 | |
| 323 | |
| 324 | |
| 325 | |
| 326 | |
| 327 | |
| 328 | |
| 329 |
|
| 330 | set(context, value) {
|
| 331 | this.#map.set(context, value);
|
| 332 | }
|
| 333 | };
|
| 334 | const unsupportedLazyRouteObjectKeys = new Set([
|
| 335 | "lazy",
|
| 336 | "caseSensitive",
|
| 337 | "path",
|
| 338 | "id",
|
| 339 | "index",
|
| 340 | "children"
|
| 341 | ]);
|
| 342 | function isUnsupportedLazyRouteObjectKey(key) {
|
| 343 | return unsupportedLazyRouteObjectKeys.has(key);
|
| 344 | }
|
| 345 | const unsupportedLazyRouteFunctionKeys = new Set([
|
| 346 | "lazy",
|
| 347 | "caseSensitive",
|
| 348 | "path",
|
| 349 | "id",
|
| 350 | "index",
|
| 351 | "middleware",
|
| 352 | "children"
|
| 353 | ]);
|
| 354 | function isUnsupportedLazyRouteFunctionKey(key) {
|
| 355 | return unsupportedLazyRouteFunctionKeys.has(key);
|
| 356 | }
|
| 357 | function isIndexRoute(route) {
|
| 358 | return route.index === true;
|
| 359 | }
|
| 360 | function defaultMapRouteProperties(route) {
|
| 361 | let updates = {};
|
| 362 | if (route.Component) {
|
| 363 | if (route.element) warning(false, "You should not include both `Component` and `element` on your route - `Component` will be used.");
|
| 364 | Object.assign(updates, {
|
| 365 | element: React.createElement(route.Component),
|
| 366 | Component: void 0
|
| 367 | });
|
| 368 | }
|
| 369 | if (route.HydrateFallback) {
|
| 370 | if (route.hydrateFallbackElement) warning(false, "You should not include both `HydrateFallback` and `hydrateFallbackElement` on your route - `HydrateFallback` will be used.");
|
| 371 | Object.assign(updates, {
|
| 372 | hydrateFallbackElement: React.createElement(route.HydrateFallback),
|
| 373 | HydrateFallback: void 0
|
| 374 | });
|
| 375 | }
|
| 376 | if (route.ErrorBoundary) {
|
| 377 | if (route.errorElement) warning(false, "You should not include both `ErrorBoundary` and `errorElement` on your route - `ErrorBoundary` will be used.");
|
| 378 | Object.assign(updates, {
|
| 379 | errorElement: React.createElement(route.ErrorBoundary),
|
| 380 | ErrorBoundary: void 0
|
| 381 | });
|
| 382 | }
|
| 383 | return updates;
|
| 384 | }
|
| 385 | function convertRoutesToDataRoutes(routes, mapRouteProperties = defaultMapRouteProperties, parentPath = [], manifest = {}, allowInPlaceMutations = false) {
|
| 386 | return routes.map((route, index) => {
|
| 387 | let treePath = [...parentPath, String(index)];
|
| 388 | let id = typeof route.id === "string" ? route.id : treePath.join("-");
|
| 389 | invariant$1(route.index !== true || !route.children, `Cannot specify children on an index route`);
|
| 390 | invariant$1(allowInPlaceMutations || !manifest[id], `Found a route id collision on id "${id}". Route id's must be globally unique within Data Router usages`);
|
| 391 | if (isIndexRoute(route)) {
|
| 392 | let indexRoute = {
|
| 393 | ...route,
|
| 394 | id
|
| 395 | };
|
| 396 | manifest[id] = mergeRouteUpdates(indexRoute, mapRouteProperties(indexRoute));
|
| 397 | return indexRoute;
|
| 398 | } else {
|
| 399 | let pathOrLayoutRoute = {
|
| 400 | ...route,
|
| 401 | id,
|
| 402 | children: void 0
|
| 403 | };
|
| 404 | manifest[id] = mergeRouteUpdates(pathOrLayoutRoute, mapRouteProperties(pathOrLayoutRoute));
|
| 405 | if (route.children) pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, mapRouteProperties, treePath, manifest, allowInPlaceMutations);
|
| 406 | return pathOrLayoutRoute;
|
| 407 | }
|
| 408 | });
|
| 409 | }
|
| 410 | function mergeRouteUpdates(route, updates) {
|
| 411 | return Object.assign(route, {
|
| 412 | ...updates,
|
| 413 | ...typeof updates.lazy === "object" && updates.lazy != null ? { lazy: {
|
| 414 | ...route.lazy,
|
| 415 | ...updates.lazy
|
| 416 | } } : {}
|
| 417 | });
|
| 418 | }
|
| 419 | |
| 420 | |
| 421 | |
| 422 | |
| 423 | |
| 424 | |
| 425 | |
| 426 | |
| 427 | |
| 428 | |
| 429 | |
| 430 | |
| 431 | |
| 432 | |
| 433 | |
| 434 | |
| 435 | |
| 436 | |
| 437 | |
| 438 | |
| 439 | |
| 440 | |
| 441 | |
| 442 | |
| 443 | |
| 444 |
|
| 445 | function matchRoutes(routes, locationArg, basename = "/") {
|
| 446 | return matchRoutesImpl(routes, locationArg, basename, false);
|
| 447 | }
|
| 448 | function matchRoutesImpl(routes, locationArg, basename, allowPartial, precomputedBranches) {
|
| 449 | let pathname = stripBasename((typeof locationArg === "string" ? parsePath(locationArg) : locationArg).pathname || "/", basename);
|
| 450 | if (pathname == null) return null;
|
| 451 | let branches = precomputedBranches ?? flattenAndRankRoutes(routes);
|
| 452 | let matches = null;
|
| 453 | let decoded = decodePath(pathname);
|
| 454 | for (let i = 0; matches == null && i < branches.length; ++i) matches = matchRouteBranch(branches[i], decoded, allowPartial);
|
| 455 | return matches;
|
| 456 | }
|
| 457 | function convertRouteMatchToUiMatch(match, loaderData) {
|
| 458 | let { route, pathname, params } = match;
|
| 459 | return {
|
| 460 | id: route.id,
|
| 461 | pathname,
|
| 462 | params,
|
| 463 | loaderData: loaderData[route.id],
|
| 464 | handle: route.handle
|
| 465 | };
|
| 466 | }
|
| 467 | function flattenAndRankRoutes(routes) {
|
| 468 | let branches = flattenRoutes(routes);
|
| 469 | rankRouteBranches(branches);
|
| 470 | return branches;
|
| 471 | }
|
| 472 | function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "", _hasParentOptionalSegments = false) {
|
| 473 | let flattenRoute = (route, index, hasParentOptionalSegments = _hasParentOptionalSegments, relativePath) => {
|
| 474 | let meta = {
|
| 475 | relativePath: relativePath === void 0 ? route.path || "" : relativePath,
|
| 476 | caseSensitive: route.caseSensitive === true,
|
| 477 | childrenIndex: index,
|
| 478 | route
|
| 479 | };
|
| 480 | if (meta.relativePath.startsWith("/")) {
|
| 481 | if (!meta.relativePath.startsWith(parentPath) && hasParentOptionalSegments) return;
|
| 482 | invariant$1(meta.relativePath.startsWith(parentPath), `Absolute route path "${meta.relativePath}" nested under path "${parentPath}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`);
|
| 483 | meta.relativePath = meta.relativePath.slice(parentPath.length);
|
| 484 | }
|
| 485 | let path = joinPaths([parentPath, meta.relativePath]);
|
| 486 | let routesMeta = parentsMeta.concat(meta);
|
| 487 | if (route.children && route.children.length > 0) {
|
| 488 | invariant$1(route.index !== true, `Index routes must not have child routes. Please remove all child routes from route path "${path}".`);
|
| 489 | flattenRoutes(route.children, branches, routesMeta, path, hasParentOptionalSegments);
|
| 490 | }
|
| 491 | if (route.path == null && !route.index) return;
|
| 492 | branches.push({
|
| 493 | path,
|
| 494 | score: computeScore(path, route.index),
|
| 495 | routesMeta: routesMeta.map((meta, i) => {
|
| 496 | let [matcher, params] = compilePath(meta.relativePath, meta.caseSensitive, i === routesMeta.length - 1);
|
| 497 | return {
|
| 498 | ...meta,
|
| 499 | matcher,
|
| 500 | compiledParams: params
|
| 501 | };
|
| 502 | })
|
| 503 | });
|
| 504 | };
|
| 505 | routes.forEach((route, index) => {
|
| 506 | if (route.path === "" || !route.path?.includes("?")) flattenRoute(route, index);
|
| 507 | else for (let exploded of explodeOptionalSegments(route.path)) flattenRoute(route, index, true, exploded);
|
| 508 | });
|
| 509 | return branches;
|
| 510 | }
|
| 511 | function explodeOptionalSegments(path) {
|
| 512 | let segments = path.split("/");
|
| 513 | if (segments.length === 0) return [];
|
| 514 | let [first, ...rest] = segments;
|
| 515 | let isOptional = first.endsWith("?");
|
| 516 | let required = first.replace(/\?$/, "");
|
| 517 | if (rest.length === 0) return isOptional ? [required, ""] : [required];
|
| 518 | let restExploded = explodeOptionalSegments(rest.join("/"));
|
| 519 | let result = [];
|
| 520 | result.push(...restExploded.map((subpath) => subpath === "" ? required : [required, subpath].join("/")));
|
| 521 | if (isOptional) result.push(...restExploded);
|
| 522 | return result.map((exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded);
|
| 523 | }
|
| 524 | function rankRouteBranches(branches) {
|
| 525 | branches.sort((a, b) => a.score !== b.score ? b.score - a.score : compareIndexes(a.routesMeta.map((meta) => meta.childrenIndex), b.routesMeta.map((meta) => meta.childrenIndex)));
|
| 526 | }
|
| 527 | const paramRe = /^:[\w-]+$/;
|
| 528 | const dynamicSegmentValue = 3;
|
| 529 | const indexRouteValue = 2;
|
| 530 | const emptySegmentValue = 1;
|
| 531 | const staticSegmentValue = 10;
|
| 532 | const splatPenalty = -2;
|
| 533 | const isSplat = (s) => s === "*";
|
| 534 | function computeScore(path, index) {
|
| 535 | let segments = path.split("/");
|
| 536 | let initialScore = segments.length;
|
| 537 | if (segments.some(isSplat)) initialScore += splatPenalty;
|
| 538 | if (index) initialScore += indexRouteValue;
|
| 539 | return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
|
| 540 | }
|
| 541 | function compareIndexes(a, b) {
|
| 542 | return a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]) ? a[a.length - 1] - b[b.length - 1] : 0;
|
| 543 | }
|
| 544 | function matchRouteBranch(branch, pathname, allowPartial = false) {
|
| 545 | let { routesMeta } = branch;
|
| 546 | let matchedParams = {};
|
| 547 | let matchedPathname = "/";
|
| 548 | let matches = [];
|
| 549 | for (let i = 0; i < routesMeta.length; ++i) {
|
| 550 | let meta = routesMeta[i];
|
| 551 | let end = i === routesMeta.length - 1;
|
| 552 | let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
|
| 553 | let pattern = {
|
| 554 | path: meta.relativePath,
|
| 555 | caseSensitive: meta.caseSensitive,
|
| 556 | end
|
| 557 | };
|
| 558 | let match = meta.matcher && meta.compiledParams ? matchPathImpl(pattern, remainingPathname, meta.matcher, meta.compiledParams) : matchPath(pattern, remainingPathname);
|
| 559 | let route = meta.route;
|
| 560 | if (!match && end && allowPartial && !routesMeta[routesMeta.length - 1].route.index) match = matchPath({
|
| 561 | path: meta.relativePath,
|
| 562 | caseSensitive: meta.caseSensitive,
|
| 563 | end: false
|
| 564 | }, remainingPathname);
|
| 565 | if (!match) return null;
|
| 566 | Object.assign(matchedParams, match.params);
|
| 567 | matches.push({
|
| 568 | params: matchedParams,
|
| 569 | pathname: joinPaths([matchedPathname, match.pathname]),
|
| 570 | pathnameBase: normalizePathname(joinPaths([matchedPathname, match.pathnameBase])),
|
| 571 | route
|
| 572 | });
|
| 573 | if (match.pathnameBase !== "/") matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
|
| 574 | }
|
| 575 | return matches;
|
| 576 | }
|
| 577 | |
| 578 | |
| 579 | |
| 580 | |
| 581 | |
| 582 | |
| 583 | |
| 584 | |
| 585 | |
| 586 | |
| 587 | |
| 588 | |
| 589 | |
| 590 |
|
| 591 | function matchPath(pattern, pathname) {
|
| 592 | if (typeof pattern === "string") pattern = {
|
| 593 | path: pattern,
|
| 594 | caseSensitive: false,
|
| 595 | end: true
|
| 596 | };
|
| 597 | let [matcher, compiledParams] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);
|
| 598 | return matchPathImpl(pattern, pathname, matcher, compiledParams);
|
| 599 | }
|
| 600 | function matchPathImpl(pattern, pathname, matcher, compiledParams) {
|
| 601 | let match = pathname.match(matcher);
|
| 602 | if (!match) return null;
|
| 603 | let matchedPathname = match[0];
|
| 604 | let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
|
| 605 | let captureGroups = match.slice(1);
|
| 606 | return {
|
| 607 | params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
|
| 608 | if (paramName === "*") {
|
| 609 | let splatValue = captureGroups[index] || "";
|
| 610 | pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
|
| 611 | }
|
| 612 | const value = captureGroups[index];
|
| 613 | if (isOptional && !value) memo[paramName] = void 0;
|
| 614 | else memo[paramName] = (value || "").replace(/%2F/g, "/");
|
| 615 | return memo;
|
| 616 | }, {}),
|
| 617 | pathname: matchedPathname,
|
| 618 | pathnameBase,
|
| 619 | pattern
|
| 620 | };
|
| 621 | }
|
| 622 | function compilePath(path, caseSensitive = false, end = true) {
|
| 623 | warning(path === "*" || !path.endsWith("*") || path.endsWith("/*"), `Route path "${path}" will be treated as if it were "${path.replace(/\*$/, "/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${path.replace(/\*$/, "/*")}".`);
|
| 624 | let params = [];
|
| 625 | let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(/\/:([\w-]+)(\?)?/g, (match, paramName, isOptional, index, str) => {
|
| 626 | params.push({
|
| 627 | paramName,
|
| 628 | isOptional: isOptional != null
|
| 629 | });
|
| 630 | if (isOptional) {
|
| 631 | let nextChar = str.charAt(index + match.length);
|
| 632 | if (nextChar && nextChar !== "/") return "/([^\\/]*)";
|
| 633 | return "(?:/([^\\/]*))?";
|
| 634 | }
|
| 635 | return "/([^\\/]+)";
|
| 636 | }).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
|
| 637 | if (path.endsWith("*")) {
|
| 638 | params.push({ paramName: "*" });
|
| 639 | regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
|
| 640 | } else if (end) regexpSource += "\\/*$";
|
| 641 | else if (path !== "" && path !== "/") regexpSource += "(?:(?=\\/|$))";
|
| 642 | return [new RegExp(regexpSource, caseSensitive ? void 0 : "i"), params];
|
| 643 | }
|
| 644 | function decodePath(value) {
|
| 645 | try {
|
| 646 | return value.split("/").map((v) => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
|
| 647 | } catch (error) {
|
| 648 | warning(false, `The URL path "${value}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${error}).`);
|
| 649 | return value;
|
| 650 | }
|
| 651 | }
|
| 652 | function stripBasename(pathname, basename) {
|
| 653 | if (basename === "/") return pathname;
|
| 654 | if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) return null;
|
| 655 | let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length;
|
| 656 | let nextChar = pathname.charAt(startIndex);
|
| 657 | if (nextChar && nextChar !== "/") return null;
|
| 658 | return pathname.slice(startIndex) || "/";
|
| 659 | }
|
| 660 | function prependBasename({ basename, pathname }) {
|
| 661 | return pathname === "/" ? basename : joinPaths([basename, pathname]);
|
| 662 | }
|
| 663 | const isAbsoluteUrl = (url) => ABSOLUTE_URL_REGEX.test(url);
|
| 664 | |
| 665 | |
| 666 | |
| 667 | |
| 668 | |
| 669 | |
| 670 | |
| 671 | |
| 672 | |
| 673 |
|
| 674 | function resolvePath(to, fromPathname = "/") {
|
| 675 | let { pathname: toPathname, search = "", hash = "" } = typeof to === "string" ? parsePath(to) : to;
|
| 676 | let pathname;
|
| 677 | if (toPathname) {
|
| 678 | toPathname = removeDoubleSlashes(toPathname);
|
| 679 | if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
|
| 680 | else pathname = resolvePathname(toPathname, fromPathname);
|
| 681 | } else pathname = fromPathname;
|
| 682 | return {
|
| 683 | pathname,
|
| 684 | search: normalizeSearch(search),
|
| 685 | hash: normalizeHash(hash)
|
| 686 | };
|
| 687 | }
|
| 688 | function resolvePathname(relativePath, fromPathname) {
|
| 689 | let segments = removeTrailingSlash(fromPathname).split("/");
|
| 690 | relativePath.split("/").forEach((segment) => {
|
| 691 | if (segment === "..") {
|
| 692 | if (segments.length > 1) segments.pop();
|
| 693 | } else if (segment !== ".") segments.push(segment);
|
| 694 | });
|
| 695 | return segments.length > 1 ? segments.join("/") : "/";
|
| 696 | }
|
| 697 | function getInvalidPathError(char, field, dest, path) {
|
| 698 | return `Cannot include a '${char}' character in a manually specified \`to.${field}\` field [${JSON.stringify(path)}]. Please separate it out to the \`to.${dest}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`;
|
| 699 | }
|
| 700 | function getPathContributingMatches(matches) {
|
| 701 | return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
|
| 702 | }
|
| 703 | function getResolveToMatches(matches) {
|
| 704 | let pathMatches = getPathContributingMatches(matches);
|
| 705 | return pathMatches.map((match, idx) => idx === pathMatches.length - 1 ? match.pathname : match.pathnameBase);
|
| 706 | }
|
| 707 | function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = false) {
|
| 708 | let to;
|
| 709 | if (typeof toArg === "string") to = parsePath(toArg);
|
| 710 | else {
|
| 711 | to = { ...toArg };
|
| 712 | invariant$1(!to.pathname || !to.pathname.includes("?"), getInvalidPathError("?", "pathname", "search", to));
|
| 713 | invariant$1(!to.pathname || !to.pathname.includes("#"), getInvalidPathError("#", "pathname", "hash", to));
|
| 714 | invariant$1(!to.search || !to.search.includes("#"), getInvalidPathError("#", "search", "hash", to));
|
| 715 | }
|
| 716 | let isEmptyPath = toArg === "" || to.pathname === "";
|
| 717 | let toPathname = isEmptyPath ? "/" : to.pathname;
|
| 718 | let from;
|
| 719 | if (toPathname == null) from = locationPathname;
|
| 720 | else {
|
| 721 | let routePathnameIndex = routePathnames.length - 1;
|
| 722 | if (!isPathRelative && toPathname.startsWith("..")) {
|
| 723 | let toSegments = toPathname.split("/");
|
| 724 | while (toSegments[0] === "..") {
|
| 725 | toSegments.shift();
|
| 726 | routePathnameIndex -= 1;
|
| 727 | }
|
| 728 | to.pathname = toSegments.join("/");
|
| 729 | }
|
| 730 | from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
|
| 731 | }
|
| 732 | let path = resolvePath(to, from);
|
| 733 | let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
|
| 734 | let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
|
| 735 | if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) path.pathname += "/";
|
| 736 | return path;
|
| 737 | }
|
| 738 | const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
|
| 739 | const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
|
| 740 | const removeTrailingSlash = (path) => path.replace(/\/+$/, "");
|
| 741 | const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
|
| 742 | const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
|
| 743 | const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
|
| 744 | var DataWithResponseInit = class {
|
| 745 | type = "DataWithResponseInit";
|
| 746 | data;
|
| 747 | init;
|
| 748 | constructor(data, init) {
|
| 749 | this.data = data;
|
| 750 | this.init = init || null;
|
| 751 | }
|
| 752 | };
|
| 753 | |
| 754 | |
| 755 | |
| 756 | |
| 757 | |
| 758 | |
| 759 | |
| 760 | |
| 761 | |
| 762 | |
| 763 | |
| 764 | |
| 765 | |
| 766 | |
| 767 | |
| 768 | |
| 769 | |
| 770 | |
| 771 | |
| 772 | |
| 773 | |
| 774 | |
| 775 | |
| 776 | |
| 777 | |
| 778 |
|
| 779 | function data(data, init) {
|
| 780 | return new DataWithResponseInit(data, typeof init === "number" ? { status: init } : init);
|
| 781 | }
|
| 782 | |
| 783 | |
| 784 | |
| 785 | |
| 786 | |
| 787 | |
| 788 | |
| 789 | |
| 790 | |
| 791 | |
| 792 | |
| 793 | |
| 794 | |
| 795 | |
| 796 | |
| 797 | |
| 798 | |
| 799 | |
| 800 | |
| 801 | |
| 802 | |
| 803 | |
| 804 | |
| 805 | |
| 806 | |
| 807 | |
| 808 | |
| 809 | |
| 810 | |
| 811 |
|
| 812 | const redirect$1 = (url, init = 302) => {
|
| 813 | let responseInit = init;
|
| 814 | if (typeof responseInit === "number") responseInit = { status: responseInit };
|
| 815 | else if (typeof responseInit.status === "undefined") responseInit.status = 302;
|
| 816 | let headers = new Headers(responseInit.headers);
|
| 817 | headers.set("Location", url);
|
| 818 | return new Response(null, {
|
| 819 | ...responseInit,
|
| 820 | headers
|
| 821 | });
|
| 822 | };
|
| 823 | |
| 824 | |
| 825 | |
| 826 | |
| 827 | |
| 828 | |
| 829 | |
| 830 | |
| 831 | |
| 832 | |
| 833 | |
| 834 | |
| 835 | |
| 836 | |
| 837 | |
| 838 | |
| 839 | |
| 840 | |
| 841 | |
| 842 | |
| 843 | |
| 844 | |
| 845 | |
| 846 | |
| 847 | |
| 848 | |
| 849 | |
| 850 | |
| 851 | |
| 852 | |
| 853 | |
| 854 | |
| 855 |
|
| 856 | const redirectDocument$1 = (url, init) => {
|
| 857 | let response = redirect$1(url, init);
|
| 858 | response.headers.set("X-Remix-Reload-Document", "true");
|
| 859 | return response;
|
| 860 | };
|
| 861 | |
| 862 | |
| 863 | |
| 864 | |
| 865 | |
| 866 | |
| 867 | |
| 868 | |
| 869 | |
| 870 | |
| 871 | |
| 872 | |
| 873 | |
| 874 | |
| 875 | |
| 876 | |
| 877 | |
| 878 | |
| 879 | |
| 880 | |
| 881 | |
| 882 | |
| 883 | |
| 884 | |
| 885 |
|
| 886 | const replace$1 = (url, init) => {
|
| 887 | let response = redirect$1(url, init);
|
| 888 | response.headers.set("X-Remix-Replace", "true");
|
| 889 | return response;
|
| 890 | };
|
| 891 | var ErrorResponseImpl = class {
|
| 892 | status;
|
| 893 | statusText;
|
| 894 | data;
|
| 895 | error;
|
| 896 | internal;
|
| 897 | constructor(status, statusText, data, internal = false) {
|
| 898 | this.status = status;
|
| 899 | this.statusText = statusText || "";
|
| 900 | this.internal = internal;
|
| 901 | if (data instanceof Error) {
|
| 902 | this.data = data.toString();
|
| 903 | this.error = data;
|
| 904 | } else this.data = data;
|
| 905 | }
|
| 906 | };
|
| 907 | |
| 908 | |
| 909 | |
| 910 | |
| 911 | |
| 912 | |
| 913 | |
| 914 | |
| 915 | |
| 916 | |
| 917 | |
| 918 | |
| 919 | |
| 920 | |
| 921 | |
| 922 | |
| 923 | |
| 924 | |
| 925 | |
| 926 | |
| 927 | |
| 928 | |
| 929 | |
| 930 | |
| 931 | |
| 932 | |
| 933 | |
| 934 | |
| 935 | |
| 936 | |
| 937 |
|
| 938 | function isRouteErrorResponse(error) {
|
| 939 | return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
|
| 940 | }
|
| 941 | function getRoutePattern(matches) {
|
| 942 | return joinPaths(matches.map((m) => m.route.path).filter(Boolean)) || "/";
|
| 943 | }
|
| 944 | typeof window !== "undefined" && typeof window.document !== "undefined" && window.document.createElement;
|
| 945 |
|
| 946 |
|
| 947 | const validMutationMethodsArr = [
|
| 948 | "POST",
|
| 949 | "PUT",
|
| 950 | "PATCH",
|
| 951 | "DELETE"
|
| 952 | ];
|
| 953 | const validMutationMethods = new Set(validMutationMethodsArr);
|
| 954 | const validRequestMethodsArr = ["GET", ...validMutationMethodsArr];
|
| 955 | const validRequestMethods = new Set(validRequestMethodsArr);
|
| 956 | const redirectStatusCodes = new Set([
|
| 957 | 301,
|
| 958 | 302,
|
| 959 | 303,
|
| 960 | 307,
|
| 961 | 308
|
| 962 | ]);
|
| 963 | const ResetLoaderDataSymbol = Symbol("ResetLoaderData");
|
| 964 | |
| 965 | |
| 966 | |
| 967 | |
| 968 | |
| 969 | |
| 970 | |
| 971 | |
| 972 | |
| 973 | |
| 974 | |
| 975 | |
| 976 | |
| 977 | |
| 978 | |
| 979 | |
| 980 | |
| 981 | |
| 982 | |
| 983 | |
| 984 | |
| 985 | |
| 986 | |
| 987 | |
| 988 | |
| 989 | |
| 990 | |
| 991 | |
| 992 | |
| 993 |
|
| 994 | function createStaticHandler(routes, opts) {
|
| 995 | invariant$1(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
| 996 | let manifest = {};
|
| 997 | let basename = (opts ? opts.basename : null) || "/";
|
| 998 | let _mapRouteProperties = opts?.mapRouteProperties;
|
| 999 | let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
|
| 1000 | ({ ...opts?.future });
|
| 1001 | if (opts?.instrumentations) {
|
| 1002 | let instrumentations = opts.instrumentations;
|
| 1003 | mapRouteProperties = (route) => {
|
| 1004 | return {
|
| 1005 | ..._mapRouteProperties?.(route),
|
| 1006 | ...getRouteInstrumentationUpdates(instrumentations.map((i) => i.route).filter(Boolean), route)
|
| 1007 | };
|
| 1008 | };
|
| 1009 | }
|
| 1010 | let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, void 0, manifest);
|
| 1011 | let routeBranches = flattenAndRankRoutes(dataRoutes);
|
| 1012 | |
| 1013 | |
| 1014 | |
| 1015 | |
| 1016 | |
| 1017 | |
| 1018 | |
| 1019 | |
| 1020 | |
| 1021 | |
| 1022 | |
| 1023 | |
| 1024 | |
| 1025 | |
| 1026 | |
| 1027 | |
| 1028 | |
| 1029 | |
| 1030 | |
| 1031 | |
| 1032 | |
| 1033 | |
| 1034 | |
| 1035 | |
| 1036 | |
| 1037 |
|
| 1038 | async function query(request, { requestContext, filterMatchesToLoad, skipLoaderErrorBubbling, skipRevalidation, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
|
| 1039 | let normalizePathImpl = normalizePath || defaultNormalizePath;
|
| 1040 | let method = request.method;
|
| 1041 | let location = createLocation("", normalizePathImpl(request), null, "default");
|
| 1042 | let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
|
| 1043 | requestContext = requestContext != null ? requestContext : new RouterContextProvider();
|
| 1044 | if (!isValidMethod(method) && method !== "HEAD") {
|
| 1045 | let error = getInternalRouterError(405, { method });
|
| 1046 | let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
|
| 1047 | let staticContext = {
|
| 1048 | basename,
|
| 1049 | location,
|
| 1050 | matches: methodNotAllowedMatches,
|
| 1051 | loaderData: {},
|
| 1052 | actionData: null,
|
| 1053 | errors: { [route.id]: error },
|
| 1054 | statusCode: error.status,
|
| 1055 | loaderHeaders: {},
|
| 1056 | actionHeaders: {}
|
| 1057 | };
|
| 1058 | return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
|
| 1059 | } else if (!matches) {
|
| 1060 | let error = getInternalRouterError(404, { pathname: location.pathname });
|
| 1061 | let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
|
| 1062 | let staticContext = {
|
| 1063 | basename,
|
| 1064 | location,
|
| 1065 | matches: notFoundMatches,
|
| 1066 | loaderData: {},
|
| 1067 | actionData: null,
|
| 1068 | errors: { [route.id]: error },
|
| 1069 | statusCode: error.status,
|
| 1070 | loaderHeaders: {},
|
| 1071 | actionHeaders: {}
|
| 1072 | };
|
| 1073 | return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
|
| 1074 | }
|
| 1075 | if (generateMiddlewareResponse) {
|
| 1076 | invariant$1(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
|
| 1077 | try {
|
| 1078 | await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
|
| 1079 | let renderedStaticContext;
|
| 1080 | let response = await runServerMiddlewarePipeline({
|
| 1081 | request,
|
| 1082 | url: createDataFunctionUrl(request, location),
|
| 1083 | pattern: getRoutePattern(matches),
|
| 1084 | matches,
|
| 1085 | params: matches[0].params,
|
| 1086 | context: requestContext
|
| 1087 | }, async () => {
|
| 1088 | return await generateMiddlewareResponse(async (revalidationRequest, opts = {}) => {
|
| 1089 | let result = await queryImpl(revalidationRequest, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, "filterMatchesToLoad" in opts ? opts.filterMatchesToLoad ?? null : filterMatchesToLoad ?? null, skipRevalidation === true);
|
| 1090 | if (isResponse(result)) return result;
|
| 1091 | renderedStaticContext = {
|
| 1092 | location,
|
| 1093 | basename,
|
| 1094 | ...result
|
| 1095 | };
|
| 1096 | return renderedStaticContext;
|
| 1097 | });
|
| 1098 | }, async (error, routeId) => {
|
| 1099 | if (isRedirectResponse(error)) return error;
|
| 1100 | if (isResponse(error)) try {
|
| 1101 | error = new ErrorResponseImpl(error.status, error.statusText, await parseResponseBody(error));
|
| 1102 | } catch (e) {
|
| 1103 | error = e;
|
| 1104 | }
|
| 1105 | if (isDataWithResponseInit(error)) error = dataWithResponseInitToErrorResponse(error);
|
| 1106 | if (renderedStaticContext) {
|
| 1107 | if (routeId in renderedStaticContext.loaderData) renderedStaticContext.loaderData[routeId] = void 0;
|
| 1108 | let staticContext = getStaticContextFromError(dataRoutes, renderedStaticContext, error, skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id);
|
| 1109 | return generateMiddlewareResponse(() => Promise.resolve(staticContext));
|
| 1110 | } else {
|
| 1111 | let staticContext = {
|
| 1112 | matches,
|
| 1113 | location,
|
| 1114 | basename,
|
| 1115 | loaderData: {},
|
| 1116 | actionData: null,
|
| 1117 | errors: { [skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, matches.find((m) => m.route.id === routeId || m.route.loader)?.route.id || routeId).route.id]: error },
|
| 1118 | statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
| 1119 | actionHeaders: {},
|
| 1120 | loaderHeaders: {}
|
| 1121 | };
|
| 1122 | return generateMiddlewareResponse(() => Promise.resolve(staticContext));
|
| 1123 | }
|
| 1124 | });
|
| 1125 | invariant$1(isResponse(response), "Expected a response in query()");
|
| 1126 | return response;
|
| 1127 | } catch (e) {
|
| 1128 | if (isResponse(e)) return e;
|
| 1129 | throw e;
|
| 1130 | }
|
| 1131 | }
|
| 1132 | let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, filterMatchesToLoad || null, skipRevalidation === true);
|
| 1133 | if (isResponse(result)) return result;
|
| 1134 | return {
|
| 1135 | location,
|
| 1136 | basename,
|
| 1137 | ...result
|
| 1138 | };
|
| 1139 | }
|
| 1140 | |
| 1141 | |
| 1142 | |
| 1143 | |
| 1144 | |
| 1145 | |
| 1146 | |
| 1147 | |
| 1148 | |
| 1149 | |
| 1150 | |
| 1151 | |
| 1152 | |
| 1153 | |
| 1154 | |
| 1155 | |
| 1156 | |
| 1157 | |
| 1158 | |
| 1159 | |
| 1160 | |
| 1161 | |
| 1162 | |
| 1163 | |
| 1164 | |
| 1165 |
|
| 1166 | async function queryRoute(request, { routeId, requestContext, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
|
| 1167 | let normalizePathImpl = normalizePath || defaultNormalizePath;
|
| 1168 | let method = request.method;
|
| 1169 | let location = createLocation("", normalizePathImpl(request), null, "default");
|
| 1170 | let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
|
| 1171 | requestContext = requestContext != null ? requestContext : new RouterContextProvider();
|
| 1172 | if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") throw getInternalRouterError(405, { method });
|
| 1173 | else if (!matches) throw getInternalRouterError(404, { pathname: location.pathname });
|
| 1174 | let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
|
| 1175 | if (routeId && !match) throw getInternalRouterError(403, {
|
| 1176 | pathname: location.pathname,
|
| 1177 | routeId
|
| 1178 | });
|
| 1179 | else if (!match) throw getInternalRouterError(404, { pathname: location.pathname });
|
| 1180 | if (generateMiddlewareResponse) {
|
| 1181 | invariant$1(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
|
| 1182 | await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
|
| 1183 | return await runServerMiddlewarePipeline({
|
| 1184 | request,
|
| 1185 | url: createDataFunctionUrl(request, location),
|
| 1186 | pattern: getRoutePattern(matches),
|
| 1187 | matches,
|
| 1188 | params: matches[0].params,
|
| 1189 | context: requestContext
|
| 1190 | }, async () => {
|
| 1191 | return await generateMiddlewareResponse(async (innerRequest) => {
|
| 1192 | let processed = handleQueryResult(await queryImpl(innerRequest, location, matches, requestContext, dataStrategy || null, false, match, null, false));
|
| 1193 | return isResponse(processed) ? processed : typeof processed === "string" ? new Response(processed) : Response.json(processed);
|
| 1194 | });
|
| 1195 | }, (error) => {
|
| 1196 | if (isDataWithResponseInit(error)) return Promise.resolve(dataWithResponseInitToResponse(error));
|
| 1197 | if (isResponse(error)) return Promise.resolve(error);
|
| 1198 | throw error;
|
| 1199 | });
|
| 1200 | }
|
| 1201 | return handleQueryResult(await queryImpl(request, location, matches, requestContext, dataStrategy || null, false, match, null, false));
|
| 1202 | function handleQueryResult(result) {
|
| 1203 | if (isResponse(result)) return result;
|
| 1204 | let error = result.errors ? Object.values(result.errors)[0] : void 0;
|
| 1205 | if (error !== void 0) throw error;
|
| 1206 | if (result.actionData) return Object.values(result.actionData)[0];
|
| 1207 | if (result.loaderData) return Object.values(result.loaderData)[0];
|
| 1208 | }
|
| 1209 | }
|
| 1210 | async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
|
| 1211 | invariant$1(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
| 1212 | try {
|
| 1213 | if (isMutationMethod(request.method)) return await submit(request, location, matches, routeMatch || getTargetMatch(matches, location), requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch != null, filterMatchesToLoad, skipRevalidation);
|
| 1214 | let result = await loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad);
|
| 1215 | return isResponse(result) ? result : {
|
| 1216 | ...result,
|
| 1217 | actionData: null,
|
| 1218 | actionHeaders: {}
|
| 1219 | };
|
| 1220 | } catch (e) {
|
| 1221 | if (isDataStrategyResult(e) && isResponse(e.result)) {
|
| 1222 | if (e.type === "error") throw e.result;
|
| 1223 | return e.result;
|
| 1224 | }
|
| 1225 | if (isRedirectResponse(e)) return e;
|
| 1226 | throw e;
|
| 1227 | }
|
| 1228 | }
|
| 1229 | async function submit(request, location, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
|
| 1230 | let result;
|
| 1231 | if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
| 1232 | let error = getInternalRouterError(405, {
|
| 1233 | method: request.method,
|
| 1234 | pathname: new URL(request.url).pathname,
|
| 1235 | routeId: actionMatch.route.id
|
| 1236 | });
|
| 1237 | if (isRouteRequest) throw error;
|
| 1238 | result = {
|
| 1239 | type: "error",
|
| 1240 | error
|
| 1241 | };
|
| 1242 | } else {
|
| 1243 | result = (await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, [], requestContext), isRouteRequest, requestContext, dataStrategy))[actionMatch.route.id];
|
| 1244 | if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
|
| 1245 | }
|
| 1246 | if (isRedirectResult(result)) throw new Response(null, {
|
| 1247 | status: result.response.status,
|
| 1248 | headers: { Location: result.response.headers.get("Location") }
|
| 1249 | });
|
| 1250 | if (isRouteRequest) {
|
| 1251 | if (isErrorResult(result)) throw result.error;
|
| 1252 | return {
|
| 1253 | matches: [actionMatch],
|
| 1254 | loaderData: {},
|
| 1255 | actionData: { [actionMatch.route.id]: result.data },
|
| 1256 | errors: null,
|
| 1257 | statusCode: 200,
|
| 1258 | loaderHeaders: {},
|
| 1259 | actionHeaders: {}
|
| 1260 | };
|
| 1261 | }
|
| 1262 | if (skipRevalidation) if (isErrorResult(result)) {
|
| 1263 | let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
|
| 1264 | return {
|
| 1265 | statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
|
| 1266 | actionData: null,
|
| 1267 | actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} },
|
| 1268 | matches,
|
| 1269 | loaderData: {},
|
| 1270 | errors: { [boundaryMatch.route.id]: result.error },
|
| 1271 | loaderHeaders: {}
|
| 1272 | };
|
| 1273 | } else return {
|
| 1274 | actionData: { [actionMatch.route.id]: result.data },
|
| 1275 | actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
|
| 1276 | matches,
|
| 1277 | loaderData: {},
|
| 1278 | errors: null,
|
| 1279 | statusCode: result.statusCode || 200,
|
| 1280 | loaderHeaders: {}
|
| 1281 | };
|
| 1282 | let loaderRequest = new Request(request.url, {
|
| 1283 | headers: request.headers,
|
| 1284 | redirect: request.redirect,
|
| 1285 | signal: request.signal
|
| 1286 | });
|
| 1287 | if (isErrorResult(result)) return {
|
| 1288 | ...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad, [(skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id)).route.id, result]),
|
| 1289 | statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
|
| 1290 | actionData: null,
|
| 1291 | actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} }
|
| 1292 | };
|
| 1293 | return {
|
| 1294 | ...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad),
|
| 1295 | actionData: { [actionMatch.route.id]: result.data },
|
| 1296 | ...result.statusCode ? { statusCode: result.statusCode } : {},
|
| 1297 | actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
|
| 1298 | };
|
| 1299 | }
|
| 1300 | async function loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
|
| 1301 | let isRouteRequest = routeMatch != null;
|
| 1302 | if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) throw getInternalRouterError(400, {
|
| 1303 | method: request.method,
|
| 1304 | pathname: new URL(request.url).pathname,
|
| 1305 | routeId: routeMatch?.route.id
|
| 1306 | });
|
| 1307 | let dsMatches;
|
| 1308 | if (routeMatch) dsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, routeMatch, [], requestContext);
|
| 1309 | else {
|
| 1310 | let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1 : void 0;
|
| 1311 | let pattern = getRoutePattern(matches);
|
| 1312 | dsMatches = matches.map((match, index) => {
|
| 1313 | if (maxIdx != null && index > maxIdx) return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, false);
|
| 1314 | return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, (match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match)));
|
| 1315 | });
|
| 1316 | }
|
| 1317 | if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) return {
|
| 1318 | matches,
|
| 1319 | loaderData: {},
|
| 1320 | errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
|
| 1321 | statusCode: 200,
|
| 1322 | loaderHeaders: {}
|
| 1323 | };
|
| 1324 | let results = await callDataStrategy(request, location, dsMatches, isRouteRequest, requestContext, dataStrategy);
|
| 1325 | if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
|
| 1326 | return {
|
| 1327 | ...processRouteLoaderData(matches, results, pendingActionResult, true, skipLoaderErrorBubbling),
|
| 1328 | matches
|
| 1329 | };
|
| 1330 | }
|
| 1331 | async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
|
| 1332 | let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, requestContext, true);
|
| 1333 | let dataResults = {};
|
| 1334 | await Promise.all(matches.map(async (match) => {
|
| 1335 | if (!(match.route.id in results)) return;
|
| 1336 | let result = results[match.route.id];
|
| 1337 | if (isRedirectDataStrategyResult(result)) {
|
| 1338 | let response = result.result;
|
| 1339 | throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename);
|
| 1340 | }
|
| 1341 | if (isRouteRequest) {
|
| 1342 | if (isResponse(result.result)) throw result;
|
| 1343 | else if (isDataWithResponseInit(result.result)) throw dataWithResponseInitToResponse(result.result);
|
| 1344 | }
|
| 1345 | dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
|
| 1346 | }));
|
| 1347 | return dataResults;
|
| 1348 | }
|
| 1349 | return {
|
| 1350 | dataRoutes,
|
| 1351 | _internalRouteBranches: routeBranches,
|
| 1352 | query,
|
| 1353 | queryRoute
|
| 1354 | };
|
| 1355 | }
|
| 1356 | |
| 1357 | |
| 1358 | |
| 1359 | |
| 1360 | |
| 1361 |
|
| 1362 | function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
|
| 1363 | let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
|
| 1364 | return {
|
| 1365 | ...handlerContext,
|
| 1366 | statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
| 1367 | errors: { [errorBoundaryId]: error }
|
| 1368 | };
|
| 1369 | }
|
| 1370 | function throwStaticHandlerAbortedError(request, isRouteRequest) {
|
| 1371 | if (request.signal.reason !== void 0) throw request.signal.reason;
|
| 1372 | throw new Error(`${isRouteRequest ? "queryRoute" : "query"}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`);
|
| 1373 | }
|
| 1374 | function defaultNormalizePath(request) {
|
| 1375 | let url = new URL(request.url);
|
| 1376 | return {
|
| 1377 | pathname: url.pathname,
|
| 1378 | search: url.search,
|
| 1379 | hash: url.hash
|
| 1380 | };
|
| 1381 | }
|
| 1382 | function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
|
| 1383 | let contextualMatches;
|
| 1384 | let activeRouteMatch;
|
| 1385 | if (fromRouteId) {
|
| 1386 | contextualMatches = [];
|
| 1387 | for (let match of matches) {
|
| 1388 | contextualMatches.push(match);
|
| 1389 | if (match.route.id === fromRouteId) {
|
| 1390 | activeRouteMatch = match;
|
| 1391 | break;
|
| 1392 | }
|
| 1393 | }
|
| 1394 | } else {
|
| 1395 | contextualMatches = matches;
|
| 1396 | activeRouteMatch = matches[matches.length - 1];
|
| 1397 | }
|
| 1398 | let path = resolveTo(to ? to : ".", getResolveToMatches(contextualMatches), stripBasename(location.pathname, basename) || location.pathname, relative === "path");
|
| 1399 | if (to == null) {
|
| 1400 | path.search = location.search;
|
| 1401 | path.hash = location.hash;
|
| 1402 | }
|
| 1403 | if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
| 1404 | let nakedIndex = hasNakedIndexQuery(path.search);
|
| 1405 | if (activeRouteMatch.route.index && !nakedIndex) path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
|
| 1406 | else if (!activeRouteMatch.route.index && nakedIndex) {
|
| 1407 | let params = new URLSearchParams(path.search);
|
| 1408 | let indexValues = params.getAll("index");
|
| 1409 | params.delete("index");
|
| 1410 | indexValues.filter((v) => v).forEach((v) => params.append("index", v));
|
| 1411 | let qs = params.toString();
|
| 1412 | path.search = qs ? `?${qs}` : "";
|
| 1413 | }
|
| 1414 | }
|
| 1415 | if (basename !== "/") path.pathname = prependBasename({
|
| 1416 | basename,
|
| 1417 | pathname: path.pathname
|
| 1418 | });
|
| 1419 | return createPath(path);
|
| 1420 | }
|
| 1421 | function shouldRevalidateLoader(loaderMatch, arg) {
|
| 1422 | if (loaderMatch.route.shouldRevalidate) {
|
| 1423 | let routeChoice = loaderMatch.route.shouldRevalidate(arg);
|
| 1424 | if (typeof routeChoice === "boolean") return routeChoice;
|
| 1425 | }
|
| 1426 | return arg.defaultShouldRevalidate;
|
| 1427 | }
|
| 1428 | const lazyRoutePropertyCache = new WeakMap();
|
| 1429 | const loadLazyRouteProperty = ({ key, route, manifest, mapRouteProperties }) => {
|
| 1430 | let routeToUpdate = manifest[route.id];
|
| 1431 | invariant$1(routeToUpdate, "No route found in manifest");
|
| 1432 | if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") return;
|
| 1433 | let lazyFn = routeToUpdate.lazy[key];
|
| 1434 | if (!lazyFn) return;
|
| 1435 | let cache = lazyRoutePropertyCache.get(routeToUpdate);
|
| 1436 | if (!cache) {
|
| 1437 | cache = {};
|
| 1438 | lazyRoutePropertyCache.set(routeToUpdate, cache);
|
| 1439 | }
|
| 1440 | let cachedPromise = cache[key];
|
| 1441 | if (cachedPromise) return cachedPromise;
|
| 1442 | let propertyPromise = (async () => {
|
| 1443 | let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
|
| 1444 | let isStaticallyDefined = routeToUpdate[key] !== void 0;
|
| 1445 | if (isUnsupported) {
|
| 1446 | warning(!isUnsupported, "Route property " + key + " is not a supported lazy route property. This property will be ignored.");
|
| 1447 | cache[key] = Promise.resolve();
|
| 1448 | } else if (isStaticallyDefined) warning(false, `Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`);
|
| 1449 | else {
|
| 1450 | let value = await lazyFn();
|
| 1451 | if (value != null) {
|
| 1452 | Object.assign(routeToUpdate, { [key]: value });
|
| 1453 | Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
|
| 1454 | }
|
| 1455 | }
|
| 1456 | if (typeof routeToUpdate.lazy === "object") {
|
| 1457 | routeToUpdate.lazy[key] = void 0;
|
| 1458 | if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) routeToUpdate.lazy = void 0;
|
| 1459 | }
|
| 1460 | })();
|
| 1461 | cache[key] = propertyPromise;
|
| 1462 | return propertyPromise;
|
| 1463 | };
|
| 1464 | const lazyRouteFunctionCache = new WeakMap();
|
| 1465 | |
| 1466 | |
| 1467 | |
| 1468 | |
| 1469 |
|
| 1470 | function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
|
| 1471 | let routeToUpdate = manifest[route.id];
|
| 1472 | invariant$1(routeToUpdate, "No route found in manifest");
|
| 1473 | if (!route.lazy) return {
|
| 1474 | lazyRoutePromise: void 0,
|
| 1475 | lazyHandlerPromise: void 0
|
| 1476 | };
|
| 1477 | if (typeof route.lazy === "function") {
|
| 1478 | let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
|
| 1479 | if (cachedPromise) return {
|
| 1480 | lazyRoutePromise: cachedPromise,
|
| 1481 | lazyHandlerPromise: cachedPromise
|
| 1482 | };
|
| 1483 | let lazyRoutePromise = (async () => {
|
| 1484 | invariant$1(typeof route.lazy === "function", "No lazy route function found");
|
| 1485 | let lazyRoute = await route.lazy();
|
| 1486 | let routeUpdates = {};
|
| 1487 | for (let lazyRouteProperty in lazyRoute) {
|
| 1488 | let lazyValue = lazyRoute[lazyRouteProperty];
|
| 1489 | if (lazyValue === void 0) continue;
|
| 1490 | let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
|
| 1491 | let isStaticallyDefined = routeToUpdate[lazyRouteProperty] !== void 0;
|
| 1492 | if (isUnsupported) warning(!isUnsupported, "Route property " + lazyRouteProperty + " is not a supported property to be returned from a lazy route function. This property will be ignored.");
|
| 1493 | else if (isStaticallyDefined) warning(!isStaticallyDefined, `Route "${routeToUpdate.id}" has a static property "${lazyRouteProperty}" defined but its lazy function is also returning a value for this property. The lazy route property "${lazyRouteProperty}" will be ignored.`);
|
| 1494 | else routeUpdates[lazyRouteProperty] = lazyValue;
|
| 1495 | }
|
| 1496 | Object.assign(routeToUpdate, routeUpdates);
|
| 1497 | Object.assign(routeToUpdate, {
|
| 1498 | ...mapRouteProperties(routeToUpdate),
|
| 1499 | lazy: void 0
|
| 1500 | });
|
| 1501 | })();
|
| 1502 | lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise);
|
| 1503 | lazyRoutePromise.catch(() => {});
|
| 1504 | return {
|
| 1505 | lazyRoutePromise,
|
| 1506 | lazyHandlerPromise: lazyRoutePromise
|
| 1507 | };
|
| 1508 | }
|
| 1509 | let lazyKeys = Object.keys(route.lazy);
|
| 1510 | let lazyPropertyPromises = [];
|
| 1511 | let lazyHandlerPromise = void 0;
|
| 1512 | for (let key of lazyKeys) {
|
| 1513 | if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) continue;
|
| 1514 | let promise = loadLazyRouteProperty({
|
| 1515 | key,
|
| 1516 | route,
|
| 1517 | manifest,
|
| 1518 | mapRouteProperties
|
| 1519 | });
|
| 1520 | if (promise) {
|
| 1521 | lazyPropertyPromises.push(promise);
|
| 1522 | if (key === type) lazyHandlerPromise = promise;
|
| 1523 | }
|
| 1524 | }
|
| 1525 | let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {}) : void 0;
|
| 1526 | lazyRoutePromise?.catch(() => {});
|
| 1527 | lazyHandlerPromise?.catch(() => {});
|
| 1528 | return {
|
| 1529 | lazyRoutePromise,
|
| 1530 | lazyHandlerPromise
|
| 1531 | };
|
| 1532 | }
|
| 1533 | function isNonNullable(value) {
|
| 1534 | return value !== void 0;
|
| 1535 | }
|
| 1536 | function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
|
| 1537 | let promises = matches.map(({ route }) => {
|
| 1538 | if (typeof route.lazy !== "object" || !route.lazy.middleware) return;
|
| 1539 | return loadLazyRouteProperty({
|
| 1540 | key: "middleware",
|
| 1541 | route,
|
| 1542 | manifest,
|
| 1543 | mapRouteProperties
|
| 1544 | });
|
| 1545 | }).filter(isNonNullable);
|
| 1546 | return promises.length > 0 ? Promise.all(promises) : void 0;
|
| 1547 | }
|
| 1548 | async function defaultDataStrategy(args) {
|
| 1549 | let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
|
| 1550 | let keyedResults = {};
|
| 1551 | (await Promise.all(matchesToLoad.map((m) => m.resolve()))).forEach((result, i) => {
|
| 1552 | keyedResults[matchesToLoad[i].route.id] = result;
|
| 1553 | });
|
| 1554 | return keyedResults;
|
| 1555 | }
|
| 1556 | function runServerMiddlewarePipeline(args, handler, errorHandler) {
|
| 1557 | return runMiddlewarePipeline(args, handler, processResult, isResponse, errorHandler);
|
| 1558 | function processResult(result) {
|
| 1559 | return isDataWithResponseInit(result) ? dataWithResponseInitToResponse(result) : result;
|
| 1560 | }
|
| 1561 | }
|
| 1562 | function runClientMiddlewarePipeline(args, handler) {
|
| 1563 | return runMiddlewarePipeline(args, handler, (r) => {
|
| 1564 | if (isRedirectResponse(r)) throw r;
|
| 1565 | return r;
|
| 1566 | }, isDataStrategyResults, errorHandler);
|
| 1567 | async function errorHandler(error, routeId, nextResult) {
|
| 1568 | if (nextResult) return Object.assign(nextResult.value, { [routeId]: {
|
| 1569 | type: "error",
|
| 1570 | result: error
|
| 1571 | } });
|
| 1572 | else {
|
| 1573 | let { matches } = args;
|
| 1574 | let maxBoundaryIdx = Math.min(Math.max(matches.findIndex((m) => m.route.id === routeId), 0), Math.max(matches.findIndex((m) => m.shouldCallHandler()), 0));
|
| 1575 | let deepestRouteId = matches[maxBoundaryIdx].route.id;
|
| 1576 | for (let match of matches.slice(0, maxBoundaryIdx + 1)) try {
|
| 1577 | await match._lazyPromises?.route;
|
| 1578 | } catch {
|
| 1579 | deepestRouteId = match.route.id;
|
| 1580 | break;
|
| 1581 | }
|
| 1582 | return { [findNearestBoundary(matches, deepestRouteId).route.id]: {
|
| 1583 | type: "error",
|
| 1584 | result: error
|
| 1585 | } };
|
| 1586 | }
|
| 1587 | }
|
| 1588 | }
|
| 1589 | async function runMiddlewarePipeline(args, handler, processResult, isResult, errorHandler) {
|
| 1590 | let { matches, ...dataFnArgs } = args;
|
| 1591 | return await callRouteMiddleware(dataFnArgs, matches.flatMap((m) => m.route.middleware ? m.route.middleware.map((fn) => [m.route.id, fn]) : []), handler, processResult, isResult, errorHandler);
|
| 1592 | }
|
| 1593 | async function callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx = 0) {
|
| 1594 | let { request } = args;
|
| 1595 | if (request.signal.aborted) throw request.signal.reason ?? new Error(`Request aborted: ${request.method} ${request.url}`);
|
| 1596 | let tuple = middlewares[idx];
|
| 1597 | if (!tuple) return await handler();
|
| 1598 | let [routeId, middleware] = tuple;
|
| 1599 | let nextResult;
|
| 1600 | let next = async () => {
|
| 1601 | if (nextResult) throw new Error("You may only call `next()` once per middleware");
|
| 1602 | try {
|
| 1603 | nextResult = { value: await callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx + 1) };
|
| 1604 | return nextResult.value;
|
| 1605 | } catch (error) {
|
| 1606 | nextResult = { value: await errorHandler(error, routeId, nextResult) };
|
| 1607 | return nextResult.value;
|
| 1608 | }
|
| 1609 | };
|
| 1610 | try {
|
| 1611 | let value = await middleware(args, next);
|
| 1612 | let result = value != null ? processResult(value) : void 0;
|
| 1613 | if (isResult(result)) return result;
|
| 1614 | else if (nextResult) return result ?? nextResult.value;
|
| 1615 | else {
|
| 1616 | nextResult = { value: await next() };
|
| 1617 | return nextResult.value;
|
| 1618 | }
|
| 1619 | } catch (error) {
|
| 1620 | return await errorHandler(error, routeId, nextResult);
|
| 1621 | }
|
| 1622 | }
|
| 1623 | function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
|
| 1624 | let lazyMiddlewarePromise = loadLazyRouteProperty({
|
| 1625 | key: "middleware",
|
| 1626 | route: match.route,
|
| 1627 | manifest,
|
| 1628 | mapRouteProperties
|
| 1629 | });
|
| 1630 | let lazyRoutePromises = loadLazyRoute(match.route, isMutationMethod(request.method) ? "action" : "loader", manifest, mapRouteProperties, lazyRoutePropertiesToSkip);
|
| 1631 | return {
|
| 1632 | middleware: lazyMiddlewarePromise,
|
| 1633 | route: lazyRoutePromises.lazyRoutePromise,
|
| 1634 | handler: lazyRoutePromises.lazyHandlerPromise
|
| 1635 | };
|
| 1636 | }
|
| 1637 | function getDataStrategyMatch(mapRouteProperties, manifest, request, path, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, shouldRevalidateArgs = null, callSiteDefaultShouldRevalidate) {
|
| 1638 | let isUsingNewApi = false;
|
| 1639 | let _lazyPromises = getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip);
|
| 1640 | return {
|
| 1641 | ...match,
|
| 1642 | _lazyPromises,
|
| 1643 | shouldLoad,
|
| 1644 | shouldRevalidateArgs,
|
| 1645 | shouldCallHandler(defaultShouldRevalidate) {
|
| 1646 | isUsingNewApi = true;
|
| 1647 | if (!shouldRevalidateArgs) return shouldLoad;
|
| 1648 | if (typeof callSiteDefaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
|
| 1649 | ...shouldRevalidateArgs,
|
| 1650 | defaultShouldRevalidate: callSiteDefaultShouldRevalidate
|
| 1651 | });
|
| 1652 | if (typeof defaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
|
| 1653 | ...shouldRevalidateArgs,
|
| 1654 | defaultShouldRevalidate
|
| 1655 | });
|
| 1656 | return shouldRevalidateLoader(match, shouldRevalidateArgs);
|
| 1657 | },
|
| 1658 | resolve(handlerOverride) {
|
| 1659 | let { lazy, loader, middleware } = match.route;
|
| 1660 | let callHandler = isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (lazy || loader);
|
| 1661 | let isMiddlewareOnlyRoute = middleware && middleware.length > 0 && !loader && !lazy;
|
| 1662 | if (callHandler && (isMutationMethod(request.method) || !isMiddlewareOnlyRoute)) return callLoaderOrAction({
|
| 1663 | request,
|
| 1664 | path,
|
| 1665 | pattern,
|
| 1666 | match,
|
| 1667 | lazyHandlerPromise: _lazyPromises?.handler,
|
| 1668 | lazyRoutePromise: _lazyPromises?.route,
|
| 1669 | handlerOverride,
|
| 1670 | scopedContext
|
| 1671 | });
|
| 1672 | return Promise.resolve({
|
| 1673 | type: "data",
|
| 1674 | result: void 0
|
| 1675 | });
|
| 1676 | }
|
| 1677 | };
|
| 1678 | }
|
| 1679 | function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, path, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
|
| 1680 | return matches.map((match) => {
|
| 1681 | if (match.route.id !== targetMatch.route.id) return {
|
| 1682 | ...match,
|
| 1683 | shouldLoad: false,
|
| 1684 | shouldRevalidateArgs,
|
| 1685 | shouldCallHandler: () => false,
|
| 1686 | _lazyPromises: getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip),
|
| 1687 | resolve: () => Promise.resolve({
|
| 1688 | type: "data",
|
| 1689 | result: void 0
|
| 1690 | })
|
| 1691 | };
|
| 1692 | return getDataStrategyMatch(mapRouteProperties, manifest, request, path, getRoutePattern(matches), match, lazyRoutePropertiesToSkip, scopedContext, true, shouldRevalidateArgs);
|
| 1693 | });
|
| 1694 | }
|
| 1695 | async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
|
| 1696 | if (matches.some((m) => m._lazyPromises?.middleware)) await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
|
| 1697 | let dataStrategyArgs = {
|
| 1698 | request,
|
| 1699 | url: createDataFunctionUrl(request, path),
|
| 1700 | pattern: getRoutePattern(matches),
|
| 1701 | params: matches[0].params,
|
| 1702 | context: scopedContext,
|
| 1703 | matches
|
| 1704 | };
|
| 1705 | let runClientMiddleware = isStaticHandler ? () => {
|
| 1706 | throw new Error("You cannot call `runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`");
|
| 1707 | } : (cb) => {
|
| 1708 | let typedDataStrategyArgs = dataStrategyArgs;
|
| 1709 | return runClientMiddlewarePipeline(typedDataStrategyArgs, () => {
|
| 1710 | return cb({
|
| 1711 | ...typedDataStrategyArgs,
|
| 1712 | fetcherKey,
|
| 1713 | runClientMiddleware: () => {
|
| 1714 | throw new Error("Cannot call `runClientMiddleware()` from within an `runClientMiddleware` handler");
|
| 1715 | }
|
| 1716 | });
|
| 1717 | });
|
| 1718 | };
|
| 1719 | let results = await dataStrategyImpl({
|
| 1720 | ...dataStrategyArgs,
|
| 1721 | fetcherKey,
|
| 1722 | runClientMiddleware
|
| 1723 | });
|
| 1724 | try {
|
| 1725 | await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
|
| 1726 | } catch (e) {}
|
| 1727 | return results;
|
| 1728 | }
|
| 1729 | async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
|
| 1730 | let result;
|
| 1731 | let onReject;
|
| 1732 | let isAction = isMutationMethod(request.method);
|
| 1733 | let type = isAction ? "action" : "loader";
|
| 1734 | let runHandler = (handler) => {
|
| 1735 | let reject;
|
| 1736 | let abortPromise = new Promise((_, r) => reject = r);
|
| 1737 | onReject = () => reject();
|
| 1738 | request.signal.addEventListener("abort", onReject);
|
| 1739 | let actualHandler = (ctx) => {
|
| 1740 | if (typeof handler !== "function") return Promise.reject( new Error(`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`));
|
| 1741 | return handler({
|
| 1742 | request,
|
| 1743 | url: createDataFunctionUrl(request, path),
|
| 1744 | pattern,
|
| 1745 | params: match.params,
|
| 1746 | context: scopedContext
|
| 1747 | }, ...ctx !== void 0 ? [ctx] : []);
|
| 1748 | };
|
| 1749 | let handlerPromise = (async () => {
|
| 1750 | try {
|
| 1751 | return {
|
| 1752 | type: "data",
|
| 1753 | result: await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler())
|
| 1754 | };
|
| 1755 | } catch (e) {
|
| 1756 | return {
|
| 1757 | type: "error",
|
| 1758 | result: e
|
| 1759 | };
|
| 1760 | }
|
| 1761 | })();
|
| 1762 | return Promise.race([handlerPromise, abortPromise]);
|
| 1763 | };
|
| 1764 | try {
|
| 1765 | let handler = isAction ? match.route.action : match.route.loader;
|
| 1766 | if (lazyHandlerPromise || lazyRoutePromise) if (handler) {
|
| 1767 | let handlerError;
|
| 1768 | let [value] = await Promise.all([
|
| 1769 | runHandler(handler).catch((e) => {
|
| 1770 | handlerError = e;
|
| 1771 | }),
|
| 1772 | lazyHandlerPromise,
|
| 1773 | lazyRoutePromise
|
| 1774 | ]);
|
| 1775 | if (handlerError !== void 0) throw handlerError;
|
| 1776 | result = value;
|
| 1777 | } else {
|
| 1778 | await lazyHandlerPromise;
|
| 1779 | let handler = isAction ? match.route.action : match.route.loader;
|
| 1780 | if (handler) [result] = await Promise.all([runHandler(handler), lazyRoutePromise]);
|
| 1781 | else if (type === "action") {
|
| 1782 | let url = new URL(request.url);
|
| 1783 | let pathname = url.pathname + url.search;
|
| 1784 | throw getInternalRouterError(405, {
|
| 1785 | method: request.method,
|
| 1786 | pathname,
|
| 1787 | routeId: match.route.id
|
| 1788 | });
|
| 1789 | } else return {
|
| 1790 | type: "data",
|
| 1791 | result: void 0
|
| 1792 | };
|
| 1793 | }
|
| 1794 | else if (!handler) {
|
| 1795 | let url = new URL(request.url);
|
| 1796 | throw getInternalRouterError(404, { pathname: url.pathname + url.search });
|
| 1797 | } else result = await runHandler(handler);
|
| 1798 | } catch (e) {
|
| 1799 | return {
|
| 1800 | type: "error",
|
| 1801 | result: e
|
| 1802 | };
|
| 1803 | } finally {
|
| 1804 | if (onReject) request.signal.removeEventListener("abort", onReject);
|
| 1805 | }
|
| 1806 | return result;
|
| 1807 | }
|
| 1808 | async function parseResponseBody(response) {
|
| 1809 | let contentType = response.headers.get("Content-Type");
|
| 1810 | if (contentType && /\bapplication\/json\b/.test(contentType)) return response.body == null ? null : response.json();
|
| 1811 | return response.text();
|
| 1812 | }
|
| 1813 | async function convertDataStrategyResultToDataResult(dataStrategyResult) {
|
| 1814 | let { result, type } = dataStrategyResult;
|
| 1815 | if (isResponse(result)) {
|
| 1816 | let data;
|
| 1817 | try {
|
| 1818 | data = await parseResponseBody(result);
|
| 1819 | } catch (e) {
|
| 1820 | return {
|
| 1821 | type: "error",
|
| 1822 | error: e
|
| 1823 | };
|
| 1824 | }
|
| 1825 | if (type === "error") return {
|
| 1826 | type: "error",
|
| 1827 | error: new ErrorResponseImpl(result.status, result.statusText, data),
|
| 1828 | statusCode: result.status,
|
| 1829 | headers: result.headers
|
| 1830 | };
|
| 1831 | return {
|
| 1832 | type: "data",
|
| 1833 | data,
|
| 1834 | statusCode: result.status,
|
| 1835 | headers: result.headers
|
| 1836 | };
|
| 1837 | }
|
| 1838 | if (type === "error") {
|
| 1839 | if (isDataWithResponseInit(result)) {
|
| 1840 | if (result.data instanceof Error) return {
|
| 1841 | type: "error",
|
| 1842 | error: result.data,
|
| 1843 | statusCode: result.init?.status,
|
| 1844 | headers: result.init?.headers ? new Headers(result.init.headers) : void 0
|
| 1845 | };
|
| 1846 | return {
|
| 1847 | type: "error",
|
| 1848 | error: dataWithResponseInitToErrorResponse(result),
|
| 1849 | statusCode: isRouteErrorResponse(result) ? result.status : void 0,
|
| 1850 | headers: result.init?.headers ? new Headers(result.init.headers) : void 0
|
| 1851 | };
|
| 1852 | }
|
| 1853 | return {
|
| 1854 | type: "error",
|
| 1855 | error: result,
|
| 1856 | statusCode: isRouteErrorResponse(result) ? result.status : void 0
|
| 1857 | };
|
| 1858 | }
|
| 1859 | if (isDataWithResponseInit(result)) return {
|
| 1860 | type: "data",
|
| 1861 | data: result.data,
|
| 1862 | statusCode: result.init?.status,
|
| 1863 | headers: result.init?.headers ? new Headers(result.init.headers) : void 0
|
| 1864 | };
|
| 1865 | return {
|
| 1866 | type: "data",
|
| 1867 | data: result
|
| 1868 | };
|
| 1869 | }
|
| 1870 | function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
|
| 1871 | let location = response.headers.get("Location");
|
| 1872 | invariant$1(location, "Redirects returned/thrown from loaders/actions must have a Location header");
|
| 1873 | if (!isAbsoluteUrl(location)) {
|
| 1874 | let trimmedMatches = matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1);
|
| 1875 | location = normalizeTo(new URL(request.url), trimmedMatches, basename, location);
|
| 1876 | response.headers.set("Location", location);
|
| 1877 | }
|
| 1878 | return response;
|
| 1879 | }
|
| 1880 | function createDataFunctionUrl(request, path) {
|
| 1881 | let url = new URL(request.url);
|
| 1882 | let parsed = typeof path === "string" ? parsePath(path) : path;
|
| 1883 | url.pathname = parsed.pathname || "/";
|
| 1884 | if (parsed.search) {
|
| 1885 | let searchParams = new URLSearchParams(parsed.search);
|
| 1886 | let indexValues = searchParams.getAll("index");
|
| 1887 | searchParams.delete("index");
|
| 1888 | for (let value of indexValues.filter(Boolean)) searchParams.append("index", value);
|
| 1889 | let search = searchParams.toString();
|
| 1890 | url.search = search ? `?${search}` : "";
|
| 1891 | } else url.search = "";
|
| 1892 | url.hash = parsed.hash || "";
|
| 1893 | return url;
|
| 1894 | }
|
| 1895 | function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
|
| 1896 | let loaderData = {};
|
| 1897 | let errors = null;
|
| 1898 | let statusCode;
|
| 1899 | let foundError = false;
|
| 1900 | let loaderHeaders = {};
|
| 1901 | let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
|
| 1902 | matches.forEach((match) => {
|
| 1903 | if (!(match.route.id in results)) return;
|
| 1904 | let id = match.route.id;
|
| 1905 | let result = results[id];
|
| 1906 | invariant$1(!isRedirectResult(result), "Cannot handle redirect results in processLoaderData");
|
| 1907 | if (isErrorResult(result)) {
|
| 1908 | let error = result.error;
|
| 1909 | if (pendingError !== void 0) {
|
| 1910 | error = pendingError;
|
| 1911 | pendingError = void 0;
|
| 1912 | }
|
| 1913 | errors = errors || {};
|
| 1914 | if (skipLoaderErrorBubbling) errors[id] = error;
|
| 1915 | else {
|
| 1916 | let boundaryMatch = findNearestBoundary(matches, id);
|
| 1917 | if (errors[boundaryMatch.route.id] == null) errors[boundaryMatch.route.id] = error;
|
| 1918 | }
|
| 1919 | if (!isStaticHandler) loaderData[id] = ResetLoaderDataSymbol;
|
| 1920 | if (!foundError) {
|
| 1921 | foundError = true;
|
| 1922 | statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
| 1923 | }
|
| 1924 | if (result.headers) loaderHeaders[id] = result.headers;
|
| 1925 | } else {
|
| 1926 | loaderData[id] = result.data;
|
| 1927 | if (result.statusCode && result.statusCode !== 200 && !foundError) statusCode = result.statusCode;
|
| 1928 | if (result.headers) loaderHeaders[id] = result.headers;
|
| 1929 | }
|
| 1930 | });
|
| 1931 | if (pendingError !== void 0 && pendingActionResult) {
|
| 1932 | errors = { [pendingActionResult[0]]: pendingError };
|
| 1933 | if (pendingActionResult[2]) loaderData[pendingActionResult[2]] = void 0;
|
| 1934 | }
|
| 1935 | return {
|
| 1936 | loaderData,
|
| 1937 | errors,
|
| 1938 | statusCode: statusCode || 200,
|
| 1939 | loaderHeaders
|
| 1940 | };
|
| 1941 | }
|
| 1942 | function findNearestBoundary(matches, routeId) {
|
| 1943 | return (routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches]).reverse().find((m) => m.route.ErrorBoundary != null || m.route.errorElement != null) || matches[0];
|
| 1944 | }
|
| 1945 | function getShortCircuitMatches(routes) {
|
| 1946 | let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || { id: `__shim-error-route__` };
|
| 1947 | return {
|
| 1948 | matches: [{
|
| 1949 | params: {},
|
| 1950 | pathname: "",
|
| 1951 | pathnameBase: "",
|
| 1952 | route
|
| 1953 | }],
|
| 1954 | route
|
| 1955 | };
|
| 1956 | }
|
| 1957 | function getInternalRouterError(status, { pathname, routeId, method, type, message } = {}) {
|
| 1958 | let statusText = "Unknown Server Error";
|
| 1959 | let errorMessage = "Unknown @remix-run/router error";
|
| 1960 | if (status === 400) {
|
| 1961 | statusText = "Bad Request";
|
| 1962 | if (method && pathname && routeId) errorMessage = `You made a ${method} request to "${pathname}" but did not provide a \`loader\` for route "${routeId}", so there is no way to handle the request.`;
|
| 1963 | else if (type === "invalid-body") errorMessage = "Unable to encode submission body";
|
| 1964 | } else if (status === 403) {
|
| 1965 | statusText = "Forbidden";
|
| 1966 | errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
|
| 1967 | } else if (status === 404) {
|
| 1968 | statusText = "Not Found";
|
| 1969 | errorMessage = `No route matches URL "${pathname}"`;
|
| 1970 | } else if (status === 405) {
|
| 1971 | statusText = "Method Not Allowed";
|
| 1972 | if (method && pathname && routeId) errorMessage = `You made a ${method.toUpperCase()} request to "${pathname}" but did not provide an \`action\` for route "${routeId}", so there is no way to handle the request.`;
|
| 1973 | else if (method) errorMessage = `Invalid request method "${method.toUpperCase()}"`;
|
| 1974 | }
|
| 1975 | return new ErrorResponseImpl(status || 500, statusText, new Error(errorMessage), true);
|
| 1976 | }
|
| 1977 | function dataWithResponseInitToResponse(data) {
|
| 1978 | return Response.json(data.data, data.init ?? void 0);
|
| 1979 | }
|
| 1980 | function dataWithResponseInitToErrorResponse(data) {
|
| 1981 | return new ErrorResponseImpl(data.init?.status ?? 500, data.init?.statusText ?? "Internal Server Error", data.data);
|
| 1982 | }
|
| 1983 | function isDataStrategyResults(result) {
|
| 1984 | return result != null && typeof result === "object" && Object.entries(result).every(([key, value]) => typeof key === "string" && isDataStrategyResult(value));
|
| 1985 | }
|
| 1986 | function isDataStrategyResult(result) {
|
| 1987 | return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" || result.type === "error");
|
| 1988 | }
|
| 1989 | function isRedirectDataStrategyResult(result) {
|
| 1990 | return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
|
| 1991 | }
|
| 1992 | function isErrorResult(result) {
|
| 1993 | return result.type === "error";
|
| 1994 | }
|
| 1995 | function isRedirectResult(result) {
|
| 1996 | return (result && result.type) === "redirect";
|
| 1997 | }
|
| 1998 | function isDataWithResponseInit(value) {
|
| 1999 | return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
|
| 2000 | }
|
| 2001 | function isResponse(value) {
|
| 2002 | return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
|
| 2003 | }
|
| 2004 | function isRedirectStatusCode(statusCode) {
|
| 2005 | return redirectStatusCodes.has(statusCode);
|
| 2006 | }
|
| 2007 | function isRedirectResponse(result) {
|
| 2008 | return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
|
| 2009 | }
|
| 2010 | function isValidMethod(method) {
|
| 2011 | return validRequestMethods.has(method.toUpperCase());
|
| 2012 | }
|
| 2013 | function isMutationMethod(method) {
|
| 2014 | return validMutationMethods.has(method.toUpperCase());
|
| 2015 | }
|
| 2016 | function hasNakedIndexQuery(search) {
|
| 2017 | return new URLSearchParams(search).getAll("index").some((v) => v === "");
|
| 2018 | }
|
| 2019 | function getTargetMatch(matches, location) {
|
| 2020 | let search = typeof location === "string" ? parsePath(location).search : location.search;
|
| 2021 | if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) return matches[matches.length - 1];
|
| 2022 | let pathMatches = getPathContributingMatches(matches);
|
| 2023 | return pathMatches[pathMatches.length - 1];
|
| 2024 | }
|
| 2025 |
|
| 2026 |
|
| 2027 | function invariant(value, message) {
|
| 2028 | if (value === false || value === null || typeof value === "undefined") {
|
| 2029 | console.error("The following error is a bug in React Router; please open an issue! https://github.com/remix-run/react-router/issues/new/choose");
|
| 2030 | throw new Error(message);
|
| 2031 | }
|
| 2032 | }
|
| 2033 |
|
| 2034 |
|
| 2035 | function getDocumentHeadersImpl(context, getRouteHeadersFn, _defaultHeaders) {
|
| 2036 | let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors[m.route.id]) : -1;
|
| 2037 | let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
|
| 2038 | let errorHeaders;
|
| 2039 | if (boundaryIdx >= 0) {
|
| 2040 | let { actionHeaders, actionData, loaderHeaders, loaderData } = context;
|
| 2041 | context.matches.slice(boundaryIdx).some((match) => {
|
| 2042 | let id = match.route.id;
|
| 2043 | if (actionHeaders[id] && (!actionData || !actionData.hasOwnProperty(id))) errorHeaders = actionHeaders[id];
|
| 2044 | else if (loaderHeaders[id] && !loaderData.hasOwnProperty(id)) errorHeaders = loaderHeaders[id];
|
| 2045 | return errorHeaders != null;
|
| 2046 | });
|
| 2047 | }
|
| 2048 | const defaultHeaders = new Headers(_defaultHeaders);
|
| 2049 | return matches.reduce((parentHeaders, match, idx) => {
|
| 2050 | let { id } = match.route;
|
| 2051 | let loaderHeaders = context.loaderHeaders[id] || new Headers();
|
| 2052 | let actionHeaders = context.actionHeaders[id] || new Headers();
|
| 2053 | let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
|
| 2054 | let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
|
| 2055 | let headersFn = getRouteHeadersFn(match);
|
| 2056 | if (headersFn == null) {
|
| 2057 | let headers = new Headers(parentHeaders);
|
| 2058 | if (includeErrorCookies) prependCookies(errorHeaders, headers);
|
| 2059 | prependCookies(actionHeaders, headers);
|
| 2060 | prependCookies(loaderHeaders, headers);
|
| 2061 | return headers;
|
| 2062 | }
|
| 2063 | let headers = new Headers(typeof headersFn === "function" ? headersFn({
|
| 2064 | loaderHeaders,
|
| 2065 | parentHeaders,
|
| 2066 | actionHeaders,
|
| 2067 | errorHeaders: includeErrorHeaders ? errorHeaders : void 0
|
| 2068 | }) : headersFn);
|
| 2069 | if (includeErrorCookies) prependCookies(errorHeaders, headers);
|
| 2070 | prependCookies(actionHeaders, headers);
|
| 2071 | prependCookies(loaderHeaders, headers);
|
| 2072 | prependCookies(parentHeaders, headers);
|
| 2073 | return headers;
|
| 2074 | }, new Headers(defaultHeaders));
|
| 2075 | }
|
| 2076 | function prependCookies(parentHeaders, childHeaders) {
|
| 2077 | let parentSetCookieString = parentHeaders.get("Set-Cookie");
|
| 2078 | if (parentSetCookieString) {
|
| 2079 | let cookies = splitSetCookieString(parentSetCookieString);
|
| 2080 | let childCookies = new Set(childHeaders.getSetCookie());
|
| 2081 | cookies.forEach((cookie) => {
|
| 2082 | if (!childCookies.has(cookie)) childHeaders.append("Set-Cookie", cookie);
|
| 2083 | });
|
| 2084 | }
|
| 2085 | }
|
| 2086 |
|
| 2087 |
|
| 2088 | const alreadyWarned = {};
|
| 2089 | function warnOnce(condition, message) {
|
| 2090 | if (!condition && !alreadyWarned[message]) {
|
| 2091 | alreadyWarned[message] = true;
|
| 2092 | console.warn(message);
|
| 2093 | }
|
| 2094 | }
|
| 2095 |
|
| 2096 |
|
| 2097 | const ERROR_DIGEST_BASE = "REACT_ROUTER_ERROR";
|
| 2098 | const ERROR_DIGEST_REDIRECT = "REDIRECT";
|
| 2099 | const ERROR_DIGEST_ROUTE_ERROR_RESPONSE = "ROUTE_ERROR_RESPONSE";
|
| 2100 | function createRedirectErrorDigest(response) {
|
| 2101 | return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_REDIRECT}:${JSON.stringify({
|
| 2102 | status: response.status,
|
| 2103 | statusText: response.statusText,
|
| 2104 | location: response.headers.get("Location"),
|
| 2105 | reloadDocument: response.headers.get("X-Remix-Reload-Document") === "true",
|
| 2106 | replace: response.headers.get("X-Remix-Replace") === "true"
|
| 2107 | })}`;
|
| 2108 | }
|
| 2109 | function createRouteErrorResponseDigest(response) {
|
| 2110 | let status = 500;
|
| 2111 | let statusText = "";
|
| 2112 | let data;
|
| 2113 | if (isDataWithResponseInit(response)) {
|
| 2114 | status = response.init?.status ?? status;
|
| 2115 | statusText = response.init?.statusText ?? statusText;
|
| 2116 | data = response.data;
|
| 2117 | } else {
|
| 2118 | status = response.status;
|
| 2119 | statusText = response.statusText;
|
| 2120 | data = void 0;
|
| 2121 | }
|
| 2122 | return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_ROUTE_ERROR_RESPONSE}:${JSON.stringify({
|
| 2123 | status,
|
| 2124 | statusText,
|
| 2125 | data
|
| 2126 | })}`;
|
| 2127 | }
|
| 2128 | function getPathsWithAncestors(paths) {
|
| 2129 | let result = new Set();
|
| 2130 | paths.forEach((path) => {
|
| 2131 | if (!path.startsWith("/")) path = `/${path}`;
|
| 2132 | for (let i = 1; i < path.length; i++) if (path[i] === "/") result.add(path.slice(0, i));
|
| 2133 | result.add(path);
|
| 2134 | });
|
| 2135 | return Array.from(result);
|
| 2136 | }
|
| 2137 |
|
| 2138 |
|
| 2139 | function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
|
| 2140 | let originHeader = request.headers.get("origin");
|
| 2141 | let originDomain = null;
|
| 2142 | try {
|
| 2143 | originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
|
| 2144 | } catch {
|
| 2145 | throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
|
| 2146 | }
|
| 2147 | let host = new URL(request.url).host;
|
| 2148 | if (originDomain && originDomain !== host) {
|
| 2149 | if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action.");
|
| 2150 | }
|
| 2151 | }
|
| 2152 | function matchWildcardDomain(domain, pattern) {
|
| 2153 | const domainParts = domain.split(".");
|
| 2154 | const patternParts = pattern.split(".");
|
| 2155 | if (patternParts.length < 1) return false;
|
| 2156 | if (domainParts.length < patternParts.length) return false;
|
| 2157 | while (patternParts.length) {
|
| 2158 | const patternPart = patternParts.pop();
|
| 2159 | const domainPart = domainParts.pop();
|
| 2160 | switch (patternPart) {
|
| 2161 | case "": return false;
|
| 2162 | case "*": if (domainPart) continue;
|
| 2163 | else return false;
|
| 2164 | case "**":
|
| 2165 | if (patternParts.length > 0) return false;
|
| 2166 | return domainPart !== void 0;
|
| 2167 | case void 0:
|
| 2168 | default: if (domainPart !== patternPart) return false;
|
| 2169 | }
|
| 2170 | }
|
| 2171 | return domainParts.length === 0;
|
| 2172 | }
|
| 2173 | function isAllowedOrigin(originDomain, allowedActionOrigins = []) {
|
| 2174 | return allowedActionOrigins.some((allowedOrigin) => allowedOrigin && (allowedOrigin === originDomain || matchWildcardDomain(originDomain, allowedOrigin)));
|
| 2175 | }
|
| 2176 |
|
| 2177 |
|
| 2178 | function getNormalizedPath(request) {
|
| 2179 | let url = new URL(request.url);
|
| 2180 | let pathname = url.pathname;
|
| 2181 | if (pathname.endsWith("/_.data")) pathname = pathname.replace(/_\.data$/, "");
|
| 2182 | else pathname = pathname.replace(/\.data$/, "");
|
| 2183 | let searchParams = new URLSearchParams(url.search);
|
| 2184 | searchParams.delete("_routes");
|
| 2185 | let search = searchParams.toString();
|
| 2186 | if (search) search = `?${search}`;
|
| 2187 | return {
|
| 2188 | pathname,
|
| 2189 | search,
|
| 2190 | hash: ""
|
| 2191 | };
|
| 2192 | }
|
| 2193 |
|
| 2194 |
|
| 2195 | const Outlet$2 = Outlet$1;
|
| 2196 | const WithComponentProps = UNSAFE_WithComponentProps;
|
| 2197 | const WithErrorBoundaryProps = UNSAFE_WithErrorBoundaryProps;
|
| 2198 | const WithHydrateFallbackProps = UNSAFE_WithHydrateFallbackProps;
|
| 2199 | const globalVar = typeof globalThis !== "undefined" ? globalThis : global;
|
| 2200 | const ServerStorage = globalVar.___reactRouterServerStorage___ ??= new AsyncLocalStorage();
|
| 2201 | function getRequest() {
|
| 2202 | const ctx = ServerStorage.getStore();
|
| 2203 | if (!ctx) throw new Error("getRequest must be called from within a React Server render context");
|
| 2204 | return ctx.request;
|
| 2205 | }
|
| 2206 | const redirect = (...args) => {
|
| 2207 | const response = redirect$1(...args);
|
| 2208 | const ctx = ServerStorage.getStore();
|
| 2209 | if (ctx && ctx.runningAction) ctx.redirect = response;
|
| 2210 | return response;
|
| 2211 | };
|
| 2212 | const redirectDocument = (...args) => {
|
| 2213 | const response = redirectDocument$1(...args);
|
| 2214 | const ctx = ServerStorage.getStore();
|
| 2215 | if (ctx && ctx.runningAction) ctx.redirect = response;
|
| 2216 | return response;
|
| 2217 | };
|
| 2218 | const replace = (...args) => {
|
| 2219 | const response = replace$1(...args);
|
| 2220 | const ctx = ServerStorage.getStore();
|
| 2221 | if (ctx && ctx.runningAction) ctx.redirect = response;
|
| 2222 | return response;
|
| 2223 | };
|
| 2224 | const cachedResolvePromise = React.cache(async (resolve) => {
|
| 2225 | return Promise.allSettled([resolve]).then((r) => r[0]);
|
| 2226 | });
|
| 2227 | const Await = (async ({ children, resolve, errorElement }) => {
|
| 2228 | let resolved = await cachedResolvePromise(resolve);
|
| 2229 | if (resolved.status === "rejected" && !errorElement) throw resolved.reason;
|
| 2230 | if (resolved.status === "rejected") return React.createElement(UNSAFE_AwaitContextProvider, {
|
| 2231 | children: React.createElement(React.Fragment, null, errorElement),
|
| 2232 | value: {
|
| 2233 | _tracked: true,
|
| 2234 | _error: resolved.reason
|
| 2235 | }
|
| 2236 | });
|
| 2237 | const toRender = typeof children === "function" ? children(resolved.value) : children;
|
| 2238 | return React.createElement(UNSAFE_AwaitContextProvider, {
|
| 2239 | children: toRender,
|
| 2240 | value: {
|
| 2241 | _tracked: true,
|
| 2242 | _data: resolved.value
|
| 2243 | }
|
| 2244 | });
|
| 2245 | });
|
| 2246 | |
| 2247 | |
| 2248 | |
| 2249 | |
| 2250 | |
| 2251 | |
| 2252 | |
| 2253 | |
| 2254 | |
| 2255 | |
| 2256 | |
| 2257 | |
| 2258 | |
| 2259 | |
| 2260 | |
| 2261 | |
| 2262 | |
| 2263 | |
| 2264 | |
| 2265 | |
| 2266 | |
| 2267 | |
| 2268 | |
| 2269 | |
| 2270 | |
| 2271 | |
| 2272 | |
| 2273 | |
| 2274 | |
| 2275 | |
| 2276 | |
| 2277 | |
| 2278 | |
| 2279 | |
| 2280 | |
| 2281 | |
| 2282 | |
| 2283 | |
| 2284 | |
| 2285 | |
| 2286 | |
| 2287 | |
| 2288 | |
| 2289 | |
| 2290 | |
| 2291 | |
| 2292 | |
| 2293 | |
| 2294 | |
| 2295 | |
| 2296 | |
| 2297 | |
| 2298 | |
| 2299 | |
| 2300 | |
| 2301 | |
| 2302 | |
| 2303 | |
| 2304 | |
| 2305 | |
| 2306 | |
| 2307 | |
| 2308 | |
| 2309 | |
| 2310 | |
| 2311 | |
| 2312 | |
| 2313 | |
| 2314 | |
| 2315 | |
| 2316 | |
| 2317 |
|
| 2318 | async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse }) {
|
| 2319 | let url = new URL(request.url);
|
| 2320 | basename = basename || "/";
|
| 2321 | let normalizedPath = url.pathname;
|
| 2322 | if (url.pathname.endsWith("/_.rsc")) normalizedPath = url.pathname.replace(/_\.rsc$/, "");
|
| 2323 | else if (url.pathname.endsWith(".rsc")) normalizedPath = url.pathname.replace(/\.rsc$/, "");
|
| 2324 | if (stripBasename(normalizedPath, basename) !== "/" && normalizedPath.endsWith("/")) normalizedPath = normalizedPath.slice(0, -1);
|
| 2325 | url.pathname = normalizedPath;
|
| 2326 | basename = basename.length > normalizedPath.length ? normalizedPath : basename;
|
| 2327 | let routerRequest = new Request(url.toString(), {
|
| 2328 | method: request.method,
|
| 2329 | headers: request.headers,
|
| 2330 | body: request.body,
|
| 2331 | signal: request.signal,
|
| 2332 | duplex: request.body ? "half" : void 0
|
| 2333 | });
|
| 2334 | const temporaryReferences = createTemporaryReferenceSet();
|
| 2335 | const requestUrl = new URL(request.url);
|
| 2336 | if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery);
|
| 2337 | let isDataRequest = isReactServerRequest(requestUrl);
|
| 2338 | let matches = matchRoutes(routes, url.pathname, basename);
|
| 2339 | if (matches) await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
|
| 2340 | const leafMatch = matches?.[matches.length - 1];
|
| 2341 | if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) return generateResourceResponse(routerRequest, routes, basename, leafMatch.route.id, requestContext, onError);
|
| 2342 | let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery);
|
| 2343 | response.headers.set("X-Remix-Response", "yes");
|
| 2344 | return response;
|
| 2345 | }
|
| 2346 | async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
|
| 2347 | let url = new URL(request.url);
|
| 2348 | if (url.toString().length > 7680) return new Response(null, {
|
| 2349 | statusText: "Bad Request",
|
| 2350 | status: 400
|
| 2351 | });
|
| 2352 | if (routeDiscovery?.mode === "initial") {
|
| 2353 | let payload = {
|
| 2354 | type: "manifest",
|
| 2355 | patches: getAllRoutePatches(routes, basename)
|
| 2356 | };
|
| 2357 | return generateResponse({
|
| 2358 | statusCode: 200,
|
| 2359 | headers: new Headers({
|
| 2360 | "Content-Type": "text/x-component",
|
| 2361 | Vary: "Content-Type"
|
| 2362 | }),
|
| 2363 | payload
|
| 2364 | }, {
|
| 2365 | temporaryReferences,
|
| 2366 | onError: defaultOnError
|
| 2367 | });
|
| 2368 | }
|
| 2369 | let pathParam = url.searchParams.get("paths");
|
| 2370 | let pathnames = pathParam ? pathParam.split(",").filter(Boolean) : [url.pathname.replace(/\.manifest$/, "")];
|
| 2371 | let routeIds = new Set();
|
| 2372 | let matchedRoutes = pathnames.flatMap((pathname) => {
|
| 2373 | let pathnameMatches = matchRoutes(routes, pathname, basename);
|
| 2374 | return pathnameMatches?.map((m, i) => ({
|
| 2375 | ...m.route,
|
| 2376 | parentId: pathnameMatches[i - 1]?.route.id
|
| 2377 | })) ?? [];
|
| 2378 | }).filter((route) => {
|
| 2379 | if (!routeIds.has(route.id)) {
|
| 2380 | routeIds.add(route.id);
|
| 2381 | return true;
|
| 2382 | }
|
| 2383 | return false;
|
| 2384 | });
|
| 2385 | let payload = {
|
| 2386 | type: "manifest",
|
| 2387 | patches: Promise.all([...matchedRoutes.map((route) => getManifestRoute(route)), getAdditionalRoutePatches(pathnames, routes, basename, Array.from(routeIds))]).then((r) => r.flat(1))
|
| 2388 | };
|
| 2389 | return generateResponse({
|
| 2390 | statusCode: 200,
|
| 2391 | headers: new Headers({ "Content-Type": "text/x-component" }),
|
| 2392 | payload
|
| 2393 | }, {
|
| 2394 | temporaryReferences,
|
| 2395 | onError: defaultOnError
|
| 2396 | });
|
| 2397 | }
|
| 2398 | function prependBasenameToRedirectResponse(response, basename = "/") {
|
| 2399 | if (basename === "/") return response;
|
| 2400 | let redirect = response.headers.get("Location");
|
| 2401 | if (!redirect || isAbsoluteUrl(redirect)) return response;
|
| 2402 | response.headers.set("Location", prependBasename({
|
| 2403 | basename,
|
| 2404 | pathname: redirect
|
| 2405 | }));
|
| 2406 | return response;
|
| 2407 | }
|
| 2408 | async function processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences) {
|
| 2409 | const getRevalidationRequest = () => new Request(request.url, {
|
| 2410 | method: "GET",
|
| 2411 | headers: request.headers,
|
| 2412 | signal: request.signal
|
| 2413 | });
|
| 2414 | const isFormRequest = canDecodeWithFormData(request.headers.get("Content-Type"));
|
| 2415 | const actionId = request.headers.get("rsc-action-id");
|
| 2416 | if (actionId) {
|
| 2417 | if (!decodeReply || !loadServerAction) throw new Error("Cannot handle enhanced server action without decodeReply and loadServerAction functions");
|
| 2418 | const actionArgs = await decodeReply(isFormRequest ? await request.formData() : await request.text(), { temporaryReferences });
|
| 2419 | const serverAction = (await loadServerAction(actionId)).bind(null, ...actionArgs);
|
| 2420 | let actionResult = Promise.resolve(serverAction());
|
| 2421 | try {
|
| 2422 | await actionResult;
|
| 2423 | } catch (error) {
|
| 2424 | if (isResponse(error)) return error;
|
| 2425 | onError?.(error);
|
| 2426 | }
|
| 2427 | let maybeFormData = actionArgs.length === 1 ? actionArgs[0] : actionArgs[1];
|
| 2428 | let skipRevalidation = (maybeFormData && typeof maybeFormData === "object" && maybeFormData instanceof FormData ? maybeFormData : null)?.has("$SKIP_REVALIDATION") ?? false;
|
| 2429 | return {
|
| 2430 | actionResult,
|
| 2431 | revalidationRequest: getRevalidationRequest(),
|
| 2432 | skipRevalidation
|
| 2433 | };
|
| 2434 | } else if (isFormRequest) {
|
| 2435 | const formData = await request.clone().formData();
|
| 2436 | if (Array.from(formData.keys()).some((k) => k.startsWith("$ACTION_"))) {
|
| 2437 | if (!decodeAction) throw new Error("Cannot handle form actions without a decodeAction function");
|
| 2438 | const action = await decodeAction(formData);
|
| 2439 | let formState = void 0;
|
| 2440 | try {
|
| 2441 | let result = await action();
|
| 2442 | if (isRedirectResponse(result)) result = prependBasenameToRedirectResponse(result, basename);
|
| 2443 | formState = decodeFormState?.(result, formData);
|
| 2444 | } catch (error) {
|
| 2445 | if (isRedirectResponse(error)) return prependBasenameToRedirectResponse(error, basename);
|
| 2446 | if (isResponse(error)) return error;
|
| 2447 | onError?.(error);
|
| 2448 | }
|
| 2449 | return {
|
| 2450 | formState,
|
| 2451 | revalidationRequest: getRevalidationRequest(),
|
| 2452 | skipRevalidation: false
|
| 2453 | };
|
| 2454 | }
|
| 2455 | }
|
| 2456 | }
|
| 2457 | async function generateResourceResponse(request, routes, basename, routeId, requestContext, onError) {
|
| 2458 | try {
|
| 2459 | return await createStaticHandler(routes, { basename }).queryRoute(request, {
|
| 2460 | routeId,
|
| 2461 | requestContext,
|
| 2462 | async generateMiddlewareResponse(queryRoute) {
|
| 2463 | try {
|
| 2464 | return generateResourceResponse(await queryRoute(request));
|
| 2465 | } catch (error) {
|
| 2466 | return generateErrorResponse(error);
|
| 2467 | }
|
| 2468 | },
|
| 2469 | normalizePath: (r) => getNormalizedPath(r)
|
| 2470 | });
|
| 2471 | } catch (error) {
|
| 2472 | return generateErrorResponse(error);
|
| 2473 | }
|
| 2474 | function generateErrorResponse(error) {
|
| 2475 | let response;
|
| 2476 | if (isResponse(error)) response = error;
|
| 2477 | else if (isRouteErrorResponse(error)) {
|
| 2478 | onError?.(error);
|
| 2479 | const errorMessage = typeof error.data === "string" ? error.data : error.statusText;
|
| 2480 | response = new Response(errorMessage, {
|
| 2481 | status: error.status,
|
| 2482 | statusText: error.statusText
|
| 2483 | });
|
| 2484 | } else {
|
| 2485 | onError?.(error);
|
| 2486 | response = new Response("Internal Server Error", { status: 500 });
|
| 2487 | }
|
| 2488 | return generateResourceResponse(response);
|
| 2489 | }
|
| 2490 | function generateResourceResponse(response) {
|
| 2491 | const headers = new Headers(response.headers);
|
| 2492 | headers.set("React-Router-Resource", "true");
|
| 2493 | return new Response(response.body, {
|
| 2494 | status: response.status,
|
| 2495 | statusText: response.statusText,
|
| 2496 | headers
|
| 2497 | });
|
| 2498 | }
|
| 2499 | }
|
| 2500 | async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
|
| 2501 | let statusCode = 200;
|
| 2502 | let url = new URL(request.url);
|
| 2503 | let isSubmission = isMutationMethod(request.method);
|
| 2504 | let routeIdsToLoad = !isSubmission && url.searchParams.has("_routes") ? url.searchParams.get("_routes").split(",") : null;
|
| 2505 | const staticHandler = createStaticHandler(routes, { basename });
|
| 2506 | let actionResult;
|
| 2507 | const ctx = {
|
| 2508 | request,
|
| 2509 | runningAction: false
|
| 2510 | };
|
| 2511 | const result = await ServerStorage.run(ctx, () => staticHandler.query(request, {
|
| 2512 | requestContext,
|
| 2513 | skipLoaderErrorBubbling: isDataRequest,
|
| 2514 | skipRevalidation: isSubmission,
|
| 2515 | ...routeIdsToLoad ? { filterMatchesToLoad: (m) => routeIdsToLoad.includes(m.route.id) } : {},
|
| 2516 | normalizePath: (r) => getNormalizedPath(r),
|
| 2517 | async generateMiddlewareResponse(query) {
|
| 2518 | let formState;
|
| 2519 | let skipRevalidation = false;
|
| 2520 | let potentialCSRFAttackError;
|
| 2521 | if (isMutationMethod(request.method)) try {
|
| 2522 | throwIfPotentialCSRFAttack(request, allowedActionOrigins);
|
| 2523 | ctx.runningAction = true;
|
| 2524 | let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
|
| 2525 | ctx.runningAction = false;
|
| 2526 | });
|
| 2527 | if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
| 2528 | skipRevalidation = result?.skipRevalidation ?? false;
|
| 2529 | actionResult = result?.actionResult;
|
| 2530 | formState = result?.formState;
|
| 2531 | request = result?.revalidationRequest ?? request;
|
| 2532 | if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
|
| 2533 | } catch (error) {
|
| 2534 | potentialCSRFAttackError = error;
|
| 2535 | }
|
| 2536 | let staticContext = await query(request, skipRevalidation || !!potentialCSRFAttackError ? { filterMatchesToLoad: () => false } : void 0);
|
| 2537 | if (isResponse(staticContext)) return generateRedirectResponse(staticContext, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
| 2538 | if (potentialCSRFAttackError) {
|
| 2539 | staticContext.errors ??= {};
|
| 2540 | staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
|
| 2541 | staticContext.statusCode = 400;
|
| 2542 | }
|
| 2543 | return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery);
|
| 2544 | }
|
| 2545 | }));
|
| 2546 | if (isRedirectResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
| 2547 | invariant(isResponse(result), "Expected a response from query");
|
| 2548 | return result;
|
| 2549 | }
|
| 2550 | function generateRedirectResponse(response, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, sideEffectRedirectHeaders) {
|
| 2551 | let redirect = response.headers.get("Location");
|
| 2552 | if (isDataRequest && basename) redirect = stripBasename(redirect, basename) || redirect;
|
| 2553 | let payload = {
|
| 2554 | type: "redirect",
|
| 2555 | location: redirect,
|
| 2556 | reload: response.headers.get("X-Remix-Reload-Document") === "true",
|
| 2557 | replace: response.headers.get("X-Remix-Replace") === "true",
|
| 2558 | status: response.status,
|
| 2559 | actionResult
|
| 2560 | };
|
| 2561 | let headers = new Headers(sideEffectRedirectHeaders);
|
| 2562 | for (const [key, value] of response.headers.entries()) headers.append(key, value);
|
| 2563 | headers.delete("Location");
|
| 2564 | headers.delete("X-Remix-Reload-Document");
|
| 2565 | headers.delete("X-Remix-Replace");
|
| 2566 | headers.delete("Content-Length");
|
| 2567 | headers.set("Content-Type", "text/x-component");
|
| 2568 | return generateResponse({
|
| 2569 | statusCode: 202,
|
| 2570 | headers,
|
| 2571 | payload
|
| 2572 | }, {
|
| 2573 | temporaryReferences,
|
| 2574 | onError: defaultOnError
|
| 2575 | });
|
| 2576 | }
|
| 2577 | async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
|
| 2578 | statusCode = staticContext.statusCode ?? statusCode;
|
| 2579 | if (staticContext.errors) staticContext.errors = Object.fromEntries(Object.entries(staticContext.errors).map(([key, error]) => [key, isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error]));
|
| 2580 | staticContext.matches.forEach((m) => {
|
| 2581 | const routeHasNoLoaderData = staticContext.loaderData[m.route.id] === void 0;
|
| 2582 | const routeHasError = Boolean(staticContext.errors && m.route.id in staticContext.errors);
|
| 2583 | if (routeHasNoLoaderData && !routeHasError) staticContext.loaderData[m.route.id] = null;
|
| 2584 | });
|
| 2585 | let headers = getDocumentHeadersImpl(staticContext, (match) => match.route.headers, sideEffectRedirectHeaders);
|
| 2586 | headers.delete("Content-Length");
|
| 2587 | const baseRenderPayload = {
|
| 2588 | type: "render",
|
| 2589 | basename: staticContext.basename,
|
| 2590 | routeDiscovery: routeDiscovery ?? { mode: "lazy" },
|
| 2591 | actionData: staticContext.actionData,
|
| 2592 | errors: staticContext.errors,
|
| 2593 | loaderData: staticContext.loaderData,
|
| 2594 | location: staticContext.location,
|
| 2595 | formState
|
| 2596 | };
|
| 2597 | const renderPayloadPromise = () => getRenderPayload(baseRenderPayload, routes, basename, routeIdsToLoad, isDataRequest, staticContext, routeDiscovery);
|
| 2598 | let payload;
|
| 2599 | if (actionResult) payload = {
|
| 2600 | type: "action",
|
| 2601 | actionResult,
|
| 2602 | rerender: skipRevalidation ? void 0 : renderPayloadPromise()
|
| 2603 | };
|
| 2604 | else if (isSubmission && isDataRequest) payload = {
|
| 2605 | ...baseRenderPayload,
|
| 2606 | matches: [],
|
| 2607 | patches: Promise.resolve([])
|
| 2608 | };
|
| 2609 | else payload = await renderPayloadPromise();
|
| 2610 | return generateResponse({
|
| 2611 | statusCode,
|
| 2612 | headers,
|
| 2613 | payload
|
| 2614 | }, {
|
| 2615 | temporaryReferences,
|
| 2616 | onError: defaultOnError
|
| 2617 | });
|
| 2618 | }
|
| 2619 | async function getRenderPayload(baseRenderPayload, routes, basename, routeIdsToLoad, isDataRequest, staticContext, routeDiscovery) {
|
| 2620 | let deepestRenderedRouteIdx = staticContext.matches.length - 1;
|
| 2621 | let parentIds = {};
|
| 2622 | staticContext.matches.forEach((m, i) => {
|
| 2623 | if (i > 0) parentIds[m.route.id] = staticContext.matches[i - 1].route.id;
|
| 2624 | if (staticContext.errors && m.route.id in staticContext.errors && deepestRenderedRouteIdx > i) deepestRenderedRouteIdx = i;
|
| 2625 | });
|
| 2626 | let matchesPromise = Promise.all(staticContext.matches.map((match, i) => {
|
| 2627 | let isBelowErrorBoundary = i > deepestRenderedRouteIdx;
|
| 2628 | let parentId = parentIds[match.route.id];
|
| 2629 | return getRSCRouteMatch({
|
| 2630 | staticContext,
|
| 2631 | match,
|
| 2632 | routeIdsToLoad,
|
| 2633 | isBelowErrorBoundary,
|
| 2634 | parentId
|
| 2635 | });
|
| 2636 | }));
|
| 2637 | let patches = routeDiscovery?.mode === "initial" && !isDataRequest ? getAllRoutePatches(routes, basename).then((patches) => patches.filter((patch) => !staticContext.matches.some((m) => m.route.id === patch.id))) : getAdditionalRoutePatches(getPathsWithAncestors([staticContext.location.pathname]), routes, basename, staticContext.matches.map((m) => m.route.id));
|
| 2638 | return {
|
| 2639 | ...baseRenderPayload,
|
| 2640 | matches: await matchesPromise,
|
| 2641 | patches
|
| 2642 | };
|
| 2643 | }
|
| 2644 | async function getRSCRouteMatch({ staticContext, match, isBelowErrorBoundary, routeIdsToLoad, parentId }) {
|
| 2645 | const route = match.route;
|
| 2646 | await explodeLazyRoute(route);
|
| 2647 | const Layout = route.Layout || React.Fragment;
|
| 2648 | const Component = route.Component;
|
| 2649 | const ErrorBoundary = route.ErrorBoundary;
|
| 2650 | const HydrateFallback = route.HydrateFallback;
|
| 2651 | const loaderData = staticContext.loaderData[route.id];
|
| 2652 | const actionData = staticContext.actionData?.[route.id];
|
| 2653 | const params = match.params;
|
| 2654 | let element = void 0;
|
| 2655 | let shouldLoadRoute = !routeIdsToLoad || routeIdsToLoad.includes(route.id);
|
| 2656 | if (Component && shouldLoadRoute) element = !isBelowErrorBoundary ? React.createElement(Layout, null, isClientReference(Component) ? React.createElement(WithComponentProps, { children: React.createElement(Component) }) : React.createElement(Component, {
|
| 2657 | loaderData,
|
| 2658 | actionData,
|
| 2659 | params,
|
| 2660 | matches: staticContext.matches.map((match) => convertRouteMatchToUiMatch(match, staticContext.loaderData))
|
| 2661 | })) : React.createElement(Outlet$2);
|
| 2662 | let error = void 0;
|
| 2663 | if (ErrorBoundary && staticContext.errors) error = staticContext.errors[route.id];
|
| 2664 | const errorElement = ErrorBoundary ? React.createElement(Layout, null, isClientReference(ErrorBoundary) ? React.createElement(WithErrorBoundaryProps, { children: React.createElement(ErrorBoundary) }) : React.createElement(ErrorBoundary, {
|
| 2665 | loaderData,
|
| 2666 | actionData,
|
| 2667 | params,
|
| 2668 | error
|
| 2669 | })) : void 0;
|
| 2670 | const hydrateFallbackElement = HydrateFallback ? React.createElement(Layout, null, isClientReference(HydrateFallback) ? React.createElement(WithHydrateFallbackProps, { children: React.createElement(HydrateFallback) }) : React.createElement(HydrateFallback, {
|
| 2671 | loaderData,
|
| 2672 | actionData,
|
| 2673 | params
|
| 2674 | })) : void 0;
|
| 2675 | const hmrRoute = route;
|
| 2676 | return {
|
| 2677 | clientAction: route.clientAction,
|
| 2678 | clientLoader: route.clientLoader,
|
| 2679 | element,
|
| 2680 | errorElement,
|
| 2681 | handle: route.handle,
|
| 2682 | hasAction: !!route.action,
|
| 2683 | hasComponent: !!Component,
|
| 2684 | hasLoader: !!route.loader,
|
| 2685 | hydrateFallbackElement,
|
| 2686 | id: route.id,
|
| 2687 | index: "index" in route ? route.index : void 0,
|
| 2688 | links: route.links,
|
| 2689 | meta: route.meta,
|
| 2690 | params,
|
| 2691 | parentId,
|
| 2692 | path: route.path,
|
| 2693 | pathname: match.pathname,
|
| 2694 | pathnameBase: match.pathnameBase,
|
| 2695 | shouldRevalidate: route.shouldRevalidate,
|
| 2696 | ...hmrRoute.__ensureClientRouteModuleForHMR ? { __ensureClientRouteModuleForHMR: hmrRoute.__ensureClientRouteModuleForHMR } : {}
|
| 2697 | };
|
| 2698 | }
|
| 2699 | async function getManifestRoute(route) {
|
| 2700 | await explodeLazyRoute(route);
|
| 2701 | const Layout = route.Layout || React.Fragment;
|
| 2702 | const errorElement = route.ErrorBoundary ? React.createElement(Layout, null, React.createElement(route.ErrorBoundary)) : void 0;
|
| 2703 | return {
|
| 2704 | clientAction: route.clientAction,
|
| 2705 | clientLoader: route.clientLoader,
|
| 2706 | handle: route.handle,
|
| 2707 | hasAction: !!route.action,
|
| 2708 | hasComponent: !!route.Component,
|
| 2709 | errorElement,
|
| 2710 | hasLoader: !!route.loader,
|
| 2711 | id: route.id,
|
| 2712 | parentId: route.parentId,
|
| 2713 | path: route.path,
|
| 2714 | index: "index" in route ? route.index : void 0,
|
| 2715 | links: route.links,
|
| 2716 | meta: route.meta
|
| 2717 | };
|
| 2718 | }
|
| 2719 | async function explodeLazyRoute(route) {
|
| 2720 | if ("lazy" in route && route.lazy) {
|
| 2721 | let { default: lazyDefaultExport, Component: lazyComponentExport, ...lazyProperties } = await route.lazy();
|
| 2722 | let Component = lazyComponentExport || lazyDefaultExport;
|
| 2723 | if (Component && !route.Component) route.Component = Component;
|
| 2724 | for (let [k, v] of Object.entries(lazyProperties)) if (k !== "id" && k !== "path" && k !== "index" && k !== "children" && route[k] == null) route[k] = v;
|
| 2725 | route.lazy = void 0;
|
| 2726 | }
|
| 2727 | }
|
| 2728 | async function getAllRoutePatches(routes, basename) {
|
| 2729 | let patches = [];
|
| 2730 | async function traverse(route, parentId) {
|
| 2731 | let manifestRoute = await getManifestRoute({
|
| 2732 | ...route,
|
| 2733 | parentId
|
| 2734 | });
|
| 2735 | patches.push(manifestRoute);
|
| 2736 | if ("children" in route && route.children?.length) for (let child of route.children) await traverse(child, route.id);
|
| 2737 | }
|
| 2738 | for (let route of routes) await traverse(route, void 0);
|
| 2739 | return patches.filter((p) => !!p.parentId);
|
| 2740 | }
|
| 2741 | async function getAdditionalRoutePatches(pathnames, routes, basename, matchedRouteIds) {
|
| 2742 | let patchRouteMatches = new Map();
|
| 2743 | let matchedPaths = new Set();
|
| 2744 | for (const pathname of pathnames) {
|
| 2745 | if (matchedPaths.has(pathname)) continue;
|
| 2746 | matchedPaths.add(pathname);
|
| 2747 | let matches = matchRoutes(routes, pathname, basename) || [];
|
| 2748 | matches.forEach((m, i) => {
|
| 2749 | if (patchRouteMatches.get(m.route.id)) return;
|
| 2750 | patchRouteMatches.set(m.route.id, {
|
| 2751 | ...m.route,
|
| 2752 | parentId: matches[i - 1]?.route.id
|
| 2753 | });
|
| 2754 | });
|
| 2755 | }
|
| 2756 | return await Promise.all([...patchRouteMatches.values()].filter((route) => !matchedRouteIds.some((id) => id === route.id)).map((route) => getManifestRoute(route)));
|
| 2757 | }
|
| 2758 | function isReactServerRequest(url) {
|
| 2759 | return url.pathname.endsWith(".rsc");
|
| 2760 | }
|
| 2761 | function isManifestRequest(url) {
|
| 2762 | return url.pathname.endsWith(".manifest");
|
| 2763 | }
|
| 2764 | function defaultOnError(error) {
|
| 2765 | if (isRedirectResponse(error)) return createRedirectErrorDigest(error);
|
| 2766 | if (isResponse(error) || isDataWithResponseInit(error)) return createRouteErrorResponseDigest(error);
|
| 2767 | }
|
| 2768 | function isClientReference(x) {
|
| 2769 | try {
|
| 2770 | return x.$$typeof === Symbol.for("react.client.reference");
|
| 2771 | } catch {
|
| 2772 | return false;
|
| 2773 | }
|
| 2774 | }
|
| 2775 | function canDecodeWithFormData(contentType) {
|
| 2776 | if (!contentType) return false;
|
| 2777 | return contentType.match(/\bapplication\/x-www-form-urlencoded\b/) || contentType.match(/\bmultipart\/form-data\b/);
|
| 2778 | }
|
| 2779 |
|
| 2780 |
|
| 2781 | |
| 2782 | |
| 2783 | |
| 2784 | |
| 2785 | |
| 2786 | |
| 2787 | |
| 2788 | |
| 2789 | |
| 2790 |
|
| 2791 | function href(path, ...args) {
|
| 2792 | let params = args[0];
|
| 2793 | let result = trimTrailingSplat(path).replace(/\/:([\w-]+)(\?)?/g, (_, param, questionMark) => {
|
| 2794 | const isRequired = questionMark === void 0;
|
| 2795 | const value = params?.[param];
|
| 2796 | if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
|
| 2797 | return value === void 0 ? "" : "/" + value;
|
| 2798 | });
|
| 2799 | if (path.endsWith("*")) {
|
| 2800 | const value = params?.["*"];
|
| 2801 | if (value !== void 0) result += "/" + value;
|
| 2802 | }
|
| 2803 | return result || "/";
|
| 2804 | }
|
| 2805 | |
| 2806 | |
| 2807 | |
| 2808 | |
| 2809 |
|
| 2810 | function trimTrailingSplat(path) {
|
| 2811 | let i = path.length - 1;
|
| 2812 | let char = path[i];
|
| 2813 | if (char !== "*" && char !== "/") return path;
|
| 2814 | i--;
|
| 2815 | for (; i >= 0; i--) if (path[i] !== "/") break;
|
| 2816 | return path.slice(0, i + 1);
|
| 2817 | }
|
| 2818 |
|
| 2819 |
|
| 2820 | const encoder = new TextEncoder();
|
| 2821 | const sign = async (value, secret) => {
|
| 2822 | let data = encoder.encode(value);
|
| 2823 | let key = await createKey(secret, ["sign"]);
|
| 2824 | let signature = await crypto.subtle.sign("HMAC", key, data);
|
| 2825 | let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/=+$/, "");
|
| 2826 | return value + "." + hash;
|
| 2827 | };
|
| 2828 | const unsign = async (cookie, secret) => {
|
| 2829 | let index = cookie.lastIndexOf(".");
|
| 2830 | let value = cookie.slice(0, index);
|
| 2831 | let hash = cookie.slice(index + 1);
|
| 2832 | let data = encoder.encode(value);
|
| 2833 | let key = await createKey(secret, ["verify"]);
|
| 2834 | try {
|
| 2835 | let signature = byteStringToUint8Array(atob(hash));
|
| 2836 | return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
|
| 2837 | } catch (e) {
|
| 2838 | return false;
|
| 2839 | }
|
| 2840 | };
|
| 2841 | const createKey = async (secret, usages) => crypto.subtle.importKey("raw", encoder.encode(secret), {
|
| 2842 | name: "HMAC",
|
| 2843 | hash: "SHA-256"
|
| 2844 | }, false, usages);
|
| 2845 | function byteStringToUint8Array(byteString) {
|
| 2846 | let array = new Uint8Array(byteString.length);
|
| 2847 | for (let i = 0; i < byteString.length; i++) array[i] = byteString.charCodeAt(i);
|
| 2848 | return array;
|
| 2849 | }
|
| 2850 |
|
| 2851 |
|
| 2852 | |
| 2853 | |
| 2854 |
|
| 2855 | const createCookie = (name, cookieOptions = {}) => {
|
| 2856 | let { secrets = [], ...options } = {
|
| 2857 | path: "/",
|
| 2858 | sameSite: "lax",
|
| 2859 | ...cookieOptions
|
| 2860 | };
|
| 2861 | warnOnceAboutExpiresCookie(name, options.expires);
|
| 2862 | return {
|
| 2863 | get name() {
|
| 2864 | return name;
|
| 2865 | },
|
| 2866 | get isSigned() {
|
| 2867 | return secrets.length > 0;
|
| 2868 | },
|
| 2869 | get expires() {
|
| 2870 | return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
|
| 2871 | },
|
| 2872 | async parse(cookieHeader, parseOptions) {
|
| 2873 | if (!cookieHeader) return null;
|
| 2874 | let cookies = parse(cookieHeader, {
|
| 2875 | ...options,
|
| 2876 | ...parseOptions
|
| 2877 | });
|
| 2878 | if (name in cookies) {
|
| 2879 | let value = cookies[name];
|
| 2880 | if (typeof value === "string" && value !== "") return await decodeCookieValue(value, secrets);
|
| 2881 | else return "";
|
| 2882 | } else return null;
|
| 2883 | },
|
| 2884 | async serialize(value, serializeOptions) {
|
| 2885 | return serialize(name, value === "" ? "" : await encodeCookieValue(value, secrets), {
|
| 2886 | ...options,
|
| 2887 | ...serializeOptions
|
| 2888 | });
|
| 2889 | }
|
| 2890 | };
|
| 2891 | };
|
| 2892 | |
| 2893 | |
| 2894 | |
| 2895 | |
| 2896 |
|
| 2897 | const isCookie = (object) => {
|
| 2898 | return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
|
| 2899 | };
|
| 2900 | async function encodeCookieValue(value, secrets) {
|
| 2901 | let encoded = encodeData(value);
|
| 2902 | if (secrets.length > 0) encoded = await sign(encoded, secrets[0]);
|
| 2903 | return encoded;
|
| 2904 | }
|
| 2905 | async function decodeCookieValue(value, secrets) {
|
| 2906 | if (secrets.length > 0) {
|
| 2907 | for (let secret of secrets) {
|
| 2908 | let unsignedValue = await unsign(value, secret);
|
| 2909 | if (unsignedValue !== false) return decodeData(unsignedValue);
|
| 2910 | }
|
| 2911 | return null;
|
| 2912 | }
|
| 2913 | return decodeData(value);
|
| 2914 | }
|
| 2915 | function encodeData(value) {
|
| 2916 | return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
|
| 2917 | }
|
| 2918 | function decodeData(value) {
|
| 2919 | try {
|
| 2920 | return JSON.parse(decodeURIComponent(myEscape(atob(value))));
|
| 2921 | } catch (e) {
|
| 2922 | return {};
|
| 2923 | }
|
| 2924 | }
|
| 2925 | function myEscape(value) {
|
| 2926 | let str = value.toString();
|
| 2927 | let result = "";
|
| 2928 | let index = 0;
|
| 2929 | let chr, code;
|
| 2930 | while (index < str.length) {
|
| 2931 | chr = str.charAt(index++);
|
| 2932 | if (/[\w*+\-./@]/.exec(chr)) result += chr;
|
| 2933 | else {
|
| 2934 | code = chr.charCodeAt(0);
|
| 2935 | if (code < 256) result += "%" + hex(code, 2);
|
| 2936 | else result += "%u" + hex(code, 4).toUpperCase();
|
| 2937 | }
|
| 2938 | }
|
| 2939 | return result;
|
| 2940 | }
|
| 2941 | function hex(code, length) {
|
| 2942 | let result = code.toString(16);
|
| 2943 | while (result.length < length) result = "0" + result;
|
| 2944 | return result;
|
| 2945 | }
|
| 2946 | function myUnescape(value) {
|
| 2947 | let str = value.toString();
|
| 2948 | let result = "";
|
| 2949 | let index = 0;
|
| 2950 | let chr, part;
|
| 2951 | while (index < str.length) {
|
| 2952 | chr = str.charAt(index++);
|
| 2953 | if (chr === "%") if (str.charAt(index) === "u") {
|
| 2954 | part = str.slice(index + 1, index + 5);
|
| 2955 | if (/^[\da-f]{4}$/i.exec(part)) {
|
| 2956 | result += String.fromCharCode(parseInt(part, 16));
|
| 2957 | index += 5;
|
| 2958 | continue;
|
| 2959 | }
|
| 2960 | } else {
|
| 2961 | part = str.slice(index, index + 2);
|
| 2962 | if (/^[\da-f]{2}$/i.exec(part)) {
|
| 2963 | result += String.fromCharCode(parseInt(part, 16));
|
| 2964 | index += 2;
|
| 2965 | continue;
|
| 2966 | }
|
| 2967 | }
|
| 2968 | result += chr;
|
| 2969 | }
|
| 2970 | return result;
|
| 2971 | }
|
| 2972 | function warnOnceAboutExpiresCookie(name, expires) {
|
| 2973 | 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.`);
|
| 2974 | }
|
| 2975 |
|
| 2976 |
|
| 2977 | function flash(name) {
|
| 2978 | return `__flash_${name}__`;
|
| 2979 | }
|
| 2980 | |
| 2981 | |
| 2982 | |
| 2983 | |
| 2984 | |
| 2985 |
|
| 2986 | const createSession = (initialData = {}, id = "") => {
|
| 2987 | let map = new Map(Object.entries(initialData));
|
| 2988 | return {
|
| 2989 | get id() {
|
| 2990 | return id;
|
| 2991 | },
|
| 2992 | get data() {
|
| 2993 | return Object.fromEntries(map);
|
| 2994 | },
|
| 2995 | has(name) {
|
| 2996 | return map.has(name) || map.has(flash(name));
|
| 2997 | },
|
| 2998 | get(name) {
|
| 2999 | if (map.has(name)) return map.get(name);
|
| 3000 | let flashName = flash(name);
|
| 3001 | if (map.has(flashName)) {
|
| 3002 | let value = map.get(flashName);
|
| 3003 | map.delete(flashName);
|
| 3004 | return value;
|
| 3005 | }
|
| 3006 | },
|
| 3007 | set(name, value) {
|
| 3008 | map.set(name, value);
|
| 3009 | },
|
| 3010 | flash(name, value) {
|
| 3011 | map.set(flash(name), value);
|
| 3012 | },
|
| 3013 | unset(name) {
|
| 3014 | map.delete(name);
|
| 3015 | }
|
| 3016 | };
|
| 3017 | };
|
| 3018 | |
| 3019 | |
| 3020 | |
| 3021 | |
| 3022 |
|
| 3023 | const isSession = (object) => {
|
| 3024 | return object != null && typeof object.id === "string" && typeof object.data !== "undefined" && typeof object.has === "function" && typeof object.get === "function" && typeof object.set === "function" && typeof object.flash === "function" && typeof object.unset === "function";
|
| 3025 | };
|
| 3026 | |
| 3027 | |
| 3028 | |
| 3029 | |
| 3030 | |
| 3031 |
|
| 3032 | function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) {
|
| 3033 | let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
|
| 3034 | warnOnceAboutSigningSessionCookie(cookie);
|
| 3035 | return {
|
| 3036 | async getSession(cookieHeader, options) {
|
| 3037 | let id = cookieHeader && await cookie.parse(cookieHeader, options);
|
| 3038 | return createSession(id && await readData(id) || {}, id || "");
|
| 3039 | },
|
| 3040 | async commitSession(session, options) {
|
| 3041 | let { id, data } = session;
|
| 3042 | let expires = options?.maxAge != null ? new Date(Date.now() + options.maxAge * 1e3) : options?.expires != null ? options.expires : cookie.expires;
|
| 3043 | if (id) await updateData(id, data, expires);
|
| 3044 | else id = await createData(data, expires);
|
| 3045 | return cookie.serialize(id, options);
|
| 3046 | },
|
| 3047 | async destroySession(session, options) {
|
| 3048 | await deleteData(session.id);
|
| 3049 | return cookie.serialize("", {
|
| 3050 | ...options,
|
| 3051 | maxAge: void 0,
|
| 3052 | expires: new Date(0)
|
| 3053 | });
|
| 3054 | }
|
| 3055 | };
|
| 3056 | }
|
| 3057 | function warnOnceAboutSigningSessionCookie(cookie) {
|
| 3058 | 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://reactrouter.com/explanation/sessions-and-cookies#signing-cookies for more information.`);
|
| 3059 | }
|
| 3060 |
|
| 3061 |
|
| 3062 | |
| 3063 | |
| 3064 | |
| 3065 | |
| 3066 | |
| 3067 | |
| 3068 | |
| 3069 | |
| 3070 |
|
| 3071 | function createCookieSessionStorage({ cookie: cookieArg } = {}) {
|
| 3072 | let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
|
| 3073 | warnOnceAboutSigningSessionCookie(cookie);
|
| 3074 | return {
|
| 3075 | async getSession(cookieHeader, options) {
|
| 3076 | return createSession(cookieHeader && await cookie.parse(cookieHeader, options) || {});
|
| 3077 | },
|
| 3078 | async commitSession(session, options) {
|
| 3079 | let serializedCookie = await cookie.serialize(session.data, options);
|
| 3080 | if (serializedCookie.length > 4096) throw new Error("Cookie length will exceed browser maximum. Length: " + serializedCookie.length);
|
| 3081 | return serializedCookie;
|
| 3082 | },
|
| 3083 | async destroySession(_session, options) {
|
| 3084 | return cookie.serialize("", {
|
| 3085 | ...options,
|
| 3086 | maxAge: void 0,
|
| 3087 | expires: new Date(0)
|
| 3088 | });
|
| 3089 | }
|
| 3090 | };
|
| 3091 | }
|
| 3092 |
|
| 3093 |
|
| 3094 | |
| 3095 | |
| 3096 | |
| 3097 | |
| 3098 | |
| 3099 | |
| 3100 |
|
| 3101 | function createMemorySessionStorage({ cookie } = {}) {
|
| 3102 | let map = new Map();
|
| 3103 | return createSessionStorage({
|
| 3104 | cookie,
|
| 3105 | async createData(data, expires) {
|
| 3106 | let id = Math.random().toString(36).substring(2, 10);
|
| 3107 | map.set(id, {
|
| 3108 | data,
|
| 3109 | expires
|
| 3110 | });
|
| 3111 | return id;
|
| 3112 | },
|
| 3113 | async readData(id) {
|
| 3114 | if (map.has(id)) {
|
| 3115 | let { data, expires } = map.get(id);
|
| 3116 | if (!expires || expires > new Date()) return data;
|
| 3117 | if (expires) map.delete(id);
|
| 3118 | }
|
| 3119 | return null;
|
| 3120 | },
|
| 3121 | async updateData(id, data, expires) {
|
| 3122 | map.set(id, {
|
| 3123 | data,
|
| 3124 | expires
|
| 3125 | });
|
| 3126 | },
|
| 3127 | async deleteData(id) {
|
| 3128 | map.delete(id);
|
| 3129 | }
|
| 3130 | });
|
| 3131 | }
|
| 3132 |
|
| 3133 | export { Await, BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Route, Router, RouterContextProvider, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, createContext, createCookie, createCookieSessionStorage, createMemorySessionStorage, createSession, createSessionStorage, createStaticHandler, data, href, isCookie, isRouteErrorResponse, isSession, matchRoutes, redirect, redirectDocument, replace, unstable_HistoryRouter, getRequest as unstable_getRequest, matchRSCServerRequest as unstable_matchRSCServerRequest };
|