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

@proxies-sx/pool-portal-react

v0.5.0

Published

Drop-in React component and headless hooks for embedding a Proxies.sx Pool Gateway reseller dashboard into any React app.

Readme

@proxies-sx/pool-portal-react

npm License: MIT

Drop-in React component and headless hooks for embedding a Proxies.sx Pool Gateway reseller dashboard into any React app. Ships with a Next.js API route factory so you can wire it up in five minutes without reinventing the backend.


Install

npm install @proxies-sx/pool-portal-react @proxies-sx/pool-sdk

Peer deps: React 18+, React-DOM 18+.

Optional default styles:

import '@proxies-sx/pool-portal-react/styles.css';

5-minute quickstart (Next.js App Router)

1. Wire up the API route

Create app/api/pool/[...path]/route.ts:

import { createPoolApiHandlers } from '@proxies-sx/pool-portal-react/server';
import { ProxiesClient } from '@proxies-sx/pool-sdk';
import { auth } from '@/lib/auth';           // your auth lib (Clerk/NextAuth/…)
import { db } from '@/lib/db';               // your DB

export const { GET, POST } = createPoolApiHandlers({
  proxies: new ProxiesClient({
    apiKey: process.env.PROXIES_SX_API_KEY!,
    proxyUsername: process.env.PROXIES_SX_USERNAME!,
  }),
  getSessionUserId: async () => (await auth())?.userId ?? null,
  getUserKeyId: async (userId) => {
    const customer = await db.customers.get(userId);
    return customer?.pakKeyId ?? null;
  },
});

2. Drop the component into any page

// app/dashboard/page.tsx
import { PoolPortal } from '@proxies-sx/pool-portal-react';
import '@proxies-sx/pool-portal-react/styles.css';

export default function Dashboard() {
  return (
    <PoolPortal
      apiRoute="/api/pool"
      branding={{ name: 'AcmeProxies', primaryColor: '#6366f1' }}
    />
  );
}

That's the whole integration. The browser never sees your PROXIES_SX_API_KEY — all calls flow through your own API route.


How auth works

Customer's browser
      │ (signed-in session via your auth lib)
      ▼
<PoolPortal apiRoute="/api/pool" />
      │ fetch("/api/pool/me", { credentials: 'same-origin' })
      ▼
createPoolApiHandlers()  (your /api/pool/[...path]/route.ts)
      │ getSessionUserId() → who is this?
      │ getUserKeyId()     → which pak_ do they own?
      │ proxies.poolKeys.list() → fetch usage from api.proxies.sx
      ▼
Respond with { proxyUsername, pakKey, usage }

The component is strictly UI — it knows nothing about api.proxies.sx. Your API route is the trust boundary.


<PoolPortal> props

| Prop | Type | Default | Description | |---|---|---|---| | apiRoute | string | "/api/pool" | Base path of your mounted handlers | | countries | Country[] | ['us','de','pl','fr','es','gb'] | Countries the dropdown offers | | defaultCountry | Country | first in countries | | | defaultProtocol | 'http' \| 'socks5' | 'http' | | | defaultRotation | RotationMode | 'none' | | | showStock | boolean | true | Show the live-endpoints indicator | | showIncidents | boolean | true | Show an incident banner when active | | showUsage | boolean | true | Show the usage bar | | branding | Branding | — | { name, logoUrl, primaryColor, accentColor, radius, fontFamily } | | classNames | PoolPortalClassNames | — | Per-part className overrides (Tailwind-friendly) | | className | string | — | Extra class on the root | | style | CSSProperties | — | Inline style on the root | | emptyState | ReactNode | — | Rendered when the user has no key yet | | onRegenerateKey | () => Promise<void> | — | Called when the user clicks "Regenerate key" |

Branding

<PoolPortal
  branding={{
    name: 'AcmeProxies',
    logoUrl: '/logo.svg',
    primaryColor: '#7c3aed',
    accentColor: '#10b981',
    radius: '12px',
    fontFamily: '"Inter", sans-serif',
  }}
/>

Brand values map to CSS custom properties (--psx-primary, --psx-accent, --psx-radius, --psx-font). Skip styles.css and write your own CSS targeting these variables for total control.

Tailwind users

<PoolPortal
  classNames={{
    root: 'w-full max-w-2xl mx-auto',
    card: 'bg-zinc-900 border-zinc-800 text-zinc-50',
    button: 'bg-indigo-500 hover:bg-indigo-600',
    usageBar: 'bg-zinc-800',
  }}
/>

Don't import styles.css and write everything in Tailwind.


Additional components (v0.4.0+ / v0.4.1+)

Compose with <PoolPortal> for full reseller-dashboard parity with client.proxies.sx/pool-proxy. All components self-contained, all honor the same branding / classNames / style props.

<PoolSessionSpawner> — multi-port URL generator (v0.4.0+)

<PoolSessionSpawner
  proxyUsername={me.proxyUsername}
  proxyPassword={me.pakKey}
  countries={['us', 'de', 'gb', 'es', 'fr', 'pl']}
  defaultPool="mbl"
  defaultRotation="sticky"
  defaultSessionType="unique"        // unique-per-row sids → unique IPs
  onSpawn={(urls) => analytics.track('proxy_spawn', { count: urls.length })}
/>

Count slider (1–100), country / pool / protocol / rotation / sid-mode controls, "Generate" → N proxy URLs, per-row Copy + bulk Copy-all + Download .txt. The showTtlControl prop (v0.4.2 default true) exposes a "Session TTL override" field that appends -ttl-<seconds> to the username DSL (range 60-2592000 = 1 min to 30 days).

Also exports buildProxyString(opts) and defaultTtlSecondsForRotation(rotation) helpers for hand-rolled UIs.

<ActiveSessionsTable> — live session manager (v0.4.0+)

<ActiveSessionsTable
  apiRoute="/api/pool"
  proxyPassword={me.pakKey}
  refreshIntervalMs={5_000}
  onSessionClosed={(key) => toast.success(`Closed ${key.slice(-12)}`)}
/>

Polls GET /api/pool/my-sessions (auto-handler) at 5 s default. Per-row: country, sid, IP, rotation, TTL countdown, bytes in/out, request count, Copy-URL (with password substitution), Close. Header Close-all + Refresh. Hides synthesized-sid sessions by default (hideSynthesizedSessions).

<PoolDocsPanel> — drop-in technical reference (v0.4.1+)

<PoolDocsPanel
  proxyUsername={me.proxyUsername}
  exampleSamplePassword={me.pakKey ?? '<YOUR_PASSWORD>'}
/>

Four collapsible sections: how-it-works flow (5-step request lifecycle), username token reference (full DSL grammar), IP rotation modes (with TTL table), example curl (parametrized by your username). Pure presentational. Pass sections={['tokens', 'rotation']} to render only specific blocks.

<PoolStockGrid> — live country stock (v0.4.1+)

<PoolStockGrid
  apiRoute="/api/pool"
  countries={['us', 'de', 'gb', 'es', 'fr', 'pl']}
  variant="grid"               // or 'compact' for one-line-per-country
  refreshIntervalMs={30_000}
/>

Live online endpoint counts per country for both mbl mobile and peer residential pools. Auto-polls /api/pool/stock every 30 s (matches server-side cache TTL). Health pills: green ≥ 5 endpoints, amber < 5.

Server handlers (v0.4.0+)

createPoolApiHandlers() now exports a third method (DELETE) for the new session routes:

| Method | Path | Action | |---|---|---| | GET | <route>/my-sessions | List current user's sessions | | DELETE | <route>/my-sessions/<sessionKey> | Close one (ownership-checked upstream) | | DELETE | <route>/my-sessions | Close all for current user |

Make sure your route file exports all three: export const { GET, POST, DELETE } = createPoolApiHandlers({...}).


Headless hooks

Prefer to build your own UI? Use the same data layer:

import { usePoolKey, usePoolStock, useIncidents, buildProxyUrl } from '@proxies-sx/pool-portal-react';

function MyDashboard() {
  const me = usePoolKey('/api/pool');
  const stock = usePoolStock('/api/pool');
  const incidents = useIncidents('/api/pool');

  if (me.loading) return <Spinner />;
  if (me.error || !me.data) return <ErrorView onRetry={me.refetch} />;

  const url = buildProxyUrl(me.data.proxyUsername, me.data.pakKey, {
    country: 'us',
    rotation: 'sticky',
  });
  return <MyCustomUI url={url} usage={me.data.usage} stock={stock.data} />;
}

All hooks return { data, loading, error, refetch }. usePoolStock and useIncidents poll every 30s/60s respectively; override with { refreshIntervalMs }.


Server API reference

createPoolApiHandlers(options) exposes these routes on whatever path you mount it:

| Method | Path | Auth | Response | |---|---|---|---| | GET | /me | required | MeResponse — proxy URL ingredients + usage | | GET | /stock | public | Live endpoint counts per country | | GET | /incidents | public | Active gateway incidents | | POST | /regenerate | required | Rotates the user's pak_ key |

Options

| Option | Required | Description | |---|---|---| | proxies | ✅ | ProxiesClient instance | | getSessionUserId(req) | ✅ | string \| null — who is making this request? | | getUserKeyId(userId) | ✅ | string \| null — which pakKeyId belongs to this user? | | gatewayHost | | Passed through to the browser for custom edge deployments | | onAudit(event) | | Called on writes (e.g. regenerate). Log to your audit trail. |

Provisioning a key for a new customer

createPoolApiHandlers only reads existing keys. Create them server-side after a successful payment:

// app/api/stripe/webhook/route.ts
import { ProxiesClient } from '@proxies-sx/pool-sdk';

const proxies = new ProxiesClient({
  apiKey: process.env.PROXIES_SX_API_KEY!,
  proxyUsername: process.env.PROXIES_SX_USERNAME!,
});

// On `checkout.session.completed`:
const key = await proxies.poolKeys.create({
  label: `customer:${session.customer}`,
  trafficCapGB: Number(session.metadata?.gb),
  // Optional: 60-day expiry. Top-ups extend it via poolKeys.topUp() (preferred over update).
  expiresAt: new Date(Date.now() + 60 * 86_400_000).toISOString(),
  idempotencyKey: `mint_${session.id}`,        // SDK ≥ 0.3.0 — protect against double-mint on retry
});
await db.customers.update(customerId, { pakKeyId: key.id });

// On subsequent top-ups, prefer topUp() over update() — atomic + idempotent.
await proxies.poolKeys.topUp(key.id, {
  addTrafficGB: 10,                            // server-side $inc, race-safe
  extendDays: 60,                              // pushes expiresAt forward
  idempotencyKey: `topup_${invoiceId}`,
});

Time-bounded credits in the dashboard (v0.2.0+)

If you mint keys with expiresAt, surface it in your /api/pool/me response so <PoolPortal /> can render the countdown banner automatically:

// In your /me handler, return the key's expiresAt + isExpired
return NextResponse.json({
  proxyUsername: process.env.PROXIES_SX_USERNAME!,
  pakKey: key.key,
  pakKeyId: key.id,
  usage: {
    usedMB: key.trafficUsedMB,
    usedGB: (key.trafficUsedMB / 1024),
    capGB: key.trafficCapGB,
    enabled: key.enabled,
    lastUsedAt: key.lastUsedAt,
    expiresAt: key.expiresAt,    // ISO string or null
    isExpired: key.isExpired,    // server-computed
  },
});

<PoolPortal /> will then render:

  • > 7 days remaining → small dim line "Expires Aug 30, 2026 (88 days remaining)"
  • ≤ 7 days → amber banner "Credits expire in N days. Top up to extend."
  • Past expiry → red banner "Credits expired. Top up to reactivate."

Customers whose key has an expiry will see the countdown; those without an expiry see nothing extra.


Security

  • Your PROXIES_SX_API_KEY lives only on the server. The component never sees it.
  • pak_ keys are only sent to the customer they belong to (enforced by your getSessionUserId + getUserKeyId).
  • If a pak_ leaks, the user can hit POST /api/pool/regenerate (wired to onRegenerateKey) to rotate it — the old value stops working immediately.
  • /me responses are sent with Cache-Control: private, no-store so they don't leak via CDN/browser cache.
  • Public endpoints (/stock, /incidents) are cacheable (30s / 60s).

Runtime compatibility

  • Works in any React 18+ environment: Next.js App/Pages Router, Vite, Remix, React Router
  • Server handlers work on any runtime that supports standard Request / Response (Node, Vercel Edge, Cloudflare Workers, Deno, Bun)
  • ESM + CJS + .d.ts types, zero runtime dependencies besides @proxies-sx/pool-sdk

License

MIT — see LICENSE.