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

@remix-run/router

Package Overview
Dependencies
Maintainers
2
Versions
217
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@remix-run/router - npm Package Compare versions

Comparing version 0.2.0-pre.5 to 0.2.0-pre.6

24

CHANGELOG.md
# @remix-run/router
## 0.2.0-pre.6
### Patch Changes
- c3406eb9: fix: Rename `<Deferred>` to `<Await>` (#9095)
- We are no longer replacing the `Promise` on `loaderData` with the value/error
when it settles so it's now always a `Promise`.
- To that end, we changed from `<Deferred value={promise}>` to
`<Await resolve={promise}>` for clarity, and it also now supports using
`<Await>` with raw promises from anywhere, not only those on `loaderData`
from a defer() call.
- Note that raw promises will not be automatically cancelled on interruptions
so they are not recommended
- The hooks are now `useAsyncValue`/`useAsyncError`
## 0.2.0-pre.5

@@ -10,6 +26,6 @@

- Support array and single promise usages
- `return deferred([ await critical(), lazy() ])`
- `return deferred(lazy())`
- `return defer([ await critical(), lazy() ])`
- `return defer(lazy())`
- Remove `Deferrable`/`ResolvedDeferrable` in favor of raw `Promise`'s and `Awaited`
- Remove generics from `useDeferredData` until `useLoaderData` generic is decided in 6.5
- Remove generics from `useAsyncValue` until `useLoaderData` generic is decided in 6.5

@@ -89,3 +105,3 @@ - feat: Add `createStaticRouter` for `@remix-run/router` SSR usage (#9013)

- fix: Handle fetcher 404s as normal boundary errors (#9015)
- feat: adds `deferred` support to data routers (#9002)
- feat: adds `defer()` support to data routers (#9002)
- feat: add basename support for data routers (#9026)

@@ -92,0 +108,0 @@ - ci: simplify dist/ directory for CJS/ESM only (#9017)

4

dist/index.d.ts
import type { BrowserHistoryOptions, HashHistoryOptions, MemoryHistoryOptions } from "./history";
import type { Router, RouterInit } from "./router";
import { convertRoutesToDataRoutes } from "./utils";
export type { ActionFunction, ActionFunctionArgs, DataRouteMatch, DataRouteObject, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, RouteMatch, RouteObject, ShouldRevalidateFunction, Submission, } from "./utils";
export { ErrorResponse, deferred, generatePath, getToPathname, invariant, isDeferredError, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, warning, } from "./utils";
export type { ActionFunction, ActionFunctionArgs, DataRouteMatch, DataRouteObject, TrackedPromise, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, RouteMatch, RouteObject, ShouldRevalidateFunction, Submission, } from "./utils";
export { ErrorResponse, defer, generatePath, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, warning, } from "./utils";
export type { BrowserHistory, HashHistory, History, InitialEntry, Location, MemoryHistory, Path, To, } from "./history";

@@ -7,0 +7,0 @@ export { Action, createBrowserHistory, createPath, createHashHistory, createMemoryHistory, parsePath, } from "./history";

import { History, Location, To } from "./history";
import { Action as HistoryAction } from "./history";
import type { DataRouteMatch, DataRouteObject, FormEncType, FormMethod, RouteData, RouteObject } from "./utils";
import { DataRouteMatch, DataRouteObject, FormEncType, FormMethod, RouteData, RouteObject } from "./utils";
import { DeferredData } from "./utils";

@@ -5,0 +5,0 @@ /**

@@ -27,3 +27,3 @@ /// <reference types="react" />

/**
* Successful deferred() result from a loader or action
* Successful defer() result from a loader or action
*/

@@ -143,8 +143,17 @@ export interface DeferredResult {

}
declare type ParamParseFailed = {
failed: true;
};
declare type ParamParseSegment<Segment extends string> = Segment extends `${infer LeftSegment}/${infer RightSegment}` ? ParamParseSegment<LeftSegment> extends infer LeftResult ? ParamParseSegment<RightSegment> extends infer RightResult ? LeftResult extends string ? RightResult extends string ? LeftResult | RightResult : LeftResult : RightResult extends string ? RightResult : ParamParseFailed : ParamParseFailed : ParamParseSegment<RightSegment> extends infer RightResult ? RightResult extends string ? RightResult : ParamParseFailed : ParamParseFailed : Segment extends `${string}:${infer Remaining}` ? Remaining : ParamParseFailed;
export declare type ParamParseKey<Segment extends string> = ParamParseSegment<Segment> extends string ? ParamParseSegment<Segment> : string;
declare type _PathParam<Path extends string> = Path extends `${infer L}/${infer R}` ? _PathParam<L> | _PathParam<R> : Path extends `${string}:${infer Param}` ? Param : never;
/**
* Examples:
* "/a/b/*" -> "*"
* ":a" -> "a"
* "/a/:b" -> "b"
* "/a/blahblahblah:b" -> "b"
* "/:a/:b" -> "a" | "b"
* "/:a/b/:c/*" -> "a" | "c" | "*"
*/
declare type PathParam<Path extends string> = Path extends "*" ? "*" : Path extends `${infer Rest}/*` ? "*" | _PathParam<Rest> : _PathParam<Path>;
export declare type ParamParseKey<Segment extends string> = [
PathParam<Segment>
] extends [never] ? string : PathParam<Segment>;
/**
* The parameters that were parsed from the URL path.

@@ -188,3 +197,5 @@ */

*/
export declare function generatePath(path: string, params?: Params): string;
export declare function generatePath<Path extends string>(path: Path, params?: {
[key in PathParam<Path>]: string;
}): string;
/**

@@ -287,3 +298,7 @@ * A PathPattern is used to match on some portion of a URL pathname.

export declare const json: JsonFunction;
declare type DeferredInput = Record<string, unknown> | Array<unknown> | Promise<unknown>;
export interface TrackedPromise extends Promise<any> {
_tracked?: boolean;
_data?: any;
_error?: any;
}
export declare class DeferredData {

@@ -293,4 +308,4 @@ private pendingKeys;

private subscriber?;
data: DeferredInput | unknown;
constructor(data: DeferredInput);
data: Record<string, unknown>;
constructor(data: Record<string, unknown>);
private trackPromise;

@@ -300,16 +315,7 @@ private onSettle;

cancel(): void;
resolveData(signal: AbortSignal): Promise<boolean>;
get done(): boolean;
get unwrappedData(): {};
}
/**
* @private
* Utility class we use to hold deferred promise rejection values
*/
export declare class DeferredError extends Error {
}
/**
* Check if the given error is a DeferredError generated from a deferred()
* promise rejection
*/
export declare function isDeferredError(e: any): e is DeferredError;
export declare function deferred(data: DeferredInput): DeferredData;
export declare function defer(data: Record<string, unknown>): DeferredData;
export declare type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;

@@ -316,0 +322,0 @@ /**

@@ -20,2 +20,3 @@ import type {

DataRouteObject,
TrackedPromise,
FormEncType,

@@ -39,7 +40,6 @@ FormMethod,

ErrorResponse,
deferred,
defer,
generatePath,
getToPathname,
invariant,
isDeferredError,
isRouteErrorResponse,

@@ -46,0 +46,0 @@ joinPaths,

{
"name": "@remix-run/router",
"version": "0.2.0-pre.5",
"version": "0.2.0-pre.6",
"description": "Nested/Data-driven/Framework-agnostic Routing",

@@ -5,0 +5,0 @@ "keywords": [

@@ -31,3 +31,3 @@ import type { Location, Path, To } from "./history";

/**
* Successful deferred() result from a loader or action
* Successful defer() result from a loader or action
*/

@@ -165,40 +165,31 @@ export interface DeferredResult {

type ParamParseFailed = { failed: true };
// Recursive helper for finding path parameters in the absence of wildcards
type _PathParam<Path extends string> =
// split path into individual path segments
Path extends `${infer L}/${infer R}`
? _PathParam<L> | _PathParam<R>
: // find params after `:`
Path extends `${string}:${infer Param}`
? Param
: // otherwise, there aren't any params present
never;
type ParamParseSegment<Segment extends string> =
// Check here if there exists a forward slash in the string.
Segment extends `${infer LeftSegment}/${infer RightSegment}`
? // If there is a forward slash, then attempt to parse each side of the
// forward slash.
ParamParseSegment<LeftSegment> extends infer LeftResult
? ParamParseSegment<RightSegment> extends infer RightResult
? LeftResult extends string
? // If the left side is successfully parsed as a param, then check if
// the right side can be successfully parsed as well. If both sides
// can be parsed, then the result is a union of the two sides
// (read: "foo" | "bar").
RightResult extends string
? LeftResult | RightResult
: LeftResult
: // If the left side is not successfully parsed as a param, then check
// if only the right side can be successfully parse as a param. If it
// can, then the result is just right, else it's a failure.
RightResult extends string
? RightResult
: ParamParseFailed
: ParamParseFailed
: // If the left side didn't parse into a param, then just check the right
// side.
ParamParseSegment<RightSegment> extends infer RightResult
? RightResult extends string
? RightResult
: ParamParseFailed
: ParamParseFailed
: // If there's no forward slash, then check if this segment starts with a
// colon. If it does, then this is a dynamic segment, so the result is
// just the remainder of the string, optionally prefixed with another string.
// Otherwise, it's a failure.
Segment extends `${string}:${infer Remaining}`
? Remaining
: ParamParseFailed;
/**
* Examples:
* "/a/b/*" -> "*"
* ":a" -> "a"
* "/a/:b" -> "b"
* "/a/blahblahblah:b" -> "b"
* "/:a/:b" -> "a" | "b"
* "/:a/b/:c/*" -> "a" | "c" | "*"
*/
type PathParam<Path extends string> =
// check if path is just a wildcard
Path extends "*"
? "*"
: // look for wildcard at the end of the path
Path extends `${infer Rest}/*`
? "*" | _PathParam<Rest>
: // look for params in the absence of wildcards
_PathParam<Path>;

@@ -209,5 +200,4 @@ // Attempt to parse the given string segment. If it fails, then just return the

export type ParamParseKey<Segment extends string> =
ParamParseSegment<Segment> extends string
? ParamParseSegment<Segment>
: string;
// if could not find path params, fallback to `string`
[PathParam<Segment>] extends [never] ? string : PathParam<Segment>;

@@ -481,11 +471,18 @@ /**

*/
export function generatePath(path: string, params: Params = {}): string {
export function generatePath<Path extends string>(
path: Path,
params: {
[key in PathParam<Path>]: string;
} = {} as any
): string {
return path
.replace(/:(\w+)/g, (_, key) => {
.replace(/:(\w+)/g, (_, key: PathParam<Path>) => {
invariant(params[key] != null, `Missing ":${key}" param`);
return params[key]!;
})
.replace(/\/*\*$/, (_) =>
params["*"] == null ? "" : params["*"].replace(/^\/*/, "/")
);
.replace(/\/*\*$/, (_) => {
const star = "*" as PathParam<Path>;
return params[star] == null ? "" : params[star].replace(/^\/*/, "/");
});
}

@@ -887,6 +884,7 @@

type DeferredInput =
| Record<string, unknown>
| Array<unknown>
| Promise<unknown>;
export interface TrackedPromise extends Promise<any> {
_tracked?: boolean;
_data?: any;
_error?: any;
}

@@ -897,20 +895,16 @@ export class DeferredData {

private subscriber?: (aborted: boolean) => void = undefined;
data: DeferredInput | unknown;
data: Record<string, unknown>;
constructor(data: DeferredInput) {
// Store all data in our internal copy and track promise keys
if (data instanceof Promise) {
this.data = data;
this.trackPromise("__single__", data);
} else if (Array.isArray(data)) {
this.data = [...data];
data.forEach((value, index) => this.trackPromise(index, value));
} else if (typeof data === "object") {
this.data = { ...data };
Object.entries(data).forEach(([key, value]) =>
this.trackPromise(key, value)
);
} else {
invariant(false, "Incorrect data type passed to deferred()");
}
constructor(data: Record<string, unknown>) {
invariant(
data && typeof data === "object" && !Array.isArray(data),
"defer() only accepts plain objects"
);
this.data = Object.entries(data).reduce(
(acc, [key, value]) =>
Object.assign(acc, {
[key]: this.trackPromise(key, value),
}),
{}
);
}

@@ -921,13 +915,25 @@

value: Promise<unknown> | unknown
) {
if (value instanceof Promise) {
this.pendingKeys.add(key);
value.then(
(data) => this.onSettle(key, null, data as unknown),
(error) => this.onSettle(key, error as unknown)
);
): TrackedPromise | unknown {
if (!(value instanceof Promise)) {
return value;
}
this.pendingKeys.add(key);
// We store a little wrapper promise that will be extended with
// _data/_error props upon resolve/reject
let promise: TrackedPromise = value.then(
(data) => this.onSettle(promise, key, null, data as unknown),
(error) => this.onSettle(promise, key, error as unknown)
);
Object.defineProperty(promise, "_tracked", { get: () => true });
return promise;
}
private onSettle(key: string | number, error: unknown, data?: unknown) {
private onSettle(
promise: TrackedPromise,
key: string | number,
error: unknown,
data?: unknown
): void {
if (this.cancelled) {

@@ -938,18 +944,6 @@ return;

let value = error ? new DeferredError(error as string) : data;
if (this.data instanceof Promise) {
this.data = value;
} else if (Array.isArray(this.data)) {
invariant(typeof key === "number", "expected key to be a number");
let data = [...this.data];
data[key] = value;
this.data = data;
} else if (typeof this.data === "object") {
this.data = {
...this.data,
[key]: value,
};
if (error) {
Object.defineProperty(promise, "_error", { get: () => error });
} else {
invariant(false, "Incorrect data type on DeferredData");
Object.defineProperty(promise, "_data", { get: () => data });
}

@@ -970,22 +964,57 @@

async resolveData(signal: AbortSignal) {
let aborted = false;
if (!this.done) {
let onAbort = () => this.cancel();
signal.addEventListener("abort", onAbort);
aborted = await new Promise((resolve) => {
this.subscribe((aborted) => {
signal.removeEventListener("abort", onAbort);
if (aborted || this.done) {
resolve(aborted);
}
});
});
}
return aborted;
}
get done() {
return this.pendingKeys.size === 0;
}
get unwrappedData() {
invariant(
this.data !== null && this.done,
"Can only unwrap data on initialized and settled deferreds"
);
return Object.entries(this.data).reduce(
(acc, [key, value]) =>
Object.assign(acc, {
[key]: unwrapTrackedPromise(value),
}),
{}
);
}
}
/**
* @private
* Utility class we use to hold deferred promise rejection values
*/
export class DeferredError extends Error {}
function isTrackedPromise(value: any): value is TrackedPromise {
return (
value instanceof Promise && (value as TrackedPromise)._tracked === true
);
}
/**
* Check if the given error is a DeferredError generated from a deferred()
* promise rejection
*/
export function isDeferredError(e: any): e is DeferredError {
return e instanceof DeferredError;
function unwrapTrackedPromise(value: any) {
if (!isTrackedPromise(value)) {
return value;
}
if (value._error) {
throw value._error;
}
return value._data;
}
export function deferred(data: DeferredInput) {
export function defer(data: Record<string, unknown>) {
return new DeferredData(data);

@@ -992,0 +1021,0 @@ }

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc