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

@pryv/delegation

v3.15.0

Published

Account-delegation client helpers for Pryv.io

Readme

Account-delegation client helpers for pryv

Account-delegation client-side helpers for the Pryv JavaScript library. Delegation lets one account (the delegate, "A") act on behalf of another (the controlled account, "B") through a delegate personal-access token (PAT) minted on B — for example a parent running a child's account, or a co-guardian added to an existing one. It is backed by the open-pryv.io delegation plugin (delegations.* API family).

Usage

@pryv/delegation is a sibling package to pryv — imported separately, not attached to the pryv namespace. It uses a standard personal pryv.Connection to talk to the server.

Importing

NPM

npm install --save pryv @pryv/delegation, then in your code:

const pryv = require('pryv');
const { Delegation } = require('@pryv/delegation');

@pryv/delegation requires pryv@^3.3.0 as a peer dependency.

<script> tag

pryv-delegation.js must be loaded after pryv.js. In browser bundles, pass the pryv module explicitly (needed by openControlled, which constructs a Connection):

const delegation = Delegation.fromConnection(conn, { pryv: window.pryv });

Concepts

A delegation relationship has two sides:

  • B — the controlled account. Invites a delegate (requestAttach), lists its delegates (listDelegates), and is the only party that can authoritatively remove one (detachDelegate). Teardown requires a genuine login on B (see below).
  • A — the delegate. Accepts or refuses an invite (acceptAttach / refuseAttach), lists the accounts it controls (listControlled), mints a token for one (getToken / openControlled), and can create brand-new controlled accounts (createAccount).

Every method maps to exactly one delegations.* API method, issued over the connection's batch call.

Surface

Construct a client by wrapping a personal pryv.Connection:

const delegation = Delegation.fromConnection(conn);

B side (the controlled account)

// Invite a delegate. Returns the pending invite record.
const invite = await delegation.requestAttach('parent-b');
// invite = { relId, delegate: { username }, status: 'invite', requestedAt, expiresAt }

// List your delegates (status: 'invite' | 'active').
const delegates = await delegation.listDelegates();
// [ { relId, delegate: { username, hostSlug }, status, requestedAt, activatedAt?, lastTokenIssuedAt? } ]

// Cancel a pending invite you issued (genuine-login-gated, see below).
await delegation.cancelInvite('parent-b');

// Remove a delegate — the authoritative teardown (genuine-login-gated).
await delegation.detachDelegate('parent-b');

A side (the delegate)

// Accept / refuse an invite you received.
const rel = await delegation.acceptAttach('kid-account');
// rel = { relId, controlled: { username }, status: 'active', activatedAt }
await delegation.refuseAttach('kid-account');

// List the accounts you control (status: 'invite' | 'active' | 'stale').
const controlled = await delegation.listControlled();
// [ { relId, controlled: { username, hostSlug }, status, requestedAt, activatedAt? } ]

// Dismiss a local 'stale' mirror (housekeeping — removes no authority).
await delegation.dismissControlled('old-account');

// Mint a delegate token for a controlled account.
const { token, apiEndpoint } = await delegation.getToken('kid-account');

// One-call "act as the controlled account": get a ready Connection onto B.
const kidConn = await delegation.openControlled('kid-account');
const events = await kidConn.apiOne('events.get', { limit: 20 }, 'events');

// Create a brand-new controlled account, active at birth.
const created = await delegation.createAccount({
  username: 'kid-account',
  email: '[email protected]', // optional
  password: 'a-strong-password',     // optional (random if omitted)
  core: 'core-2'                     // optional target core (default: your own)
});
// created = { delegation: { relId, controlled: { username, hostSlug }, status: 'active', activatedAt }, apiEndpoint? }

createAccount with no email/password yields an account reachable only through delegates until a delegate sets a password.

Detach requires a genuine login on B

When the delegate accepted cross-account consents for the account, the owner may keep some of them: detachDelegate('parent-b', { keepAccessIds: ['<grant id>'] }) (core 2.0.0-rc.31 or later; each id must be a consent grant given through this relationship, else delegation-invalid-keep-list and nothing changes). The others are withdrawn and their requesters told.

detachDelegate and cancelInvite remove a delegation relationship, so they require the account owner to be genuinely logged in to the controlled account — a delegate PAT or a control token is rejected. A delegated session therefore cannot walk away with, or tear down, the relationship on its own.

The rejection surfaces as a typed DelegationError with id === delegationErrorIds.GENUINE_LOGIN_REQUIRED. Surface it distinctly in your UI:

const { errorIds: delegationErrorIds } = require('@pryv/delegation');

try {
  await delegation.detachDelegate('parent-b');
} catch (err) {
  if (err.id === delegationErrorIds.GENUINE_LOGIN_REQUIRED) {
    // Prompt the account owner to sign in directly (not via a delegated
    // session) before retrying the detach.
  } else {
    throw err;
  }
}

Typed errors

When a delegations.* call rejects with a delegation-* id, the method throws a DelegationError whose .id is one of errorIds (a stable, kebab-case mirror of the server-side catalogue). Match on the constant instead of parsing error.message. Non-delegation errors (transport, validation, auth) propagate unchanged.

const { DelegationError, errorIds } = require('@pryv/delegation');

| Constant | String | When | |---|---|---| | GENUINE_LOGIN_REQUIRED | delegation-genuine-login-required | detachDelegate / cancelInvite called without a genuine B login. | | NOT_ACTIVE | delegation-not-active | getToken on a torn-down / non-active relationship (mirror flips to stale). | | NOT_FOUND | delegation-not-found | No relationship for the given username. | | ALREADY_EXISTS | delegation-already-exists | requestAttach duplicates an existing invite/relationship. | | UNKNOWN_USERNAME | delegation-unknown-username | The target username does not resolve to an account. | | SELF_NOT_ALLOWED | delegation-self-not-allowed | An account may not delegate to itself. | | INVITE_EXPIRED | delegation-invite-expired | acceptAttach on an expired invite. | | USERNAME_TAKEN | delegation-username-taken | createAccount username already claimed. | | UNKNOWN_CORE | delegation-unknown-core | createAccount target core not known to the platform. | | CREATION_FAILED | delegation-creation-failed | Account creation failed (rolled back). | | DELIVERY_FAILED | delegation-delivery-failed | Cross-core delivery to the counterparty core failed. | | MIRROR_NOT_STALE | delegation-mirror-not-stale | dismissControlled on a mirror that is not stale. | | PERSONAL_TOKEN_REQUIRED | delegation-personal-token-required | A personal token was required but an app/shared token was used. | | DELEGATE_MISMATCH | delegation-delegate-mismatch | Delegate identity does not match the recorded relationship. |

The STATUS export mirrors the relationship-status enum used in list entries: STATUS.INVITE, STATUS.ACTIVE, STATUS.STALE.

Example

See examples/delegation-usage.js for a runnable end-to-end sample (parent creates a child account, mints a token, acts as the child, then detaches).

Contributing

See the Pryv JavaScript library README

License

BSD-3-Clause