Runtime and dependencies

So far as I can tell, the issue is that the backend service that generates the module files at api.observablehq.com/d/notebook.js can create duplicate variables inside the module arrays when variables are imported multiple times. Here’s a function which deduplicates variables in general imported notebook modules (it automates what you did manually in your fix notebook):

function dedup({id, modules}) {
  return {id, modules:modules.map(({id, variables}) => {
    const set = new Set();
    const newVariables = [];
    for (const v of variables) {
      if(!set.has(v.name)) {
        newVariables.push(v);
        set.add(v.name);
      }
    }
    return {id, variables:newVariables};
  })};
}

(Just use e.g. dedup(notebook2) in place of notebook2).

Note that these modules served by the API are written for the V1 runtime, which may become unsupported at some point. A more future-proof solution is to switch to using the V3 runtime like this:

notebook2v3 = (await import("https://api.observablehq.com/d/8e5e6d7c80d7e3dd.js?v=3")).default
new Promise((resolve, reject) => {
  (new Runtime()).module(notebook2v3, name => {
    if (name === "str2") {
      return { fulfilled: resolve, rejected: reject };
    }
  });
})

This also avoids the duplicate variable issue.

Now, if you want to play around with a really convoluted solution that stays in the V1 runtime world, you can try this:

import {fromV1, toV1, utoa} from '@bryangingechen/fromv1'
mm = {
  const fV1 = fromV1(await import('https://api.observablehq.com/d/8e5e6d7c80d7e3dd.js'));
  const nbObj = await toV1(({id:'xx', version:'0', nodes:fV1}));
  const mm = await import(`data:text/javascript;base64,${utoa(nbObj)}`);
  return mm;
}
new Promise((resolve, reject) => {
  Runtime.load(mm.default, variable => {
    if (variable.name === "str2") {
      return { fulfilled: resolve, rejected: reject };
    }
  });
})

This uses the following two functions I wrote some time back:

  • fromV1 reverse-engineers a V1 module to generate cell definitions
  • toV1 creates a V1 module from cell definitions (and luckily, my hacked-together implementation does remove duplicates from imports)

See the fromv1 notebook and its parent for more behind the scenes details.

1 Like