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

taskcluster-lib-scopes

v11.0.0

Published

Scope checking utilities for Taskcluster

Downloads

5,344

Readme

Scopes Library

Simple utilities to validate scopes, scope-sets, and scope-expression satisfiability.

For information on scopes, see the Taskcluster documentation.

Usage

let scopeUtils = require('taskcluster-lib-scopes');

Valid Scopes

The validScope function will determine if its input is a valid scope (string containing ascii characters):

// Check if input is a valid scope.
assert(scopeUtils.validScope("..."));

Scope Expressions

Throughout Taskcluster, we need to represent the scopes required to perform some operation. In some cases, the requirements are complex, including alternatives. For example, creating a task can use either scopes containing priority levels or the older form without priority levels.

These requirements take the form of scope expression. Such an expression is either a valid scope or an object with a single key -- either AnyOf or AllOf -- mapping to an array of scope expressions.

A scope expression can be evaluated against an array of scopes to determine if the scope expression is "satisfied" by the array of scopes. Satisfaction in this context means that the following clauses are satisfied:

AllOf: [..] All sub-expressions must be satisfied.

AnyOf: [..] At least one sub-expression must be satisfied.

"<scope>": The <scope> is satisfied by the scope-set.

Examples:

"hooks:trigger-hook:proj-taskcluster/release"
{AllOf: [
  "hooks:modify-hook:proj-taskcluster/release",
  "assume:hook-id:proj-taskcluster/release",
]}
{AnyOf: [
  {AllOf: [
    "queue:scheduler-id:taskcluster-ui",
    {AnyOf: [
      "queue:create-task:lowest:proj-taskcluster/ci",
      "queue:create-task:very-low:proj-taskcluster/ci",
      "queue:create-task:low:proj-taskcluster/ci",
    ]}
  ]},
  "queue:create-task:proj-taskcluster/ci",
  "queue:define-task:proj-taskcluster/ci",
]}

Correctness

The validateExpression function validates that an expression matches the structur described above.

// Scope Expression
assert(scopeUtils.validExpression({AnyOf: [{AllOf: ['a', 'b'}, {AllOf: ['c']}]});

Satisfaction

Given an array of valid scopes, also referred to as a scopeset, this library can check to see if the scopeset "satisfies" a given expression.

Scope set satisfaction is checked with with satisfiesExpression which takes a scopeset as the first argument and a scope expression as the second.

NOTE: this function is entirely local and does no expansion of assume: scopes. Call the authentication service's expandScopes endpoint to perform such expansion first, if necessary.

Examples:

// Evaluates to true
scopeUtils.satisfiesExpression(
  [
    'abc*',
  ],
  {
    AnyOf: ['abcd'],
  }
)

// Evaluates to false
scopeUtils.satisfiesExpression(
  [
    'abc*',
  ],
  {
    AnyOf: ['def'],
  }
)

// Evaluates to true
scopeUtils.satisfiesExpression(
  [
    'abc*',
  ],
  {
    AnyOf: [
      {AllOf: ['abcdef']},
      'def',
    ]
  }
)

Scopes Satisfying an Expression

If you wish to understand why a certain expression was satisfied by a scopeset you can use the scopesSatisfying function. This takes the same (scopeset, expression) argument as satisfiesExpression, and returns undefined when the expression is not satisfied. When the expression is satisfied, it returns a set of scopes that satisfied the expression. The returned set of scopes is always a subset of the input scopeset. In the case that the expression is satisfied, it is always true that satisfiesExpression(scopesSatisfying(scopeset, expression)). The returned set of scopes is intuitively "the minimal set of scopes required to satisfy the expression" but is not quite minimal in one sense: If several alternatives of an AnyOf are satisfied, then the scopes used to satisfy all such alternatives are included.

Scopes Required to Satisfy an Expression

If you wish to understand why a certain expression was not satisfied by a scopeset you can use the removeGivenScopes function. The function returns a scope expression where all scopes that exist are missing from the scopeset. Any scopes under an AllOf key are definitely needed to satisfy the expression and at least one of the scopes under an AnyOf must be provided to satisfy. If the scope expression is satisfied by the scopes provided, this function returns null.

scopeUtils.removeGivenScopes(
  [
    'abc',
  ],
  {
    AllOf: [
      {AnyOf: ['abc']},
      'def',
    ]
  }
)
// Returns
// {AllOf: ['def']}

Set Operations

This library supports some operations to combine sets of scopes (referred to as "scopesets").

The intersection of two scopesets A and B is the largest scopeset C which is satisfied by both A and B. Less formally, it's the set of scopes in both scopesets. The scopeIntersection function will compute this value:

const scope1 = ['bar:*'];
const scope2 = ['foo:x', 'bar:x'];
assert.equal(['bar:x'], scopeUtils.scopeIntersection(scope1, scope2));

The scopeUnion function will compute the union of two scopesets. The union of two scopesets A and B is the largest scopeset C such that any scope satisfied by C is satisfied by at least one of A or B.

Note that this function will change the order of the given scopesets.

Sorting, Merging, and Normalizing

The scopeCompare function sorts the scopes such that a scope ending with a * comes before anything else with the same prefix. For example, a* comes before a and ax.

In a given scopeset, one scope may satisfy another, making the latter superfluous. For example, in ['ab*', 'abcd', 'xyz'] the first scope satisfies the second, so the scopeset is equivalent to ['ab*', 'xyz']. A scopeset that is minimized using this technique is said to be "normalized".

The normalizeScopeSet function will normalize a scopeset. However, it requires that its input is already sorted using scopeCompare. The whole operation looks like this:

let scopeset = ['a', 'a*', 'ab', 'b'];
scopeset.sort(scopeUtils.scopeCompare);
assert.equal(
    ['a*', 'b'],
    scopeUtils.normalizeScopeSet(scopeset));

Given two properly-sorted, normalized scopesets, the mergeScopeSets function will merge them into a new, sorted, normalized scopeset such that any scope satisfied by at least one of the input scopesets is satisfied by the resulting scopeset.