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

@particle/esim-tooling

v1.1.1

Published

TypeScript library for eSIM profile provisioning (SGP.22)

Readme

@particle/esim-tooling

TypeScript library for eSIM profile provisioning (SGP.22). Manages eSIM profiles on devices with an eUICC — list, enable, disable, delete, and install profiles.

Installation

npm install @particle/esim-tooling

Quick Start

import { EsimLpa } from '@particle/esim-tooling';
import type { DeviceAdapter, ServerAdapter } from '@particle/esim-tooling';

// Create adapters for your environment
const device: DeviceAdapter = createMyDeviceAdapter();
const server: ServerAdapter = createMyServerAdapter();

const lpa = new EsimLpa({ device, server });

// List profiles
const profiles = await lpa.listProfiles();
for (const p of profiles) {
  console.log(`${p.iccid} ${p.profileName} [${p.state === 1 ? 'enabled' : 'disabled'}]`);
}

// Enable a profile
await lpa.enableProfile('89012345678901234567');

// Disable a profile
await lpa.disableProfile('89012345678901234567');

// Delete a profile (must be disabled first)
await lpa.deleteProfile('89012345678901234567');

// Install a new profile
const result = await lpa.installProfile({
  activationCode: '1$smdp.example.com$ACTIVATION-TOKEN',
  onProgress: (p) => console.log(p.step),
});
console.log(`Installed: ${result.iccid}`);

// Process pending notifications
const notifResult = await lpa.processNotifications();
console.log(`Sent ${notifResult.sent}/${notifResult.total} notifications`);

Adapters

This library does not directly communicate with devices or servers. You provide two adapters that handle I/O.

DeviceAdapter

Sends a single APDU to the eUICC and returns the response. The library handles all higher-level protocol concerns (logical channels, STORE DATA chunking, GET RESPONSE chaining, BPP segmentation).

interface DeviceAdapter {
  sendApdu(apdu: Uint8Array): Promise<Uint8Array>;
}

Example: Particle USB adapter

import { openDeviceById } from 'particle-usb';
import DeviceOSProtobuf from '@particle/device-os-protobuf';

const proto = {
  ManagerRequest: DeviceOSProtobuf.schema.system.esim.ManagerRequest,
  ManagerResponse: DeviceOSProtobuf.schema.system.esim.ManagerResponse,
};

function createParticleUsbAdapter(device): DeviceAdapter {
  return {
    async sendApdu(apdu: Uint8Array): Promise<Uint8Array> {
      // Encode APDU as protobuf ManagerRequest
      const request = proto.ManagerRequest.create({ apdu: { data: apdu } });
      const requestBytes = proto.ManagerRequest.encode(request).finish();
      const response = await device.sendControlRequest(10, Buffer.from(requestBytes));
      // Decode protobuf ManagerResponse, return APDU response
      const managerResponse = proto.ManagerResponse.decode(response.data);
      return new Uint8Array(managerResponse.apdu.data);
    }
  };
}

ServerAdapter

Forwards ES9+ ASN.1 DER requests to SM-DP+ servers. SM-DP+ servers use TLS certificates signed by the GSMA RSP2 Root CI, which is not in standard system CA stores. The adapter owns the entire HTTP lifecycle — URL construction, headers, TLS.

interface ServerAdapter {
  sendRsp(smdpAddress: string, endpoint: string, requestData: Uint8Array): Promise<ServerResponse>;
}

interface ServerResponse {
  statusCode: number;
  responseData?: Uint8Array;
}

Example: direct adapter

const server: ServerAdapter = {
  async sendRsp(smdpAddress, endpoint, requestData) {
    const response = await fetch(`https://${smdpAddress}/gsma/rsp2/asn1`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-gsma-rsp-asn1',
        'User-Agent': 'gsma-rsp-lpad',
        'X-Admin-Protocol': 'gsma/rsp/v2.1.0',
      },
      body: requestData,
    });
    const statusCode = response.status;
    if (statusCode === 204) return { statusCode };
    const responseData = new Uint8Array(await response.arrayBuffer());
    return { statusCode, responseData };
  }
};

Logging

The library provides utilities for logging APDU and ES9+ traffic, useful for debugging adapter implementations.

LoggingDeviceAdapter wraps any DeviceAdapter to log all APDU traffic:

import { LoggingDeviceAdapter } from '@particle/esim-tooling';

// Wrap your adapter with default console logging
const loggingAdapter = new LoggingDeviceAdapter(rawAdapter);

// Or provide a custom logger
const loggingAdapter = new LoggingDeviceAdapter(rawAdapter, (event) => {
  // event.index: sequential APDU number (1-based)
  // event.direction: 'request' | 'response'
  // event.formatted: human-readable string
  // event.raw: Uint8Array of raw bytes
  // event.isError: true if response SW1 is not 0x90 or 0x61
  myLogger.debug(`APDU ${event.direction}: ${event.formatted}`);
});

const lpa = new EsimLpa({ device: loggingAdapter, server });

LoggingServerAdapter wraps any ServerAdapter to log all ES9+ traffic:

import { LoggingServerAdapter } from '@particle/esim-tooling';

// Wrap your adapter with default console logging
const loggingAdapter = new LoggingServerAdapter(rawAdapter);

// Or provide a custom logger
const loggingAdapter = new LoggingServerAdapter(rawAdapter, (event) => {
  // event.index: sequential request number (1-based)
  // event.direction: 'request' | 'response'
  // event.formatted: human-readable string
  // event.endpoint: ES9+ endpoint name
  // event.smdpAddress: SM-DP+ server address
  // event.statusCode: HTTP status (response only)
  // event.raw: Uint8Array of raw bytes
  // event.isError: true if HTTP status >= 400
  myLogger.debug(`ES9+ ${event.direction}: ${event.formatted}`);
});

const lpa = new EsimLpa({ device, server: loggingAdapter });

Standalone formatting functions for custom logging:

import {
  formatApduRequest, formatApduResponse, isApduError,
  formatServerRequest, formatServerResponse, isServerError,
} from '@particle/esim-tooling';

// APDU formatting
formatApduRequest(apdu);      // "ch1 STORE_DATA(255) MORE blk=0 (81e21100 ff ...)"
formatApduResponse(response); // "OK (9000)" or "MORE_DATA(16) (6110)"
isApduError(response);        // true if SW1 not 0x90 or 0x61

// ES9+ formatting
formatServerRequest('initiateAuthentication', 'smdp.example.com', data); // "initiateAuthentication smdp.example.com (123 bytes)"
formatServerResponse(200, data); // "HTTP 200 (456 bytes)"
isServerError(statusCode);       // true if status >= 400

API Reference

EsimLpa

| Method | Description | |--------|-------------| | getEid() | Get the 32-character hex EID | | listProfiles() | List all profiles on the eUICC | | enableProfile(iccid) | Enable a disabled profile | | disableProfile(iccid) | Disable the active profile | | deleteProfile(iccid) | Delete a disabled profile | | installProfile(options) | Download and install a profile from SM-DP+ | | listNotifications() | List pending notification metadata | | processNotifications() | Send all pending notifications to SM-DP+ servers |

Profile States

| Value | State | |-------|-------| | 0 | Disabled | | 1 | Enabled |

Notification Events

| Value | Event | |-------|-------| | 0x80 | Install | | 0x40 | Enable | | 0x20 | Disable | | 0x10 | Delete |

Error Handling

import { Es10Error, Es9PlusError, DeviceError } from '@particle/esim-tooling';

try {
  await lpa.enableProfile(iccid);
} catch (error) {
  if (error instanceof Es10Error) {
    // eUICC returned an error (e.g., profile not found, policy violation)
    console.log('eUICC error:', error.resultCode, error.message);
  } else if (error instanceof Es9PlusError) {
    // SM-DP+ server error
    console.log('Server error:', error.statusCode, error.serverStatus);
  } else if (error instanceof DeviceError) {
    // Device communication error
    console.log('Device error:', error.result);
  }
}

License

Apache-2.0