Socket
Socket
Sign inDemoInstall

@solidjs/router

Package Overview
Dependencies
Maintainers
2
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solidjs/router - npm Package Compare versions

Comparing version 0.13.6 to 0.14.0

8

dist/data/action.d.ts
import { JSX } from "solid-js";
import type { Submission, SubmissionStub } from "../types.js";
export type Action<T extends Array<any>, U> = (T extends [FormData] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<U>) & {
import type { Submission, SubmissionStub, NarrowResponse } from "../types.js";
export type Action<T extends Array<any>, U> = (T extends [FormData] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<NarrowResponse<U>>) & {
url: string;
with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<U>, ...args: A): Action<B, U>;
with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<NarrowResponse<U>>, ...args: A): Action<B, U>;
};

@@ -12,3 +12,3 @@ export declare const actions: Map<string, Action<any, any>>;

export declare function useSubmission<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, U> | SubmissionStub;
export declare function useAction<T extends Array<any>, U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>;
export declare function useAction<T extends Array<any>, U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<NarrowResponse<U>>;
export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>;

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

import { CacheEntry } from "../types.js";
import type { CacheEntry, NarrowResponse } from "../types.js";
export declare function revalidate(key?: string | string[] | void, force?: boolean): Promise<void>;

@@ -6,3 +6,3 @@ export declare function cacheKeyOp(key: string | string[] | void, fn: (cacheEntry: CacheEntry) => void): void;

[K in keyof A]-?: A[K];
} ? (...args: never[]) => R : T) & {
} ? (...args: never[]) => R extends Promise<infer P> ? Promise<NarrowResponse<P>> : NarrowResponse<R> : (...args: A) => R extends Promise<infer P> ? Promise<NarrowResponse<P>> : NarrowResponse<R>) & {
keyFor: (...args: A) => string;

@@ -9,0 +9,0 @@ key: string;

import { createSignal, getListener, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
import { getRequestEvent, isServer } from "solid-js/web";
import { useNavigate, getIntent, getInLoadFn } from "../routing.js";
import { useNavigate, getIntent, getInPreloadFn } from "../routing.js";
const LocationHeader = "Location";

@@ -50,3 +50,3 @@ const PRELOAD_TIMEOUT = 5000;

const intent = getIntent();
const inLoadFn = getInLoadFn();
const inPreloadFn = getInPreloadFn();
const owner = getOwner();

@@ -98,3 +98,3 @@ const navigate = owner ? useNavigate() : undefined;

}
inLoadFn && "then" in res && res.catch(() => { });
inPreloadFn && "then" in res && res.catch(() => { });
return res;

@@ -130,3 +130,3 @@ }

}
inLoadFn && "then" in res && res.catch(() => { });
inPreloadFn && "then" in res && res.catch(() => { });
// serialize on server

@@ -133,0 +133,0 @@ if (isServer &&

@@ -114,6 +114,6 @@ import { delegateEvents } from "solid-js/web";

evt.preventDefault();
const data = new FormData(evt.target);
if (evt.submitter && evt.submitter.name)
data.append(evt.submitter.name, evt.submitter.value);
handler.call({ r: router, f: evt.target }, data);
const data = new FormData(evt.target, evt.submitter);
handler.call({ r: router, f: evt.target }, evt.target.enctype === "multipart/form-data"
? data
: new URLSearchParams(data));
}

@@ -120,0 +120,0 @@ }

@@ -1,6 +0,4 @@

export type RouterResponseInit = Omit<ResponseInit, "body"> & {
revalidate?: string | string[];
};
export declare function redirect(url: string, init?: number | RouterResponseInit): never;
export declare function reload(init?: RouterResponseInit): never;
export declare function json<T>(data: T, init?: RouterResponseInit): T;
import type { RouterResponseInit, CustomResponse } from "../types";
export declare function redirect(url: string, init?: number | RouterResponseInit): CustomResponse<never>;
export declare function reload(init?: RouterResponseInit): CustomResponse<never>;
export declare function json<T>(data: T, init?: RouterResponseInit): CustomResponse<T>;
export * from "./routers/index.js";
export * from "./components.jsx";
export * from "./lifecycle.js";
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, usePreloadRoute } from "./routing.js";
export { mergeSearchString as _mergeSearchString } from "./utils.js";
export * from "./data/index.js";
export type { Location, LocationChange, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouteDescription, RouteMatch, RouterIntegration, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types.js";
export type { Location, LocationChange, MatchFilter, MatchFilters, NavigateOptions, Navigator, OutputMatch, Params, PathMatch, RouteSectionProps, RoutePreloadFunc, RoutePreloadFuncArgs, RouteDefinition, RouteDescription, RouteMatch, RouterIntegration, RouterUtils, SetParams, BeforeLeaveEventArgs, RouteLoadFunc, RouteLoadFuncArgs, RouterResponseInit, CustomResponse } from "./types.js";

@@ -249,2 +249,3 @@ import { isServer, getRequestEvent, createComponent as createComponent$1, memo, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';

const useIsRouting = () => useRouter().isRouting;
const usePreloadRoute = () => useRouter().preloadRoute;
const useMatch = (path, matchFilters) => {

@@ -286,2 +287,3 @@ const location = useLocation();

component,
preload,
load,

@@ -295,3 +297,3 @@ children,

component,
load,
preload: preload || load,
info

@@ -413,8 +415,8 @@ };

}
let inLoadFn = false;
function getInLoadFn() {
return inLoadFn;
let inPreloadFn = false;
function getInPreloadFn() {
return inPreloadFn;
}
function setInLoadFn(value) {
inLoadFn = value;
function setInPreloadFn(value) {
inPreloadFn = value;
}

@@ -600,6 +602,6 @@ function createRouterContext(integration, branches, getContext, options = {}) {

const {
load
preload
} = route;
inLoadFn = true;
options.preloadData && load && runWithOwner(getContext(), () => load({
inPreloadFn = true;
options.preloadData && preload && runWithOwner(getContext(), () => preload({
params,

@@ -616,3 +618,3 @@ location: {

}));
inLoadFn = false;
inPreloadFn = false;
}

@@ -635,8 +637,8 @@ intent = prevIntent;

component,
load
preload
} = match().route;
const path = createMemo(() => match().path);
component && component.preload && component.preload();
inLoadFn = true;
const data = load ? load({
inPreloadFn = true;
const data = preload ? preload({
params,

@@ -646,3 +648,3 @@ location,

}) : undefined;
inLoadFn = false;
inPreloadFn = false;
const route = {

@@ -688,4 +690,4 @@ parent,

},
get load() {
return props.rootLoad;
get preload() {
return props.rootPreload || props.rootLoad;
},

@@ -707,5 +709,5 @@ get children() {

const params = props.routerState.params;
const data = createMemo(() => props.load && untrack(() => {
setInLoadFn(true);
props.load({
const data = createMemo(() => props.preload && untrack(() => {
setInPreloadFn(true);
props.preload({
params,

@@ -715,3 +717,3 @@ location,

});
setInLoadFn(false);
setInPreloadFn(false);
}));

@@ -821,3 +823,3 @@ return createComponent$1(Show, {

} = matches[match];
route.load && route.load({
route.preload && route.preload({
params,

@@ -937,3 +939,3 @@ location: routerState.location,

const intent = getIntent();
const inLoadFn = getInLoadFn();
const inPreloadFn = getInPreloadFn();
const owner = getOwner();

@@ -976,3 +978,3 @@ const navigate = owner ? useNavigate() : undefined;

}
inLoadFn && "then" in res && res.catch(() => {});
inPreloadFn && "then" in res && res.catch(() => {});
return res;

@@ -1002,3 +1004,3 @@ }

}
inLoadFn && "then" in res && res.catch(() => {});
inPreloadFn && "then" in res && res.catch(() => {});
// serialize on server

@@ -1295,8 +1297,7 @@ if (isServer && sharedConfig.context && sharedConfig.context.async && !sharedConfig.context.noHydrate) {

evt.preventDefault();
const data = new FormData(evt.target);
if (evt.submitter && evt.submitter.name) data.append(evt.submitter.name, evt.submitter.value);
const data = new FormData(evt.target, evt.submitter);
handler.call({
r: router,
f: evt.target
}, data);
}, evt.target.enctype === "multipart/form-data" ? data : new URLSearchParams(data));
}

@@ -1331,3 +1332,3 @@ }

const getSource = () => {
const url = window.location.pathname + window.location.search;
const url = window.location.pathname.replace(/^\/+/, "/") + window.location.search;
return {

@@ -1663,2 +1664,2 @@ value: props.transformUrl ? props.transformUrl(url) + window.location.hash : url + window.location.hash,

export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, usePreloadRoute, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
export * from "./routers/index.js";
export * from "./components.jsx";
export * from "./lifecycle.js";
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, usePreloadRoute } from "./routing.js";
export { mergeSearchString as _mergeSearchString } from "./utils.js";
export * from "./data/index.js";
import type { Component, JSX } from "solid-js";
import type { MatchFilters, RouteLoadFunc, RouteDefinition, RouterIntegration, RouteSectionProps } from "../types.js";
import type { MatchFilters, RouteDefinition, RouterIntegration, RouteSectionProps, RoutePreloadFunc } from "../types.js";
export type BaseRouterProps = {

@@ -9,6 +9,8 @@ base?: string;

root?: Component<RouteSectionProps>;
rootLoad?: RouteLoadFunc;
rootPreload?: RoutePreloadFunc;
singleFlight?: boolean;
children?: JSX.Element | RouteDefinition | RouteDefinition[];
transformUrl?: (url: string) => string;
/** @deprecated use rootPreload */
rootLoad?: RoutePreloadFunc;
};

@@ -19,7 +21,9 @@ export declare const createRouterComponent: (router: RouterIntegration) => (props: BaseRouterProps) => JSX.Element;

children?: JSX.Element;
load?: RouteLoadFunc<T>;
preload?: RoutePreloadFunc<T>;
matchFilters?: MatchFilters<S>;
component?: Component<RouteSectionProps<T>>;
info?: Record<string, any>;
/** @deprecated use preload */
load?: RoutePreloadFunc<T>;
};
export declare const Route: <S extends string, T = unknown>(props: RouteProps<S, T>) => JSX.Element;
/*@refresh skip*/
import { getRequestEvent, isServer } from "solid-js/web";
import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInLoadFn } from "../routing.js";
import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInPreloadFn } from "../routing.js";
export const createRouterComponent = (router) => (props) => {

@@ -17,3 +17,3 @@ const { base } = props;

return (<RouterContextObj.Provider value={routerState}>
<Root routerState={routerState} root={props.root} load={props.rootLoad}>
<Root routerState={routerState} root={props.root} preload={props.rootPreload || props.rootLoad}>
{(context = getOwner()) && null}

@@ -27,7 +27,7 @@ <Routes routerState={routerState} branches={branches()}/>

const params = props.routerState.params;
const data = createMemo(() => props.load &&
const data = createMemo(() => props.preload &&
untrack(() => {
setInLoadFn(true);
props.load({ params, location, intent: getIntent() || "initial" });
setInLoadFn(false);
setInPreloadFn(true);
props.preload({ params, location, intent: getIntent() || "initial" });
setInPreloadFn(false);
}));

@@ -110,4 +110,4 @@ return (<Show when={props.root} keyed fallback={props.children}>

const { route, params } = matches[match];
route.load &&
route.load({
route.preload &&
route.preload({
params,

@@ -114,0 +114,0 @@ location: routerState.location,

@@ -10,3 +10,3 @@ import { isServer } from "solid-js/web";

const getSource = () => {
const url = window.location.pathname + window.location.search;
const url = window.location.pathname.replace(/^\/+/, "/") + window.location.search;
return {

@@ -13,0 +13,0 @@ value: props.transformUrl ? props.transformUrl(url) + window.location.hash : url + window.location.hash,

@@ -26,4 +26,4 @@ import { JSX, Accessor } from "solid-js";

export declare function getIntent(): Intent | undefined;
export declare function getInLoadFn(): boolean;
export declare function setInLoadFn(value: boolean): void;
export declare function getInPreloadFn(): boolean;
export declare function setInPreloadFn(value: boolean): void;
export declare function createRouterContext(integration: RouterIntegration, branches: () => Branch[], getContext?: () => any, options?: {

@@ -30,0 +30,0 @@ base?: string;

@@ -62,3 +62,3 @@ import { runWithOwner, batch } from "solid-js";

export function createRoutes(routeDef, base = "") {
const { component, load, children, info } = routeDef;
const { component, preload, load, children, info } = routeDef;
const isLeaf = !children || (Array.isArray(children) && !children.length);

@@ -68,3 +68,3 @@ const shared = {

component,
load,
preload: preload || load,
info

@@ -191,8 +191,8 @@ };

}
let inLoadFn = false;
export function getInLoadFn() {
return inLoadFn;
let inPreloadFn = false;
export function getInPreloadFn() {
return inPreloadFn;
}
export function setInLoadFn(value) {
inLoadFn = value;
export function setInPreloadFn(value) {
inPreloadFn = value;
}

@@ -356,7 +356,7 @@ export function createRouterContext(integration, branches, getContext, options = {}) {

route.component.preload();
const { load } = route;
inLoadFn = true;
const { preload } = route;
inPreloadFn = true;
options.preloadData &&
load &&
runWithOwner(getContext(), () => load({
preload &&
runWithOwner(getContext(), () => preload({
params,

@@ -373,3 +373,3 @@ location: {

}));
inLoadFn = false;
inPreloadFn = false;
}

@@ -385,3 +385,3 @@ intent = prevIntent;

const { base, location, params } = router;
const { pattern, component, load } = match().route;
const { pattern, component, preload } = match().route;
const path = createMemo(() => match().path);

@@ -391,5 +391,5 @@ component &&

component.preload();
inLoadFn = true;
const data = load ? load({ params, location, intent: intent || "initial" }) : undefined;
inLoadFn = false;
inPreloadFn = true;
const data = preload ? preload({ params, location, intent: intent || "initial" }) : undefined;
inPreloadFn = false;
const route = {

@@ -396,0 +396,0 @@ parent,

@@ -59,3 +59,3 @@ import type { Component, JSX, Signal } from "solid-js";

export type Intent = "initial" | "native" | "navigate" | "preload";
export interface RouteLoadFuncArgs {
export interface RoutePreloadFuncArgs {
params: Params;

@@ -65,3 +65,3 @@ location: Location;

}
export type RouteLoadFunc<T = unknown> = (args: RouteLoadFuncArgs) => T;
export type RoutePreloadFunc<T = unknown> = (args: RoutePreloadFuncArgs) => T;
export interface RouteSectionProps<T = unknown> {

@@ -76,6 +76,8 @@ params: Params;

matchFilters?: MatchFilters<S>;
load?: RouteLoadFunc<T>;
preload?: RoutePreloadFunc<T>;
children?: RouteDefinition | RouteDefinition[];
component?: Component<RouteSectionProps<T>>;
info?: Record<string, any>;
/** @deprecated use preload */
load?: RoutePreloadFunc;
};

@@ -106,3 +108,3 @@ export type MatchFilter = readonly string[] | RegExp | ((s: string) => boolean);

component?: Component<RouteSectionProps>;
load?: RouteLoadFunc;
preload?: RoutePreloadFunc;
matcher: (location: string) => PathMatch | null;

@@ -188,1 +190,13 @@ matchFilters?: MatchFilters;

}];
export type NarrowResponse<T> = T extends CustomResponse<infer U> ? U : Exclude<T, Response>;
export type RouterResponseInit = Omit<ResponseInit, "body"> & {
revalidate?: string | string[];
};
export type CustomResponse<T> = Omit<Response, "clone"> & {
customBody: () => T;
clone(...args: readonly unknown[]): CustomResponse<T>;
};
/** @deprecated */
export type RouteLoadFunc = RoutePreloadFunc;
/** @deprecated */
export type RouteLoadFuncArgs = RoutePreloadFuncArgs;

@@ -9,3 +9,3 @@ {

"license": "MIT",
"version": "0.13.6",
"version": "0.14.0",
"homepage": "https://github.com/solidjs/solid-router#readme",

@@ -12,0 +12,0 @@ "repository": {

@@ -13,3 +13,3 @@ <p>

It supports all of Solid's SSR methods and has Solid's transitions baked in, so use it freely with suspense, resources, and lazy components. Solid Router also allows you to define a load function that loads parallel to the routes ([render-as-you-fetch](https://epicreact.dev/render-as-you-fetch/)).
It supports all of Solid's SSR methods and has Solid's transitions baked in, so use it freely with suspense, resources, and lazy components. Solid Router also allows you to define a preload function that loads parallel to the routes ([render-as-you-fetch](https://epicreact.dev/render-as-you-fetch/)).

@@ -384,7 +384,7 @@ - [Getting Started](#getting-started)

## Load Functions
## Preload Functions
Even with smart caches it is possible that we have waterfalls both with view logic and with lazy loaded code. With load functions, we can instead start fetching the data parallel to loading the route, so we can use the data as soon as possible. The load function is called when the Route is loaded or eagerly when links are hovered.
Even with smart caches it is possible that we have waterfalls both with view logic and with lazy loaded code. With preload functions, we can instead start fetching the data parallel to loading the route, so we can use the data as soon as possible. The preload function is called when the Route is loaded or eagerly when links are hovered.
As its only argument, the load function is passed an object that you can use to access route information:
As its only argument, the preload function is passed an object that you can use to access route information:

@@ -397,9 +397,9 @@ ```js

// load function
function loadUser({params, location}) {
// do loading
// preload function
function preloadUser({params, location}) {
// do preloading
}
// Pass it in the route definition
<Route path="/users/:id" component={User} load={loadUser} />;
<Route path="/users/:id" component={User} preload={preloadUser} />;
```

@@ -414,3 +414,3 @@

A common pattern is to export the load function and data wrappers that corresponds to a route in a dedicated `route.data.js` file. This way, the data function can be imported without loading anything else.
A common pattern is to export the preload function and data wrappers that corresponds to a route in a dedicated `route.data.js` file. This way, the data function can be imported without loading anything else.

@@ -420,10 +420,10 @@ ```js

import { Route } from "@solidjs/router";
import loadUser from "./pages/users/[id].data.js";
import preloadUser from "./pages/users/[id].data.js";
const User = lazy(() => import("/pages/users/[id].js"));
// In the Route definition
<Route path="/users/:id" component={User} load={loadUser} />;
<Route path="/users/:id" component={User} preload={preloadUser} />;
```
The return value of the `load` function is passed to the page component when called at anytime other than `"preload"`, so you can initialize things in there, or alternatively use our new Data APIs:
The return value of the `preload` function is passed to the page component when called at anytime other than `"preload"` intent, so you can initialize things in there, or alternatively use our new Data APIs:

@@ -433,3 +433,3 @@

Keep in mind these are completely optional. To use but showcase the power of our load mechanism.
Keep in mind these are completely optional. To use but showcase the power of our preload mechanism.

@@ -450,7 +450,7 @@ ### `cache`

1. It does just deduping on the server for the lifetime of the request.
2. It does preload cache in the browser which lasts 10 seconds. When a route is preloaded on hover or when load is called when entering a route it will make sure to dedupe calls.
2. It does preload cache in the browser which lasts 5 seconds. When a route is preloaded on hover or when preload is called when entering a route it will make sure to dedupe calls.
3. We have a reactive refetch mechanism based on key. So we can tell routes that aren't new to retrigger on action revalidation.
4. It will serve as a back/forward cache for browser navigation up to 5 mins. Any user based navigation or link click bypasses it. Revalidation or new fetch updates the cache.
Using it with load function might look like:
Using it with preload function might look like:

@@ -464,4 +464,4 @@ ```js

// load function
function loadUser({params, location}) {
// preload function
function preloadUser({params, location}) {
void getUser(params.id)

@@ -471,3 +471,3 @@ }

// Pass it in the route definition
<Route path="/users/:id" component={User} load={loadUser} />;
<Route path="/users/:id" component={User} preload={preloadUser} />;
```

@@ -782,3 +782,3 @@

| children | `JSX.Element` | Nested `<Route>` definitions |
| load | `RouteLoadFunc` | Function called during preload or when the route is navigated to. |
| preload | `RoutePreloadFunc` | Function called during preload or when the route is navigated to. |

@@ -942,3 +942,3 @@ ## Router Primitives

These have been replaced by a load mechanism. This allows link hover preloads (as the load function can be run as much as wanted without worry about reactivity). It support deduping/cache APIs which give more control over how things are cached. It also addresses TS issues with getting the right types in the Component without `typeof` checks.
These have been replaced by a preload mechanism. This allows link hover preloads (as the preload function can be run as much as wanted without worry about reactivity). It support deduping/cache APIs which give more control over how things are cached. It also addresses TS issues with getting the right types in the Component without `typeof` checks.

@@ -953,4 +953,4 @@ That being said you can reproduce the old pattern largely by turning off preloads at the router level and then injecting your own Context:

// load function
function loadUser({params, location}) {
// preload function
function preloadUser({params, location}) {
const [user] = createResource(() => params.id, fetchUser);

@@ -962,3 +962,3 @@ return user;

<Router preload={false}>
<Route path="/users/:id" component={User} load={loadUser} />
<Route path="/users/:id" component={User} preload={preloadUser} />
</Router>

@@ -965,0 +965,0 @@ ```

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