Giter VIP home page Giter VIP logo

feathers-hooks-rediscache's Introduction

About me

Tech Lead/Software engineer at Axa REV. I am currently working on Legal Technologies and Computational Law. My background reflects my interests and curiosity. I started in mechanical automation, then obtained a Master of Arts in English Linguistics, American Literature, Russian and Musicology, while working as a developer.

feathers-hooks-rediscache's People

Contributors

corymsmith avatar greenkeeper[bot] avatar idealley avatar jamesvillarrubia avatar juckerf avatar oppodeldoc avatar sassninja avatar smccamley 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

Watchers

 avatar  avatar

feathers-hooks-rediscache's Issues

Server crash - Cannot read property 'expiresOn' of undefined

Steps to reproduce

If my service return a "Feather Error" instead of "throwing" it, the redis cache will try to save it in redis. The next time I access the same route, the server crash (need to restart the server !) with this error:

/node_modules/feathers-hooks-rediscache/lib/library.min.js:1
(function (exports, require, module, __filename, __dirname) { !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("library",[],t):"object"==typeof exports?exports.library=t():e.library=t()}(global,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=ty

TypeError: Cannot read property 'expiresOn' of undefined
    at Command.a.get [as callback] (/home/node_modules/feathers-hooks-rediscache/lib/library.min.js:1:2398)
    at normal_reply (/home/node_modules/redis/index.js:726:21)
    at RedisClient.return_reply (/home/node_modules/redis/index.js:824:9)
    at JavascriptRedisParser.returnReply (/home/node_modules/redis/index.js:192:18)
    at JavascriptRedisParser.execute (/home/node_modules/redis-parser/lib/parser.js:574:12)
    at Socket.<anonymous> (/home/node_modules/redis/index.js:274:27)
    at emitOne (events.js:116:13)
    at Socket.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at Socket.Readable.push (_stream_readable.js:208:10)
    at TCP.onread (net.js:601:20)

Expected behavior

Server should be able to continue to process other requests.

Actual behavior

I need to restart the server and clear the cache to be able to test again.

System configuration

Module versions

"@feathersjs/configuration": "^2.0.6",
"@feathersjs/errors": "^3.3.6",
"@feathersjs/express": "^1.3.1",
"@feathersjs/feathers": "^3.3.1",

"feathers-hooks-common": "^4.20.7",
"feathers-hooks-rediscache": "^1.1.5",

Handling lost connections to Redis server

I am trying to get these hooks not to crash my application when the connection to the Redis server is lost. According to the docs, I would have to set "no_ready_check": true, to stop the client from checking the status of the connection. This does not work as expected. @idealley, Is there a workaround?

Thanks

questions on v1.1.0

First, thanks so much for this amazing repo~!

I have been using this repo for a couple of weeks, and it works great. While, when I upgraded to v1.1.0 and ran my tests (with NODE_ENV=test), I got extra [redis] info, like this:

image

Apparently although on github the source code shows the logs should only be printed when NODE_ENV !==test (https://github.com/idealley/feathers-hooks-rediscache/blob/master/src/hooks/redis.js#L31-L34):

          if (process.env.NODE_ENV !== 'test') {
            console.log(`${chalk.cyan('[redis]')} returning cached value for ${chalk.green(path)}.`);
            console.log(`Expires on ${duration}.`);
          }

But in fact in the real code from npm it is like this:

          if (true) {
            console.log(_chalk2.default.cyan('[redis]') + ' returning cached value for ' + _chalk2.default.green(path) + '.');
            console.log('Expires on ' + duration + '.');
          }

Is this a temporary version for npm to do some debug or something?

Nested route with optional params not being indexed properly

Hi there! Thanks for this great project, we're really hoping we can use it as it fits our needs perfectly at the moment, but we've found a weird issue that I'm hoping has an easy solution.

Steps to reproduce

  1. Create a service in Feathers with a route like /posts/:site?, this is i.e. to handle posts across several sites that may have identical IDs, slugs, etc. So a posts.service.js would have this line:
// Initialize our service with any options it requires
app.use('/posts/:something?', createService(options)) 
  1. Configure this project as per the docs.
  2. Start up the service and go to http://localhost:3030/posts/foo
  3. Check out http://localhost:3030/cache/index to see how things are indexed

Expected behavior

I'd expect to see a response like:

[
    "group-foo",
    "posts/foo"
]

Actual behavior

Instead I see:

[
    "group-posts/:something?",
    "posts/:something?"
]

This means that any of our other site routes (posts/bar, posts/baz) will all be cached under the same key. Nested routes like posts/foo/1234 are indexed as:

[
    "group-posts/:something?",
    "posts/:something?/1234"
]

So nested routes are working, it's just that the dynamic part of the route seems to be broken.

System configuration

Here's the config/default.json file:

{
  "host": "localhost",
  "port": 3030,
  "public": "../public/",
  "paginate": {
    "default": 10,
    "max": 50
  },
  "redisCache" : {
    "defaultDuration": 60,
    "parseNestedRoutes": true,
    "removePathFromCacheKey": false
  },
  "redis" : {
    "host": "localhost",
    "port": 6379
  }
}

I'm running the latest version of your repo 0.3.4

I'm happy to submit a PR, but not quite sure where to look. Can you shed some light as to why the dynamic routes aren't being handled? Thanks again!

Remove too specific code

if (hook.result._sys.status === 200 && !hook.result.cache.cached)

this hook.result._sys.status is specific to an API I developed from which I extracted the plugin.

Not everybody may want to have such properties. The cache.cached property is added by one of the hooks.

Nested routes - Incompatibility with Feathers 3.x

Steps to reproduce

Hi! It looks as though Feathers 3.x introduces a breaking change to the way nested routes are handled by this plugin. I'm fairly certain it's a simple fix, but here's how to reproduce it:

  • Create a project with Feathers 3.x
  • Set up this plugin with nested routes
  • Note that routes are cached as expressions (i.e. /foo/:variable?something=else instead of /foo/bar?something=else

Expected behavior

Nested paths and params should be parsed properly to avoid conflicting cache routes.

This is being caused by a change in either Feathers or Express which changes the way params are passed into parseNestedPath() here. A property ':foo' passed as a route to '/bar' used to come through like this in params:

params = {
    foo: 'bar' 
}

It now comes through nested in a 'route' object, like so:

params = {
    route: {
        foo: 'bar'
    }
}

I was able to fix this pretty easily in our app by tweaking your function so that params.route is now passed instead of the entire params object:

function parseNestedPath(path, params) {
  const re = new RegExp(':([^\\/\\?]+)\\??', 'g');
  let match = null;

  while ((match = re.exec(path)) !== null) {
    if (Object.keys(params.route).includes(match[1])) {
      path = path.replace(match[0], params.route[match[1]]);
    }
  }

  return path;
}

But there are two issues here. The most important is that it will definitely break compatibility with Feathers 2. That can be remedied with a conditional, if that's the way to go. The other issue is that I'm not sure if this will mess with any other parts of the plugin. The tests pass with this code included, but I wanted to check with you @idealley before submitting a PR to see if there's a better way you can think of to do this.

Let me know, we have to fix this in our app pretty soon. I can try and just use a fork but it'd be great if you can either push this fix or merge our fix in, as above. Let me know, thanks!

Limit keys per endpoint, add purge hook

First thank you for library really helpful!!
Just a couple suggestion to improve:

  • would be nice to have hooks to purge cache lets say after patch or create
  • also would be really great to be able to set a limit let's say store only latest 100 keys per endpoint etc. could be really useful.

Support for additional params for multi tenant use case (how to add?)

My use case is multi-tenant and as such I have several different subdomains coming into the same service endpoint.

I want to add caching to make suer that two subdomains remain separate paths.
From the code it looks like I could possibly add the subdomain back in as a query parameter and would do it?

Do you have suggestions on how to do this?

Cheers

Pagination hook

if pagination hook is used, using this lib. maybe really dangerous. For example it will cache response data when pagination is enabled, then I want to query with disabled pagination using $limit: -1 it will still return response as if it would have pagination enabled

duration is in milliseconds

moments js duration() is per default in milliseconds.
Proposed change: juckerf@02fc95a
(unfortunately I'm not able to create a PR right now, because some other changes in my fork will probably break the code)

feathers configuration not considered

I've implemented the hooks as described in the docs & the complete example and was surprised my config didn't get considered.

The appropriate part in my config/default.json is

  "redis": {
    "host": "localhost",
    "port": 6379
  },
  "redisCache" : {
    "defaultDuration": 300,
    "parseNestedRoutes": true,
    "removePathFromCacheKey": true,
    "env": "NODE_ENV"
  }

The expiry data was never 5 min but always 1 hour!

After digging into the source code I think the problem is the cache.js doesn't consider the feathers config and thus always falls back to 1 hour.
https://github.com/idealley/feathers-hooks-rediscache/blob/master/src/hooks/cache.js#L24

@idealley can you confirm it's a bug or am I overseeing anything?

clear/group/id is broken in v1.1.2

in v1.1.1, if we only know the redis key, we are able to use the route clear/group/:key to detect which group the key belongs to, and then clear the whole group. The code shows:

image

This feature is very useful when we detect a change and want all related keys in cache to be removed.

but in v1.1.2 this is broken. The new code shows:

image

Add option to invalid cache in hooks

Hi,
Thanks for the great work. Is it possible to invalidate the cache using the function in this package? Ideally what I wanted to do is when the user made a CRUD request that is not a find request. I want to invalidate the cache in the after hook.
Thanks for your help.

Ability to specify cutsom keys (or paths)

I have a use case where my app responds to multiple domains.

As such the cache returns the same data for /somepath/someId for each of the domains. Is there a way to specify custom keys so I can add my own key in? perhaps in some before hook?

An in-range update of babel7 is breaking the build 🚨

There have been updates to the babel7 monorepo:

    • The devDependency @babel/cli was updated from 7.1.2 to 7.1.5.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

This monorepo update includes releases of one or more dependencies which all belong to the babel7 group definition.

babel7 is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).
  • coverage/coveralls: First build on greenkeeper/monorepo.babel7-7.1.5 at 92.683% (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Passing client options

These hooks use the the module node_redis

Is it possible to configure the client by passing options as in the underlying module?

redis.createClient([options])

Thanks

custom cache key

I'm missing a way to specify a custom cacheKey. Right now the hook does create the key itself using the hook params etc.
The problem is I do a lot of query manipulation in my hooks what increase the key's length.

My current setup looks similar to:

return {
	before: {
		find: [lorem(), ipsum(), dolor(), redisBeforeHook()]
	}
	after: {
		find: [lorem(), ipsum(), dolor(), redisAfterHook()]
	}
};

As you can see I've placed both redis hooks at the end of the chain because the cache key wouldn't match otherwise.
But I'd prefer to place the redisBeforeHook at the beginning what only works if the cache key is created at the beginning of the after hook chain (before all the query manipulation).

Therefore I need a way to override the default cache key. I'm thinking about adding a custom hook at the beginning of the after hook chain that writes params.cacheKey. But then the redis hook has to check if this param is present and use it instead of the default one.

@idealley would you accept a PR that adds this feature?

route cache/clear not found

// configure the redis client app.configure(redisClient) app.use('/cache', routes(app))
im use this code in app.js in feathersjs version 4.
error /cache/clear is not found. thank you

Unknown command with /cache/clear with client.flushall()

Steps to reproduce

Submitting GET request to /cache/clear

Expected behavior

Cache Clears

Actual behavior

Response is correct, but when monitoring the CLI, a redis error is thrown:

{ ReplyError: ERR unknown command `flushall`, with args beginning with: 
    at new Command 
    ... 
   command: 'FLUSHALL', code: 'ERR' }

Proposed solution

Utilize the new FlushAll Async construction:

router.get('/clear', (req, res) => {
    client.flushall()
      res.status(HTTP_OK).json({
        message: 'Cache cleared',
        status: HTTP_OK
      });
  }); // clear a unique route

becomes

router.get('/clear', (req, res) => {
    client.flushall('ASYNC', () => {
      res.status(HTTP_OK).json({
        message: 'Cache cleared',
        status: HTTP_OK
      });
    });
  });

Paginate returns the same results

The following two finds returns different results but when cached, will return the first cached result.

const roles = await app.service('role').find({
    paginate: false,
    query: {
      enabled: true,
    },
  });
const roles = await app.service('role').find({
    paginate: true,
    query: {
      enabled: true,
    },
  });

I think that certain params should be taken into account when generating the cache key.

Key missing

Steps to reproduce

set config to:

{
            redisCache {
              defaultDuration: 3600,
              removePathFromCacheKey: true
 };

key is missing if there is no id e.g. /users the key should be users now it is empty.

Add option to configure regex for route parsing

For now a regex is hardcoded it is used to parse nested routes:

  let re = new RegExp(':([^\\/\\?]+)\\??', 'g');

It would be nice to be able to specify it if it should be something else than the default

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml
  • The new Node.js version is in-range for the engines in 1 of your package.json files, so that was left alone

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of webpack is breaking the build 🚨

Version 4.17.3 of webpack was just published.

Branch Build failing 🚨
Dependency webpack
Current Version 4.17.2
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

webpack is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).

Release Notes v4.17.3

Bugfixes

  • Fix exit code when multiple CLIs are installed
  • No longer recommend installing webpack-command, but still support it when installed
Commits

The new version differs by 7 commits.

  • ee27d36 4.17.3
  • 4430524 Merge pull request #7966 from webpack/refactor-remove-webpack-command-from-clis
  • b717aad Show only webpack-cli in the list
  • c5eab67 Merge pull request #8001 from webpack/bugfix/exit-code
  • 943aa6b Fix exit code when multiple CLIs are installed
  • 691cc94 Spelling
  • 898462d refactor: remove webpack-command from CLIs

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Clearing group cache - "Can't set headers after they are sent"

Hi, I created a custom hook on my services for after: create, update, patch and delete in order to clear the group cache made on find and get.

Say my service is called items .
Calling /cache/clear/group/group-items will return Error 500 stating that "Can't set headers after they are sent".

That is due to the **if (err) ** handler in the router not featuring an else statement for the rest of the callback. res.status().json is then called twice, hence the error.

router.get('/clear/group/:target', function (req, res) {
    client.get('' + req.params.target, function (err, reply) {
      if (err) res.status(500).json({ message: 'something went wrong' });
      var group = reply ? JSON.parse(reply).cache.group : '';
      console.log('TESTTSTTT')
      h.clearGroup(group).then(function (r) {
        res.status(200).json({
          message: 'cache ' + (r ? '' : 'already') + ' cleared for the group key: ' + req.params.target,
          status: r ? 200 : 204
        });
      });
    });
  });

Shouldn't the router be something like the following ?

router.get('/clear/group/:target', function (req, res) {
  client.get('' + req.params.target, function (err, reply) {
    if (!!err) {
      res.status(500).json({ message: 'something went wrong' });
    }
    else {
      var group = reply ? JSON.parse(reply).cache.group : '';
      
      h.clearGroup(group).then(function (r) {
        res.status(200).json({
          message: 'cache ' + (r ? '' : 'already') + ' cleared for the group key: ' + req.params.target,
          status: r ? 200 : 204
        });
      }); 
    }
  });   
});

Is also think it could be interesting to add hooks to directly clear the actual redis cache (and not only remove it from the hook return object). Having an internal router only makes the feature available through other HTTP request modules.

Thank you,

Renaud

An in-range update of webpack-cli is breaking the build 🚨

The devDependency webpack-cli was updated from 3.1.1 to 3.1.2.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

webpack-cli is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).
  • coverage/coveralls: First build on greenkeeper/webpack-cli-3.1.2 at 92.683% (Details).

Commits

The new version differs by 17 commits.

  • 795ac48 v0.1.2
  • 8225339 chore: v.3.1.2>
  • fb6945b Merge pull request #618 from webpack/fix/watch-info
  • 99f5a69 Merge pull request #616 from rishabh3112/patch-4
  • 4cba51d Merge pull request #615 from rishabh3112/patch-3
  • d4e1614 fix: replace test regex
  • f3a225a docs(readme): update webpack-cli to webpack CLI
  • 9aed0dc fix(tapable): fix hook options
  • dc4ded9 docs(init): update headers
  • 747aef9 docs(readme): change addons to scaffolds
  • f8187f1 docs(readme): update links
  • 2ccf9a9 docs(init): update init documentation
  • 3844671 docs(readme): update README.md (#614)
  • da05c2f docs(readme): update Readme based on feedback
  • 93ebcc2 chore(docs): update readme

There are 17 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Route Subfolders break cache clearing

Steps to reproduce

Setup cache on a nested route with a version number ahead of the service. Example:
example.com/v1/resources/1?test=true

To clear the cache, I'm running GET cache/clear/single/v1/resources/1?test=true

Expected behavior

Cache key should be 'v1/resources/1?test=true'

Actual behavior

Error. Cannot find path.

Proposed Solution (What worked for me)

 router.get('/clear/single/:target', (req, res) => {
    let target = decodeURIComponent(req.params.target);

becomes

  router.get('/clear/single/*', (req, res) => {
    let target = decodeURIComponent(req.params[0]); 

If this sounds like a useful tweak, I'm happy to submit a PR.

ISSUE: results format without paging

- Cache working well if has paging enable in Service.

Configs

const options = {
    name: 'service-name',
    Model,
    paginate
}

Results format

{
    "total": 135,
    "limit": 50,
    "skip": 0,
    "data": [...],
    "cache": {
        "cached": true,
        "duration": 900,
        "expiresOn": "2018-02-28T04:44:12.601Z",
        "parent": "xxx",
        "group": "group-xxx",
        "key": "xxx"
    }
} 

- But Service without paging! results format will different the first time (before cache)

Configs

const options = {
    name: 'service-name',
    Model
}

Results format

{
    "cache": {
        "wrapped": [ ... // data cache HERE ... ],
        "cached": true,
        "duration": 900,
        "expiresOn": "2018-02-28T04:44:12.601Z",
        "parent": "xxx",
        "group": "group-xxx",
        "key": "xxx"
}

Caching arrays

First: thank you for initiating this project. I really like it :-)

I have one problem: if the data processed by the hooks is an array instead of an object, the hooks don't work.
When reading the cached array feathers fails with the following exception:
` var duration = (0, _moment2.default)(hook.result.cache.expiresOn).format('DD MMMM YYYY - HH:mm:ss');
^

TypeError: Cannot read property 'expiresOn' of undefined
at Command.callback (C:\Users\fabi\dev\ba\backend\node_modules\feathers-hooks-rediscache\lib\library.js:442:65)
at normal_reply (C:\Users\fabi\dev\ba\backend\node_modules\redis\index.js:726:21)
at RedisClient.return_reply (C:\Users\fabi\dev\ba\backend\node_modules\redis\index.js:824:9)
at JavascriptRedisParser.returnReply (C:\Users\fabi\dev\ba\backend\node_modules\redis\index.js:192:18)
at JavascriptRedisParser.execute (C:\Users\fabi\dev\ba\backend\node_modules\redis-parser\lib\parser.js:553:10)
at Socket. (C:\Users\fabi\dev\ba\backend\node_modules\redis\index.js:274:27)
at emitOne (events.js:96:13)
at Socket.emit (events.js:191:7)
at readableAddChunk (_stream_readable.js:178:18)
at Socket.Readable.push (_stream_readable.js:136:10)
`

I guess the reason for this exception is, that in the after hooks (when saving the array to redis) hook.result.cache is created on hook result (which should be an object, but is an array in my case) (https://github.com/idealley/feathers-hooks-rediscache/blob/master/src/hooks/cache.js#L13) and some cache properties are assigned to hook.result.cache (https://github.com/idealley/feathers-hooks-rediscache/blob/master/src/hooks/redis.js#L67). And since hook.result is an array instead of an object, the cache property don't really gets added (because arrays can't have properties) and is not saved in redis.

When trying to read the data from redis in a subsequent request this leads to the error listed above.

I'd be really happy to help you to fix this issue, but I'd like to know what your thoughts are about handling arrays.

Some different ways that came to my mind:

  • don't handle arrays at all => "force" users of the hooks to encapsulate arrays in objects (e.g. {"data": ["obj1", "obj2", ...]}) before they get processed by the hooks
  • if processed data is an array: encapsulate it in an object before saving to redis and extract it when loading from redis (follow up questions: how to handle the cache property in the http response)
  • ommit the cache property and directly check if the data is already cached or not (assumes the cache property is not really needed in the http response)

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.