Skip to content

Commit

Permalink
mask provider feature, ava -> intern, misc.
Browse files Browse the repository at this point in the history
  • Loading branch information
lxghtless committed Sep 8, 2020
1 parent f5f8386 commit 7e73c15
Show file tree
Hide file tree
Showing 14 changed files with 1,659 additions and 1,878 deletions.
5 changes: 5 additions & 0 deletions intern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"suites": "test/**/*.ts",
"coverage": ["src/**/*.ts"],
"reporters": ["runner", "jsoncoverage", "lcov"]
}
48 changes: 6 additions & 42 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,66 +16,30 @@
"mask"
],
"scripts": {
"build": "rimraf dist && tsc",
"test": "eslint ./src/**.* && nyc ava",
"build": "rimraf dist && tsc --project src",
"test": "eslint ./src/**.* && intern",
"clean": "rimraf .nyc_output coverage dist",
"lint": "tsc --noEmit && eslint ./src/**.* --fix"
"lint": "tsc --noEmit && eslint ./src/**.* ./test/**.* --fix"
},
"main": "dist/index.js",
"files": [
"dist",
"src"
"src/*.ts"
],
"dependencies": {},
"devDependencies": {
"@types/node": "14.6.4",
"@typescript-eslint/eslint-plugin": "^2.26.0",
"@typescript-eslint/parser": "^2.26.0",
"@typescript-eslint/typescript-estree": "^4.0.1",
"ava": "^3.12.1",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.1",
"eslint-plugin-prettier": "^3.1.2",
"husky": "^4.3.0",
"minimist": "^1.2.2",
"nyc": "^15.0.0",
"intern": "^4.8.7",
"prettier": "^2.0.2",
"rimraf": "^3.0.2",
"ts-node": "^9.0.0",
"typescript": "^4.0.2",
"typescript-eslint": "^0.0.1-alpha.0"
},
"nyc": {
"all": true,
"reporter": [
"lcov",
"text"
],
"extension": [
".ts"
],
"require": [
"ts-node/register"
],
"include": [
"src/*.ts"
]
},
"ava": {
"extensions": [
"ts",
"js"
],
"require": [
"ts-node/register"
],
"files": [
"test/*.ts"
],
"cache": true,
"concurrency": 2,
"failFast": false,
"verbose": true
"typescript": "^4.0.2"
},
"husky": {
"hooks": {
Expand Down
146 changes: 120 additions & 26 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
</p>

<p align="center">
Given an array of strings build a mask data structure to manage things like user <b>permissions</b> in apps. Servers that are enabled with certain <b>modes</b> or <b>features</b> might also find this useful.
Given an array of strings or a {} of {string: number} build a mask data structure to manage things like user <b>permissions</b> in apps. Servers that are enabled with certain <b>modes</b> or <b>features</b> might also find this useful.
</p>

<p align="center">
<a href="https://www.npmjs.com/package/mode-mask"><img src="https://img.shields.io/npm/v/mode-mask?color=blue"/></a>&nbsp;<a href="https://www.typescriptlang.org/"><img src="https://badgen.net/badge/icon/typescript?icon=typescript&label" /></a>&nbsp;<a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" /></a>
<a href="https://www.npmjs.com/package/mode-mask">
<img src="https://img.shields.io/npm/v/mode-mask?color=blue" />
</a>
<a href="https://www.typescriptlang.org/">
<img src="https://aleen42.github.io/badges/src/typescript.svg" />
</a>
<a href="https://eslint.org/">
<img src="https://aleen42.github.io/badges/src/eslint.svg" />
</a>
</p>

<p align="center">
Expand All @@ -20,31 +28,117 @@

<pre align="center">yarn add mode-mask</pre>

### Basic Usage with MaskFactory

### Basic Usage
```ts
import {buildMaskFactory} from 'mode-mask'

```js
import {buildMaskFactory} from 'mode-mask'
const modes = {
READ: 1,
WRITE: 2,
DELETE: 4,
AUTO_CREATE: 8
}

const modes = {
READ: 1,
WRITE: 2,
DELETE: 4,
AUTO_CREATE: 8
}
// build mask with factory from array of strings
const mask = buildMaskFactory({
values: Object.keys(modes)
})()

// get the MaskDatum of a given permissions sum
const rda = mask.indexOf(modes.READ + modes.DELETE + modes.AUTO_CREATE)
// with an explicit number
const rda = mask.indexOf(13)
// from string values
const rda = mask.fromValues(['READ', 'DELETE', 'AUTO_CREATE'])
```

### Basic Usage with MaskProvider

```ts
import {MaskProvider} from 'mode-mask'

const modes = {
READ: 1,
WRITE: 2,
DELETE: 4,
AUTO_CREATE: 8
}

// build mask with provider with a map of <string, number>
const mask = MaskProvider.resolveMask(modes)

// get the MaskDatum of a given permissions sum
const rda = mask.indexOf(modes.READ + modes.DELETE + modes.AUTO_CREATE)
// with an explicit number
const rda = mask.indexOf(13)
// from string values
const rda = mask.fromValues(['READ', 'DELETE', 'AUTO_CREATE'])
```

### MaskProvider from values with derived mask

```ts
import {MaskProvider} from 'mode-mask'

const expectedModes = {
READ: 1,
WRITE: 2,
DELETE: 4,
AUTO_CREATE: 8
}

const maskProvider = MaskProvider.fromModesOrValues(Object.keys(expectedModes))
const {mask, modes, values} = maskProvider

const buildMask = buildMaskFactory({
values: Object.keys(modes)
})

const mask = buildMask()

// each of these locate a defined index
t.truthy(mask[modes.READ])
t.truthy(mask[modes.WRITE + modes.DELETE])
t.truthy(mask.indexOf(modes.DELETE))
t.truthy(mask.indexOf(modes.READ + modes.DELETE + modes.AUTO_CREATE))
// test simple value combos work & ignore case
t.truthy(mask.fromValues(['READ', 'WRITE']))
t.truthy(mask.fromValues(['read', 'Write'], true))
```
// get the MaskDatum of a given permissions sum
const rda = mask.indexOf(modes.READ + modes.DELETE + modes.AUTO_CREATE)
// with an explicit number
const rda = mask.indexOf(13)
// from string values
const rda = mask.fromValues(['READ', 'DELETE', 'AUTO_CREATE'])

// modes are derived from values
expect(modes).to.deep.equal(expectedModes)
```

### MaskProvider from values with derived mask

```ts
import {MaskProvider} from 'mode-mask'

const expectedModes = {
READ: 1,
WRITE: 2,
DELETE: 4,
AUTO_CREATE: 8
}

const maskProvider = MaskProvider.fromModesOrValues(expectedModes)
const {mask, modes, values} = maskProvider

// get the MaskDatum of a given permissions sum
const rda = mask.indexOf(modes.READ + modes.DELETE + modes.AUTO_CREATE)
// with an explicit number
const rda = mask.indexOf(13)
// from string values
const rda = mask.fromValues(['READ', 'DELETE', 'AUTO_CREATE'])

// values are derived from modes
expect(values).to.deep.equal(Object.keys(expectedModes))
```

### rda output from usage examples

```json
{
"sum": 13,
"values": ["READ", "DELETE", "AUTO_CREATE"],
"nums": [1, 4, 8],
"map": {
"READ": 1,
"DELETE": 4,
"AUTO_CREATE": 8
}
}
```
9 changes: 8 additions & 1 deletion src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {calcSum, validateAndCreateContext} from './helper'

export type BuildMaskFunction = () => Mask

export const buildMaskFactory = ({values}: MaskOptions): BuildMaskFunction => {
export const buildMaskFactory = (
{values}: MaskOptions,
modesRef?: {[x: string]: number}
): BuildMaskFunction => {
const ctx = validateAndCreateContext(values)
const {mask} = ctx
let {map, combos, pw2s} = ctx
Expand All @@ -28,6 +31,10 @@ export const buildMaskFactory = ({values}: MaskOptions): BuildMaskFunction => {
combos.push(values[j])
pw2s.push(pw2)
map[values[j]] = pw2

if (modesRef) {
modesRef[values[j]] = pw2
}
}
}

Expand Down
29 changes: 28 additions & 1 deletion src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,40 @@
@module: helper
*/

import {MaskContext} from './interfaces'
import {MaskContext, ValidateMaskModesResult} from './interfaces'
import {MdMask} from './mask'

export const calcSum = (nums: number[]): number => {
return nums.reduce((total: number, num: number) => total + num)
}

export const isPowerOf2 = (n: number): boolean => {
if (typeof n !== 'number' || !Number.isInteger(n)) {
return false
}

return n !== 0 && (n & (n - 1)) === 0
}

export const validateMaskModes = (modes: {
[x: string]: number
}): ValidateMaskModesResult => {
const errors: string[] = []

for (const [value, n] of Object.entries(modes)) {
if (!isPowerOf2(n)) {
errors.push(
`numeric value of ${n} for "${value}" is not a power of 2`
)
}
}

return {
errors,
isValid: errors.length === 0
}
}

export const emptyArray = (values: string[]): string | undefined => {
if (!Array.isArray(values)) {
return 'values must be []'
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export {
} from './interfaces'
export {MdMask} from './mask'
export {registerMask, resolveMask} from './registry'
export {MaskProvider} from './mask-provider'
7 changes: 6 additions & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

export interface Mask {
[x: number]: MaskDatum
indexOf(n: number): MaskDatum | undefined
fromValues(values: string[], ignoreCase?: boolean): MaskDatum | undefined
indexOf(n: number): MaskDatum | undefined
}

export interface MaskCache {
Expand Down Expand Up @@ -33,3 +33,8 @@ export interface MaskOptions {
export interface StrNumMap {
[x: string]: number
}

export interface ValidateMaskModesResult {
errors: string[]
isValid: boolean
}
Loading

0 comments on commit 7e73c15

Please sign in to comment.