js-dojo

8 the realm seam

GOAL

get the host page's row check to accept a list the embedded widget built, then watch a function the widget wrote run on the host's own array as if no seam were there at all.

CONCEPT

a custom element runs in the host page's one realm. An iframe gives the code inside it a SECOND realm - its own Array, its own Object, its own copy of every built-in. Values cross the seam intact, but identity checks lie: `instanceof` asks whether the HOST's prototype sits on the value's chain, and an array built inside the frame inherits from the frame's Array.prototype instead.

HINT

two questions that sound alike. `x instanceof Array` asks "did MY Array stamp this?". Array.isArray(x) asks "is this an array?" - no chain walk, no realm in the question.

MIRRORS

a reporting widget another team ships inside an iframe. The rows it hands back are a real array with real entries in it, and the host page still refuses to render them, because the check asks whose Array stamped the list rather than what the list is.

Run

node 08-the-realm-seam.cjs

Source

// DOJO · Module 10 / Exercise 8 — the realm seam
// GOAL: get the host page's row check to accept a list the embedded
//       widget built, then watch a function the widget wrote run on
//       the host's own array as if no seam were there at all.
// CONCEPT: a custom element runs in the host page's one realm. An
//       iframe gives the code inside it a SECOND realm - its own
//       Array, its own Object, its own copy of every built-in. Values
//       cross the seam intact, but identity checks lie: `instanceof`
//       asks whether the HOST's prototype sits on the value's chain,
//       and an array built inside the frame inherits from the frame's
//       Array.prototype instead.
// HINT: two questions that sound alike. `x instanceof Array` asks
//       "did MY Array stamp this?". Array.isArray(x) asks "is this an
//       array?" - no chain walk, no realm in the question.
// MIRRORS: a reporting widget another team ships inside an iframe. The
//       rows it hands back are a real array with real entries in it,
//       and the host page still refuses to render them, because the
//       check asks whose Array stamped the list rather than what the
//       list is.
// Run: node 08-the-realm-seam.cjs

'use strict';
const assert = require('node:assert');
const vm = require('node:vm');

// The widget is a panel another team wrote and shipped. In a browser
// it would load inside an iframe of its own; here node:vm builds the
// same seam in plain Node - a second realm with its own built-ins.
// Nothing below is broken code. The widget is a well-behaved
// neighbour.
const widgetRealm = vm.createContext({});
const widget = vm.runInContext(
  `({
    rows: ['cpu', 'memory', 'disk'],
    settings: { theme: 'dark' },
    countRows(list) {
      return Array.isArray(list) ? list.length : -1;
    },
  })`,
  widgetRealm,
);

const hostRows = ['errors', 'latency'];

// --- TODO 1 --------------------------------------------------------
// Three readings the host takes on values the widget built. Two of
// them use `instanceof`, which walks the value's prototype chain
// hunting for the HOST's Array.prototype or Object.prototype. The
// third asks the value what it is and never looks at a chain.
// In place of null, write the three booleans this object really holds.
const seamReadings = {
  arrayInstanceof: widget.rows instanceof Array,
  objectInstanceof: widget.settings instanceof Object,
  isArray: Array.isArray(widget.rows),
};

const answerSeam = null; // your answer: the same three keys, booleans

assert.deepStrictEqual(
  seamReadings,
  answerSeam,
  'TODO 1 — what does each reading report across the seam?',
);

// --- TODO 2 --------------------------------------------------------
// acceptsRows is the host page's input check: it refuses to render a
// panel whose rows are not a list. It waves the host's own arrays
// through and turns the widget's away, which is the whole bug report.
// Rewrite the check so it asks the question it actually means, and
// keep saying no to things that are not lists.
function acceptsRows(value) {
  return value instanceof Array;
}

assert.strictEqual(
  acceptsRows(hostRows),
  true,
  'TODO 2 — the check must still accept the host rows',
);
assert.strictEqual(
  acceptsRows(widget.rows),
  true,
  'TODO 2 — the check must accept the widget rows as well',
);
assert.strictEqual(
  acceptsRows('cpu'),
  false,
  'TODO 2 — the check must still reject a value that is not a list',
);

// --- TODO 3 --------------------------------------------------------
// Behaviour crosses the seam even where identity does not. countRows
// was written inside the widget realm, so over here
// `widget.countRows instanceof Function` is false while
// `typeof widget.countRows` is still 'function' - and the host is
// about to call it on an array the HOST built. Read countRows above,
// decide what it sees from its side of the seam, and write the number
// it hands back for hostRows.
const rowCount = widget.countRows(hostRows);

const answerRowCount = null; // your answer

assert.strictEqual(
  rowCount,
  answerRowCount,
  'TODO 3 — what does the widget function report for the host rows?',
);

console.log('PASS');

Solution