Giter VIP home page Giter VIP logo

harmon's Introduction

harmon

A middleware component for node-http-proxy using trumpet to parse and transform the response from the proxied server.

build status

npmico

harmon

install

$ npm install harmon

examples

Overview


In this example the HTML below is returned from the remote server and parsed:

<html>
	<head></head>
	<body>
		<div class="a">Nodejitsu Http Proxy</div>
		<div class="b">&amp; Frames</div>
	</body>
</html>

The following line is removed:

<div class="b">&amp; Frames</div> 

And is replaced with:

<div>+ Trumpet</div>

Run It!


from your project root:

$ cd node_modules/harmon/examples
$ node simple.js

Browse to localhost:8000 and you should see:

simple output

Code

var http = require('http'),
    connect = require('connect'),
    httpProxy = require('http-proxy');


    var selects = [];
    var simpleselect = {};

        simpleselect.query = '.b';
        simpleselect.func = function (node) {
           node.createWriteStream().end('<div>+ Trumpet</div>');
        }

        selects.push(simpleselect);

        //
        // Basic Connect App
        //
        var app = connect();

        var proxy = httpProxy.createProxyServer({
              target: 'http://localhost:9000'
        })

        //Additional true parameter can be used to ignore js and css files. 
        //app.use(require('../')([], selects, true));

        app.use(require('../')([], selects));

        app.use(function (req, res) {
                   proxy.web(req, res);
                });

        http.createServer(app).listen(8000);

        http.createServer(function (req, res) {
             res.writeHead(200, { 'Content-Type': 'text/html' });
               res.write('<html><head></head><body><div class="a">Nodejitsu Http Proxy</div><div class="b">&amp; Frames</div></body></html>');
                 res.end();
        }).listen(9000);

or See how images could be rotated.

$ cd node_modules/harmon/examples
$ node rotate.js

See trumpet for the types of queries and functions you can use.

Contributors

fabiosantoscode

no9

smazurov

sergiator

harmon's People

Contributors

arcanoxdragon avatar blakmatrix avatar brettz9 avatar ecolinet avatar fabiosantoscode avatar gnrlbzik avatar guypaddock avatar jbarna avatar jhorlin avatar leveneg avatar lonerifle avatar mikehedman avatar no9 avatar peebles avatar smazurov 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

harmon's Issues

How to use in non-html?

I know how to modify proxy response is should use harmon, but I should modify non-html.
it based line. how to edit that?

doge.js is redirecting to original site with no injection

I tried running dodge.js locally (see code below). But when I navigate to localhost:8000 I am redirected from my proxy to https://nodejs.org/en/ and there is no image injection. Did something change that would break this code or am I doing something wrong?

var http = require('http'),
    https = require('https'),
    connect = require('connect'),
    httpProxy = require('http-proxy');
    harmon = require('harmon');

var selects = [];
var simpleselect = {};

//<img id="logo" src="/images/logo.svg" alt="node.js">
simpleselect.query = 'img';
simpleselect.func = function (node) {

    //Create a read/write stream wit the outer option 
    //so we get the full tag and we can replace it
    var stm = node.createStream({ "outer" : true });

    //variable to hold all the info from the data events
    var tag = '';

    //collect all the data in the stream
    stm.on('data', function (data) {
        tag += data;
    });

    //When the read side of the stream has ended..
    stm.on('end', function () {

        //Print out the tag you can also parse it or regex if you want
        process.stdout.write('tag:   ' + tag + '\n');
        process.stdout.write('end:   ' + node.name + '\n');

        //Now on the write side of the stream write some data using .end()
        //N.B. if end isn't called it will just hang.  
        stm.end('<img id="logo" src="http://i.imgur.com/LKShxfc.gif" alt="node.js">');

    });
}

selects.push(simpleselect);

//
// Basic Connect App
//
var app = connect();

var proxy = httpProxy.createProxyServer({
    target: 'https://nodejs.org',
    agent  : https.globalAgent, 
    headers: { host: 'nodejs.org' }
})


app.use(harmon([], selects, true));

app.use(
    function (req, res) {
        proxy.web(req, res);
    }
);

http.createServer(app).listen(8000);

got error on zlib

my code

const http = require('http')
const https = require('https')
const httpProxy = require('http-proxy')
const url = require('url')
const connect = require('connect')
const harmon = require('harmon')

selects = []
simpleselect = {}
simpleselect1 = {}

simpleselect.query = '[src*="jquery.min"]'
simpleselect1.query = '[src*="jquery-ui.min"]'


simpleselect.func = function(node) {
	node.setAttribute('src', 'http://libs.baidu.com/jquery/1.5.1/jquery.min.js')
}
simpleselect1.func = function(node) {
	node.setAttribute('src', 'http://libs.baidu.com/jqueryui/1.8.10/jquery-ui.min.js')
}

selects.push(simpleselect)
selects.push(simpleselect1)


finalUrl = 'https://evemaps.dotlan.net/'
parsedUrl = url.parse(finalUrl)

proxy = httpProxy.createProxyServer({
	target: finalUrl,
	agent: https.globalAgent,
	headers: {
		host: parsedUrl.hostname
	},
	prependPath: false,
	hostRewrite: finalUrl.host,
	protocolRewrite: parsedUrl.protocol,
	xfwd: true
})

const app = connect()

app.use(harmon([],selects))
app.use(function(req,res){
	proxy.web(req,res)
})

http.createServer(app).listen(8000)

error got

Error: unexpected end of file
    at Zlib.zlibOnError [as onerror] (zlib.js:170:17)
Emitted 'error' event on Gunzip instance at:
    at Zlib.zlibOnError [as onerror] (zlib.js:173:8) {
  errno: -5,
  code: 'Z_BUF_ERROR'

Documentation - createWriteStream() in example versus the Trumpet link

It looks like your README links to a custom fork of Trumpet (https://github.com/No9/node-trumpet) that has an older API than the one in the example you provide.

Namely, createWriteStream() doesn't exist at all in your custom fork but does exist in the current version of Trumpet. This definitely led me to some head scratching as to why your example code works but I couldn't find any of the functions in the code you link to.

If I look at upstream Trumpet (https://github.com/substack/node-trumpet), all is clear again.

Example code does not work

Hi,

Was trying out your code today and I can't get the examples to work. I get the following error message:

mydir/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js:103
    var proxyReq = (options.target.protocol === 'https:' ? https : http).reque
                                  ^
TypeError: Cannot read property 'protocol' of undefined
    at Array.stream [as 3] (mydir/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js:103:35)
    at ProxyServer.<anonymous> (mydir/node_modules/http-proxy/lib/http-proxy/index.js:83:21)
    at Server.closure (mydir/node_modules/http-proxy/lib/http-proxy/index.js:125:43)
    at Server.EventEmitter.emit (events.js:98:17)
    at HTTPParser.parser.onIncoming (http.js:2108:12)
    at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:121:23)
    at Socket.socket.ondata (http.js:1966:22)
    at TCP.onread (net.js:525:27)

A quick search on Google leads me to http-party/node-http-proxy#499 and http-party/node-http-proxy#510 which seem to indicate that some API change has occurred.

rotate.js creates malformed HTML

The example replaces the <head> tag with a style tag. I am guessing that the outer parameter should be false, or the tag should be included in the content that's added.

Response can't change size

When a server sends a response with a content-length header set and we change the content, the content-length header will go through unchanged, even if the HTML string's length is changed.

Added a failing test in 9fee09f

I think this can be fixed by monkey-patching the writeHead function and deleting the content-length header in time. This should allow us to change the content at will, but not buffer everything up.

Use cases for changing response size are adding elements. For example, adding <script> tags with live-reload features or settings (window.DEBUG = true). Disclaimer: I use harmon in a development proxy.

prepareRequestSelectors

  if (_reqSelectors.length) {
    prepareRequestSelectors(req, res);
  }

there are no example for prepareRequestSelectors, what does it do?

Doesn't work nowadays? Examples and tests fail

This project looks exactly like what I was looking for, but I got nothing to work.

It seems to be a general issue, with CircleCI on a fork of the current master failing as well: https://circleci.com/gh/motin/harmon/1

npm test


> [email protected] test /home/ubuntu/harmon
> node test/host.js

BODY: <html><head></head><body><div class="a">Nodejitsu Http Proxy</div><div class="b">&amp; Frames</div></body></html>

assert.js:89
  throw new assert.AssertionError({
  ^
AssertionError: '<html><head></head><body><div>Harmon Middleware</div><div>+ Trumpet</div></body></html>' == '<html><head></head><body><div class="a">Nodejitsu Http Proxy</div><div class="b">&amp; Frames</div></body></html>'
    at IncomingMessage.<anonymous> (/home/ubuntu/harmon/test/host.js:79:14)
    at emitNone (events.js:72:20)
    at IncomingMessage.emit (events.js:166:7)
    at endReadableNT (_stream_readable.js:905:12)
    at nextTickCallbackWith2Args (node.js:441:9)
    at process._tickCallback (node.js:355:17)
npm ERR! Test failed.  See above for more details.

npm test returned exit code 1

I tried node versions 8, 4 and 0.10 to no avail.

Received only a part of the data

Hi.
If in the script simple.js change target for example to the http://dou.ua/test.
We will receive only a part of the html page data.
How to use it correctly?
Thx

Connection hangs for very generic selectors

Hi, I'm building a forward proxy using node-http-proxy and I'm employing harmon to do some transformations.

I'm experiencing some issues with the connection kept hanging for selectors like div or *, eventually leading to an net::ERR_INCOMPLETE_CHUNKED_ENCODING in Chrome. Probably the issue lies in the number of elements to scan.

A website good for reproducing this is https://weather.com, or http://www.aliexpress.com for plain http.

Any ideas on why this is happening? Thanks

write() getting called before writeHead() on gzip'd content causes failures

It seems like harmon depends on the assumption that writeHead() gets called before write()

For whatever reason, there are situations where res.writeHead() doesn't get called until after one (or all!) res.write() calls.

So if the content is gzipped, then harmon doesn't set res.isGziped until after some of the data has already been written to the output, and either the gzip'd content gets sent to the output, or gzip fails because it gets only part of the data.

A possible solution would be to check content encoding on the first write() call, and store that.

Gzip support?

Hi, I am using this for a site that returns gzip. I think that this was causing problems for Harmon. I have JUST realized that in order to solve this easily, we could modify the headers to show that we do not support gzip, and the server would respond appropriately. What do you think of a change like this?

I will be writing a snippet to accomplish this using http-proxy's proxyReq event, would you like a PR update to your README to teach others this strategy?

Cheers,
D

PS Thanks for Harmon, it is pretty cool & useful :)

PPS Here is a change to host headers which allows another site to show at e.g. localhost:8888

The key line is

proxyReq.setHeader('host', dest.host)
var http = require('http')
var url = require('url')

var connect = require('connect')
var httpProxy = require('http-proxy')

var PORT = process.env.PORT || 8888

//
// properly configure the proxy to pretend that the request came from the
// original host.
//
var proxy = httpProxy.createProxyServer()
var dest = url.parse('http://dtrejo.com')
var proxyOptions = {
  target: dest.href
, hostRewrite: false // not super sure what this does.
}
proxy.on('proxyReq', function(proxyReq, req, res, options) {
  proxyReq.setHeader('host', dest.host)
  // console.log('proxyReq', proxyReq)
  // proxyReq.setHeader('X-Special-Proxy-Header', 'foobar')
})

// .... the rest of the examples/simple.js file ...

Issue with simple external proxing with Harmon

Hi I am trying to make a simple external proxy POC however am having issues with Harmon returning strange data.

I have written the following code and simplified it to illustrate what is happening. Ignore the fact that I am passing harmon an empty action as it does the same thing with a valid action (the action does work if it is passed in) however I am omitting it for the sake of simplicity in this example. More details in comments for each server below.

var http = require('http'),
httpProxy = require('http-proxy');

/* If a client connects to this server plan text web pages will work fine however most web sites will hang half way through loading, images won't load and will generally display wrong */
httpProxy.createServer(
require('harmon')([], []), 9000, 'localhost'
).listen(8000);

/* If client connects directly to this server node http proxy will proxy as expected. - so node http proxy must be working as expected. */
httpProxy.createServer(function (req, res, proxy) {
proxy.proxyRequest(req, res, {
host: req.headers.host,
port: 80
});
}).listen(9000);

Basically the page will load however the data will be distorted and sometimes the node server will hang.

I have read through the examples and looked at the source but can't work out what is happening if anybody could enlighten me that would be great!

Select a tag, keep only inner content

Hello friends,

Iā€™m rather new to the shiny stream adventure and Iā€™m having a hard time to wrap my head around the following:

I would like to select a tag and only keep its inner content, like:

<div class="outer"><div class="inner"></div></div>

becomes

<div class="inner"></div>

Any help would be greatly appreciated :)

Content encoding chunked / mime types

It doesn't seem like Harmon works correctly when the proxy is serving up image content and the server is sending back the data using "chunked" content-encoding.

I would guess that the logic that determines whether or not to act on HTML only should base the decision on the mime type rather than the file extension.

js/css filter with query params

This code:
if ((lowercaseUrl.indexOf('.js', req.url.length - 3) !== -1) || (lowercaseUrl.indexOf('.css', req.url.length - 4) !== -1)) {
won't work if there are query params after the js/css path.
Am I missing something?

Do you filter if the response was actually html?

Hello,

I was trying harmon and the example works very well. I'm pretty new to this but I then tried it with a real server and I get a oob exception from trumpet (Out of buffer). I am not sure, but I think that it's because harmon does not check if the response is actually html and will run trumpet on any proxied file. But I am very new to this so maybe it's not that.

It probably should check for Content-Type: text/html in headers or simply can you tell me where I'm wrong.

Here is the exception:

/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/buffers/index.js:169
    if (i < 0 || i >= this.length) throw new Error('oob');
                                   ^
Error: oob
    at Buffers.pos (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/buffers/index.js:169:42)
    at Buffers.get (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/buffers/index.js:183:20)
    at makeFn (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/lib/tokenize.js:57:26)
    at SAXStream.parser.(anonymous function) (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/lib/tokenize.js:26:50)
    at SAXStream.EventEmitter.emit (events.js:95:17)
    at Object.me._parser.(anonymous function) [as onopentag] (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/sax/lib/sax.js:245:15)
    at emit (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/sax/lib/sax.js:615:33)
    at emitNode (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/sax/lib/sax.js:620:3)
    at openTag (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/sax/lib/sax.js:801:3)
    at Object.write (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/sax/lib/sax.js:1206:11)
    at SAXStream.write (/Users/AoDev/git-repos/ssp-proxy/node_modules/harmon/node_modules/trumpet/node_modules/sax/lib/sax.js:227:16)

am i doing this right?

i want to transform the </body> tag

var https = require('https'),
    http  = require('http'),
    util  = require('util'),
    path  = require('path'),
    fs    = require('fs'),
    colors = require('colors'),
    httpProxy = require('http-proxy'),
    connect = require('connect');

var selects = [];
var simpleselect = {};

simpleselect.query = '</body>';
simpleselect.func = function (node) {
node.createWriteStream().end('<center>MY INSERTED TEXT</center></body>');}

selects.push(simpleselect);

var app = connect();
var proxy = httpProxy.createProxyServer({target: 'https://DOMAIN.COM', agent  : https.globalAgent, headers:{ host: 'DOMAIN.COM' }}).listen(8011);

app.use(require('harmon')([], selects));
app.use(function(req, res) {
  proxy.web(req, res);
});

util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8011'.yellow);

it serves up the proxy but there is no html transformation i suspect the simpleselect.query is wrong

Update Trumpet to 1.6.5

In 1.6.4 @substack made updates to the parsing engine and among other things fixed substack/node-trumpet#30 which is the issue I'm running into while parsing a remote site (which I have no control over)

Can we update trumpet to latest version? A quick version bump broke the test, it seems like the output ends after first selector now.

How to use Harmon in SSL proxy server?

The code below is my sample code, where/how can I add the harmon code to modify the response? like this "require('harmon')([], actions),", where to add?

var proxySSL = httpProxy.createProxyServer({
  xfwd: true,
  ssl: httpsOpts,
  target: 'https://c45.target.com/profiles',
  secure: false
}
);
https.createServer(httpsOpts,
    function (req,res) {
        proxySSL.web(req, res);
    }
).listen(443);

Variable Targets

I'm trying to do something like this to setup the following.

But I'm getting a 404 on the /proxy/about request when changing lines 47 - 60 in doge.js:

var proxy = httpProxy.createProxyServer({})

app.use(require('../')([], selects, true));

app.use("/proxy", 
  function (req, res) {
    proxy.web(req, res, {
      target: 'https://nodejs.org/en'+req.url,
      agent: https.globalAgent, 
      headers: { host: 'nodejs.org' }
  });
});

Should I be thinking about this differently?

Content Length not deleted on HTML rewrite if HTML Only

      /* Sniff out the content-type header.
       * If the response is HTML, we're safe to modify it.
       */
      if (!_htmlOnly && res.isHtml()) {
        res.removeHeader('Content-Length');
        delete headers['content-length'];
      }

If I have an HTML response, but I'm targeted to HTML only, the content length won't get overwritten, which leads to
"Error: Transferred a partial file" errors on the response.

Why is this guarded by !_htmlOnly?

Example doge.js <img> tag replacing not working on nodejs.org from 301 Redirect

The environment is running from http://localhost:8000.

doge.js is not working as expected from a 301 redirect. Harmon intercepts the response body of the 301 redirect instead of expected the response body from nodejs.org. This means the logo <img .. > image from nodejs.org is never replaced with dog picture from the example.

Is there a setting to node-http-proxy or in harmon to do the replacing on the response body from the response body of the end resulting page, for this example nodejs.org?

gzip content length issue

I'm running a proxy whose target uses gzip content-encoding and content-length headers. The proxy works fine until I introduce the harmon middleware without even any selectors:

app.use(harmon([], []))

Many of the proxy responses are getting truncated. I presume this is because the content is being decompressed (gunzip'd) but the content length is not being changed.

I've used harmon on proxy targets without gzip encoding or content lengths and have not had any issues. What do you recommend for a workaround?

Request hangs

Sorry that this isn't really an issue with Harmon. Its an issue with implementing it correctly. I've looked through some other issues and all the documentation but I can't figure it out -

var http = require('http'),
    connect = require('connect'),
    httpProxy = require('http-proxy');

var through = require('through');
//require("./static");

var selects = [{
    query: "head",
    func: function(elem) {
        var s = elem.createStream({outer : true });
        s.pipe(through(function(data) {
            console.log(3);
            if(data.toString().indexOf('</head>') > -1) {
                console.log(1);
                s.write('<script>alert("hello");</script>');
                console.log(2);
            }
            s.write(data);
        }));
    }
}];

var app = connect();

var proxy = httpProxy.createProxyServer({
    target: "ip_address_here"
});

proxy.on("error", function(e) {
    console.log(e);
});

app.use(require("harmon")([], selects, true));

app.use(
    function(req, res) {
        proxy.web(req, res);
    }
);

http.createServer(app).listen(80);

when I go to it in the web browser the alert pops up and then the page hangs.
Thanks

Proxy a m3u8 file

hi, id like to know if can i proxy a m3u8 live stream file with this?
if so, could give me a example of the code?

Thanks!

Dealing with bad html

Depending on the source markup there are times when the trumpet pipeline will not finish processing even though the response has been written entirely (see below). It would be great if there was a way to set a timeout and cancel the pipeline somehow. For example:

var http = require('http'),
    connect = require('connect'),
    through = require('through'),
    httpProxy = require('http-proxy');


var selects = [];
var simpleselect = {};

simpleselect.query = 'body';
simpleselect.func = function (node) {
    var ts = node.createStream();
    ts.pipe(through(null, function(){
        console.log("Injecting");
        this.queue("FOOBAR");
        this.queue(null);
    })).pipe(ts);

    ts.on('end', function() {
     console.log("BODY");
    });
}

selects.push(simpleselect);

var app = connect();

var proxy = httpProxy.createProxyServer({
    target: 'http://localhost:8002'
})

app.use(require('harmon')([], selects, true));

app.use(
  function (req, res) {
    proxy.web(req, res);
  }
);

http.createServer(app).listen(8001);

http.createServer(function(req, res) {
    res.setHeader('Content-Type', 'text/html; charset=UTF-8');
    res.setHeader('Transfer-Encoding', 'chunked');
    res.write('<html><head><title>foo</title></head><body><h1>Foo</h1>');
    /* With this line the request hangs
    res.write('<div><svg><defs><circle /></defs><circle /></svg></div>');
    */
    res.end('</body></html>');
}).listen(8002);

If you run the example and curl http://localhost:8001 it works fine. But if you uncomment the commented line and try again it hangs. I've tracked down the specific underlying reason for this in the html-select module, and I'll raise this specific issue with them. But what I'm interested in is shielding myself from these kinds of bugs (which may be triggered by broken proxied web pages) by doing setTimeout and aborting the trumpet pipeline somehow. I've tried various combinations of res.end() and tr.end() but the stream from tr.createStream just never seems to die. I would appreciate if you could show me a way, perhaps even a totally different approach.

Replacing the value inside an element

Hi,
Thanks for your wonderful lib.
I'm struggling with replacing the value inside a div.

Here's an example of what I'm facing:

<div>hello</div>
<div>world</div>

The div's don't have id or class. I just need to replace the hello with hi after selecting the div based on the content inside the element. I need to achieve this somehow. I would appreciate any help.

Thanks

No selector callbacks being invoked for certain sites

I apologize if this is actually an issue with the underlying trumpet module but this seems like the right place to start.

For some sites, everything works perfectly. For others, no selector matching works. Digging into the code, I can see that the selectors are being registered with tr.selectAll (one for head and one for body), but the callbacks are being invoked.

The only obvious difference between the two sites is that one is gzipped and one is not.

To reproduce:

  • Clone the simple-harmon-example repo I set up to demonstrate the issue
  • npm install dependencies
  • Test a working site by doing the following:
    • Run node index.js https://www.intellimize.com
    • Load https://localhost:8000
    • See the following console output, indicating that the selectors are matched and callbacks invoked:
in head
in body
  • Next test a site that does not work - you will not see any console output:
node index.js https://www.blackriflecoffee.com

Any help would be greatly appreciated.

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.