• Stars
    star
    211
  • Rank 186,867 (Top 4 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 9 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Seamless in-page cold updates using iframes.

Isolated Core

Build Status Coverage Status npm npm

A library for seamless in-page cold updates using iframes.

⚑ DEMO

Introduction

In long running web apps, such as chat clients or music players, users leave pages open for weeks. It's useful to push code updates to existing clients, but in-page updates must be extremely fast and reliable to not become disruptive to the user experience.

With Isolated Core, your client-side JS (the "core") is contained within an <iframe>. To render UI, the iframe reaches up to manipulate the DOM of its parent document. This pattern decouples app execution from the visible UI, making it possible to seamlessly reload the entire app separately from the page without navigation or jank. This has some cool advantages:

  • Speed: updates load in the background and "swapping cores" is extremely fast.
  • Fault-tolerance: network and JS errors during init are caught before performing an update.
  • Predictability: loading an update runs the same code paths as reloading the page.

Isolated Core is complementary to existing techniques like Hot Module Replacement (HMR) and is framework agnostic. Unlike HMR, Isolated Core reloads your entire app environment *cold* from a blank slate. This makes updates predictable and easy to reason about, and facilitates updating components that previously required a full page reload. In addition, Isolated Core makes rollouts safer: since updates load and initialize in the background, failures can be caught rapidly without disrupting the user.

Browser Compatibility

Isolated core works on IE9+ and all other modern browsers. IE8 support is possible but not complete -- if you need it, please file an issue!

Sauce Test Status

Usage

In the entry point of your app, call coreInit, passing it the URL to the current script and a function to run to initialize your app. When coreInit is first invoked from a script tag, it will create a new iframe, injecting the script you specify. Then, inside the iframe, your script runs again and coreInit calls the run function you specify.

This design makes Isolated Core compatible with a Content Security Policy and browsers which do not support Data URIs in iframes.

main.js:

import { coreInit } from 'isolated-core'

coreInit({
  // In non-IE, you can use document.currentScript.src here.
  scriptURL: '/main.js',

  // Note: we are deferring require()ing our code until the "run" function
  // executes inside the iframe. Our init function is exported by index.js.
  run: core => require('./').init(core),
})

In your initialization function, take a core argument and call core.ready() with handlers to attach and detach your UI. These handlers are responsible for instantiating and decontructing your UI in the parent document when your core is loaded or replaced. Both of these handlers receive the parent document ("uidocument") as an argument. For example, here's a basic React setup:

index.js:

import React from 'react'
import ReactDOM from 'react-dom'
import MyComponent from './MyComponent'

export function init(core) {
  core.ready({
    attach(uidocument) {
      ReactDOM.render(<MyComponent />, uidocument.getElementById('container'))
    },

    detach(uidocument) {
      ReactDOM.unmountComponentAtNode(uidocument.getElementById('container'))
    },
  })
}

When coreInit creates the first iframe, it automatically attaches it, calling your attach handler.

To load an update, call loadCore with a script URL to execute. It returns a promise which resolves when the new core is loaded and ready to attach. It rejects if a script request fails to load (script tag onerror) or a JS exception is thrown during initialization. Under the hood, loadCore is creating a new iframe, injecting the script specified. Inside the new iframe, coreInit runs like before, and when it calls core.ready(), the promise resolves. For example:

loadCore({
  scriptURL: '/main.js',
}).then(
  function success(coreRef) => {
    // Call launchCore to detach the current core and attach the new one.
    coreRef.launchCore()
  },

  function failure(coreErr) {
    // coreErr.type will be either "request" or "js"
    // "request" type errors have a "src" property with the URL that failed to load.
    // "js" type errors have an "err" property with the exception.
    console.error(`core #${coreErr.id} failed to load: ${coreErr.type} error`)

    // Call destroyCore to remove the iframe from the document.
    coreErr.destroyCore()
  }
)

You should use CSS to hide core iframes. The easiest way to do it is to match the data-coreid attribute:

iframe[data-coreid] { display: none }

Caveats

While in general the Isolated Core pattern provides a lot of benefits, there are a few trade-offs worth mentioning:

  • Cores typically share the same browser thread for a page, so if an update initializing in the background ties up the thread, it can cause framerate drops or pauses in the UI.

  • By its nature, cold loading requires more initialization time and memory than hot replacing modules. When a core is ready but not attached yet, it adds significant memory footprint to the page.

  • It's necessary to set aggressive HTTP caching headers for your core script because it will be loaded in both the initial page and then immediately again inside the first core iframe. For best results, use a CDN and include the hash of your bundles in the filename.

API

coreInit({ scriptURL, run, args })

Initialize a core, creating an iframe on first page load if necessary.

When called outside a core iframe, coreInit passes its options to loadCore and automatically attaches the first core when it's ready.

When called inside a core iframe, coreInit invokes the run function with a core object, e.g.:

{
  id: 0,              // A unique numeric id for the core
  args: {...},        // An object passed to loadCore by the invoking context
  ready: <function>,  // Call with a handlers object when finished loading
}

The core.ready() function must be called by your run function when your core is ready to attach:

core.ready({
  attach(uidocument) {
    // render your UI to uidocument
  }

  detach(uidocument) {
    // clean up your event handlers, etc.
  }
})

loadCore({ scriptURL, args })

Load a new core with specified args by creating an iframe and injecting a script with the specified scriptURL into it. Returns a promise which resolves when the core is ready to attach, or rejects in case of request or JS error.

When the promise resolves or rejects, it passes an object of the form:

{
  id: 0,                    // A unique numeric id for the core
  args: {...},              // The args object you specified
  context: <window>,        // A reference to the window object of the iframe
  destroyCore: <function>,  // Call to remove the core's iframe
}

If the promise resolves, the return type will also include:

{
  launchCore: <function>,   // Call to detach the current core and attach this new one
}

Calling launchCore will remove the current execution context from the DOM. Statements following launchCore will not execute.

If the promise rejects, the return type will also include:

{
  type: 'request'|'js',     // Either 'request' on network error, or 'js' on exception
  src: <url>,               // If type: 'request', the URL that failed to load
  err: <Error>,             // If type: 'js', the exception object thrown
}

License

MIT

More Repositories

1

react-html-email

Create elegant HTML email templates using React.
JavaScript
895
star
2

xkcdfools

xkcd CLI + jQuery terminal implementation
JavaScript
563
star
3

picnic

Be spontaneous. Have a picnic!
JavaScript
173
star
4

xkcd-pixels

The frontend code behind "Pixels".
JavaScript
149
star
5

been

Life stream collector.
Python
125
star
6

pow

An experimental HTML5 presentation tool.
JavaScript
80
star
7

wake

A simple life stream web frontend to been.
Python
77
star
8

coalesce

Edit audio at the speed of text
TypeScript
42
star
9

karmabot

A highly extensible IRC karma+information bot written in Python.
Python
37
star
10

time-thief

A calm, habit-forming offline journal.
TypeScript
30
star
11

shine

a Chrome extension for reddit.
JavaScript
22
star
12

socialite

a Firefox extension for reddit social news sites.
JavaScript
18
star
13

redditron

Markov chain analysis of reddit comments
Python
17
star
14

reddit-sidebar-updater

a Google Apps Script to update your subreddit sidebar
JavaScript
16
star
15

diablo

yo-yo.js, with added components
JavaScript
15
star
16

k5

v5 of
JavaScript
15
star
17

time

A serial art project of interactive seekable animations.
JavaScript
11
star
18

bicyclejs-talk

🚲 A talk about Web Bluetooth and cyclejs @ jsconf.is
JavaScript
11
star
19

jam

An unfinished web audio live coding experiment.
JavaScript
10
star
20

conductor

A learning playlist generation engine in Python.
Python
7
star
21

blog

blah blah blah
MDX
6
star
22

exactly

A tiny wrapper around npm to shrinkwrap by content hash
JavaScript
6
star
23

imgurfox

A Firefox extension to upload images to imgur.com.
JavaScript
6
star
24

live

Real-time presentation synchronization across multiple devices in node.
JavaScript
6
star
25

havoc

Haskell Chess AI
Haskell
6
star
26

fcurve

JS port of Blender's animation bezier curve evaluator
JavaScript
6
star
27

wtfcnn

Breadpig project (http://breadpig.com/category/projects/wtfcnn/): A website to compare news websites.
JavaScript
6
star
28

ivy

A minimalistic hierarchical pubsub server with persistent event logs.
Go
5
star
29

srtfan

Take a single SRT stream and fan it out to multiple SRT callers
Go
5
star
30

josie

Old-school Python text adventure game engine.
Python
4
star
31

myo-scripts

Script / hacks / experiments with the Myo controller.
4
star
32

lookatallofus

sonder:
4
star
33

twine

A collaborative audio looper for the web.
JavaScript
4
star
34

danceparty

An animated gif dance club.
JavaScript
4
star
35

linux-vm-tools

Tools I use for developing with VMs.
Python
4
star
36

multicopter

NodeCopter multiboxing
JavaScript
3
star
37

typesetters-son

Utility for generating images of lightly modified webpages
JavaScript
3
star
38

zefram

An isomorphic app core.
JavaScript
3
star
39

synthruary

#synthruary
TypeScript
2
star
40

morphdeck

Beautiful animated slide decks using SVG and JS
JavaScript
2
star
41

skirmish

Python wrapper script to connect to Internet Mini Chess Servers
Python
1
star
42

reshape

My daily art journal from 2006 - 2007, restored for the 2023 web.
Astro
1
star
43

spanner

Chat for hackers.
JavaScript
1
star
44

simple-xkcd-viewer

A bare-bones xkcd viewer.
JavaScript
1
star
45

reddit-extralife-utils

quick and dirty scripts for our Extra Life marathon
Python
1
star
46

grace

say grace, automatically
JavaScript
1
star
47

node-simple-cert

Automatically fetch and renew an SSL certificate using Let's Encrypt
JavaScript
1
star
48

BlankSpaces

A song and drum machine for WaffleJS by Andrew and Max
HTML
1
star
49

phoenix

A pretty fast reddit mobile web frontend.
JavaScript
1
star