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

@phosra/connect

v0.2.0

Published

Embeddable Phosra Connect component — the Plaid Link for parental-controls apps. Headless hook + unstyled reference + styled drop-in, web and React Native. Ships as source; runs in the PCA app's own trust boundary.

Readme

@phosra/connect

Phosra Connect is a Plaid Link-style embeddable flow that lets a parent authorize safety rules on an enforcement platform (e.g. Snaptr) directly inside a parental-controls app (PCA). It has three layers: a headless useConnect hook (in @phosra/connect/core) that drives the full ceremony state machine; an unstyled ConnectFlow reference component with stable data-phosra-connect hooks for custom styling; and a styled PhosraConnect drop-in that ships the approved Phosra design out of the box. Both web (React DOM) and React Native are supported via separate entry points. The package ships as TypeScript source and runs entirely inside the PCA app's own trust boundary — there is no Phosra-hosted page or iframe. The component calls only the PCA's own BFF routes, which wrap @phosra/link server-side; the browser never talks to Phosra directly and never sees the endpoint_id_label (the enforcement handle). The ceremony is router-blind: Phosra verifies signed enforcement receipts but cannot read message content. The safety path is never metered, rate-limited, or blocked.


Install

npm install @phosra/connect

Peer dependencies for web: react ^18 || ^19, react-dom ^18 || ^19.

Peer dependencies for React Native (additional): react-native, react-native-svg, expo-web-browser.

This package ships as TypeScript source (so it compiles into your bundle and runs on your own origin — never a Phosra-hosted script). Your bundler transpiles it:

  • React Native / Metro and Vite — works out of the box.
  • Next.js — add the package to transpilePackages:
    // next.config.js
    module.exports = { transpilePackages: ['@phosra/connect'] };

Entry Points

| Import | Contents | |--------|----------| | @phosra/connect | PhosraConnect + ConnectFlow + all core types (web default) | | @phosra/connect/web | Same, explicit web entry | | @phosra/connect/core | useConnect hook, createConnectController, all shared types — no UI | | @phosra/connect/native | PhosraConnect for React Native (needs react-native-svg) | | @phosra/connect/connect.css | Default styles for the web drop-in; import once in your app root |


Web Usage

import '@phosra/connect/connect.css';
import { PhosraConnect } from '@phosra/connect';
import type { ConnectTransport } from '@phosra/connect/core';

const transport: ConnectTransport = {
  async init(req) {
    const res = await fetch('/api/phosra/connect/init', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(req),
    });
    if (!res.ok) throw new Error(await res.text());
    return res.json();
  },
  async complete(req) {
    const res = await fetch('/api/phosra/connect/complete', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(req),
    });
    if (!res.ok) throw new Error(await res.text());
    return res.json();
  },
  async bind(req) {
    const res = await fetch('/api/phosra/connect/bind', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(req),
    });
    if (!res.ok) throw new Error(await res.text());
    return res.json(); // { grant_id }
  },
};

export default function ConnectPage() {
  return (
    <PhosraConnect
      platform={{ did: 'did:ocss:snaptr', name: 'Snaptr' }}
      rules={[
        { category: 'addictive_pattern_block', label: 'Block addictive feed patterns' },
        { category: 'dm_restriction', label: 'Restrict direct messages' },
      ]}
      grantedScope={['addictive_pattern_block', 'dm_restriction']}
      redirectUri="https://yourapp.com/phosra/callback"
      transport={transport}
      onSuccess={(result) => console.log('connected, grant_id:', result.grant_id)}
      onExit={() => router.back()}
    />
  );
}

Optional props

| Prop | Type | Default | |------|------|---------| | childId | string | — (parent picks from list) | | ageHint | 'under_13' \| '13_15' \| '16_17' | — | | childName | string | — (grant preview says "your child" instead of the name) | | provisioningForm | 'batch' \| 'single' | — (no create-and-link step; a server provisioningForm overrides) | | provisionChildren | ProvisionChild[] | — (one child derived from ageHint + childName) | | openAuthorizeUrl | AuthorizeOpener | Same-origin popup + postMessage | | platformGlyph | React.ReactNode | Generic platform glyph |

childName names the child in the grant/rules preview ("…applies and confirms these rules for Ava") so consent is legible. provisioningForm + a transport.provision implementation together enable the in-modal create-and-link step when the parent has no existing profile on the platform (see below).


React Native Usage

import { PhosraConnect } from '@phosra/connect/native';
// transport shape is identical to the web example above.

export default function ConnectScreen({ navigation }) {
  return (
    <PhosraConnect
      platform={{ did: 'did:ocss:snaptr', name: 'Snaptr' }}
      rules={[
        { category: 'addictive_pattern_block', label: 'Block addictive feed patterns' },
      ]}
      grantedScope={['addictive_pattern_block']}
      redirectUri="yourapp://phosra/callback"
      transport={transport}
      onSuccess={(result) => navigation.navigate('Success', { grantId: result.grant_id })}
      onExit={() => navigation.goBack()}
    />
  );
}

Additional peer dependencies for React Native: react-native-svg (for icons) and expo-web-browser (for the in-app browser opener). The component injects a native AuthorizeOpener automatically when running on React Native; pass openAuthorizeUrl to override.


The BFF Contract

This is the load-bearing section. The ConnectTransport interface maps directly onto route handlers that your BFF implements. Your BFF wraps @phosra/link server-side. The component never talks to Phosra or to the platform directly.

The @phosra/link server functions in play:

| @phosra/link function | Transport call | Role | |-------------------------|----------------|------| | initPlatformOAuth | transport.init | PKCE S256 authorize URL + state | | completePlatformOAuth | transport.complete | Code exchange + child-profile list (+ optional provisioningForm) | | bindProfile + runConnectCeremony | transport.bind | Bind child, mint consent, provision enforcement endpoint, deliver label to platform → { grant_id, verified? } | | provisionProfiles + runConnectCeremony | transport.provision (optional) | Create-and-link: stand up new profile(s) on the platform, then bind → { grant_id, verified?, provisioned } |

transport.provision is optional — implement it only to enable the in-modal create-and-link step (see "Create-and-link" below). The component works with just the three core calls.

The verified flag (never a fake green). transport.bind and transport.provision return { grant_id, verified?, receiptFingerprint? }. Set verified: true only when the census returned a signed write-receipt that verified to the pinned OCSS trust root (@phosra/link's ceremony surfaces this). When verified is true, the success screen shows the earned "Verified on the OCSS Trust List" cue; when it is false/absent, the screen shows an honest "{platform} connected — confirming enforcement…" pending state instead. Never hard-code verified: true — that is the fake green the component is designed to prevent.

Shared config (once at startup)

// lib/phosra-link.ts
import type { LinkConfig } from '@phosra/link';
import { loadSenderKey } from '@openchildsafety/ocss';
import { pool } from './db'; // your pg Pool

export const linkCfg: LinkConfig = {
  censusBaseUrl:    process.env.PHOSRA_CENSUS_URL!,
  trustRootXB64Url: process.env.PHOSRA_TRUST_ROOT_X!,
  parentKey:        loadSenderKey(process.env.PARENT_SIGNING_KEY_PEM!),
  writerKey:        loadSenderKey(process.env.WRITER_SIGNING_KEY_PEM!),
  writerDid:        process.env.WRITER_DID!,
  routerDid:        process.env.ROUTER_DID!,
  householdSecret:  process.env.HOUSEHOLD_SECRET!,
  pool,
  developerOrgId:   process.env.PHOSRA_DEVELOPER_ORG_ID, // from your Phosra dashboard
};

POST /api/phosra/connect/init

Generates a PKCE S256 pair, stores a pending platform session, and returns the authorize URL.

// app/api/phosra/connect/init/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { initPlatformOAuth } from '@phosra/link';
import { linkCfg } from '@/lib/phosra-link';
import { getServerSession } from '@/lib/auth'; // your parent auth

export async function POST(req: NextRequest) {
  const { platformDid, redirectUri, grantedScope, ageHint, childId } = await req.json();
  const session = await getServerSession(req); // authenticated parent session

  const result = await initPlatformOAuth(linkCfg, {
    platformDid,
    redirectUri,
    parentSessionRef: session.id, // binds state to the authenticated parent (CSRF defense)
    childHint: childId,           // optional login_hint for the platform
  });

  // Returns: { authorizeUrl: string, state: string, sessionId: string }
  return NextResponse.json(result);
}

POST /api/phosra/connect/complete

Entirely server-side: verifies state (CSRF + session-fixation defense), performs the PKCE code exchange with the platform (code_verifier stays server-side, never reaches the client — GC5), fetches and stores the child-profile list, then discards the access token.

// app/api/phosra/connect/complete/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { completePlatformOAuth } from '@phosra/link';
import { linkCfg } from '@/lib/phosra-link';
import { getServerSession } from '@/lib/auth';

export async function POST(req: NextRequest) {
  const { code, state, sessionId } = await req.json();
  const session = await getServerSession(req);

  const result = await completePlatformOAuth(linkCfg, {
    code,
    state,
    parentSessionRef: session.id,
  });

  // Returns: { sessionId: string, childProfiles: ChildProfile[] }
  // ChildProfile: { id: string, displayName: string, ageHint?: number }
  return NextResponse.json(result);
}

POST /api/phosra/connect/bind

The ceremony's final leg. bindProfile records the parent's confirmed child pick and returns a LinkSession. runConnectCeremony then calls completeLink (mints the consent attestation and provisions the §9.3(b) enforcement endpoint signed by the writer key), then calls your deliver closure to POST the endpoint_id_label server-to-server to the platform's callback. The endpoint_id_label never reaches the browser. The route returns only { grant_id }.

// app/api/phosra/connect/bind/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { bindProfile, runConnectCeremony, deliverLabelToPlatform } from '@phosra/link';
import { linkCfg } from '@/lib/phosra-link';

// Your registry mapping a platform DID to its server-side callback URL.
function resolvePlatformCallbackUrl(platformDid: string): string {
  const registry: Record<string, string> = {
    'did:ocss:snaptr': 'https://api.snaptr.example/ocss/connect-callback',
  };
  const url = registry[platformDid];
  if (!url) throw new Error(`unknown platform: ${platformDid}`);
  return url;
}

export async function POST(req: NextRequest) {
  const { sessionId, platformChildProfileId, childId, grantedScope, ageHint } = await req.json();

  // Leg 1: record the parent's child pick, advance platform session → bound,
  // produce a LinkSession ready for completeLink.
  const linkSession = await bindProfile(linkCfg, {
    sessionId,
    platformChildProfileId,
    childId,
    granted_scope: grantedScope,  // note: snake_case in @phosra/link
    ageHint,
  });

  // The platform's registered connect_url BASE; deliverLabelToPlatform appends the
  // well-known /api/ocss/connect path (the same convention the census test-connect uses).
  const platformConnectUrl = resolvePlatformConnectUrl(linkSession.audience_did);

  // Leg 2: completeLink (consent + endpoint) then deliver the label server-to-server.
  // runConnectCeremony calls completeLink internally; your deliver closure is called
  // with the cleartext endpoint_id_label exactly once.
  const { grant_id } = await runConnectCeremony(
    linkCfg,
    linkSession,
    async (endpoint_id_label) => {
      const r = await deliverLabelToPlatform(platformConnectUrl, {
        endpoint_id_label,
        state: sessionId, // correlates to the platform's active session
      });
      if (!r.ok) throw new Error(`platform callback failed: ${r.status}`);
    },
  );

  // endpoint_id_label is consumed by the deliver closure and never returned.
  // `verified` reflects whether the census write-receipt verified to the pinned
  // root; pass it through so the modal shows the earned "Verified" cue only when true.
  return NextResponse.json({ grant_id, verified, receiptFingerprint });
}

grant_id is the value surfaced to your onSuccess callback as result.grant_id. verified drives the success screen's trust cue (see "The verified flag" above).


Create-and-link (parent has no profile yet)

When the parent has no existing profile on the platform, the old flow dead-ended (or bounced them to a new tab to make one). Instead, when the platform advertises a provisioning_form (batch or single) and you implement transport.provision, the modal offers an in-modal Create & connect step that stands up the profile and binds the grant in one action — never leaving your app.

Wire it two ways (both optional):

  1. Declare the form on the component: pass provisioningForm="batch" (or "single") to PhosraConnect. The BFF can also return provisioningForm from transport.complete (resolved from the census discovery entry) — a server value overrides the prop.
  2. Implement transport.provision — the 4th route, wrapping @phosra/link's provisionProfiles:
// app/api/phosra/connect/provision/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { provisionProfiles, bindProfile, runConnectCeremony, deliverLabelToPlatform } from '@phosra/link';
import { linkCfg } from '@/lib/phosra-link';

export async function POST(req: NextRequest) {
  const { sessionId, children, childId, grantedScope } = await req.json();
  // children: { ageHint: 'under_13'|'13_15'|'16_17', displayName?: string }[]

  // Leg 1: create the profile(s) on the platform (signed EXT-01 delivery).
  const { provisioned } = await provisionProfiles(linkCfg, /* platformDid */ resolvePlatformDid(sessionId), {
    state: sessionId,
    children: children.map((c) => ({ endpoint_id_label: newLabel(), age_band: c.ageHint, display_hint: c.displayName })),
  });

  // Leg 2: bind the grant + deliver the enforcement label (same ceremony as /bind).
  const linkSession = await bindProfile(linkCfg, { sessionId, childId, granted_scope: grantedScope /* … */ });
  const { grant_id, verified } = await runConnectCeremony(linkCfg, linkSession, deliverClosure);

  return NextResponse.json({ grant_id, verified, provisioned });
}

The component drives it automatically: on an empty profile list it moves to a no_profiles state (a rules preview + a Create & connect button); pressing it calls transport.provision and lands on the same success screen. If you don't implement provision, an empty list falls back to the existing (empty) picker — fully back-compatible.


Redirect Page (Web)

The authorize popup (or tab) lands on the redirectUri on your own origin. That page reads code and state from the query string, postMessages them back to the opener, and closes. Nothing Phosra-specific is required here — it is a standard OAuth redirect receiver.

<!-- public/phosra/callback/index.html  (or a minimal Next.js page at /phosra/callback) -->
<script>
  const params = new URLSearchParams(location.search);
  const code  = params.get('code');
  const state = params.get('state');
  if (window.opener && code && state) {
    window.opener.postMessage(
      { type: 'phosra-connect', code, state },
      window.location.origin,    // same-origin only
    );
    window.close();
  }
</script>

The component's built-in createWebAuthorizeOpener opens the popup and listens for { type: 'phosra-connect' } from the same origin. If you use a mobile deep-link scheme or a custom web-view, pass a custom openAuthorizeUrl prop (an AuthorizeOpener) to PhosraConnect — it receives (authorizeUrl, redirectUri) and must resolve to { code, state } or { canceled: true }.


Honesty and Trust Boundary

Never a fake green. The success screen's "Verified on the OCSS Trust List" cue renders only when the ceremony returned verified: true — i.e. the census signed a write-receipt that verified to the pinned OCSS trust root. If the connection landed but the signed receipt hasn't verified yet, the screen shows an honest "{platform} connected — confirming enforcement…" pending state instead. The verified badge is gated on the server's verified flag, never a client-side optimistic assumption; hard-coding verified: true in your BFF is the one thing that defeats this guarantee.

Router-blind. Phosra verifies enforcement receipts (what rule applied, to which child, on which platform) but is structurally prevented from reading message content. The §3A.3 harm-context lane is sealed end-to-end; Phosra is on the signal-and-receipt path, not the content path.

Ships as source, runs on your domain. There is no Phosra-hosted page, iframe, or remotely loaded script. You review the source, you bundle it, it runs inside your application's trust boundary. The component's transport calls go to your own BFF routes; no browser request is ever made to Phosra infrastructure directly.

The safety path is never metered or blocked. @phosra/link enforces rule writes unconditionally once a valid grant exists. Billing applies to the grant lifecycle (connecting and managing integrations), not to individual safety-rule evaluations. An expired or suspended billing state cannot prevent a rule from being applied to a child's account.

No hidden failures. The controller never leaves the parent on a spinner that hides an error: a blocked popup, a rejected webview, or a failed BFF call all surface as an honest error state with a retry. The web opener rejects if the browser blocks the popup. Because the transport fetches are yours, give each one an AbortSignal/timeout so a stalled network also fails into the error state rather than spinning forever — the component honors whatever your transport throws.