npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

camas

v1.0.0

Published

React authorization library.

Downloads

4

Readme

Codecov npm bundle size npm

Camas is a OO design minimal React authorization library.

Live demo

  • TypeScript friendly
  • Zero dependencies
  • React Hooks
  • Scalable
  • Easy to test
  • Only 500 Bytes after gzip

Installation

Use npm:

$ npm install camas --save

Use yarn:

$ yarn add camas

Policies

Camas is focused around the notion of policy classes. I suggest that you put these classes in src/policies. This is a simple example that allows updating a post if the user is an admin, or if the post is unpublished:

interface Post {
  title: string;
  body: string;
  isPublished: boolean;
}

interface User {
  username: string;
  isAdmin: boolean;
}

class PostPolicy {
  protected user: User;

  construtor(context: { user: User }) {
    this.user = context.user;
  }

  update(post: Post) {
    return this.user.isAdmin || !post.isPublished;
  }
}

As you can see, this is just a plain JavaScript class.

Usually you can set up a base class to inherit from:

class BasePolicy {
  protected user: User;

  construtor(context: { user: User }) {
    this.user = context.user;
  }
}

class PostPolicy extends BasePolicy {
  update(post: Post) {
    return this.user.isAdmin || !post.isPublished;
  }
}

Context provider

import { Provider } from 'camas';

const App = () => (
  <Provider context={{ user: currentUser }}>
    <Routes />
  </Provider>
);

Camas will pass context to the policy class when initializing it.

Consume policy

Using hook

import { usePolicy } from 'camas';

const PostList = ({ posts }) => {
  const postPolicy = usePolicy(PostPolicy);

  return (
    <div>
      <ul>
        {posts.map(post => (
          <li>
            {post.title}
            {postPolicy.update(post) && <span>Edit</span>}
          </li>
        ))}
      </ul>
    </div>
  );
};

Using component

import { Authorize } from 'camas';

const PostList = ({ posts }) => {
  return (
    <div>
      <ul>
        {posts.map(post => (
          <li>
            {post.title}
            <Authorize with={PostPolicy} if={policy => policy.update(post)}>
              <span>Edit</span>
            </Authorize>
          </li>
        ))}
      </ul>
    </div>
  );
};

Testing

Since polices are just plain classes, testing your polices can be very easy.

Here is a simple example with jest:

import PostPolicy from './PostPolicy';

descript('PostPolicy', () => {
  const admin = {
    isAdmin: true;
  };

  const normalUser = {
    isAdmin: false;
  }

  const publishedPost = {
    isPublished: true;
  }

  const unpublishedPost = {
    isPublished: false;
  }

  it("denies access if post is published", () => {
    expect(new PostPolicy({ user: normalUser }).update(publishedPost)).toBe(false);
  });

  it("grants access if post is unpublished", () => {
    expect(new PostPolicy({ user: normalUser }).update(unpublishedPost)).toBe(true);
  });

  it("grants access if post is published and user is an admin", () => {
    expect(new PostPolicy({ user: admin }).update(publishedPost)).toBe(true);
  });
});

API

Provider

Props

  • context - Camas passes conetxt to the policy class when initializing it.
<Provider conetxt={{ user: currentUser }}>
  <App />
</Provider>

Authorize

Props

  • with - Policy class.
  • if - Check function for the policy, it accepts the policy instance as it's first argument.
  • fallback - Fallback element when policy check now pass.
<Authorize with={PostPolicy} if={policy => policy.show()} fallback={<div>You are not allow to view these posts.</div>}>
  <PostList />
</Authorize>

usePolicy(...policies)

The usePolicy hook receive policies class as it's arguments and return instances of them.

const postPolicy = usePolicy(PostPolicy);

withPolicies(policies)

A HOC injects policy instance to class component.

@withPolicies({
  postPolicy: PostPolicy,
})
class PostList extends React.Component {
  render() {
    const { posts, postPolicy } = this.props;
    return (
      <div>
        <ul>
          {posts.map(post => (
            <li>
              {post.title}
              {postPolicy.update(post) && <span>Edit</span>}
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

authorize(policy, check, fallback)

A HOC to apply policy to the component.

@authorize(
  PostPolicy,
  ({ policy }) => policy.show(),
  <Unauthorized />
)
class PostList extends React.Component {
  render() {
    return ...
  }
}

License

MIT