referencejs
Advanced tools
+6
-2
| language: node_js | ||
| node_js: | ||
| - node | ||
| - '7' | ||
| - '6' | ||
| script: | ||
| script: | ||
| - npm run lint | ||
@@ -11,1 +11,5 @@ - npm run flow | ||
| - npm run build | ||
| cache: | ||
| directories: | ||
| - node_modules |
+1
-1
| { | ||
| "name": "referencejs", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "description": "Easily create, update, and manipulate references to values in a JSON document.", | ||
@@ -5,0 +5,0 @@ "main": "lib/reference.js", |
+72
-1
@@ -1,2 +0,3 @@ | ||
| # Referencejs | ||
| # Referencejs [](https://travis-ci.org/mindblight/referencejs) | ||
| Reference JS lets you create references to any location in a JSON tree. | ||
@@ -7,1 +8,71 @@ This is useful for | ||
| 2. For referencing data that doesn't yet exist in the store (e.g. async data) | ||
| 3. (COMING) For normalizing and denormalizing immutable data | ||
| ## A Basic Example | ||
| import { | ||
| createReference, | ||
| resolveReference, | ||
| dereference, | ||
| } from 'referencejs'; | ||
| const user = { | ||
| id: 'abc', | ||
| name: 'John Doe' | ||
| }; | ||
| const reference = createReference(['users', user.id]); | ||
| let store = {}; | ||
| store = resolveReference(store, reference, user); | ||
| dereference(store, reference) === user; | ||
| ## dereferencing using `smartDereference` | ||
| Dereferencing one reference at a time isn't that useful. `smartDereference` dereferences | ||
| every reference in an object. This is useful if you have data structures that differ | ||
| significantly from your store | ||
| import { | ||
| createReference, | ||
| smartDereference, | ||
| } from 'referencejs'; | ||
| const store = { | ||
| users: { | ||
| user1: { | ||
| name: 'John Doe' | ||
| }, | ||
| user2: { | ||
| name: 'Jane Doe' | ||
| }, | ||
| user3: { | ||
| name: 'Billy Doe' | ||
| }, | ||
| user4: { | ||
| name: 'Lucy Doe' | ||
| } | ||
| } | ||
| }; | ||
| const john = createReference(['users', 'user1']); | ||
| const jane = createReference(['users', 'user2']); | ||
| const billy = createReference(['users', 'user3']); | ||
| const lucy = createReference(['users', 'user4']); | ||
| const familyTreeReferences = { | ||
| father: john, | ||
| mother: jane, | ||
| children: [billy, lucy] | ||
| }; | ||
| // familyTree will contain the user objects instead of the references | ||
| const familyTree = smartDereference(store, familyTree); | ||
| ## What's a Reference? | ||
| A reference is an object that contains everything needed to find a value in a JSON object (called the store). | ||
| The signature looks like: | ||
| { | ||
| path : Array<number|string> | ||
| } |
+10
-6
| // @flow | ||
| import isArray from 'lodash/isArray'; | ||
| import isString from 'lodash/isString'; | ||
| import isInteger from 'lodash/isInteger'; | ||
| import isNill from 'lodash/isNil'; | ||
@@ -14,3 +15,4 @@ import isPlainObject from 'lodash/isPlainObject'; | ||
| /* Types */ | ||
| export type Path = string[]; | ||
| export type PathSegment = string | number; | ||
| export type Path = PathSegment[]; | ||
| export type Reference = { | ||
@@ -25,4 +27,6 @@ path :Path, | ||
| path.length > 0 && | ||
| path.every((pathPart) => { | ||
| return isString(pathPart) && pathPart.length > 0; | ||
| path.every((pathSegment) => { | ||
| // TODO: Add lodash typings and remove 'any' typecast | ||
| return (isInteger(pathSegment) && (pathSegment :any) >= 0) | ||
| || isString(pathSegment) && (pathSegment :any).length > 0; | ||
| }); | ||
@@ -36,5 +40,5 @@ } | ||
| /* Reference */ | ||
| export function createReference(path :Path) { | ||
| export function createReference(path :Path) :Reference { | ||
| if (!isPath(path)) { | ||
| throw Error(`path" must be a non-empty array of strings. Received ${path.join(', ')}`); | ||
| throw Error(`path" must be a non-empty array of strings and integers. Received ${path.join(', ')}`); | ||
| } | ||
@@ -45,3 +49,3 @@ | ||
| export function resolveReference(store :Store, reference :Reference, value :*) { | ||
| export function resolveReference(store :Store, reference :Reference, value :*) :Store { | ||
| if (isNill(store)) { | ||
@@ -48,0 +52,0 @@ throw new Error('"store" must be defined'); |
+286
-246
@@ -16,317 +16,357 @@ /* eslint-disable prefer-arrow-callback */ | ||
| describe('isPath', function() { | ||
| it('should return false on non-array', function() { | ||
| expect(isPath({})).to.be.false; | ||
| }); | ||
| describe('reference', function() { | ||
| it('should return false on empty array', function() { | ||
| expect(isPath([])).to.be.false; | ||
| }); | ||
| describe('isPath', function() { | ||
| it('should return false on non-array', function() { | ||
| expect(isPath({})).to.be.false; | ||
| }); | ||
| it('should return false on non-string, non-integer array', function() { | ||
| expect(isPath([{}, 'hi'])).to.be.false; | ||
| }); | ||
| it('should return false on empty array', function() { | ||
| expect(isPath([])).to.be.false; | ||
| }); | ||
| it('should return false on string array with empty string', function() { | ||
| expect(isPath(['', 'hi'])).to.be.false; | ||
| }); | ||
| it('should return false on non-string array', function() { | ||
| expect(isPath([1, 'hi'])).to.be.false; | ||
| }); | ||
| it('should return true on string array', function() { | ||
| expect(isPath(['bye', 'hi'])).to.be.true; | ||
| }); | ||
| it('should return false on string array with empty string', function() { | ||
| expect(isPath(['', 'hi'])).to.be.false; | ||
| }); | ||
| it('should support array indices', function() { | ||
| expect(isPath(['array', 5])).to.be.true; | ||
| }); | ||
| }); | ||
| it('should return true on string array', function() { | ||
| expect(isPath(['bye', 'hi'])).to.be.true; | ||
| }); | ||
| describe('isReference', function() { | ||
| it('should return false on null', function() { | ||
| expect(isReference(null)).to.be.false; | ||
| }); | ||
| it('should return false on object without valid path', function() { | ||
| expect(isReference({})).to.be.false; | ||
| }); | ||
| describe('isReference', function() { | ||
| it('should return false on null', function() { | ||
| expect(isReference(null)).to.be.false; | ||
| }); | ||
| it('should return true on valid reference', function() { | ||
| expect(isReference({ | ||
| path: ['a', 'b'], | ||
| })).to.be.true; | ||
| }); | ||
| }); | ||
| it('should return false on object without valid path', function() { | ||
| expect(isReference({})).to.be.false; | ||
| }); | ||
| it('should return true on valid reference', function() { | ||
| expect(isReference({ | ||
| path: ['a', 'b'], | ||
| })).to.be.true; | ||
| }); | ||
| describe('createReference', function() { | ||
| it('should throw error on null path', function() { | ||
| expect(isValueAtReference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| it('should throw error on invalid path', function() { | ||
| expect(isValueAtReference.bind(null, [3])).to.throw(Error); | ||
| }); | ||
| describe('createReference', function() { | ||
| it('should throw error on null path', function() { | ||
| expect(isValueAtReference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| it('should return valid reference', function() { | ||
| const path = ['a', 'b']; | ||
| const reference = createReference(path); | ||
| it('should throw error on invalid path', function() { | ||
| expect(isValueAtReference.bind(null, [3])).to.throw(Error); | ||
| }); | ||
| expect(isReference(reference)).to.be.true; | ||
| expect(reference).to.have.property('path').equal(path); | ||
| }); | ||
| }); | ||
| it('should return valid reference', function() { | ||
| const path = ['a', 'b']; | ||
| const reference = createReference(path); | ||
| expect(isReference(reference)).to.be.true; | ||
| expect(reference).to.have.property('path').equal(path); | ||
| }); | ||
| describe('resolveReference', function() { | ||
| it('should throw error on empty store', function() { | ||
| expect(resolveReference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| it('should throw error on invalid reference', function() { | ||
| expect(resolveReference.bind(null, {}, {})).to.throw(Error); | ||
| }); | ||
| describe('resolveReference', function() { | ||
| it('should throw error on empty store', function() { | ||
| expect(resolveReference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| it('should not mutate the passed state', function() { | ||
| const reference = createReference(['a', 'b']); | ||
| const state = { | ||
| a: 1, | ||
| b: 'hi', | ||
| }; | ||
| it('should throw error on invalid reference', function() { | ||
| expect(resolveReference.bind(null, {}, {})).to.throw(Error); | ||
| }); | ||
| const expectedState = cloneDeep(state); | ||
| it('should not mutate the passed state', function() { | ||
| const reference = createReference(['a', 'b']); | ||
| const state = { | ||
| a: 1, | ||
| b: 'hi', | ||
| }; | ||
| resolveReference(state, reference, 5); | ||
| const expectedState = cloneDeep(state); | ||
| expect(state).to.deep.equal(expectedState); | ||
| }); | ||
| resolveReference(state, reference, 5); | ||
| it('should return state with value placed at reference.path', function() { | ||
| const path = ['a', 'b']; | ||
| const reference = createReference(path); | ||
| const value = 5; | ||
| const initialState = { | ||
| c: 6, | ||
| }; | ||
| const state = resolveReference(initialState, reference, value); | ||
| expect(state).to.deep.equal(expectedState); | ||
| }); | ||
| expect(state).to.have.property('c').equal(6); | ||
| expect(state).to.have.deep.property(path.join('.')).equal(value); | ||
| }); | ||
| it('should return state with value placed at reference.path', function() { | ||
| const path = ['a', 'b']; | ||
| const reference = createReference(path); | ||
| const value = 5; | ||
| const initialState = { | ||
| c: 6, | ||
| }; | ||
| const state = resolveReference(initialState, reference, value); | ||
| it('should handle arrays in store', function() { | ||
| const path = ['a', 0]; | ||
| const reference = createReference(path); | ||
| const value = 'foo'; | ||
| const initialState = { | ||
| a: [], | ||
| }; | ||
| const state = resolveReference(initialState, reference, value); | ||
| const expectedState = { | ||
| a: [value], | ||
| }; | ||
| expect(state).to.have.property('c').equal(6); | ||
| expect(state).to.have.deep.property(path.join('.')).equal(value); | ||
| }); | ||
| expect(state).to.deep.equal(expectedState); | ||
| }); | ||
| it('should handle deep object creation', function() { | ||
| const path = ['a', 0, 'b']; | ||
| const reference = createReference(path); | ||
| const value = 'foo'; | ||
| const initialState = {}; | ||
| const state = resolveReference(initialState, reference, value); | ||
| const expectedState = { | ||
| a: [{ | ||
| b: value, | ||
| }], | ||
| }; | ||
| describe('isValueAtReference', function() { | ||
| it('should throw error on empty store', function() { | ||
| expect(dereference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| expect(state).to.deep.equal(expectedState); | ||
| }); | ||
| }); | ||
| it('should throw error on invalid reference', function() { | ||
| expect(dereference.bind(null, {}, {})).to.throw(Error); | ||
| }); | ||
| it('should return false when reference does not exist', function() { | ||
| const reference = createReference(['a']); | ||
| expect(isValueAtReference({}, reference)).to.be.false; | ||
| }); | ||
| describe('isValueAtReference', function() { | ||
| it('should throw error on empty store', function() { | ||
| expect(dereference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| it('should return value at reference', function() { | ||
| const value = 9; | ||
| const store = { | ||
| a: { | ||
| b: value, | ||
| }, | ||
| }; | ||
| const reference = createReference(['a', 'b']); | ||
| it('should throw error on invalid reference', function() { | ||
| expect(dereference.bind(null, {}, {})).to.throw(Error); | ||
| }); | ||
| expect(isValueAtReference(store, reference)).to.be.true; | ||
| }); | ||
| it('should return false when reference does not exist', function() { | ||
| const reference = createReference(['a']); | ||
| expect(isValueAtReference({}, reference)).to.be.false; | ||
| }); | ||
| it('should return value at reference', function() { | ||
| const value = 9; | ||
| const store = { | ||
| a: { | ||
| b: value, | ||
| }, | ||
| }; | ||
| const reference = createReference(['a', 'b']); | ||
| describe('dereference', function() { | ||
| it('should throw error on empty store', function() { | ||
| expect(dereference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| expect(isValueAtReference(store, reference)).to.be.true; | ||
| }); | ||
| }); | ||
| it('should throw error on invalid reference', function() { | ||
| expect(dereference.bind(null, {}, {})).to.throw(Error); | ||
| }); | ||
| it('should return EMPTY_REFERENCE when reference does not exist', function() { | ||
| expect(dereference({}, createReference(['a']))).to.equal(EMPTY_REFERENCE); | ||
| }); | ||
| describe('dereference', function() { | ||
| it('should throw error on empty store', function() { | ||
| expect(dereference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| it('should return value at reference', function() { | ||
| const value = 9; | ||
| const store = { | ||
| a: { | ||
| b: value, | ||
| }, | ||
| }; | ||
| const reference = createReference(['a', 'b']); | ||
| it('should throw error on invalid reference', function() { | ||
| expect(dereference.bind(null, {}, {})).to.throw(Error); | ||
| }); | ||
| expect(dereference(store, reference)).to.equal(value); | ||
| }); | ||
| it('should return EMPTY_REFERENCE when reference does not exist', function() { | ||
| expect(dereference({}, createReference(['a']))).to.equal(EMPTY_REFERENCE); | ||
| }); | ||
| it('should return value at reference', function() { | ||
| const value = 9; | ||
| const store = { | ||
| a: { | ||
| b: value, | ||
| }, | ||
| }; | ||
| const reference = createReference(['a', 'b']); | ||
| describe('smartDereference', function() { | ||
| const value1 = 9; | ||
| const value2 = 20; | ||
| let store; | ||
| let reference1; | ||
| let reference2; | ||
| expect(dereference(store, reference)).to.equal(value); | ||
| }); | ||
| beforeEach(function() { | ||
| store = { | ||
| a: { | ||
| b: value1, | ||
| c: value2, | ||
| }, | ||
| }; | ||
| reference1 = createReference(['a', 'b']); | ||
| reference2 = createReference(['a', 'c']); | ||
| }); | ||
| it('should derefence arrays', function() { | ||
| const value = {}; | ||
| const store = { | ||
| a: [value], | ||
| }; | ||
| const reference = createReference(['a', 0]); | ||
| it('should throw error on null store', function() { | ||
| expect(smartDereference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| expect(dereference(store, reference)).to.equal(value); | ||
| }); | ||
| }); | ||
| it('should return EMPTY_REFERENCE when reference does not exist', function() { | ||
| expect(smartDereference({}, createReference(['a']))).to.equal(EMPTY_REFERENCE); | ||
| }); | ||
| it('should return value for non-reference input', function() { | ||
| const value = Symbol(); | ||
| expect(smartDereference(store, value)).to.equal(value); | ||
| }); | ||
| describe('smartDereference', function() { | ||
| const value1 = 9; | ||
| const value2 = 20; | ||
| let store; | ||
| let reference1; | ||
| let reference2; | ||
| it('should return value at reference', function() { | ||
| expect(smartDereference(store, reference1)).to.equal(value1); | ||
| }); | ||
| beforeEach(function() { | ||
| store = { | ||
| a: { | ||
| b: value1, | ||
| c: value2, | ||
| }, | ||
| }; | ||
| reference1 = createReference(['a', 'b']); | ||
| reference2 = createReference(['a', 'c']); | ||
| }); | ||
| it('should return reference in array', function() { | ||
| const input = [reference1, reference2]; | ||
| const expected = [value1, value2]; | ||
| it('should throw error on null store', function() { | ||
| expect(smartDereference.bind(null, null)).to.throw(Error); | ||
| }); | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| it('should return EMPTY_REFERENCE when reference does not exist', function() { | ||
| expect(smartDereference({}, createReference(['a']))).to.equal(EMPTY_REFERENCE); | ||
| }); | ||
| it('should ignore non-reference in array', function() { | ||
| const input = [reference1, value2]; | ||
| const expected = [value1, value2]; | ||
| it('should return value for non-reference input', function() { | ||
| const value = Symbol(); | ||
| expect(smartDereference(store, value)).to.equal(value); | ||
| }); | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| it('should return value at reference', function() { | ||
| expect(smartDereference(store, reference1)).to.equal(value1); | ||
| }); | ||
| it('should deeply handle arrays', function() { | ||
| const input = [reference1, [reference2]]; | ||
| const expected = [value1, [value2]]; | ||
| it('should return reference in array', function() { | ||
| const input = [reference1, reference2]; | ||
| const expected = [value1, value2]; | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| it('should handle plain objects', function() { | ||
| const input = { | ||
| foo: reference1, | ||
| bar: reference2, | ||
| }; | ||
| const expected = { | ||
| foo: value1, | ||
| bar: value2, | ||
| }; | ||
| it('should ignore non-reference in array', function() { | ||
| const input = [reference1, value2]; | ||
| const expected = [value1, value2]; | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| it('should deep handle plain objects', function() { | ||
| const input = { | ||
| foo: reference1, | ||
| bar: { | ||
| baz: reference2, | ||
| }, | ||
| }; | ||
| const expected = { | ||
| foo: value1, | ||
| bar: { | ||
| baz: value2, | ||
| }, | ||
| }; | ||
| it('should deeply handle arrays', function() { | ||
| const input = [reference1, [reference2]]; | ||
| const expected = [value1, [value2]]; | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| it('should not recurse by default', function() { | ||
| reference1 = createReference(['a']); | ||
| reference2 = createReference(['c']); | ||
| const object = { | ||
| b: reference2, | ||
| }; | ||
| store = { | ||
| a: object, | ||
| c: value2, | ||
| }; | ||
| it('should handle plain objects', function() { | ||
| const input = { | ||
| foo: reference1, | ||
| bar: reference2, | ||
| }; | ||
| const expected = { | ||
| foo: value1, | ||
| bar: value2, | ||
| }; | ||
| const input = { | ||
| foo: reference1, | ||
| }; | ||
| const expected = { | ||
| foo: object, | ||
| }; | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| it('should deep handle plain objects', function() { | ||
| const input = { | ||
| foo: reference1, | ||
| bar: { | ||
| baz: reference2, | ||
| }, | ||
| }; | ||
| const expected = { | ||
| foo: value1, | ||
| bar: { | ||
| baz: value2, | ||
| }, | ||
| }; | ||
| it('should dereference itself', function() { | ||
| reference1 = createReference(['b', 'b1']); | ||
| reference2 = createReference(['c']); | ||
| store = { | ||
| a: reference1, | ||
| b: { | ||
| b1: value1, | ||
| }, | ||
| c: value2, | ||
| d: { | ||
| d1: reference2, | ||
| }, | ||
| e: reference1, | ||
| }; | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| const expected = { | ||
| a: value1, | ||
| b: { | ||
| b1: value1, | ||
| }, | ||
| c: value2, | ||
| d: { | ||
| d1: value2, | ||
| }, | ||
| e: value1, | ||
| }; | ||
| it('should not recurse by default', function() { | ||
| reference1 = createReference(['a']); | ||
| reference2 = createReference(['c']); | ||
| const object = { | ||
| b: reference2, | ||
| }; | ||
| store = { | ||
| a: object, | ||
| c: value2, | ||
| }; | ||
| expect(smartDereference(store, store)).to.deep.equal(expected); | ||
| }); | ||
| const input = { | ||
| foo: reference1, | ||
| }; | ||
| const expected = { | ||
| foo: object, | ||
| }; | ||
| it.skip('should not recurse when recurse=true', function() { | ||
| reference1 = createReference(['a']); | ||
| reference2 = createReference(['c']); | ||
| const object = { | ||
| b: reference2, | ||
| }; | ||
| store = { | ||
| a: object, | ||
| c: value2, | ||
| }; | ||
| expect(smartDereference(store, input)).to.deep.equal(expected); | ||
| }); | ||
| const input = { | ||
| foo: reference1, | ||
| }; | ||
| const expected = { | ||
| foo: { | ||
| b: value2, | ||
| }, | ||
| }; | ||
| it('should dereference itself', function() { | ||
| reference1 = createReference(['b', 'b1']); | ||
| reference2 = createReference(['c']); | ||
| store = { | ||
| a: reference1, | ||
| b: { | ||
| b1: value1, | ||
| }, | ||
| c: value2, | ||
| d: { | ||
| d1: reference2, | ||
| }, | ||
| e: reference1, | ||
| }; | ||
| expect(smartDereference(store, input, true)).to.deep.equal(expected); | ||
| }); | ||
| const expected = { | ||
| a: value1, | ||
| b: { | ||
| b1: value1, | ||
| }, | ||
| c: value2, | ||
| d: { | ||
| d1: value2, | ||
| }, | ||
| e: value1, | ||
| }; | ||
| expect(smartDereference(store, store)).to.deep.equal(expected); | ||
| }); | ||
| it.skip('should not recurse when recurse=true', function() { | ||
| reference1 = createReference(['a']); | ||
| reference2 = createReference(['c']); | ||
| const object = { | ||
| b: reference2, | ||
| }; | ||
| store = { | ||
| a: object, | ||
| c: value2, | ||
| }; | ||
| const input = { | ||
| foo: reference1, | ||
| }; | ||
| const expected = { | ||
| foo: { | ||
| b: value2, | ||
| }, | ||
| }; | ||
| expect(smartDereference(store, input, true)).to.deep.equal(expected); | ||
| }); | ||
| }); |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
19612
15.96%459
9.29%78
1014.29%