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 🙏

© 2024 – Pkg Stats / Ryan Hefner

safe-rpc-iframe

v0.1.8

Published

Safe and easy cross-domain iframe communication

Downloads

4

Readme

safe-rpc-iframe

npm version npm downloads bundle Codecov License JSDocs

Safe and easy cross-domain iframe communication

Fully encrypted & authenticated communication between an iframe and a parent window using safe-rpc.

🚀 Quick Start

Install:

# npm
npm i safe-rpc safe-rpc-iframe

# yarn
yarn add safe-rpc safe-rpc-iframe

Usage

1. First define the RPC interfaces that your parent and child iframes will expose

Note: You can skip this if you are not using TypeScript

interface IParentFrameRPCInterface {
  stringLength(str: string): Promise<number>;
  // note that you can only pass in one parameter.
  // so if you need to send multiple parameters, put it in an array or an object like the line below
  splitString(params: {str: string, delimiter: string}): Promise<string[]>;
}
interface IChildFrameRPCInterface {
  sumNumbers(numbers: number[]): Promise<number>;
  reverseArray(arr: any[]): Promise<any[]>;
}

2. Next implement the child frame's code

interface IParentFrameRPCInterface {
  stringLength(str: string): Promise<number>;
  // note that you can only pass in one parameter.
  // so if you need to send multiple parameters, put it in an array or an object like the line below
  splitString(params: {str: string, delimiter: string}): Promise<string[]>;
}
interface IChildFrameRPCInterface {
  sumNumbers(numbers: number[]): Promise<number>;
  reverseArray(arr: any[]): Promise<any[]>;
  setName(name: string): Promise<void>;
  getName(): Promise<string>;
}


abstract class InternalLogic {
  peer: IParentFrameRPCInterface;
  name: string = "";
  constructor(peer: IParentFrameRPCInterface){
    this.peer = peer;
  }
  // this function will NOT be exposed over RPC
  // only functions implemented in the class instance passed to registerHandlerClass will be exposed
  // the functions in classes inherited by the instance will NOT be exposed via RPC
  hidden(){
    return "i am hidden";
  }
}
class ChildRPCHandlerClass extends InternalLogic implements IChildFrameRPCInterface{
  constructor(peer: IParentFrameRPCInterface){
    super(peer);
  }
  // this function will be exposed over RPC
  async sumNumbers({numbers}: { numbers: number[]; }): Promise<number> {
    return numbers.reduce((a,b)=>a+b,0)+splitTest;
  }

  // this function will be exposed over RPC
  async reverseArray(arr: any[]): Promise<any[]> {
    // you can call a function exposed by the parent frame inside an RPC function
    const splitTest = await this.peer.stringLength({str:"test string"});
    console.log("splitTest: ",splitTest);
    return arr.concat([]).reverse();
  }
  async setName(name: string): Promise<void> {
    this.name = name;
  }
  async getName(): Promise<string> {
    if(!this.name){
      throw new Error("my name has not yet been set");
    }
    return this.name;
  }
}

// boilerplate initialization code
async function demoChild(){
  // create an RPC session using the information in the "#" of the URL
  const rpcSession = await createChildIFrameRPCSession();

  // expose class ChildRPCHandlerClass to a parent which exposes an RPC interface 'IParentFrameRPCInterface'
  const handler = rpcSession.registerHandlerClass<ChildRPCHandlerClass, IParentFrameRPCInterface>(
    (peer)=>new ChildRPCHandlerClass(peer)
  );
}
demoChild()
  .then(()=>{
    console.log("[child] RPC API listening for calls")
  })
  .catch(err=>{
    console.error("[child] Error starting RPC API listener: ",err);
  });

3. Now build your child iframe and upload to your favorite static web host (For example, AWS S3)

For this tutorial, we will assume that the iframe was uploaded to https://example.com/iframe.html

4. Now implement the parent window's code

interface IParentFrameRPCInterface {
  stringLength(str: string): Promise<number>;
  // note that you can only pass in one parameter.
  // so if you need to send multiple parameters, put it in an array or an object like the line below
  splitString(params: {str: string, delimiter: string}): Promise<string[]>;
}
interface IChildFrameRPCInterface {
  sumNumbers(numbers: number[]): Promise<number>;
  reverseArray(arr: any[]): Promise<any[]>;
  setName(name: string): Promise<void>;
  getName(): Promise<string>;
}

abstract class InternalLogic {
  peer: IChildFrameRPCInterface;
  rpcSession: EncryptedRPCSession;
  constructor(peer: IChildFrameRPCInterface, rpcSession: EncryptedRPCSession){
    this.peer = peer;
    this.rpcSession = rpcSession;
  }
  hidden(){
    return "i am hidden (parent)";
  }
  dispose(){
    this.rpcSession.dispose();
  }
}
class ParentRPCHandlerClass extends InternalLogic implements IParentFrameRPCInterface{
  constructor(peer: IChildFrameRPCInterface, rpcSession: EncryptedRPCSession){
    super(peer, rpcSession);
  }
  async stringLength({str}: { str: string; }): Promise<number> {
    return str.length;
  }
  async splitString({str, delimiter}: { str: string; delimiter: string; }): Promise<string[]> {
    return str.split(delimiter);
  }

}

async function createChildFrame(childFrameSrc: string): Promise<ParentRPCHandlerClass>{
  const rpcSession = await createParentIFrameRPCSession(childFrameSrc);
  const handler = rpcSession.registerHandlerClass<ParentRPCHandlerClass, IChildFrameRPCInterface>(
    (peer, rpcSession)=>new ParentRPCHandlerClass(peer, rpcSession)
  );
  return handler;
}

async function demoParent() {
  // replace the line below with your iframe's URL
  const childFrameSrc = "https://example.com/iframe.html";

  // create a child iframe instance (this will add an invisible iframe to document.body)
  const childFrame1 = await createChildFrame(childFrameSrc);

  // you can call functions on childFrame.peer now
  const reversedArrayExample = await childFrame1.peer.reverseArray([1, 3, 5, 7]);
  console.log("your reversed array: ", reversedArrayExample);

  // the child iframe can throw exceptions and messages will be forwarded to the parent caller
  try {
    const childFrame1OldName = await childFrame1.peer.getName();
    // the code on the line below will not run because an exception is thrown
    console.log("the first iframe's name is: "+childFrame1OldName)
  }catch(err){
    // this will show the error 'my name has not yet been set'
    console.error("Child iframe threw an error: ",err);
  }

  await childFrame1.peer.setName("Mike");
  const childFrame1Name = await childFrame1.peer.getName();
  // the code on the line below will print "the iframe's name is: Mike"
  console.log("the first iframe's name is: "+childFrame1Name);

  // you can have multiple iframe instances at once
  const childFrame2 = await createChildFrame(childFrameSrc);
  await childFrame2.peer.setName("Sally");
  const childFrame2Name = await childFrame2.peer.getName();
  // the code on the line below will print "the iframe's name is: Mike"
  console.log("the second iframe's name is: "+childFrame2Name);

  // you can dispose of iframes (after this line, the first iframe will be removed from document.body)
  childFrame1.dispose();


  await childFrame2.peer.setName("I can still call functions on the second iframe because it has not been disposed");

  childFrame2.dispose();
}


demoParent()
  .catch(err=>{
    console.error("[parent] Error: ",err);
  });

❓ FAQ

Why do we need to encrypt and sign messages between parent and child? Cross-domain iframe communication works using the postMessage API, and can leak information/messages sent from other windows/iframe instances from the same origin.

License

MIT. Copyright 2023 Zero Knowledge Labs Limited