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

browser-script-canary

v1.0.0

Published

canary deployment for your script

Downloads

1

Readme

Browser Script Canary

An easy way to do a user based Canary Deployment of your Javascript script!

Really useful for SDK deployments.

What this module does:

  • Rolls out if the current end-user should load the Canary version of your script.
  • Seamlessly load the Canary version of your script.

Simple example

In Typescript:

import {ICanaryConfig, Canary} from "browser-script-canary";

(async function main() {
    const canary = new Canary({
        probability: 50, // percentage
        canaryScriptUrl: '/mySdk.js?version=canary'
    } as ICanaryConfig);
    
    const wasCanaryLoaded = await canary.bootstrap();
    
    if (!wasCanaryLoaded) {
        // initialize the current sdk:
        window.mySdk = {
            isReady: () => true,
        };
    }
})();

Inner Flow

  • Script loads.
  • Participating in the Canary:
    • If the end-user never rolled its participation in the Canary
      • Roll the canary participation according to the probability.
    • If the end-user participates in Canary
      • Loading of Canary Script:
        • According to configuration

ICanaryConfig

export interface ICanaryConfig {
    // Participating in Canary:
    probability: number,
    version?: string,
    cookiesNames?: {
            isCanary?: string,
            version?: string
    },
    
    // Loading of Canary Script:
    canaryScriptUrl?: string,
    loadAsync?: boolean,
    supportCORs?: boolean,
    
    // Helpers:
    globalCanaryIndicationName?: string
}

Participating in the Canary

probability : number

Required in range: 0 <= probability <= 100.

The higher the probability, the more likely an end-user will receive the Canary version.

version?: string

Optional.

Changing the version will cause reset of all participating Canary end-users.

cookiesNames?: Object

Optional.

isCanary: string - the end-user's Canary indication cookie name.

version: string - the end-user's Canary version cookie name. If no version parameter, it won't be set.

Loading of Canary Script

canaryScriptUrl?: string

Optional.

The URL the Canary version script will be loaded from.

The default is the URL of the current script (document.currentScript) with the query string parameter: version=canary. To change this - check the 'Advanced Customization' section.

loadAsync?: boolean

Optional. The Default is false.

If true, will load the Canary script in an async matter by appending a script tag to the document.head's children. This could be problematic in the following case:

<html>
    <head>
        <script>
            const canary = new Canary({
                probability: 100,
                canaryScriptUrl: '/mySdk.js?version=canary',
                loadAsync: true
            });        
            canary.bootstrap();
        </script>
        <script>
           mySdk.doAction();
           // because in `head`, scripts are loaded in a sync matter,
           //  this will run before the above finished bootstrapping - so it'll throw.
        </script>
    </head>
</html>

supportCORs?: boolean

Optional. The default is false. Relevant only if loadAsync !== true.

If true the canary script URL will be loaded by a sync XHR request - so it must support CORs.

Else it'll be loaded via document.write of a the relevant script tag (which works fine if the script is in head).

Helpers

globalCanaryIndicationName?: string

Optional. The default is '___canary'.

The property name on window that the indication for the Canary bootstrapping will be stored.

More about changing this in the 'Advanced Customization' section.

Example

Inside the repo there's an example that can walk you through the usage and value of most of the above parameters. After cloning the repo:

  • In its directory run: npm i && npm run example
  • Add to your hosts file: 127.0.0.1 example.com
  • Using a modern browser, navigate to http://example.com:8080/example/index.html

Advanced Customization

When creating an instance of the Canary class, except for the ICanaryConfig Object you can provide other alternate implementations of different depedendencies:

export class Canary {
    constructor(config: ICanaryConfig,
                cookieProvider = new CookieProvider(),
                scriptLoader = new ScriptLoader(),
                randomFactory = () => Math.random() * 100,
                defaultScriptFactory = () =>
                    (!document || !document.currentScript)
                        ? undefined
                        : (document.currentScript as HTMLScriptElement).src,
                globalCanaryIndication = {
                    get: () => window[config.globalCanaryIndicationName],
                    set: (val) => window[config.globalCanaryIndicationName] = val
                }) {}
}

CookieProvider

export interface ICookieProvider {
    get: (key: string) => string | null;
    put: (key: string, val: string) => void;
    remove: (key: string) => void;
}

How cookies are read, set and deleted.

ScriptLoader

export interface IScriptLoader {
    load(url: string, isAsync?: boolean, supportCORs?: boolean): Promise<any>;
}

How the Canary script will be loaded according to configuration.

randomFactory

() => number

How to generate a random number.

defaultScriptFactory

() => string|undefined

How to get the default script url (when canaryScriptUrl is not provided).

globalCanaryIndication

export interface IGlobalCanaryIndication {
    get: () => boolean,
    set: (val: boolean) => void
}

How to get and set the global indication of loading Canary.