@dmytromykhailiuk/location-context-resolver
v1.0.0
Published
Resolve where your app runs — origin, base-path-relative pathname, query params — and where its remote counterpart lives. Typed, SSR-friendly, zero dependencies.
Maintainers
Readme
@dmytromykhailiuk/location-context-resolver
Resolve where your app runs — origin, base-path-relative pathname, query params — and where its remote counterpart lives. Typed, SSR-friendly, zero dependencies.
Full documentation: open Docs in a browser — every option, with examples, a table of contents and cross-links. This README is the short form.
Built for apps that are served from more than one place — a micro-frontend mounted under
/my-app on a host shell, mirrored on its own remote origin; a white-label product deployed
under different base paths per customer; any setup where "what is my URL?" has two answers. At
that point hand-rolled window.location parsing starts to hurt: base-path stripping via
split() breaks on paths it doesn't expect, remote URLs are glued together with fragile string
concatenation, and none of it runs during SSR.
This library answers with one small, deliberate API. You declare the topology once — the remote origin and the base path — and get back a resolver: a function that, at call time, reads the location and returns a typed, frozen context: both origins, both application origins, the pathname relative to the base path, and parsed query params.
Install
npm i @dmytromykhailiuk/location-context-resolverQuick start
import { createLocationContextResolver } from "@dmytromykhailiuk/location-context-resolver";
const resolveLocationContext = createLocationContextResolver({
remoteOrigin: "https://remote.example.com",
applicationBasePath: "/my-app",
});
// current URL: https://host.example.com/my-app/users/42?tab=posts
const context = resolveLocationContext();
context.origin; // "https://host.example.com"
context.remoteOrigin; // "https://remote.example.com"
context.applicationOrigin; // "https://host.example.com/my-app"
context.remoteApplicationOrigin; // "https://remote.example.com/my-app"
context.pathname; // "/users/42" — relative to the base path
context.queryParams.get("tab"); // "posts"- The resolver reads the location at call time — every call is a fresh snapshot.
- The context is frozen (
Object.freeze) — a snapshot can't drift after the fact. - Creating a resolver never touches the location — module scope is safe everywhere, SSR included.
API
const resolve = createLocationContextResolver({
remoteOrigin: string; // required, non-empty; trailing slashes ignored
applicationBasePath?: string; // "/my-app" — slashes on both ends optional
location?: LocationLike | (() => LocationLike); // defaults to the browser's location
onBasePathMismatch?: (pathname, basePath) => void; // defaults to console.warn
});
const context = resolve();
context.origin; // where the document is served from right now
context.remoteOrigin; // normalized remote origin
context.applicationOrigin; // origin + base path
context.remoteApplicationOrigin; // remote origin + base path
context.pathname; // relative to the base path, leading slash kept
context.queryParams; // fresh URLSearchParams per resolveAn empty remoteOrigin throws at creation — a resolver that can't name its remote is a
configuration bug worth failing loudly on.
Base path
applicationBasePath is normalized — /my-app, my-app, my-app/ and /my-app/ all mean
the same thing. Stripping happens only at a segment boundary:
// applicationBasePath: "/my-app"
"/my-app/users/42" → "/users/42"
"/my-app" → "/"
"/my-app-admin/x" → "/my-app-admin/x" // different app — not stripped
"/other/users" → "/other/users" // mismatch — reported, returned unchangedA pathname outside the base path is never mangled: it is returned unchanged and reported
through onBasePathMismatch (default: console.warn), so a misconfigured mount is visible in
development without breaking navigation.
SSR & custom locations
The resolver reads globalThis.location by default. Where there is none — Node during SSR,
tests, workers — pass the location explicitly: an object for a fixed snapshot (the request
URL), or a function for a live source read on every resolve.
const resolve = createLocationContextResolver({
remoteOrigin: "https://remote.example.com",
applicationBasePath: "/my-app",
location: { origin: requestOrigin, pathname: requestPath, search: requestSearch },
});Creating a resolver is always safe; only calling it without any location throws — with a clear message naming the fix.
TypeScript
Everything is typed end to end: LocationContext fields are readonly, LocationLike is the
three-field subset of Location (so the browser's location satisfies it, and so does any
plain object), and remoteOrigin is required by the types — forgetting it is a compile error.
import type {
LocationContext,
LocationContextResolver,
LocationContextResolverOptions,
LocationLike,
} from "@dmytromykhailiuk/location-context-resolver";License
MIT
