GithubHelp home page GithubHelp logo

carlo's Introduction

Carlo - headful Node app framework


Carlo provides Node applications with Google Chrome rendering capabilities, communicates with the locally-installed browser instance using the Puppeteer project, and implements a remote call infrastructure for communication between Node and the browser.

image

What can I do?

With Carlo, users can create hybrid applications that use Web stack for rendering and Node for capabilities:

  • For Node applications, the web rendering stack lets users visualize the dynamic state of the app.
  • For Web applications, additional system capabilities are accessible from Node.
  • The application can be bundled into a single executable using pkg.
How does it work?
  • Carlo locates Google Chrome installed locally.
  • Launches Chrome and establishes a connection over the process pipe.
  • Exposes a high-level API for rendering in Chrome with the Node environment.

Usage

Install Carlo

npm

npm i carlo
# yarn add carlo

Carlo requires at least Node v7.6.0.

Example - Display local environment

Save file as example.js

const carlo = require('carlo');

(async () => {
  // Launch the browser.
  const app = await carlo.launch();

  // Terminate Node.js process on app window closing.
  app.on('exit', () => process.exit());

  // Tell carlo where your web files are located.
  app.serveFolder(__dirname);

  // Expose 'env' function in the web environment.
  await app.exposeFunction('env', _ => process.env);

  // Navigate to the main page of your app.
  await app.load('example.html');
})();

Save file as example.html

<script>
async function run() {
  // Call the function that was exposed in Node.
  const data = await env();
  for (const type in data) {
    const div = document.createElement('div');
    div.textContent = `${type}: ${data[type]}`;
    document.body.appendChild(div);
  }
}
</script>
<body onload="run()">

Run your application:

node example.js

Check out systeminfo and terminal examples with richer UI and RPC-based communication between the Web and Node in the examples folder.

API

Check out the API to get familiar with Carlo.

Testing

Carlo uses Puppeteer project for testing. Carlo application and all Carlo windows have corresponding Puppeteer objects exposed for testing. Please refer to the API and the systeminfo project for more details.

Contributing to Carlo

Look at the contributing guide to get an overview of Carlo's development.

FAQ

Q: What was the motivation behind this project when we already have Electron and NW.js? How does this project differ from these platforms, how does it achieve something that is not possible/harder with Electron or NW.js?

  • One of the motivations of this project is to demonstrate how browsers that are installed locally can be used with Node out of the box.
  • Node v8 and Chrome v8 engines are decoupled in Carlo, providing a maintainable model with the ability to independently update underlying components. Carlo gives the user control over bundling and is more about productivity than branding.

Q: Can a Node app using Carlo be packaged as a Desktop app?

The pkg project can be used to package a Node app as a Desktop app. Carlo does not provide branding configurability such as application icons or customizable menus, instead, Carlo focuses on productivity and Web/Node interoperability. Check out the systeminfo example and call pkg package.json to see how it works.

Q: What happens if the user does not have Chrome installed?

Carlo prints an error message when Chrome can not be located.

Q: What is the minimum Chrome version that Carlo supports?

Chrome Stable channel, versions 70.* are supported.

carlo's People

Contributors

0xflotus avatar alexkozy avatar aslushnikov avatar chalker avatar derekroy avatar djyde avatar geekplux avatar iamuchejude avatar joeleinbinder avatar jwharrie avatar kraciasty avatar markozxuu avatar mustafaismail22 avatar p01 avatar paulirish avatar pavelfeldman avatar styfle avatar vsemozhetbyt avatar xat avatar zeke 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  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

carlo's Issues

link tag does not load (I can not load my CSS)

The only way to give styles to our application is as follows:

<header>
      <style>
             body {
                  background: red;
              }
       </style>
</header>

for some strange reason I can not link my css styles through the link tag, It appears on the page but does not load. If someone could explain me why this behavior happens it would be incredible 🙏
screen shot 2018-11-01 at 23 47 08

Have a nice day 😄 ❤️

Allow other browsers

Allow to use other browsers if Chrome is not available. Maybe all features are not available, but for just launching a web page that connect to de Node.js server would be far from enough... :-)

Correct behaviour of chrome on "Relaunch" after version upgrade

I gave a spin to Carlo on mac (with latest chrome version 70+) and it worked perfectly.

I also built the binary systeminfo-app-win.exe for windows and tried running the same. Turned out my windows chrome version wasn't upgraded to 70+ so I went to chrome://settings/help and could see the newer version being upgraded... Now after the completion, I was provided with the "Relaunch" button.

Now I click this relaunch button (while systeminfo-app-win.exe running in the background), chrome restarts but the version is not yet changed as there is a running instance of the chrome through the carlo app systeminfo-app-win.exe.

Question: How should this be handled and should the carlo apps be informed about the relaunch is being triggered?

Added [app.serveOrigin] UnhandledPromiseRejectionWarning

Originally posted by @pavelfeldman in #45 (comment)
Commit: e384301

App.serveOrigin(origin) doesn't seem to work properly.

(node:2452) UnhandledPromiseRejectionWarning: TypeError: app.serveOrigin is not a function
    at carlo.launch.then (D:\Dokumenty\carlo-serve-origin-test\index.js:5:7)
    at process.internalTickCallback (internal/process/next_tick.js:77:7)
(node:2452) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:2452) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Why is this called Carlo?

The web is now full of projects and companies with Italian names, or random Italian words with no apparent meaning or connection to the technology used and service provided. They're also very hard to find through google.it, and I'd say that's really odd in our field.

This also causes excessive hilarity, but also unnecessary awkwardness, in an office environment, especially when Carlo here says that he's using this new fantastic tool called Carlo. Other people started saying "just carlo it up!" and Carlo doesn't really know how to take it sometimes. Is it something positive? Are they making fun of him while using this project as a cover?

Some people also refuse to "install carlo" in their computers, saying that's inhumane.

Can I suggest you rename the project to "Giancarlo Altrepicchi Della Valera born on the 18 of August 1987 in Rome", hoping it's unique enough?

Cheers

please not use "pack" instructions in npm scripts at example project

actually, when I exec yarn pack, it will trigger a "pack" instructions, pack your project code, rather than pkg package.json. I have to say it's a bad experience of developer, I even thought it's a bug of pkg and I search from issues to find a solution at a long time....

By the way, my environment is osx 10.13.6, yarn 1.12.1, node 8.11.3

How to use exposed function from Vue Single File Components?

demo
main.js (expose function homepath)

'use strict';

const carlo = require('carlo');
const os = require('os');
const path = require('path');

(async () => {
    const app = await carlo.launch(
        {
            width: 1000,
            height: 500,
            userDataDir: path.join(os.homedir(), '.carlosysinfo'),
            args: process.env.DEV === 'true' ? ['--auto-open-devtools-for-tabs'] : []
        });
    app.on('exit', () => process.exit());
    app.serveFolder(__dirname + '/dist');
    await app.exposeFunction('homepath', homepath);
    await app.load('index.html');
})();
function homepath() {
    return os.homedir()
}

main.vue (Vue Single File Components)

<template>
  <div id="app">
    {{ path }}
  </div>
</template>

<script>
    export default {
        name: 'app',
        components: {},
        data() {
            return {
                path: ''
            }
        },
        async mounted () {
            // ???? How to 
            this.path = homepath()
        }
    }
</script>

it not work

Uncaught (in promise) ReferenceError: homepath is not defined

Additional Chrome arguments

How can I specify my own arguments for launching Chrome? I want to automatically open DevTools on my first page.

eventEmitter never fired after app.load()

const carlo = require("carlo");
const { EventEmitter } = require("events");

(async () => {
  app = await carlo.launch();
  app.on("exit", () => process.exit());
  app.serveFolder(__dirname);
  await app.exposeFunction("env", _ => process.env);
  const api = new Api();
  api.on("h", console.log); // only log 1
  await app.exposeObject("api", api);
  api.hello(1); // works
  await app.load("index.html");
  api.hello(2); // not working
})();

class Api extends EventEmitter {
  hello(data) {
    this.emit("h", data);
  }
}

Motivation

Hi,

Can you tell something in README (or here) what was the motivation behind this project when we already have Electron and NW.js? How this differs from these platforms, how it helps to achieve something that's not possible/harder with these two?

Thank you

inside Carlo terminal, run another Carlo app

this may not be an issue, but I want to know if it would be supported.
after bring up Carlo terminal, inside the terminal, run another Carlo node app, for now it would not work.

rpc.md example hangs

  1. I've created 3 files described in rpc.md#example.
  2. I've replaced requiring with these lines:
const { rpc } = require('carlo/rpc');
const { rpc_process } = require('carlo/rpc');
  1. I've fixed this typo in family.js (see #62):
-    const ordinal = this.children_.length;
+    const ordinal = this.children.length;
  1. I've run the main.js and, after this output, the script stuck with no errors or progress:
Adding child #0
Adding child #1

Have I done something wrong?

Point carlo to an existing chrome instance?

Hi, could this scenario ever be supported?

  1. I open chrome and go to localhost:3000
  2. A node process on localhost serves some content to chrome and somehow does a handshake (prompts the user for approval?)
  3. The running chrome instance can then be attached to as if carlo had launched it.

Canary is downloaded despite it is already installed

I have dev and canary channels installed on my Windows 7 x64 side by side. If I do not use channel option in carlo.launch(), installed dev is used. If I use channel: 'canary', I get:

Downloading Chromium r599821...
Chromium downloaded to C:\Users\[user]\AppData\Roaming\npm\node_modules\carlo\lib\.local-data\win64-599821

Is it possible to use already installed canary using channel option, without executablePath option?

Is `icon` option platform-restricted?

I've tried both the path and the Buffer variants with Canary on Windows 7 x64 (using an icon from an example folder), but only got the default icon in system taskbar and window titlebar. Does Canary on Windows support this feature?

Specify minimum Chrome version / expose version info

If I'm developing, say, an interesting WebRTC app, I may need some guarantees about what minimum version of the browser I can accept. It would be nice to have some API that lets me specify a minimum, before we try to launch, and which fails if that's not provided.

Alternatively, we could follow something like Node.js's process.versions object that exposes the version, such that users can readily get this information & do with it as they want.

These APIs would go a long way towards taking care of a series of complaints on Twitter that go something like:

Feature fragmentation for desktop apps? Hell no.

https://mobile.twitter.com/roman01la/status/1058029771693678593

This could also be an answer to #17.

examples/systeminfo on Windows 10 doesn't appear to work (no Window is opened)

On Windows 10 (2016 Surface Book) when I run the systeminfo example no window is opened and the example appears to be broken. I'm not sure where it's getting hung up, I tried a bit of debugging but since I am not very familiar with how Carlo works I was not able to get very far.

NOTE: The Terminal example works fine

disable "Hold command-q to quit"

I have to hold command-q to quit Chrome, which is nice if I use Chrome as a browser, but for "native" applications it is kinda weird. Is it possible to override this for Carlo applications?

IPC/RPC "pipe" mechanism

Awesome project! I noticed that this project uses the "pipe" option to launch and communicate with chrome via puppeteer. Is this mechanism documented somewhere, similar to the WebSocket debug interface?

ServiceWorker fetch failed (ERR_NAME_NOT_RESOLVED)

Installing serviceworker using following code fails with ERR_NAME_NOT_RESOLVED
I am able to load sw.js file using ajax and via script tag just fine

navigator.serviceWorker.register('sw.js').then(function(registration) {
  console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
  console.log('ServiceWorker registration failed: ', err);
});

The result is

An unknown error occurred when fetching the script.
Failed to load resource: net::ERR_NAME_NOT_RESOLVED
ServiceWorker registration failed:  TypeError: Failed to register a ServiceWorker: An unknown error occurred when fetching the script.

The given example failed to run. (Protocol error - Browser.grantPermissions.)

Hello!
I try to run the given example systeminfo but failed. Here's the error throwed:

Error: Protocol error (Browser.grantPermissions): 'Browser.grantPermissions' wasn't found
    at Promise (/media/sar/extlinux/codes/SarKerson/to_learn/carlo/examples/systeminfo/node_modules/puppeteer-core/lib/Connection.js:73:56)
    at new Promise (<anonymous>)
    at Connection.send (/media/sar/extlinux/codes/SarKerson/to_learn/carlo/examples/systeminfo/node_modules/puppeteer-core/lib/Connection.js:72:12)
    at BrowserContext.overridePermissions (/media/sar/extlinux/codes/SarKerson/to_learn/carlo/examples/systeminfo/node_modules/puppeteer-core/lib/Browser.js:314:28)
    at BrowserContext.<anonymous> (/media/sar/extlinux/codes/SarKerson/to_learn/carlo/examples/systeminfo/node_modules/puppeteer-core/lib/helper.js:145:23)
    at Object.launch (/media/sar/extlinux/codes/SarKerson/to_learn/carlo/examples/systeminfo/node_modules/carlo/lib/carlo.js:202:13)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

I can not figure out what's wrong here.
My environment infos:

  • Ubuntu 18.04.1 LTS
  • Chome: Version 69.0.3497.100 (Official Build) (64-bit)
  • node: v8.10.0

Solved: How do you test?

Hey there,
I am interested in working with carlo instead of electron, however electron comes with a testing framework named spectron. Can you provide an example of how to test carlo? It would also be great to have an example of debugging in Code.

install React DevTools for debug?

I've been trying to get React Devtools installed in the Carlo-launched Chrome instance without success. I have a fledgling React app running OK but would like to be able to use the React tools for debugging. Each time I try to install the extension it tells me 'Installation is not enabled'. The Extensions menu item is also greyed out.

This sounds like a permissions problem, so I tried adding --enable-remote-extensions in the args to Carlo.launch() but this didn't work. I also tried adding --load-extension=<path to react devtools> (from my default Chrome profile) but that didn't work either.

Anyone got this to work? Is there something inherent with Carlo/Puppeteer usage that make it impossible to install extensions?

Using Carlo 0.9.11, MacOS High Sierra, Node 10.8.0

Allow configurable puppeteer chrome locations

I want to be able to use this on Lambda / etc and point to a chrome installed in node_modules/*. Is there a way to have the puppeteer instance options be configurable instead of magically having a chrome instance found or the user providing the puppeteer browser themselves?

[Problem] Domain name

It can't resolve the domain name "domain" with the URL "https://domain/${uri}".
Could you please tell me how I can fix it ?

$ node sample.js
(node:16568) UnhandledPromiseRejectionWarning: Error: net::ERR_NAME_NOT_RESOLVED at https://domain/example.html
    at navigate (D:\Projects\Demo\carlo\node_modules\puppeteer-core\lib\FrameManager.js:103:37)
    at process._tickCallback (internal/process/next_tick.js:68:7)
  -- ASYNC --
    at Frame.<anonymous> (D:\Projects\Demo\carlo\node_modules\puppeteer-core\lib\helper.js:144:27)
    at Page.goto (D:\Projects\Demo\carlo\node_modules\puppeteer-core\lib\Page.js:579:49)
    at Page.<anonymous> (D:\Projects\Demo\carlo\node_modules\puppeteer-core\lib\helper.js:145:23)
    at App.load (D:\Projects\Demo\carlo\node_modules\carlo\lib\carlo.js:95:23)
    at D:\Projects\Demo\carlo\sample.js:7:12
    at process._tickCallback (internal/process/next_tick.js:68:7)

Add evaluateOnNewDocument method to API

I need some data to be synchronously available in app from its start. E.g., I would like to fetch settings from my own storage and inject them to the app by evaluating on new document something like:

evaluateOnNewDocument(`Settings = { a: 1, b: 2, c: 3}`);

I can create file with settings somewhere in temporary directory and then serve it but it looks much less convenient.

window.open behavior

How is window.open handled in the context of Carlo/Puppeteer? In Chrom(ium), a new tab would be created. But since Puppeteer/Carlo creates the window, it's unclear what UX would exist.

In the context of Carlo's use case, it seems the best thing to do would be to leave the behavior up to the Carlo developer via some sort of callback and override. For my use case, I'd like window.open calls to create another iframe, rather than a new window. Is this possible to do given Puppeteer's API support?

Any tips or tricks on serving a react bundle?

Here's my initial attempt at serving a react bundle.

(async () => {
  // Launch the browser.
  const app = await carlo.launch();

  // Tell carlo where your web files are located.
  app.serveFolder(path.join(__dirname, 'build'));

  // Expose 'env' function in the web environment.
  await app.exposeFunction('env', _ => process.env);

  // Navigate to the main page of your app.
  await app.load('index.html');
})();

It's not going so hot. Any tips?
image

eventEmitter never fired after executing app.load()

const carlo = require("carlo");
const { EventEmitter } = require("events");

(async () => {
  app = await carlo.launch();
  app.on("exit", () => process.exit());
  app.serveFolder(__dirname);
  await app.exposeFunction("env", _ => process.env);
  const api = new Api();
  api.on("h", console.log); // only log 1
  await app.exposeObject("api", api);
  api.hello(1); //  works
  await app.load("index.html");
  api.hello(2); //  not working!!
})();

class Api extends EventEmitter {
  hello(data) {
    this.emit("h", data);
  }
}

eventEmitter not working after app.load()

const carlo = require("carlo");
const { EventEmitter } = require("events");

(async () => {
  app = await carlo.launch();
  app.on("exit", () => process.exit());
  app.serveFolder(__dirname);
  await app.exposeFunction("env", _ => process.env);
  const api = new Api();
  api.on("h", console.log); // only log 1
  await app.exposeObject("api", api);
  api.hello(1); // works
  await app.load("index.html");
  api.hello(2); // not working
})();

class Api extends EventEmitter {
  hello(data) {
    this.emit("h", data);
  }
}


Is an iframe to an internal http site possible?

Hi,

Is an iframe to an internal http site possible? I would like to link to a development / local site starting http://

I tried this based upon the Terminal example but got an error. Can it be overridden / turned-off?

<html>

<title>Carlo Terminal</title>
<style>
body {
  width: 100%;
  height: 100%;
  /* display: flex; */
  /* flex: auto; */
}
</style>

<body>
  <iframe src="http://host-entry:8080/ui/"></iframe>
</body>
</html>

Mixed Content: The page at 'https://domain/index.html' was loaded over HTTPS, but requested an insecure resource 'http://host-entry:8080/ui/'. This request has been blocked; the content must be served over HTTPS.

Way to customize carlo.serveFolder more

Imagine I have following local directories:
/frontend/app.html
/frontend/app.json
/frontend/moduleA
/override/app.json
/override/moduleB

/frontend/ folder is located inside another package and I can not just put my overrides there. Instead I'd like to call carlo.serveFolders(['/override', '/frontend']) method, later carlo should lookup file in override folder first and if it does not exist fallback to frontend folder. As a result my app can access following files:
/frontend/app.html
/override/app.json
/frontend/moduleA
/frontend/moduleB

I think it is possible to solve this problem for modules with prefixes but it looks like there is no easy way to make possible per file override, e.g. app.json.

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.