@salesforce/oasis

An Oasis in a world of Shadow DOMs

Usage no npm install needed!

<script type="module">
  import salesforceOasis from 'https://cdn.skypack.dev/@salesforce/oasis';
</script>

README

Oasis JavaScript Library

Node.js CI

Experimental library to virtualize script evaluation that should not be subject to the shadow DOM encapsulation, and instead, provide the illusion of a page with the flat-tree representation of the current DOM.

Goals

  • Provide an alternative mechanism to the HTMLScriptElement to evaluated code inside the virtualized environment that exhibits the flat-tree representation of the current DOM. In other words, without any Shadow DOM boundaries.
  • Provide a mechanism to interchange data between the virtualized sandbox and the incubator window.
  • Polyfill and other language, and DOM API modification performed by evaluated scripts should only affect the virtualized environment (AKA: integrity preserving semantics).

Note: Google Tag Manager, Pendo and Evergage are some representative examples of scripts that you might want to evaluate inside Oasis in order to make those libraries work even when using Web Components using Shadow DOM encapsulation. Those libraries do not understand Shadow DOM, but they work great with a flat-tree representation of the DOM.

Non-goals

  • This library does not provide security guarantees.

Note: Code evaluated inside the virtualized environment is high-privilege, and can carry on all kinds of operations that might affect the incubator window.

How to use

yarn add @salesforce/oasis

Once installed as a dependency in your project, you must import it into the code to be evaluated in the client side:

import '@salesforce/oasis';

Note: it is important that this library runs as soon as possible.

You can evaluate as many scripts as you want by using the following declaration in HTML:

<x-oasis-script>
...code to be evaluated...
</x-oasis-script>
<x-oasis-script src="/path/to/the/script/to/be/evaluated.js"></x-oasis-script>

or via JS as following:

import VirtualScript from '@salesforce/oasis';
const elm = new VirtualScript();
elm.hidden = true;
elm.textContent = '...code to be evaluated...';
// or using a src to fetch the script:
elm.src = '/path/to/the/script/to/be/evaluated.js';
document.body.appendChild(elm);

Node: VirtualScript is going to be registered as a web component under the x-oasis-script tag name by default when evaluating this library. You can extend it and define it as a custom element under another tag name if desired.

It is important to note that <x-oasis-script> is almost identical to the built-in <script> element, with a couple of exceptions:

  • Inline scripts as well as src scripts are always evaluated with async attribute on.
  • The content of the <x-oasis-script> is visible, that's why you see the usage of the hidden attribute or DOM Property on the examples above.

Design

TBD

Implementation Details

This library is designed to work in browsers where we don't have a way to create a light-weight Realm that is synchronously accessible (that will be solved in part by the stage 2 Realms Proposal), we are forced to use a same-domain iframe in order to virtualize the code to be evaluated inside a sandbox for a particular window.

Requirements

The only requirement for the in-browser virtualization mechanism described above is the usage of eval and dynamic script elements creation as the main mechanism for evaluating code inside the virtualized environment. This means your CSP rules should include at least script-src: 'unsafe-eval' in order for this library to function, and the proper rules for dynamic scripts to be evaluated as well.

Performance

Many of the libraries that might require a flat-tree are not critical in term of performance, in the sense that they are reactionary to user-interaction, instead of being part of the critical path.

You should not run any code in Oasis which is a part of your critical path, that is for building your page or any segment of the page, but just to provide you a flat-tree representation of a page created by regular scripts evaluated outside of the virtualized environment.

That being said, it should be fairly fast, fast initialization, and fast evaluation as well. At the moment we haven't identified any bottlenecks.

The Code

  • This library is implemented using TypeScript, and produces the proper TypeScript types, in case you care about it.
  • The ES Modules distribution is produced that can be used via www.pika.dev or similar services, or by using the experimental module flag in nodejs 12.x, or above. There is also a CommonJS and UMD bundle you can copy into your project and drop in.
  • The src/ folder contains the library code, while the lib/ folder will contain the compiled ES Modules and the drop-in bundled after executing the build script from package.json.
  • The test/ folder contains a set of Karma tests to validate the different scenarios where this library can change the behavior of the runtime.

Missing Capabilities

  • Inspecting closed shadow roots (TBD)
  • query selectors and deep traversal APIs (WIP)
  • document.evaluate XPath (TBD)
  • support to inspect content inside a declarative shadow root (chrome behind a flag)
  • node compareDocumentPosition and contains operations to make it all nodes look like they are part of a single document (TBD)
  • mutation observer for childNode mutations (TBD)

Browsers Support and Stats

  • Modern browsers with support for ES6 Proxy and WeakMaps.