Coder Social home page Coder Social logo

feathers-permissions's Introduction

feathers-permissions

CI Dependency Status Download Status

Simple role and service method permissions for Feathers

Note: This module implements a hook simple role and service method based permissions checked against the permissions in a user (entity) object. More complex requirements can already be implemented as custom Feathers hooks. See here and here for more information.

Installation

npm install feathers-permissions --save

Important: The feathers-permissions hook should be used after the authenticate() hook from @feathersjs/authentication.

Simple example

The following example will limit all messages service calls to users that have admin in their roles:

const feathers = require('@feathersjs/feathers');
const memory = require('feathers-memory');
const checkPermissions = require('feathers-permissions');
const app = feathers();

app.use('/messages', memory());

app.service('messages').hooks({
  before: [
    authenticate('jwt'),
    checkPermissions({
      roles: [ 'admin' ]
    })
  ]
});

// User from the database
const user = {
  email: '[email protected]',
  permissions: [ 'admin' ]
}

Documentation

Feathers permissions allows you to grant and manage permissions in a flexible nature based on role and service method. Each object that requires permissions must have an array or a comma separated string of permissions stored on it (typically in your database).

Options

The following options are available:

  • roles - A list of permission roles to check or a function that takes the hook context and returns a list of roles. Can be a comma separated string of roles or an array of roles.
  • entity (default: user) - The name of the entity (params[entity])
  • field (default: permissions) - The name of the permissions field on the entity. May be dot separated to access nested fields.
  • error - If set to false will not throw a Forbidden error but instead set params.permitted to true or false. Useful for chaining permission hooks.

Permission format

The list of permissions will be obtained from params[entity] and field. It can be a comma separate list or an array of permissions in the following format:

  • * - Allow everything
  • ${role} or ${role}:* - Allow every service method (find, get, create, update, patch, remove) for role
  • *:${method} - Allow method service method for any role
  • ${role}:${method} - Allow method service method for role

This means the following use of feathers-permissions:

app.service('messages').hooks({
  before: checkPermissions({
    roles: [ 'admin', 'user' ]
  })
});

Will allow user permissions containing *, admin:*, user:* and the service method that is being called (e.g. admin:create or user:find and *:create and *:find).

The following will create a dynamic permission based on the hook context.path:

app.service('messages').hooks({
  before: checkPermissions({
    roles: context => {
      return [ 'admin', context.path ];
    }
  })
});

Permissions can also be assembled asynchronously:

app.service('messages').hooks({
  before: checkPermissions({
    roles: async context => {
      const { user } = context.params;
      const roles = await app.service('roles').find({
        query: {
          userId: user._id
        }
      });

      return roles.data;
    }
  })
});

Conditionally restricting permissions

To conditionally either allow access by roles or otherwise restrict to the current user, a combination of feathers-permissions - setting the error option to false - feathers-authentication-hooks and feathers-hooks-common#iff (checking for params.permitted) can be used:

app.service('messages').hooks({
  before: {
    find: [
      checkPermissions({
        roles: ['super_admin', 'admin'],
        field: 'roles',
        error: false
      }),
      iff(context => !context.params.permitted,
        setField({
          from: 'params.user._id',
          as: 'params.query.userId'
        })
      )
    ]
  }
});

More examples

const feathers = require('@feathersjs/feathers');
const memory = require('feathers-memory');
const checkPermissions = require('feathers-permissions');
const app = feathers();

app.use('/messages', memory());

app.service('messages').hooks({
  before: checkPermissions({
    roles: [ 'admin', 'messages' ]
  })
});

// User from the database (e.g. added via @feathersjs/authentication)
const user = {
  email: '[email protected]',
  permissions: [ 'messages:find', 'messages:get' ]
  // Also possible
  permissions: 'messages:find,messages:get'
}

const admin = {
  email: '[email protected]',
  permissions: [ 'admin:*' ]
}

// Will pass
app.service('messages').find({
  user
});

// Will fail
app.service('messages').create({
  user
});

// Will pass
app.service('messages').create({
  provider: 'rest', // this will be set automatically by external calls
  user: admin
});

License

Copyright (c) 2019

Licensed under the MIT license.

feathers-permissions's People

Contributors

antarasi avatar bartduisters avatar corymsmith avatar daffl avatar dependabot-preview[bot] avatar ekryski avatar greenkeeper[bot] avatar indatawetrust avatar marshallswain avatar mcchrish avatar michaelermer avatar mkovel avatar piyush-koradiya avatar semantic-release-bot avatar soullivaneuh avatar sqveeze 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

feathers-permissions's Issues

How do we handle permissions in channels?

I've been trying to find references that go over channels with respect to permissions and access control but haven't been able to find anything.

What's the recommended way to handle permissions for sending out real time updates without it becoming a mess?

An in-range update of @types/node is breaking the build 🚨

The devDependency @types/node was updated from 12.12.1 to 12.12.2.

🚨 View failing branch.

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

@types/node 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).

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 🌴

Implement setPermissions hook

This hook should be able to be used as a before or after hook. If before it just adds the specified permissions to the permissionsField. If used as an after hook it will have to patch the resource with the specified permissions on the permissionsField.

Unexpected behaviour when using multiple "checkPermissions" in one method

Steps to reproduce

export default {
  before: {
    all: [authenticate('jwt')],
    get: [
      // Allow only users with the "products:get" permission
      checkPermissions({ roles: ['products:get'] }),

      // The user will only get his product unless he is an admin
      checkPermissions({ roles: ['admin'], error: false }),
      (context: any) => console.log(context.params.permitted, context.params.user),
      iff(
          (context) => !context.params.permitted,
          setField({
               from: 'params.user._id',
               as: 'params.query.user',
         })
      ),
    ],
  }
}

Expected behavior

I was expecting this code first to allow users with the products:get permission and second to check if the user is an admin or not. In case he is an admin don't limit the results, otherwise, let the user get only the products that belong to him.

Actual behavior

The context.params.permitted is always true no matter whether the user is an admin or not.
I believe this is due to the order here. The second call which is making the permitted: false is overwritten by the first one since you are destructing ...params after setting the value of permitted.
This makes the first call to checkPermissions to have the max priority when it would make sense to have the last call of checkPermissions being able to determine whether a user has access to perform the requested action.
Also since this is a permissions and roles library it should be strict by default, meaning even if one permission check has failed it should make the permitted value false.

Is NULL a valid value for the permissions field?

At present, I suspect that NULL is not a valid value for the permissions field. When that field is set to NULL I get the following error:

Cannot read property 'some' of null

In my application, a majority of users will have only a basic set of permissions over their own content. I had planned to leave permissions blank for them, but instead it seems I'll need to give them a basic "user" permission to satisfy this need.

In the future, it'd be neat if the hook could fail out (based on the value of the error parameter) when there are no roles to check.

"users:*:own" special permission

Feature Request

Add a special permission somewhat like "users:*:own" where it add permission access level to the user's own information.

In creating a new users, usually, the id is auto generate by the database upon creation. So there's no easy way of knowing what the id would be before it will be populated in the database.
So setPermissions probably won't work as a before hook with an example:

permissions.hooks.setPermissions({permissions: ['users:*:[id]'], field: 'permissions'})

An alternative would be is to have an after hook (when the id is already generated) that updates the permission. But that means there will be at least two write operations just to achieve the desired result.

With an own permission, this can be simply:

permissions.hooks.setPermissions({permissions: ['users:*:own'], field: 'permissions'})

Dynamic Permission Creation not working as expected

I assume I'm doing something incorrect here...

const checkPermissions = require('feathers-permissions');
module.exports = {
  before: {
    all: [
        checkPermissions({
          roles: async context => {
            const { user } = context.params;
            console.log('USER ROLES:', user.roles);
            const roles = await context.app.service('roles').find({
              query: {
                name: {
                  $in: user.roles
                }
              }
            });
            console.log('ROLES:', roles);
            let permissions = [];
            for (let role of roles.data) {
                permissions = permissions.concat(role.permissions);
            }
            console.log('PERMISSIONS:', permissions);
            return permissions;
          }
        })
    ],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  },

  after: {
    all: [],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  },

  error: {
    all: [],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  }
};                                                                                                                                                                                                                                                                         

This is designed to allow roles that can be changed thus persisting new sets of permissions to users, rather than storing permissions in every user record. I've verified that in my test permissions returns ['*'] so it should not get any denied error. Am I using this method incorrectly?

Thanks!

Mismatching permissions and requiredPermissions comparison

Steps to reproduce

I am trying to check permissions on a user that has a field role set to admin.
Here is my setup in user hooks:

const permissionsOptions = {
	permissions: ['admin'],
	on: 'user',
	service: 'user',
	field: 'role'
}

exports.before = {
	find: [
		auth.hooks.authenticate('jwt'),
		permissions.hooks.checkPermissions(permissionsOptions),
		permissions.hooks.isPermitted()
}

Expected behavior

I would expect that permissionsOptions.permissions would be compared against the actual field role on user.

Actual behavior

Instead, it is compared against the requiredPermissions array that, given the setup above, always becomes:
[ '*', '*:find', '*:find:*', 'user', 'user:*', 'user:*:*', 'user:find', 'user:find:*' ]

Here is the output from debug console log:

  feathers-permissions:hooks:check-permissions Running checkPermissions hook with options:
{ on: 'user',
  field: 'role',
  permissions: [ 'admin' ],
  service: 'user' } +0ms
  feathers-permissions:hooks:check-permissions Active permissions [ 'admin' ] +3ms
  feathers-permissions:hooks:check-permissions Required Permissions
[ '*',
  '*:find',
  '*:find:*',
  'user',
  'user:*',
  'user:*:*',
  'user:find',
  'user:find:*' ] +1ms
error: (403) Route: /users - You do not have the correct permissions.

What am I doing wrong here? The documentation as to how to set up permissions options seems to be invalid or lacking details. The current readme on the plugin page is not very helpful either. I could also help with the docs, if I get it right myself. Thaks!

An in-range update of @feathersjs/errors is breaking the build 🚨

The dependency @feathersjs/errors was updated from 4.3.11 to 4.4.0.

🚨 View failing branch.

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

@feathersjs/errors is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

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

Release Notes for v4.4.0

4.4.0 (2019-11-27)

Bug Fixes

  • authentication-client: Reset authentication promise on socket disconnect (#1696) (3951626)
  • core: Improve hook missing parameter message by adding the service name (#1703) (2331c2a)
  • rest-client: Allow to customize getting the query (#1594) (5f21272)
  • transport-commons: Allow to properly chain SocketIo client.off (#1706) (a4aecbc)
  • typescript: Allow specific service typings for Hook and HookContext (#1688) (f5d0ddd)

Features

  • authentication: Add parseStrategies to allow separate strategies for creating JWTs and parsing headers (#1708) (5e65629)
  • authentication-oauth: Set oAuth redirect URL dynamically (#1608) (1293e08)
Commits

The new version differs by 10 commits.

  • e157e5f chore(release): publish v4.4.0
  • 1293e08 feat(authentication-oauth): Set oAuth redirect URL dynamically (#1608)
  • 5e65629 feat(authentication): Add parseStrategies to allow separate strategies for creating JWTs and parsing headers (#1708)
  • 18ba231 chore: Update all Node engine versions
  • a4aecbc fix(transport-commons): Allow to properly chain SocketIo client.off (#1706)
  • 2331c2a fix(core): Improve hook missing parameter message by adding the service name (#1703)
  • 5f21272 fix(rest-client): Allow to customize getting the query (#1594)
  • 3951626 fix(authentication-client): Reset authentication promise on socket disconnect (#1696)
  • f5d0ddd fix(typescript): Allow specific service typings for Hook and HookContext (#1688)
  • 42d81b2 chore: Update version and changelog

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 🌴

context.params.permitted not set in child hook

context object not available in checkPermissions() when called from before hook to custom hook.

Steps to reproduce

user.hook.js

const  checkForAccess  =  require('../../hooks/check-for-access');
module.exports  = {
before: {
...,
get : [authenticate('jwt'), checkForAccess() ],
...
}

check-for-access.js

const {restrictToOwner} =require('feathers-authentication-hooks');
module.exports  =  function (options  = {}) {
return  async  context  => {
	checkPermissions({
		roles: ['admin', 'user'],
		error:  false
	});
	if(!context.params.permitted)
		restrictToOwner({ idField:  'id', ownerField:  'id' });
};

Expected behavior

context.params.permitted should be either true or false based on the permission.

Actual behavior

context.params.permitted is undefined thus the function restrictToOwner always executes.

Additional information

Break line in checkPermissions function in node_modules/feathers-permission/lib/

module.exports  =  function  checkPermissions (options  = {}) {
options  =  Object.assign({
entity:  'user',
field:  'permissions'
}, options);
...
return  function (context) {
	...
}

context is not available (see screenshot at the end)

Debug in VS code.

VSCode version
Version: 1.25.1 Commit: 1dfc5e557209371715f655691b1235b6b26a06be Date: 2018-07-11T15:40:20.190Z Electron: 1.7.12 Chrome: 58.0.3029.110 Node.js: 7.9.0 V8: 5.8.283.38 Architecture: x64

System configuration

Module versions
"feathers-permissions": "^0.2.1",
"feathers-authentication-hooks": "^0.3.0"

NodeJS version:
v10.6.0
Operating System:
LSB Version: 1.4 Distributor ID: Arch Description: Arch Linux Release: rolling Codename: ISO-Rolling

image

No error check when missing entity

If doing a permissions check on a route that may be accessed by a logged out user there is no check for the error flag.
https://github.com/feathersjs-ecosystem/feathers-permissions/blob/release/lib/index.js#L33

This causes an issue because currently I'm using permissions check in users before hooks with error false. This makes it easier than implementing it on every method except POST (which you would obviously be logged out for in the event of a new registration).

Is there some reason the error check is not implemented there?

setPermissions hook is not persisting the user permissions

Steps to reproduce

1- Add the setPermissions hook as a after create hook, and add a password remove hook as an after all hook.

exports.after = {
  all: [hooks.remove('password')],
  find: [],
  get: [],
  create: [
    permissions.hooks.setPermissions({ permissions: ['users:*:id'] })
  ],
  update: [],
  patch: [],
  remove: []
};

2- Create an user by calling the user service.

Expected behavior

The user should be created successfully, and afterwards updated with the new permissions.

Actual behavior

The user is created but the permissions are not saved correctly (the promises fail with an error).
The error message is password cannot be null.

Solution

Patch the resource instead of updating it.

var promises = items.map(function (item) {
        return service.update(hook.id || item[service.id], item);
      });

to:

var promises = items.map(function (item) {
        return service.patch(hook.id || item[service.id], { [options.field]: item[options.field] });
      });

The item has been stripped of it's password (as it should always), and since this is only a partial update, it makes sense changing it into a patch operation.

System configuration

Module versions:
I'm using the latest version on the filters branch of the feathers-permissions repo.

NodeJS version: 6.9.4

Unexpected behavior on checkPermissions with error: false

Expected behavior

What I expect when using it is to not throw even when user is not authenticated (provider is undefined)
Instead just set context.params.permitted to false

Actual behavior

throws 403 with You do not have the correct permissions (invalid permission entity).

Status of this module

What is the status of this module? There is a message in the Readme still stating that its not ready yet

"This module is not published yet."

When can we hope to use it in production?

Thanks

Upgrade feathers-permissions to be compatible with Feathers v5 (Dove)

Steps to reproduce

feathers-permissions currently uses Feathers 4.5.15. This library is not compatible with the latest Feathers v5 hook types. Specifically, the return type of checkPermissions (Hook) is not compatible with the new HookFunction type - Argument of type 'Hook' is not assignable to parameter of type 'HookFunction<A, S>.

The common solution seems to be to cast the checkPermissions hook to any (or some other matching type), which isn't ideal.

  • Tell us what broke. The more detailed the better.
  • If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc.

Expected behavior

Using the checkPermissions hook with Feathers v5 does not produce a type error.

Actual behavior

Type error when using checkPermissions hook with Feathers v5.

System configuration

Tell us about the applicable parts of your setup.

Module versions (especially the part that's not working):

  • "@feathersjs/feathers": "^5.0.24"
  • "feathers-permissions": "^2.1.4"

NodeJS version:
v20.3.0

Operating System:
14.3.1 (23D60)

Browser Version:
Chrome Version 123.0.6312.59

SetPermissions is not working correctly when used as a before create hook

Steps to reproduce

1- Add the setPermissions hook as a before create hook to the users service, with a template permission using the :id string.

create: [
    permissions.hooks.setPermissions({ permissions: ['users:*:id'] })
  ]

2- Create an user by calling the users service.

Expected behavior

Given a user id = 1.
The created user should have a permission equal to users:*:1.

Actual behavior

The created user has a permission equal to users:*:id.

Solution

The issue happens because as a before create hook, the setPermissions hook doesn't have access to the user id (the user hasn't been created yet), thus it cannot replace it on the template.
Using the setPermissions hook as an after create hook works correctly.

permissions = permissions.map(function (permission) {
      if (id !== undefined && permission.includes(':id')) {
        permission = permission.replace(':id', ':' + id);
      }

      return permission;
    });

The solution is to simply check if the hook is being used as a before create hook, and warn the user accordingly (as a before update or before patch hook it works correctly).

System configuration

Module versions:
I'm using the latest commit from the feathers-permissions filters branch (commit no. b33fcdb).

NodeJS version: 6.9.4

how to set a user to admin role?

Steps to reproduce

(First please check that this issue is not already solved as described
here
)

  • Tell us what broke. The more detailed the better.
  • If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc.

Expected behavior

Tell us what should happen

Actual behavior

Tell us what happens instead

System configuration

Tell us about the applicable parts of your setup.

Module versions (especially the part that's not working):

NodeJS version:

Operating System:

Browser Version:

React Native Version:

Module Loader:

[api enhancement] checkPermissions could omit service option

Currently the checkPermissions hook has to have a service option:

before: {
  all: [checkPermissions({service: 'xxx'})]
}

But since now we have hook.path representing the path of current service, this service option becomes unnecessary. It's reasonable to allow omitting the service option, which is needed only when we want to check permission based on other services.

Use checkPermissions with nested field

Steps to reproduce

Hello. I'm starting to use feathers. I don't know how to use checkPermissions with nested permissions without add permissions field to root of my model.

Expected behavior

{
            "_id": "5cb3bb8115afab14a74e5ad3",
            "email": "[email protected]",
            "roleId": "5cb3af204ddf5e03c15868a9",
            "_include": [
                "roles"
            ],
            "roles": {
                "_id": "5cb3af204ddf5e03c15868a9"
                "permissions": [
                    "documents-types:*",
                ],
                "name": "documents-types admin",
            }
}

User hook

after: {
    all: [ 
      populate({ schema: userRoleSchema })
      ...
    ]

Documents-types hooks

before: {
    all: [checkPermissions({
      roles: [ 'documents-types' ],
      field: 'roles.permissions',
    })],
   ...
}

Actual behavior

It doesn't work. I had to add this hook to user to extract the permissions.

    after: {
    ...
    get: [(context) => { context.result.permissions = context.result.roles.permissions; return context;}],

System configuration

feathers --version 3.9.0
"feathers-hooks-common": "^4.20.7",
"feathers-mongoose": "^7.3.2",
"feathers-permissions": "^0.2.1",

Suggestion: Client Library

I am suggesting that there should client library for this module, so that we can do conditional rendering/routing/etc without have to make service requests.

After authentication I cache a subset of the current user's data, including their permissions. I would like to do some conditional rendering/routing based on those permissions.

Add a client-side counterpart

After integrating feathers-permissions (backed by PostgreSQL), I want to make it possible for UI devs to use the same logic.

contrived example: class="'userInstance.can('item:remove') || 'disabled'"

I'd like to move https://github.com/feathersjs-ecosystem/feathers-permissions/blob/master/lib/index.js#L39-L60 into its own function so that it can be used by the existing hook and a more generic implementation.

e.g.

permissionsContain(permissions, roles, method);

checkPermission(permission, permissions);
checkPermission('item:get', [ 'item:*' ]);

FrontendUserModel.prototype.can = function(permission) {
  return checkPermission(permission, this.permissions);
}

Allow user to modify his own data

Hi,

is it possible to allow user edit only his own record ? For example:

permissions: [ 'users:update:HIS_ID' ]  (service_name:method:ID)

thanks for explanation if is not It would be great implement this feature I guess

Subtractive permission

Feature Request

Allow subtracting permissions:

[
  "users:*:*", // users
  "-:users:*:10" // except user with id 10
]

Added - at the front to indicate negative permission, but there must be a better way to do this.

`setPermissions` not available?

In the source code setPermissions.js is not exported to hooks? And the content of setPermissions.js seems just an old version of checkPermissions.

Permission System Enhancement

Enhancement

This library provides the basic needs for permissions usage in an application, but it does not allow for more advanced systems to take place, including the ability to provide context to frontend applications about what the user is able to do. I've started working on a project where I needed this advanced system, and the basics can be found here.

The current version I'm working on will be for SQL based systems. The best course of action for No-SQL would probably be creating a second repo. The main focus is to get the SQL version working / released though.

Todo

  1. Review current code base to determine the work that needs to be done.
  2. Define a structure for the plugin that is performant and secure.
  3. Implement the structure and best practices.
  4. Add tests.
  5. Convert to plugin format.
  6. Convert to typescript.

Idea: custom required permissions

From reading through the source code for checkPermissions, it looks like there are two significant steps:

  1. Map the current hook to permissions which essentially builds out the necessary permissions for the current action being taken, based on the options.namespace and hook's method and (potentially id.
  2. Check those permissions against those in the entity.

What this doesn't address, is a scenario where the permission is based on the incoming payload, or other request factors e.g. I may have have a user who can update another user to an admin, but not necessarily to an owner. In this scenario, the required permissions step won't be able to distinguish between these two, as both would be update methods and map to the same required permissions array.

I think there would be a couple of different ways to address this:

  • Allow a function on options to override the classify function, or append to it. Not sure if this is too heavy handed or
  • Allow a selector option which selects the required permission from somewhere on the hook.params e.g. checkPermissions({ permissionsFrom: 'requiredPermissions' }); and a hook with hook.params === { requiredPermissions: [ 'promote_user' ] }. That way you can just add a hook before the checkPermissions method which would attach custom permissions to the hook.

Would love to get some feedback on this idea / alternative solutions to the overall issue. Cheers!

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.