npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ptech-shell-dev

v2.9.0

Published

Standalone/mock shell service implementations for Module Federation apps.

Downloads

1,911

Readme

ptech-shell-dev

Standalone and development implementations for the contracts exported by ptech-shell-sdk.

Install

npm i ptech-shell-dev ptech-shell-sdk

React is a peer dependency through ptech-shell-sdk.

What This Package Owns

  • initStandaloneServices: registers a complete local shell service set.
  • createTestServices: creates the standalone services without registration.
  • createDevPreset: creates reusable standalone bootstrap defaults.
  • Standalone services for i18n, user, tenant, app settings, onboarding, config, permissions, shared state, navigation, realtime, observability, notification, analytics, request context, and API client.
  • Adapters such as createMsalUserService and createReactRouterNavigationAdapter.
  • API error mapping with mapApiErrorToUiError.

Standalone Bootstrap

Use this in a remote that must run without a real host.

import { initStandaloneServices } from 'ptech-shell-dev';

initStandaloneServices();

Default bootstrap values:

  • apiBase: http://localhost:4000
  • apiBaseRoutes: {}
  • lang: vi
  • user: { id: 'dev', name: 'Dev User' }
  • navigationPath: /standalone
  • registerMode: if-missing

Override defaults per remote:

import { initStandaloneServices } from 'ptech-shell-dev';

initStandaloneServices({
  apiBase: 'http://localhost:5010',
  apiBaseRoutes: {
    audit: '/audit/api',
    ats: { url: 'http://localhost:5020/api' },
  },
  originPolicy: {
    trustedOrigins: ['http://localhost:5020'],
  },
  lang: 'en',
  navigationPath: '/remote-a',
  featureFlags: {
    'ui.experimental': true,
  },
});

Use registerMode: 'always' only when intentionally replacing existing services. In host-composed mode, the default if-missing protects real host services from being overwritten by standalone mocks.

Creating Services Without Registration

import { createTestServices } from 'ptech-shell-dev';

const services = createTestServices({
  apiBase: 'http://localhost:4000',
});

const result = await services.apiClient.request<{ id: string }>({
  url: '/v1/items/1',
});

Standalone onboarding accepts optional campaign seeds. With no campaign, a registered page tour remains available for manual replay but never auto-starts.

const services = createTestServices({
  onboarding: {
    campaigns: [{
      campaignId: 'local-audit-intro',
      tourKey: 'audit-tool.evidence',
      appKey: 'audit-tool',
      contentVersion: '3',
      revision: 1,
      scope: 'app',
      enabled: true,
      autoShow: true,
      progress: 'pending',
    }],
  },
});

API Client Behavior

createStandaloneApiClient implements the ApiClient contract from ptech-shell-sdk.

It provides:

  • base URL resolution with optional apiBaseRoutes and per-request appKey,
  • JSON body handling for plain objects,
  • request context headers (traceparent, x-correlation-id),
  • tenant header propagation through X-Tenant-Id,
  • optional Authorization header propagation from UserService.acquireAccessToken,
  • destination trust enforcement from ptech-shell-runtime: arbitrary cross-origin URLs receive no shell credentials, tenant, or trace context,
  • timeout and caller abort handling,
  • retry for idempotent methods by default,
  • ProblemDetails/error normalization into ApiError,
  • invalid JSON handling for explicit responseType: 'json',
  • POST failure notification side effect that never interrupts error propagation.

UI code should map normalized errors with mapApiErrorToUiError and translate the returned i18n key.

import { TOKENS, getService } from 'ptech-shell-sdk';
import { mapApiErrorToUiError } from 'ptech-shell-dev';

const api = getService(TOKENS.apiClient);

try {
  await api?.request({ url: '/v1/items', method: 'POST', body: { name: 'A' } });
} catch (error) {
  const uiError = mapApiErrorToUiError(error as Parameters<typeof mapApiErrorToUiError>[0]);
  console.error(uiError.i18nKey, uiError.supportTraceId);
}

MSAL Adapter

import { PublicClientApplication } from '@azure/msal-browser';
import { TOKENS, registerService } from 'ptech-shell-sdk';
import { createMsalUserService } from 'ptech-shell-dev';

const msal = new PublicClientApplication(msalConfig);

registerService(
  TOKENS.userService,
  createMsalUserService({
    msal,
    defaultScopes: ['User.Read'],
    loginMode: 'popup',
  }),
);

React Router Navigation Adapter

import { TOKENS, registerService } from 'ptech-shell-sdk';
import { createReactRouterNavigationAdapter } from 'ptech-shell-dev';

registerService(
  TOKENS.navigation,
  createReactRouterNavigationAdapter({
    navigate,
    location,
    createHref,
  }),
);

Host / standalone base-path (mount prefix)

The shell owns base-path so remotes never hand-roll it. Internal links work whether a remote runs standalone (/settings) or is mounted under a host prefix (/audit-tool/settings).

Basename authority — exactly one source at a time:

  • Bound to a real react-router → react-router's own basename is the sole authority. nav.navigate(to) receives the app-relative to unchanged (react-router applies the prefix); nav.createHref(to) is prefixed by the adapter's basename kept in sync with it.
  • Unbound / standalone → the adapter/mock applies its own static basename option.

Never set both a bound react-router createHref and a static basename — a dev-mode warning fires if you do. Invariants: getPath() / getSnapshot().location.pathname are always app-relative (basename-stripped) in both modes; only createHref() returns the host-prefixed form.

Batteries-included react-router integration (optional subpath)

react-router is an optional peer dependency, isolated behind the ptech-shell-dev/react-router subpath — the main entry stays react-router-free.

// App owns its own top-level Router (standalone, or mounted at a fixed host prefix):
import { ShellRouterProvider } from 'ptech-shell-dev/react-router';

<ShellRouterProvider basename="/audit-tool">
  <App />
</ShellRouterProvider>;
// Creates <BrowserRouter basename>, wires the shell NavigationService, and registers it.
// Plain <Link to="/settings"> and useNavigate() become host-prefix-correct for free.
// Remote nested inside a Router the host already owns — do NOT nest a second Router:
import { ShellRouterBridge } from 'ptech-shell-dev/react-router';

<>
  <ShellRouterBridge adapter={adapter} basename={hostBasename} />
  <App />
</>;

For imperative navigation / link generation from anywhere (including non-router code), use the sdk hooks:

import { useNavigation, useNavigationSnapshot } from 'ptech-shell-sdk';

const nav = useNavigation();
const snapshot = useNavigationSnapshot(); // location.pathname is always app-relative
nav.createHref('/settings'); // '/audit-tool/settings' when embedded, '/settings' standalone

To rehearse a host prefix while running standalone, seed initStandaloneServices:

initStandaloneServices({ navigationBasename: '/audit-tool' });

Module Federation note

ptech-preset shares react-router across remotes. Each adopting remote mounts its own Router, so react-router does not need to be a true singleton for correctness. Keep the default shared singleton when all consumers are on the same major; add mf.shared: { 'react-router': { singleton: false } } in a remote's rsbuild config only if its react-router major diverges from the rest.

Build and Test

npm run build -w ptech-shell-dev
npm run test -w ptech-shell-dev

npm run test -w ptech-shell-dev builds the package and runs node --test tests/*.test.mjs.