Giter Club home page Giter Club logo

webr's Introduction

Build, test and deploy webR codecov

WebR - R in the Browser

This project aims to compile the statistical language R (https://www.r-project.org/) into WebAssembly for use with a browser, via Emscripten (https://emscripten.org/). The repo includes patches to R's source code so that it can work in the browser environment provided by Emscripten and also includes a web-based IDE through the use of xterm.js and CodeMirror.

Demo

A demo of the webR IDE can be found at https://webr.r-wasm.org/latest/. Please be patient as the Wasm runtime downloads and executes. R will display a banner message when it is ready to use.

Documentation

Documentation showing how to use webR in your own projects can be found at https://docs.r-wasm.org/webr/latest/

Downloading webR

The webR JavaScript package is available for download through npm and on CDN.

Complete release packages, including R WebAssembly binaries, are available to download for self hosting in the GitHub Releases section.

Docker images containing a pre-built webR development environment can be found in the GitHub Packages section.

Building webR from source

R's source code and supporting libraries are written in both C/C++ and Fortran. Source files can be compiled with either a custom development version of LLVM flang (the default) or with gfortran and Dragonegg (using the --with-dragonegg configure option).

If you are compiling webR using the default toolchain, ensure that you first install the following required prerequisites in your build environment:

  • Emscripten SDK (>=3.1.35)
  • cmake
  • gperf
  • liblzma
  • libpcre2
  • node (>=17.0.0)
  • quilt
  • wget

Build instructions

Clone the repo into a new directory, cd into the directory, then run ./configure && make. You can configure make variables in a ~/.webr-config.mk file.

A dist directory is created which when finished contains the R Wasm files and an index.html file ready to serve the included webR IDE.

WebAssembly libraries

WebR relies on additional libraries compiled for Wasm for both Cairo graphics support and for building R packages that depend on certain system libraries. By default, only a minimal set of libraries are built for use with webR.

If you'd prefer to build all of the available system libraries for Wasm, cd into the libs directory and run make all to build the additional libraries, then finally cd .. and run make clean-webr && make to rebuild webR. R will automatically detect the additional Wasm libraries and integrate Cairo graphics support as part of the build.

Building with Docker

Included in the source repository is a Dockerfile which can be used to setup everything that's required for the webR build environment, including LLVM flang and all supported WebAssembly system libraries.

Pre-built docker images can be found in the GitHub Packages section. To build the docker image and webR from source, cd into the webR source directory then run docker build .

The resulting docker image contains a version of R configured to be built for WebAssembly, and so the image can also be used to build custom R packages for webR.

Building with Nix

If you are using Nix, you can start a development environment by running nix develop. Then you can build as usual, with ./configure && make.

Note that this requires that your Nix installation has support for flakes enabled. The easiest way to do this is to install using the Nix installer from Determinate Systems.

Node and Emscripten versioning

WebR requires compiler and runtime support for WebAssembly.Exception, used internally for R error handling. This requires a version of Emscripten >= 3.1.35 and Node >= 17.0.0, which may be newer than the versions provided by your system package manager. An easy way to install and manage multiple versions of Node and Emscripten is by using nvm and emsdk.

The version of Node currently bundled by emsdk is 16.0.0. When building webR with this version of Node the process will fail with configure logs containing the error

WebAssembly.Tag is not a constructor

If this occurs, a newer version of Node should be installed and the following environment variable set before building webR, instructing Emscripten to use the newer version of Node:

export EM_NODE_JS=$(HOME)/.nvm/versions/node/v20.1.0/bin/node

If you are unsure of the correct path to Node the command which node should print the path in full.

Building on macOS Ventura 13.0+

At the time of writing the version of R used as the base for webR does not build cleanly using the macOS Ventura development SDK. If you are not using the included Dockerfile to build webR, the following extra setup must be done before starting the build process,

  • Install the GNU version of the patch program: e.g. brew install gpatch

Using Dragonegg (Optional)

If you intend to build webR using Dragonegg to handle Fortran sources, older versions of the gcc/gfortran toolchain are required than what is provided by modern operating system repositories. The docker file tools/dragonegg/Dockerfile can be used to setup the required C/C++/Fortran toolchain and development environment for compiling webR with Dragonegg.

webr's People

Contributors

bahadzie avatar bugzpodder avatar coatless avatar colinfay avatar dipterix avatar drgomulka avatar georgestagg avatar hatemhosny avatar jeroen avatar josiahparry avatar lionel- avatar polkas avatar wch avatar yutannihilation avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webr's Issues

Memory management in webR

Current state

This section shows (approximatively, some tweaks are still needed) the current state of memory management in webR.

Protecting and unprotecting singular objects

Objects must be protected from the worker thread, not from the main thread. Consider:

async function foo() {
  const obj = await webR.evalR('rnorm(1)');
  await obj.keep();
}

Because of concurrency, R code may run between the two await expressions, causing obj to be GC'd before it has a chance to be protected. We don't have any choice but to automatically preserve all objects returned to the main thread. The keep() method must be called implicitly by the worker.

On the other hand, because Javascript doesn't have reliable object finalisers, free() can't be called implicitly. If the main thread doesn't explictly free these objects, memory is leaked:

async function foo() {
  const obj = await webR.evalR('rnorm(1)');

  return await bar(obj);
}

async function bar(obj: RObject) {
  const other = await webR.evalR('runif(1)');
  const out = await webR.evalR('x + y', { x: obj, y: other});

  return await out.toNumber();
}

Code on the main thread must protect-unwind object release in finally clause:

async function foo() {
  // Implicit `keep()`
  const obj = await webR.evalR('rnorm(1)');

  // Need to protect-unwind `free()` as soon as created
  try {
    return await bar(obj);
  } finally {
    obj.free();
  }
}

try / finally can quickly get out of hands with this singular unprotection approach though:

async function bar(obj: RObject) {
  const other = await webR.evalR('runif(1)');

  try {
    const out = await webR.evalR('x + y', { x: obj, y: other});

    try {
      return await out.toNumber();
    } finally {
      out.free();
    }
  } finally {
    other.free();
  }
}

To avoid this increasing nestedness we need some way of unprotecting multiple objects at once.

Evaluating for side effects

Implicit protection gets in the way when evaluating R code for side effects. min the following example the return value is leaked because the user forgot to explictly release it.

async function printNorm(): void {
  await webR.evalR('print(rnorm(1))');
}

The user needs to remember to release the returned object immediately with a rather heavy boilerplate:

async function printNorm(): void {
  await (await webR.evalR('print(rnorm(1))')).free();
}

Alternatively we could add arguments to evalR(), captureR(), newRObject(), etc to turn off implicit protection:

async function printNorm(): void {
  await webR.evalR('print(rnorm(1))', undefined, false);
}

This spares a channel communication (one fewer await). But this has the downside that users would need to look up the signature of these methods to figure out which argument they need to pass. In the example above, we need to skip the env argument with an explicit undefined.

The case of captureR()

The return value of captureR() is more complicated to deal with than that of evalR(). It currently returns two objects, a result: RObject and an output: (string | RObject)[]. The second object is an array that contains stdout/stderr outputs converted to JS strings and message/warning conditions wrapped in RObjects. There is an indefinite number of objects protected by captureR() that need to be released.

Dealing with the outputs using free() wouldn't be practical as the caller would have to loop over the array in a finally clause. One way to solve this would be for captureR() to be paired with a helper that frees everything automatically, but this is one more idiosyncratic thing to keep in mind for users of this API. It would be easy to only manage the memory of result and forget about output, especially since most of the time the output array only contains JS objects.

Proposals

Minimalistic shelters

To simplify the release of objects and avoid nested try / finally blocks, we need to create some sort of shelter to which objects can be added and released all at once.

We could implement shelters with a simple JS object:

interface Shelter {
  [key: string]: RObject;
}

The following purge helper would be exposed as webR.purge. It's not an async function because users shouldn't manipulate objects after calling purge(). The objects can be freed at any time in the future.

function shelterPurge(shelter: Shelter) {
  Object.keys(shelter).forEach((key) => shelter[key].free());
}

Here is an example where the callee bar() creates a shelter for itself. The caller foo() frees its own object with free(), as before:

async function foo() {
  const obj = await webR.evalR('rnorm(1)');

  try {
    return await bar(obj);
  } finally {
    obj.free();
  }
}

async function bar(obj: RObject) {
  const shelter: RObject[] = [];

  try {
    shelter.other = await webR.evalR('runif(1)');
    shelter.out = await webR.evalR('x + y', { x: obj, y: shelter.other});
    return await out.toNumber();
  } finally {
    webR.purge(shelter);
  }
}

Shelters may also be passed by arguments. This makes the memory management as granular as we want to. In this example, foo() creates a shelter that is also used by bar():

async function foo() {
  const shelter: RObject[] = [];

  try {
    shelter.obj = await webR.evalR('rnorm(1)');
    return await bar(obj, shelter);
  } finally {
    webR.purge(shelter);
  }
}

async function bar(obj: RObject, shelter: Shelter) {
  shelter.other = await webR.evalR('runif(1)');
  shelter.out = await webR.evalR('x + y', { x: obj, y: shelter.other});
  return await out.toNumber();
}

Issues with the dictionary approach

Passing these shelters to other functions is potentially unsafe. For instance bar() does not necessarily know what objects have been added to shelter and might overwrite an object added upstack.

This could be worked around by structuring the shelter differently with a more verbose API (nested calls):

async function bar(obj: RObject, shelter: Shelter) {
  const other = shelter.add(await webR.evalR('runif(1)'));
  const out = shelter.add(await webR.evalR('x + y', { x: obj, y: shelter.other}));
  return await out.toNumber();
}

Shelters and captureR()

The minimalistic shelter API doesn't really work for captureR() which returns outputs in an array of string | RObject. The caller would have to loop over the output to add the RObject conditions to the shelter.

Alternatively, captureR() (and for consistency evalR() etc) could take a shelter as argument and be in charge of adding objects there.

await evalR('1', undefined, shelter);
await captureR('1', undefined, undefined, shelter);

This requires users to memorise function signatures or look them up, and sort of hides the important task of memory management in auxiliary arguments.

Evaluating shelters

In this proposal, shelters are objects that carry all the webR methods that are able to transfer R objects on the main thread. Instead of using webR to evaluate, you call the corresponding shelter method:

async function foo() {
  // Create a new shelter
  const shelter = new webR.shelter();

  try {
    // Use it to evaluate and transfer objects there
    const obj = await shelter.evalR('rnorm(1)');

    // Pass it down by argument
    return await bar(obj, shelter);
  } finally {
    // Purge it all at once
    shelter.purge();
  }
}

async function bar(obj: RObject, shelter: Shelter) {
  // Evaluate within the shelter
  const other = await shelter.evalR('runif(1)');
  const out = await shelter.evalR('x + y', { x: obj, y: other});

  return await out.toNumber();
}

In addition, webR includes a "discarding shelter" (called discard?) that doesn't protect objects. It is useful when you need to evaluate R code only for side effects:

async function printNorm(): void {
  await webR.discard.evalR('print(rnorm(1))');
}

The discarding shelter is easier to use than free() because the result is never protected. And easier to use than an extra argument because you don't need to look at the signature to see which argument you need to fill out, and don't need to closely inspect the call to determine which memory management steps are taken.

The captureR() issue is trivially solved by calling the corresponding shelter method:

await shelter.captureR('1');

// Frees the result and all conditions
shelter.purge()

You can still evaluate with webR.evalR() and friends, in which case you must call free() manually. There is no webR.purge() method to free all objects at once because it would be easy to create incorrect code with it.

Using embedded single quote in string in graphics causes crash/hang.

Using the amazing: https://webr.gwstagg.co.uk/

> plot(1:10)
> title("This is")

works fine but then:

> title("This isn't")

causes the session to hang with a JS error on the console (latest Firefox, not tried other browsers yet...):

Uncaught (in promise) SyntaxError: missing ) after argument list
    <anonymous> repl.ts:180
    async* repl.ts:189

First manifest running demo(graphics) which makes a pie chart subtitle with a single quote in. System seems find with single quotes embedded in strings for printing etc. Can only make it crash like this when used in a plot...

Support for external database networking with DBI

WebR Support of relational database will be a very important milestone, even if, Emscripten full support of Sockets in browser environment limit this task a lot. However, in his latest version they used some workaround and right now, this idea is more feasible using one of the followings ways:
a) POSIX TCP Sockets over WebSockets;
b) Full POSIX Sockets over WebSocket Proxy Server;

The Wordpress WASM team had some results by using fetch()
WordPress/wordpress-playground#85
other, Postgres WASM use this https://github.com/benjamincburns/websockproxy
but on x86 virtualization on the browser.

Which License?

I'd like to release this under MIT, as I prefer permissive licenses. However, R is released under the GPL. It could certainly be argued that this entire project is a derivative work and so also required to be released under GPL license. Particularly if we are distributing webR binaries.

Perhaps we could release the infrastructure and build/make scripts under MIT, and the R patches and binary separately under GPL?

Perhaps it would be easier just to license this entire project GPL?

Error: Dynamic require of "worker_threads" is not supported

I was trying to run this in node but getting the following error when trying to import WebR.

import { WebR } from '@r-wasm/webr';
const webR = new WebR();

(async () => {
    await webR.init();

    let result = await webR.evalR('rnorm(10,5,1)');
    let output = await result.toArray();
    
    console.log('Result of running `rnorm` from webR: ', output);

})();

file:///workspaces/codespaces-blank/node_modules/@r-wasm/webr/dist/webr.mjs:12
throw new Error('Dynamic require of "' + x + '" is not supported');
^

Error: Dynamic require of "worker_threads" is not supported
at file:///workspaces/codespaces-blank/node_modules/@r-wasm/webr/dist/webr.mjs:12:9
at file:///workspaces/codespaces-blank/node_modules/@r-wasm/webr/dist/webr.mjs:561:23
at ModuleJob.run (node:internal/modules/esm/module_job:193:25)

Node.js v19.7.0

This was run in a fresh vscode codespace. Repo with reproducible example is here.

Sorry if I'm doing something wrong!

Assertion error in Safari, unless the Web Inspector is open

I'm having a weird bug that WebR produces an assertion error in Safari but not if I open the Web Inspector. If I have the inspector open, refresh the page, it all works fine. It also works fine in Chrome. Any clues as to what may cause an error like this?

A screenshot of the error message:
Screenshot 2023-04-04 at 23 29 13

A link to the hosted page:
https://642c95d6d914dd11c91843b1--webr-template.netlify.app

Link to the code:
https://github.com/WillemSleegers/webr-template

How to read file while using WebRFS.

I have created the folder structure using WebRFS.
Here i have created a folder RCode.

This is the object that i generated when i use
await fs.lookupPath("/RCode");

{
    "id": 1477,
    "name": "RCode",
    "mode": 16895,
    "isFolder": true,
    "contents": [
        {
            "id": 1478,
            "name": "src",
            "mode": 16895,
            "isFolder": true,
            "contents": [
                {
                    "id": 1479,
                    "name": "plot",
                    "mode": 16895,
                    "isFolder": true,
                    "contents": [
                        {
                            "id": 1482,
                            "name": "graph.R",
                            "mode": 33206,
                            "isFolder": false,
                            "contents": {}
                        }
                    ]
                },
                {
                    "id": 1480,
                    "name": "main.R",
                    "mode": 33206,
                    "isFolder": false,
                    "contents": {}
                },
                {
                    "id": 1481,
                    "name": "second.R",
                    "mode": 33206,
                    "isFolder": false,
                    "contents": {}
                }
            ]
        }
    ]
}

In the code editor that i am building i want to import a function divide_by_two from second.R in main.R using source.
main.R

source("second.R")
    Num <-2
    result <- divide_by_two(Num)
    print(result)

second.R

 divide_by_two <- function(x) {
      return(x/2)
    }

I get the following error when i run main.R

Error in file(filename, "r", encoding = encoding) :
cannot open the connection
In addition: Warning message:
In file(filename, "r", encoding = encoding) :
cannot open file 'second.R': No such file or directory

Although second.R is created alongside main.R, it could not the file. I tried using absolute path as well but nothing works.

Please let me know how can i make it work.

@georgestagg @lionel-

README - available packages

It will be great to have such paragraph in the README.

webR is a subset of the R ecosystem with its own RSPM package repository. 
The base R packages are available by default. 
Other parts of the ecosystem, like dplyr or tidyverse are present only after being installed on a per-session basis. 
The available to install packages can get by calling `rownames(available.packages(repos = "https://repo.r-wasm.org/"))`. 
We will continuously add more packages, taking into account many factors.

Libraries should be loaded dynamically

Dynamic loading with Emscripten's version of dlopen() has been disabled and currently the default libraries are just statically linked in at compile time. One of the reasons for that is a problem with dynamic libraries larger than 4K, emscripten-core/emscripten#11753.

If loading R packages is ever to work properly, dynamic linking will need to be turned back on. Potentially a solution exists in using loadDynamicLibrary() in JS rather than dlopen() in C.

Persistent filesystem storage in the webR REPL

Perhaps by mounting an Emscripten IDBFS filesystem somewhere like /storage.

We will need to think about long term storage implications. Browser localStorage will work in the short term but could be cleared away at any moment.

Build/compile error at stage 2

Very sorry to bother and feel free to dismiss this, since I know it is a personal issue where I am looking for a clue to get me to a finished build. I have been working to replicate the build step on a Google Cloud instance, so that I have all the necessary resources/dependencies to start exploring compilation of packages not in the current list at webr-repo. I have overcome all other stumbling blocks, but am stuck at stage 2 with the following error:

checking whether mixed C/Fortran code can be run... configure: WARNING: cannot run mixed C/Fortran code
configure: error: Maybe check LDFLAGS for paths to Fortran libraries?
emconfigure: error: '../configure --prefix=/app/webr/wasm/R-4.1.3 --with-x=no --with-readline=no --with-jpeglib=no --with-cairo=no --disable-openmp --with-recommended-packages=no --enable-R-profiling=no --with-pcre2 --disable-nls --enable-byte-compiled-packages=no --enable-static=yes --host=wasm32-unknown-emscripten --with-internal-tzcode' failed (returned 1)
make[1]: *** [Makefile:126: /app/webr/R/build/state/r-stage2-configured] Error 

Everything I found on the internet says this means the compiler did not find the Fortran lib, but earlier in the logs I see:

  MAIN_LDFLAGS="-s MAIN_MODULE=1 -s WASM=1 -s WASM_BIGINT -s ALLOW_MEMORY_GROWTH=1 -s STACK_SIZE=1MB -s EXIT_RUNTIME=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s EXPORTED_RUNTIME_METHODS=['allocateUTF8','UTF8ToString','callMain'] -s FETCH=1 --extern
-pre-js /app/webr/src/webR/pre.js -L/app/webr/wasm/lib -lFortranRuntime -Oz -s DECLARE_ASM_MODULE_EXPORTS=0" \
  SHLIB_LDFLAGS="-s SIDE_MODULE=1 -s WASM_BIGINT -Oz -s DECLARE_ASM_MODULE_EXPORTS=0" \
  CPPFLAGS=" -I/app/webr/wasm/include -DEXPEL_OLD_TO_NEW=1 -s USE_BZIP2=1 -s USE_ZLIB=1" \
  CFLAGS="  -I/app/webr/wasm/include -DEXPEL_OLD_TO_NEW=1 -s USE_BZIP2=1 -s USE_ZLIB=1  -fPIC -fno-exceptions -fno-rtti -Oz" \
  LDFLAGS=" -L/app/webr/wasm/lib" \

...and if I look in /app/webr/wasm/lib I find libFortranRuntime.a which I think is the correct file in the appropriate flagged location.

root@instance-1:/app/webr# ls /app/webr/wasm/lib
libFortranRuntime.a  liblzma.a  liblzma.la  libpcre2-8.a  libpcre2-8.la  libpcre2-posix.a  libpcre2-posix.la  libpng.a  libpng.la  libpng16.a  libpng16.la  pkgconfig

I'll keep trying to figure this out, but anybody could give just a little clue I would really appreciate it, so that I can hopefully try to contribute to this amazing and unbelievable project.

Thanks.

Correctly handle `\r\n` in `webR.runRAsync`

Windows style line endings currently cause an unexpected input error in webR. One way to handle this would be to replace \r\n line endings with \n when we populate the buffer just before calling Rf_ReplIteration.

The replacement already seems to happen in R, even inside string literals, at least when built for unix without readline support:

$ echo -e '"this is a\r\n string"' | /app/host/R-4.1.3/bin/R --vanilla --quiet           
> "this is a
+  string"
[1] "this is a\n string"

so it would not be too unexpected.

Error in creating docker container from webR repository

Hello.

I’m a novice programmer very interested in what you created with webR and attempted today to compile my own wasm files from your repository source. As I understood the instructions, I was supposed to create a docker image using the Dockerfile in the webR repository then create a docker container using that docker image in which I would clone in the repository into a new director in that container then got to the top of that directory and “./configure && make”. However, I tried that process multiple times and was able to create a docker image fine but not an appropriate container image. Each time I would go to use the new container I created I found that the container already stopped itself due to a make error listen in the docker container log (“ Make: *** No rule to make target ‘webr’. Stop.)

I’m using Docker on on a x64 iMac running OSX Monterey. Here is an example of one of my attempts to create a usable docker container to put webR in:

-iMac ~ % docker build https://github.com/georgestagg/webR.git#main
[+] Building 2.1s (9/9) FINISHED
=> [internal] load git source https://github.com/georgestagg/webR.git#main 0.8s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 1.2s
=> [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
=> [1/5] FROM docker.io/library/ubuntu:20.04@sha256:47f14534bda344d9fe6ffd6effb95eefe579f4be0d508b7445cf77f61a0e5724 0.0s
=> CACHED [2/5] RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty main restricted universe" >> /etc/apt/sources.list && echo "deb http://sec 0.0s
=> CACHED [3/5] WORKDIR /app 0.0s
=> CACHED [4/5] RUN mkdir -p /webr-tools && cd /webr-tools && git clone --depth=1 https://github.com/emscripten-core/emsdk.git && cd emsd 0.0s
=> CACHED [5/5] RUN ln -s /usr/bin/gfortran-4.6 /usr/bin/gfortran 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:ac280cc7722234b9b339e88fa3a954aaea77ad58b9698da66975546b3bca2ce0

iMac ~ % docker create ac280cc77222

(However, every time I then attempted to enter the above container to try and clone in the repository, it would give me the error that the container I created from the above command had stopped running. I would restart the container but the container would stop itself again; when I went to the log file for the docker container it displayed the following:)

Make: *** No rule to make target ‘webr’. Stop.

Am I doing something wrong in this process?

I appreciate any advice you could give.

Thanks! :-)

Problem loading packages

Thanks for this project!

I am trying to load packages into webr. When I run webr::install("package") I get the following error message:

Error: 'install' is not an exported object from 'namespace:webr'

I suspect that maybe this has to do with the webr support package? But I am not totally sure and the link to that page appears to be broken.

Unable to make it work on production.

@georgestagg @lionel-

I implemented a working application that runs R on browser using @r-wasm/webr package locally. Everything works fine there but i am getting this error after build.
Screenshot 2023-02-16 at 16 05 38
I have also added the necessary share headers in the setupProxy.js and in the nginx server
This is the code in the nginx server

location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
add_header 'Cross-Origin-Embedder-Policy' 'require-corp';
add_header 'Cross-Origin-Opener-Policy' 'same-origin';
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}

Please let me know how can i resolve this?

Memory and file system for current sessions

I have a few questions about running webR in a long running node webserver:

  • If I start multiple R istances by running const session = new x.WebR() for each incoming http request on the server, do these sessions interfere with each other? Or is each WebR instance independent?
  • How do I completely destroy and free such an R instance, including its Shelter and all objects? Does it happen automatically when the object that was instantiated from new x.WebR() goes out of scope? Can I manually destroy it to immediately release all memory?
  • Is the virtual file system shared between R instances?

To illustrate, below a simple NodeJS script, that runs the same code in 5 parallel R sessions:

const x  = require("@r-wasm/webr");

for (i in [1,2,3,4,5]) {
  const session = new x.WebR();
  session.init().then(function(){
    console.log("Session started!")
    session.evalR(`pi <- pi*2; pi`).then(function(obj){
      obj.toArray().then(function(data){
        console.log(data)
      });
    });
  });
}

Running R from a web worker crashes on Safari

Currently the webR UI blocks until the current R command finishes. This makes webR less comfortable to use and prevents printf-debugging in case of crashes or freezes.

I've ported a trimmed down version of the webR app to a web worker as a POC and it seemed to work fine: lionel-/webr-build@1436305. I'll look into porting this work to the main repo so that we can more easily printf-debug.

Issues with lazyloading

I'm having issues with macOS builds of webR with dev flang and the new emscripten-based builds. It appears these issues are caused by the way we currently work around lazyload databases.

From what I gather, the base package is loaded from R files rather than a lazyload database at some point, and this fails when defining .leap.seconds because the evaluation depends on values that are not loaded yet. This error happens early during startup when base is loaded via R_ReplFile(), and it is not reported to stdout or stderr. Instead, the problem manifests with various symbols not found, such as R.version or .ArgsEnv. I'm unsure why this happens on macOS and not within the docker environment.

I've explored a way of creating lazyload databases more systematically in lionel-/webR@build-fixes...lionel-:lazyload. This is not perfect because it creates the database from the host package (itself lazyloaded) instead of from the omnibus loader file (except for the methods package which uses a different strategy because of the way it creates). Also it makes the makefiles patches more complicated. However, the build invocation on the webr side is correspondingly simpler, and it solves the build errors I encountered with the flang branch.

If in the longer term we manage to run R as a node.js application, then we would no longer need these cross-building workarounds. So I'm thinking the workarounds in the lazyload branch should be sufficient for now?

Supported packages?

hi @georgestagg I played around with the demo and it's fairly impressive to see R running entirely within the browser. I tried loading a few packages, like dplyr, ggplot and a few others, and it seems there is some limitation as to what packages are currently supported. How do packages support work exactly? Are they precompiled and therefore only a specific set is available? What is the work required to add new packages?

Initializing `WebR` in react server components gives error "cannot determine runtime environment"

I have a nextjs app that uses react server components to initialize a WebR object. I got an error "cannot determine runtime environment"

error - node_modules/.pnpm/@[email protected]/node_modules/@r-wasm/webr/dist/webr.mjs (78:8) @ eval
error - Error: Cannot determine runtime environment
    at eval (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]/node_modules/@r-wasm/webr/dist/webr.mjs:81:11)
    at (sc_server)/./node_modules/.pnpm/@[email protected]/node_modules/@r-wasm/webr/dist/webr.mjs (/Users/qiushi/workspace/tmp/next-webr/.next/server/app/page.js:1603:1)
    at __webpack_require__ (/Users/qiushi/workspace/tmp/next-webr/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///(sc_server)/./src/app/page.tsx:6:70)
    at (sc_server)/./src/app/page.tsx (/Users/qiushi/workspace/tmp/next-webr/.next/server/app/page.js:481:1)
    at Function.__webpack_require__ (/Users/qiushi/workspace/tmp/next-webr/.next/server/webpack-runtime.js:33:42)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async collectGenerateParams (/Users/qiushi/workspace/tmp/next-webr/node_modules/.pnpm/[email protected]_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/build/utils.js:741:17) {
  type: 'Error',
  page: '/'
}
null
wait  - compiling...
event - compiled successfully in 81 ms (178 modules)
error - node_modules/.pnpm/@[email protected]/node_modules/@r-wasm/webr/dist/webr.mjs (78:8) @ eval
error - Error: Cannot determine runtime environment
    at eval (webpack-internal:///(sc_server)/./node_modules/.pnpm/@[email protected]/node_modules/@r-wasm/webr/dist/webr.mjs:81:11)
    at (sc_server)/./node_modules/.pnpm/@[email protected]/node_modules/@r-wasm/webr/dist/webr.mjs (/Users/qiushi/workspace/tmp/next-webr/.next/server/app/page.js:1603:1)
    at __webpack_require__ (/Users/qiushi/workspace/tmp/next-webr/.next/server/webpack-runtime.js:33:42)
    at eval (webpack-internal:///(sc_server)/./src/app/page.tsx:6:70)
    at (sc_server)/./src/app/page.tsx (/Users/qiushi/workspace/tmp/next-webr/.next/server/app/page.js:481:1)
    at Function.__webpack_require__ (/Users/qiushi/workspace/tmp/next-webr/.next/server/webpack-runtime.js:33:42)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async collectGenerateParams (/Users/qiushi/workspace/tmp/next-webr/node_modules/.pnpm/[email protected]_biqbaboplfbrettd7655fr4n2y/node_modules/next/dist/build/utils.js:741:17) {
  type: 'Error',
  page: '/'
}

Plotting should support PNG and other bitmap output formats

Plotting an image within WASM seems to work okay, at least when using PDF output. The resulting PDF file can be accessed through Emscripten's virtual filesystem via the FS object in JS[1].

Bitmap output (e.g. PNG) does not yet work however, due to missing X11. Probably this can be worked around by taking advantage of Emscripten's ports of libraries such as libpng.

Plotting output commands should be modified to work in this environment, or otherwise some other scheme to convert plotting output from PDF to bitmap should be implemented.


[1] As an example, attached is a sample image of plotting something directly in the browser using the method above. In this example the resulting PDF is injected into the page via an iFrame.

Screenshot 2022-01-25 at 13 18 30

Support screen reader accessibility

Current webR does not seem to support the accessibility mode xterm.js provides. Could you please add the accessibility switch? In In VSCode, it is Alt+F1 followed by Ctrl+E key.

A headache to install and compile the R

I used an Ubuntu distro, then just ran ./configure, and it took hours plus fixing a lot of issues and error messages, but finally, it happened. And then for the "make" step, again, I had a lot of problems, I fixed them, and eventually, I came to this error. I will give up unless you can help me please.

ubuntu@ip-ubuntu@ip-XXX-XXX:~/webR$ sudo make
cd R && make && make install
make[1]: Entering directory '/home/ubuntu/webR/R'
mkdir -p /home/ubuntu/webR/R/build/R-4.1.3/build-stage1/doc
cd /home/ubuntu/webR/R/build/R-4.1.3/build-stage1/doc && \
  touch NEWS NEWS.pdf NEWS.rds NEWS.2.rds NEWS.3.rds
cd /home/ubuntu/webR/R/build/R-4.1.3/build-stage1 && \
  FC="gfortran" \
    CXX="clang  " \
    CC="clang" \
    FC="gfortran" \
    CPPFLAGS="" \
    CFLAGS="" \
    LDFLAGS="" \
    ../configure \
      --prefix="/home/ubuntu/webR/host/R-4.1.3" \
      --with-x=no \
      --with-readline=no \
      --with-jpeglib=no \
      --with-cairo=no \
      --disable-openmp \
      --with-recommended-packages=no \
      --enable-R-profiling=no \
      --with-pcre2 \
      --disable-nls \
      --enable-byte-compiled-packages=no \
      --enable-long-double=no
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
loading site script '../config.site'
checking for pwd... /usr/bin/pwd
checking whether builddir is srcdir... no
checking whether ln -s works... yes
checking for ar... ar
checking for a BSD-compatible install... /usr/bin/install -c
checking for sed... /usr/bin/sed
checking for which... /usr/bin/which
checking for less... /usr/bin/less
checking for gtar... no
checking for gnutar... no
checking for tar... /usr/bin/tar
checking for tex... no
checking for pdftex... no
configure: WARNING: you cannot build PDF versions of the R manuals
checking for pdflatex... no
configure: WARNING: you cannot build PDF versions of vignettes and help pages
checking for makeindex... no
checking for texi2any... no
configure: WARNING: you cannot build info or HTML versions of the R manuals
checking for texi2dvi... no
checking for kpsewhich... no
checking for latex inconsolata package... checking for unzip... no
checking for zip... no
checking for gzip... /usr/bin/gzip
checking for bzip2... /usr/bin/bzip2
checking for firefox... no
checking for mozilla... no
checking for galeon... no
checking for opera... no
checking for xdg-open... no
checking for kfmclient... no
checking for gnome-moz-remote... no
checking for open... no
configure: WARNING: I could not determine a browser
checking for acroread... no
checking for acroread4... no
checking for xdg-open... no
checking for evince... no
checking for xpdf... no
checking for gv... no
checking for gnome-gv... no
checking for ggv... no
checking for okular... no
checking for kpdf... no
checking for open... no
checking for gpdf... no
checking for kghostview... no
configure: WARNING: I could not determine a PDF viewer
checking for working aclocal... missing
checking for working autoconf... missing
checking for working autoheader... missing
checking for bison... no
checking for byacc... no
checking for yacc... no
checking for notangle... false
checking for realpath... /usr/bin/realpath
checking for pkg-config... no
checking for pkgconf... no
checking for gcc... clang
checking whether the C compiler works... no
configure: error: in `/home/ubuntu/webR/R/build/R-4.1.3/build-stage1':
configure: error: C compiler cannot create executables
See `config.log' for more details
make[1]: *** [Makefile:74: /home/ubuntu/webR/R/build/state/r-stage1-configured] Error 77
make[1]: Leaving directory '/home/ubuntu/webR/R'
make: *** [Makefile:13: webr] Error 2
ubuntu@ip-XXX-XXX:~/webR$  

Extend the webR console example - plot output with canvas

I've been working with the webR console example, and plot output still needs to be implemented.
Only the web app supports the plot output, but its source seems hidden.
I've added plot output support functionality with canvas in my webR console.
Expanding the documentation to make this clearer for others who want to do the same would be helpful.
The current example needs only around 15 additional code lines to add plot output support (without collapsible functionality).

I would be happy to submit a PR to add this information to the documentation (console example), but I wanted to check with you first to see if you would be interested in this. Please let me know if this sounds good and if you have any suggestions.

Tidy up R patches

Currently the patches just modify the R source directly. This is a quick and easy way to do things for now but isn't great in the long run.

Instead, the preprocessor should be used in the case where there's ifdefs already in place, and other code tweaks should be done using ifdef __EMSCRIPTEN__ or similar.

build error

Hi,

I got the following error message when tried to build:

docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /build.

`webr` exists on CRAN

There's a package called webr on CRAN (since 2020). Even if r-wasm/webr can't go on CRAN because technical reasons, it is still potentially going to clash and confuse people. You might want to consider renaming this package (and maybe pushing a placeholder package to CRAN with the new name if CRAN are happy with that) before it gets too much use.

Wrap webR as a JupyterLite Kernel

Hi

Loving this demo :-) But I guess it's limited in output terms, eg when generating graphical output?

It would be really interesting if this could be wrapped as a JupyterLite kernel, to provide access to R environments in the browser via JupyterLite WASM powered Jupyter environment. This should (?) then provide support for graphical outputs (assuming the appropriate plotting libraries are available in the R distribution?)

I'm not sure what's involved (I'm an end-user rather than a developer), but there are some demo kernels for other languages in the jupyterlite Github org: https://github.com/orgs/jupyterlite/repositories

sf package

Are there any plans to add the sf package, which is the basis for all geographic analyses in R? It's probably complicated because there are some dependencies. But is it planned? Possible?

Investigate using Atomics as an alternative to our current R REPL patches

Being able to Atomics.wait() on the webR worker thread would be a good thing to have, as it would allow us to simplify or practically remove our patches to R's internal REPL.

I think we might have to be careful with how this is implemented to avoid locking up. For example, we would still like to be able to interact with the Module object in the worker thread to do things like upload & download files to the virtual filesystem, even when the worker thread is wait-ing inside R's REPL for input.

Expose Emscripten's FS tracking delegate

ref: https://emscripten.org/docs/api_reference/Filesystem-API.html#FS.trackingDelegate%5Bcallback%20name%5D

This would let apps, like my janky IDE, watch for filesystem changes in a far more lightweight way. Helpful for knowing when to update the FS display without having to run a regular async job or (in my current case) re-update it after every input call.

I am also planning to work on a "seamless" sync of the web_user tree to local storage that happens in the background, and the evented approach would be far more repsonsive.

I can try to take a poke at this if it sounds like an ok idea to the core maintainers.

Automatically preserve objects wrapped in a JS RObj

As discussed in #38, R objects currently the target of a JS RObj class instance should be automatically preserved or protected, so that the R GC does not reclaim the memory backing the objects.

It looks like it should be possible to use FinalizationRegistry to also automatically release the R object, once the RObj JS class has itself been garbage collected.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource

Hi, thanks for this new initiative. I tried to run update.packages() and got error. Is it a browser issue that I need to fix or a WASM issue?

OS: Win 10
browser: Firefox 110

R CLI Message

Selection: 1
Warning: unable to access index for repository https://cloud.r-project.org/src/contrib:
  download from 'https://cloud.r-project.org/src/contrib/PACKAGES' failed
Warning message:
In download.file(url, destfile = f, quiet = TRUE) :
  URL https://cran.r-project.org/CRAN_mirrors.csv: Download failed. See the Javascript console for further information

Javascript Console Message

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://cloud.r-project.org/src/contrib/PACKAGES.gz. (Reason: CORS request did not succeed). Status code: (null).

screenshot R CLI screen

image

screenshot of JS Console Page

image

Dockerfile build issue

When building the Dockerfile, I got this error: #7 3.703 E: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty-security/main/binary-arm64/Packages 404 Not Found [IP: 91.189.91.38 80]

This is fixed if you change the Dockerfile to run the apt-get update command first (see below).

May be worth changing the order (see below) unless there's a security/other reason not to do this.

RUN apt-get update && \
    echo "deb http://archive.ubuntu.com/ubuntu/ trusty main restricted universe" >> /etc/apt/sources.list && \
    echo "deb http://security.ubuntu.com/ubuntu/ trusty-security main" >> /etc/apt/sources.list && \
    apt-get -y install --no-install-recommends \
        build-essential curl wget git gnutls-bin bash make cmake ca-certificates xz-utils \
        gfortran-4.6 g++-4.6 gcc-4.6 gcc-4.6-plugin-dev gfortran-4.6 llvm-3.3-dev python3 \
		libbz2-dev liblzma-dev libpcre2-dev libcurl4-openssl-dev libpng-dev clang-3.3 zlib1g-dev && \
    rm -rf /var/lib/apt/lists/*

I'm on an M1 Mac, which is likely why this issue occurred.

React + WebR

Hi,

I have the following code:

import React, {useEffect, useState} from 'react';
import { WebR } from '@r-wasm/webr';

import {Objects} from "./Objects";

export default function Demo () {
  const [data, setData] = useState();

  useEffect(() => {
      (async () => {
          console.log("aaaa")
          const webR = new WebR();
          console.log(webR)
          await webR.init();
          console.log("After init")
          let result = await webR.evalR('rnorm(10,5,1)');
          let output = await result.toArray();
          console.log('Result of running `rnorm` from webR: ', output);
      })();
  }, []);

  console.log("data", data)

  return (
    <div>
      Demo
    </div>
  )
}

But when running that via npm start, I get the following errors

localhost/:1 Uncaught (in promise) DOMException: Failed to register a ServiceWorker for scope ('http://localhost:3000/') with script ('http://localhost:3000/webr-serviceworker.js'): The script has an unsupported MIME type ('text/html').

:3000/webr-worker.js:1 Uncaught SyntaxError: Unexpected token '<'

Any idea what I'm missing here?

Building with ASYNCIFY causes "Maximum call stack size exceeded" on Safari

However, Asyncify is required for XHR, for url() and download.file() functionality.

Stability is important, so I have left the demo site on an older version (set things up so that the XHR module is not included and ASYNCIFY is simply left off) until the stack use can be reduced in some way, or Safari gives us more stack.

Discussion: Would it be possible to have a webR standalone version?

Good afternoon.

I have a question about the possibility of using WebR server-side (possibly with WASI):

  • Is there any expectation (or plan) to generate a WebR WebAssembly Standalone version?
    • Or if there have been any attempts to have something like this in the past?

I ask this because it would be extremely interesting to be able to use/test WebR server-side, mainly,
in experimental Serverless platforms (Function as a Service) that use Wasm as a containerization tool.

Thanks in advance.

Build without SharedArrayBuffer

Is it possible to compile webR without using workers? I'm interested in running some R code natively in the browser, but the requirement is that it has to run without SharedArrayBuffers (have to be able to load locally from file:// which is currently not supported yet in Chrome/FF).

If it's a matter of performance (takes a while to download the WASM binary, load dependencies, etc.) that's not an issue, just wondering if it's possible. It does seem the emscripten flags use fetch, is SharedArrayBuffers required due to using fetch's async mode?

Multiple plots at the same time

I have been playing with this lib and react, and wanted to display 2 plots at the same time as this:

<Plot webR={webR} script="plot(data)" />
<Plot webR={webR} script="plot(data2)" />

I managed to do it with a queue that sends the plot prompt and reads until no more canvasExec type messages are received, and I'm wondering if there's an easier way.

Something that would help is a way to identify what prompt is generating the messages received with webR.read(), but I'm not sure if that's actually possible

mgcv errors on demo page

Hello! Excellent work with this project. When running the demo at https://webr.r-wasm.org/latest, I get the following error:

> webr::install("mgcv")
Downloading webR package: lattice
Downloading webR package: nlme
Downloading webR package: Matrix
Downloading webR package: mgcv
> mod <- mgcv::gam(hp ~ s(disp), data=mtcars)

fatal Fortran runtime error(./../../../../src/modules/lapack/dlapack.f:114618): Internal error: RUNTIME_CHECK(catKind.has_value()) failed at extrema.cpp(243)
Aborted()

Is this on a CDN anywhere?

We've just realised that the Numbas extension is fixed to a commit from March, loaded from jsdelivr. It looks like you got rid of the dist folder at some point in April, and the demo page loads a local copy.

It looks like you're just working on the repl at the moment, but the standalone script is useful for us.
Would it be possible to use something like unpkg[https://unpkg.com] to automatically produce the compiled files?

Could GitHub Actions do the compilation?

I'm happy to put together a pull request doing this (or something like this) if you'll accept it.

Is something on iOS blocking the REPL runtime of webR?

Dear Dr. Stagg,

Thanks for making such great contribution to making R more accessible to everyone! I’m a mobile medical professional and novice programmer and researcher who uses my iPad primarily for my computing at and off-work and have been looking for ways to use R more natively on my iPad. Only one developer that I’m aware of has created an app that runs R natively on the iPad (R Analyst) and that app is now outdated (R 3.1.2), does not allow the user to install packages, has difficult to use GUI, and is not being updated nor working well with the newest iOS version. There are other solutions; the UTM Linux emulator (QEMU port) works very well but needs to be sideloaded to run and is significantly slower running through the emulator than running natively on iPad. The iSH Linux emulator running Alpine Linux doesn’t require emulation but runs R very much slower and frequently is broken. I tried the webR demo and even though it is still in its infancy, I love it! It is a little slow on startup but then is fast executing its commands. The simple split screen of having CLI next to half of a screen that can alternate between displaying R’s file system and plots works great! Here is a quick time trial experiment I did comparing the different systems running R:

Task: read in a csv data file containing approximately 2.6 million rows and 38 columns into R with read.csv:

Computer R version. Time to load
iMac x86 i5. 4.1.3. 18 s
webR (iMac). 4.1.3. 33 s
R analyst (M1 iPad Pro). 3.1.2. 38 s
UTM (M1 iPad Pro) 3.5.2. 4 min, 56 s
ish App (M1 iPad Pro) <version 4 currently broken and unable to test>

Unfortunately, the REPL doesn’t seem to work when I try and run webR in any browser on my iPad (e.g. Safari, Chrome, Firefox). webR starts up okay and lists that it loads up all the R dependencies okay and lets me see the R filesystem fine and import files, but never produces the “>” R command-line prompt. I can tell that webR’s execution is not frozen since I can type on the CLI side of the screen but since there is no command-line prompt it just tells me that there for any R command that I type that there isn’t any such command. Is something on iOS blocking the REPL runtime of webR? If so, hopefully it can be fixed? :-)

PS,
I don’t know if you are familiar with the a-shell app developed by Dr. Holzschuch (https://github.com/holzschu/a-Shell); his app can compile and run wasm files. I’ve communicated with him about my interested in getting R to run natively on the iPad and passed along information about your repository to him. I’m going to see if I can get his a-shell app to run your webR .wasm code.

Fyi.

Have a good upcoming week.

Sincerely,
Mike

Problem in `canvasExec`

Found by testing the evaluate package:

x <- seq(-10, 10, length = 30)
y <- x
ff <- function(x,y) { r <- sqrt(x^2 + y^2); 10 * sin(r) / r }
z <- outer(x, y, ff)
z[is.na(z)] <- 1
for (i in 1:3) {
  persp(x, y, z, phi = 30 + i * 10, theta = 30)
}

Causes:

SyntaxError: Invalid left-hand side expression in prefix operation

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.