Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fun-model

Package Overview
Dependencies
Maintainers
1
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fun-model - npm Package Compare versions

Comparing version 6.5.0 to 7.0.0

8

CHANGELOG.md
CHANGELOG
===
6.5.0
7.0.0
--
Set
Fixed
-
Error for throw cases instead of simple message for better call stack in those cases.
Typescript 4.8.3 state type constraints.
* removed IState constraint in CursorType,
* fixed Object type check for shallowCopy.

@@ -11,0 +13,0 @@ 6.4.0

@@ -0,0 +0,0 @@ import * as s from './src/store';

{
"name": "fun-model",
"version": "6.5.0",
"version": "7.0.0",
"description": "fun-model is pure functional implementation of FLUX architecture.",

@@ -34,4 +34,8 @@ "main": "./index.js",

},
"prettier": {
"tabWidth": 4,
"singleQuote": true
},
"devDependencies": {},
"license": "MIT"
}

@@ -0,0 +0,0 @@ # FUN-Model

@@ -7,3 +7,3 @@ import * as s from '../src/store';

describe('actionFactory', () => {
let debugCallback: d.debugCallbackType
let debugCallback: d.debugCallbackType;
let renderCallback: () => void;

@@ -23,3 +23,6 @@

expect(debugCallback).toHaveBeenCalledWith('Action factory has been initialized.', undefined);
expect(debugCallback).toHaveBeenCalledWith(
'Action factory has been initialized.',
undefined
);
});

@@ -35,4 +38,7 @@ });

givenTodosStore({
todos: [{ done: false, name: 'First todo' }, { done: false, name: 'Second todo' }],
nullableNumber: null
todos: [
{ done: false, name: 'First todo' },
{ done: false, name: 'Second todo' },
],
nullableNumber: null,
});

@@ -44,7 +50,12 @@

return { key: `todos.${params.index}` };
}
},
},
(_state: tds.ITodo, params: tds.ITodoParams) => { return params.todo }
(_state: tds.ITodo, params: tds.ITodoParams) => {
return params.todo;
}
);
testAction({ index: 1, todo: { done: false, name: 'New second todo' } });
testAction({
index: 1,
todo: { done: false, name: 'New second todo' },
});

@@ -57,7 +68,19 @@ expect(getTodos()[1].done).toBeFalsy();

givenTodosStore({
todos: [{ done: false, name: 'First todo' }, { done: false, name: 'Second todo' }],
nullableNumber: null
todos: [
{ done: false, name: 'First todo' },
{ done: false, name: 'Second todo' },
],
nullableNumber: null,
});
let testAction = af.createAction<boolean, number>({ create: (index) => { return { key: `todos.${index}.done` } } }, () => { return true; });
let testAction = af.createAction<boolean, number>(
{
create: (index) => {
return { key: `todos.${index}.done` };
},
},
() => {
return true;
}
);
testAction(1);

@@ -82,5 +105,8 @@

af.bootstrap(renderCallback, false);
throwingAction = af.createParamLessAction(NestedCursorTestFixture, () => {
throw 'MyDummyException';
});
throwingAction = af.createParamLessAction(
NestedCursorTestFixture,
() => {
throw 'MyDummyException';
}
);
});

@@ -97,5 +123,8 @@

af.bootstrap(renderCallback, true);
throwingAction = af.createParamLessAction(NestedCursorTestFixture, () => {
throw 'MyDummyException';
});
throwingAction = af.createParamLessAction(
NestedCursorTestFixture,
() => {
throw 'MyDummyException';
}
);
});

@@ -110,3 +139,6 @@

expect(debugCallback).toHaveBeenCalledWith('Action factory has been initialized.', undefined);
expect(debugCallback).toHaveBeenCalledWith(
'Action factory has been initialized.',
undefined
);
});

@@ -117,3 +149,6 @@ });

it('does not throw if action has been only declared.', () => {
let testAction = af.createAction(NestedCursorTestFixture, (state: INestedState) => state);
let testAction = af.createAction(
NestedCursorTestFixture,
(state: INestedState) => state
);
expect(testAction).not.toBeUndefined();

@@ -124,5 +159,12 @@ });

expect(() => {
let testAction = af.createParamLessAction(NestedCursorTestFixture, () => { return { state: 'new nested state' }; });
let testAction = af.createParamLessAction(
NestedCursorTestFixture,
() => {
return { state: 'new nested state' };
}
);
testAction();
}).toThrowError('Render callback must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).');
}).toThrowError(
'Render callback must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).'
);
});

@@ -142,5 +184,11 @@ });

af.createParamLessAction(NestedCursorTestFixture, (state: INestedState) => state)();
af.createParamLessAction(
NestedCursorTestFixture,
(state: INestedState) => state
)();
expect(debugCallback).not.toHaveBeenCalledWith('Global state has been changed.', undefined);
expect(debugCallback).not.toHaveBeenCalledWith(
'Global state has been changed.',
undefined
);
});

@@ -152,5 +200,11 @@

af.createParamLessAction(NestedCursorTestFixture, () => newState)();
af.createParamLessAction(
NestedCursorTestFixture,
() => newState
)();
expect(debugCallback).toHaveBeenCalledWith('Global state has been changed.', undefined);
expect(debugCallback).toHaveBeenCalledWith(
'Global state has been changed.',
undefined
);
});

@@ -161,3 +215,6 @@

let testAction = af.createParamLessAction(NestedCursorTestFixture, (state: INestedState) => state);
let testAction = af.createParamLessAction(
NestedCursorTestFixture,
(state: INestedState) => state
);
testAction();

@@ -171,3 +228,8 @@

let testAction = af.createParamLessAction(NestedCursorTestFixture, () => { return { state: 'newValue' }; })
let testAction = af.createParamLessAction(
NestedCursorTestFixture,
() => {
return { state: 'newValue' };
}
);
testAction();

@@ -181,10 +243,20 @@

const nestedAction2 = af.createParamLessAction(NestedCursorTestFixture, (state: INestedState) => {
return { state: `${state.state} -> newValueFromNestedAction2` };
});
const nestedAction2 = af.createParamLessAction(
NestedCursorTestFixture,
(state: INestedState) => {
return {
state: `${state.state} -> newValueFromNestedAction2`,
};
}
);
const nestedAction = af.createParamLessAction(NestedCursorTestFixture, (state: INestedState) => {
nestedAction2();
return { state: `${state.state} -> newValueFromNestedAction` };
});
const nestedAction = af.createParamLessAction(
NestedCursorTestFixture,
(state: INestedState) => {
nestedAction2();
return {
state: `${state.state} -> newValueFromNestedAction`,
};
}
);

@@ -196,4 +268,5 @@ af.createParamLessAction(NestedCursorTestFixture, () => {

expect(s.getState(NestedCursorTestFixture).state)
.toBe('newValue -> newValueFromNestedAction -> newValueFromNestedAction2');
expect(s.getState(NestedCursorTestFixture).state).toBe(
'newValue -> newValueFromNestedAction -> newValueFromNestedAction2'
);
});

@@ -204,6 +277,9 @@

givenStore(aState('nestedStateValue'));
const testAction = af.createParamLessAction(SomeCursorTestFixture, (state: ISomeState) => {
state.nested.state = state.nested.state + 'newValue';
return state;
});
const testAction = af.createParamLessAction(
SomeCursorTestFixture,
(state: ISomeState) => {
state.nested.state = state.nested.state + 'newValue';
return state;
}
);

@@ -223,3 +299,5 @@ expect(() => {

givenStore(aState('nestedStateValue'));
const testAction = af.createReplaceAction<string>(NestedStateCursorTestFixture);
const testAction = af.createReplaceAction<string>(
NestedStateCursorTestFixture
);

@@ -232,3 +310,5 @@ testAction('newNestedStateValue');

givenStore(aState('nestedStateValue'));
const testAction = af.createReplaceAction<string>(NestedStateCursorTestFixture);
const testAction = af.createReplaceAction<string>(
NestedStateCursorTestFixture
);

@@ -240,17 +320,25 @@ testAction('nestedStateValue');

givenStore(aState('nestedStateValue'));
const testAction = af.createReplaceAction<string>(NestedStateCursorTestFixture);
const testAction = af.createReplaceAction<string>(
NestedStateCursorTestFixture
);
testAction('newNestedStateValue');
expect(s.getState(NestedStateCursorTestFixture)).toBe('newNestedStateValue');
expect(s.getState(NestedStateCursorTestFixture)).toBe(
'newNestedStateValue'
);
});
it('does not replace the same state', () => {
givenStore(aState('nestedStateValue'));
const testAction = af.createReplaceAction<string>(NestedStateCursorTestFixture);
const testAction = af.createReplaceAction<string>(
NestedStateCursorTestFixture
);
testAction('nestedStateValue');
expect(s.getState(NestedStateCursorTestFixture)).toBe('nestedStateValue');
expect(s.getState(NestedStateCursorTestFixture)).toBe(
'nestedStateValue'
);
});
})
});

@@ -263,6 +351,8 @@ describe('createActions', () => {

cursor: NestedCursorTestFixture,
handler: (state: INestedState): INestedState => state
handler: (state: INestedState): INestedState => state,
});
testAction();
}).toThrowError('Render callback must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).');
}).toThrowError(
'Render callback must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).'
);
});

@@ -285,7 +375,9 @@ });

cursor: NestedCursorTestFixture,
handler: (state: INestedState): INestedState => state
}, {
handler: (state: INestedState): INestedState => state,
},
{
cursor: NestedCursorTestFixture,
handler: (state: INestedState): INestedState => state
});
handler: (state: INestedState): INestedState => state,
}
);
testAction();

@@ -299,11 +391,19 @@

let testAction = af.createParamLessActions(
let testAction = af.createParamLessActions<any>(
{
cursor: SomeCursorTestFixture,
handler: (state: ISomeState): ISomeState => { return { nested: state.nested, state: 'newStateValue' } }
handler: (state: ISomeState): ISomeState => {
return {
nested: state.nested,
state: 'newStateValue',
};
},
},
{
cursor: NestedCursorTestFixture,
handler: (): INestedState => { return { state: 'newNestedStateValue' }; }
});
handler: (): INestedState => {
return { state: 'newNestedStateValue' };
},
}
);
testAction();

@@ -325,3 +425,6 @@

function aState(nestedState: string = 'aNestedState', state: string = 'aState'): IStateTestFixture {
function aState(
nestedState: string = 'aNestedState',
state: string = 'aState'
): IStateTestFixture {
return { some: { nested: { state: nestedState }, state: state } };

@@ -335,3 +438,3 @@ }

interface ISomeState extends s.IState {
nested: INestedState
nested: INestedState;
state: string;

@@ -345,11 +448,11 @@ }

var NestedCursorTestFixture: s.ICursor<INestedState> = {
key: 'some.nested'
}
key: 'some.nested',
};
var NestedStateCursorTestFixture: s.ICursor<string> = {
key: 'some.nested.state'
}
key: 'some.nested.state',
};
var SomeCursorTestFixture: s.ICursor<ISomeState> = {
key: 'some'
}
key: 'some',
};

@@ -13,4 +13,4 @@ import * as h from '../src/helpers';

subObject: {
id: 'anSubId'
}
id: 'anSubId',
},
};

@@ -42,3 +42,3 @@ });

it('sets properties in callback without return', () => {
let newState = h.shallowCopy(aState, s => {
let newState = h.shallowCopy(aState, (s) => {
s.id = 'newId';

@@ -53,7 +53,9 @@ s.subObject = { id: 'newSubId' };

it('sets properties in nested shallowCopy', () => {
let newState = h.shallowCopy(aState, s => h.shallowCopy(s, a => {
a.id = 'newId';
a.subObject = { id: 'newSubId' };
return a;
}));
let newState = h.shallowCopy(aState, (s) =>
h.shallowCopy(s, (a) => {
a.id = 'newId';
a.subObject = { id: 'newSubId' };
return a;
})
);

@@ -65,3 +67,3 @@ expect(newState.id).toBe('newId');

it('sets properties in inline style', () => {
let newState = h.shallowCopy(aState, s => {
let newState = h.shallowCopy(aState, (s) => {
s.id = 'newId';

@@ -80,3 +82,3 @@ s.subObject = { id: 'newSubId' };

beforeEach(() => {
newState = h.shallowCopy(aState, ns => {
newState = h.shallowCopy(aState, (ns) => {
ns.list = h.shallowCopy(ns.list);

@@ -96,7 +98,10 @@ });

for (let key in newState.list) {
if (newState.list.hasOwnProperty(key) && typeof key !== 'function') {
if (
newState.list.hasOwnProperty(key) &&
typeof key !== 'function'
) {
expect(newState.list[key]).toBe(aState.list[key]);
}
}
})
});
});

@@ -126,3 +131,6 @@

it('respects returned value', () => {
const returnValue = h.shallowCopy('test', (_original: string) => 'changed value');
const returnValue = h.shallowCopy(
'test',
(_original: string) => 'changed value'
);

@@ -142,7 +150,7 @@ expect(returnValue).toBe('changed value');

subObject: {
id: 'anSubId'
}
id: 'anSubId',
},
};
h.deepFreeze(state);
})
});

@@ -160,9 +168,9 @@ it('freezes root object', () => {

});
})
});
});
interface IDummyState {
id: string
list: number[]
subObject: any
}
id: string;
list: number[];
subObject: any;
}

@@ -40,3 +40,5 @@ import * as s from '../src/store';

it('returns false when value does not exist in state through cursor', () => {
const exists = s.isExistingCursor({ key: 'some.nested.notExistingState' });
const exists = s.isExistingCursor({
key: 'some.nested.notExistingState',
});

@@ -48,7 +50,11 @@ expect(exists).toBeFalsy();

beforeEach(() => {
s.bootstrap({ some: { nested: { arrayState: ['value1', 'value2'] } } });
s.bootstrap({
some: { nested: { arrayState: ['value1', 'value2'] } },
});
});
it('returns true when value exists in state through cursor', () => {
const exists = s.isExistingCursor({ key: 'some.nested.arrayState.0' });
const exists = s.isExistingCursor({
key: 'some.nested.arrayState.0',
});

@@ -59,3 +65,5 @@ expect(exists).toBeTruthy();

it('returns false when value does not exist in state through cursor', () => {
const exists = s.isExistingCursor({ key: 'some.nested.arrayState.5' });
const exists = s.isExistingCursor({
key: 'some.nested.arrayState.5',
});

@@ -70,4 +78,5 @@ expect(exists).toBeFalsy();

it('throws if key does not exist', () => {
expect(() => s.getState<s.IState>(s.rootCursor))
.toThrowError('Default state must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).');
expect(() => s.getState<s.IState>(s.rootCursor)).toThrowError(
'Default state must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).'
);
});

@@ -98,4 +107,7 @@ });

it('throws if key does not exist', () => {
expect(() => s.getState<s.IState>({ key: 'notExistingKey' }))
.toThrowError('State for cursor key (notExistingKey) does not exist.');
expect(() =>
s.getState<s.IState>({ key: 'notExistingKey' })
).toThrowError(
'State for cursor key (notExistingKey) does not exist.'
);
});

@@ -106,6 +118,9 @@

const state = s.getState({ key: 'some.nested.optional', isUndefinable: true });
const state = s.getState({
key: 'some.nested.optional',
isUndefinable: true,
});
expect(state).toBeUndefined();
})
});
});

@@ -120,4 +135,7 @@

givenTodoStore({
todos: [{ done: false, name: 'First Todo' }, { done: false, name: 'Second Todo' }],
nullableNumber: null
todos: [
{ done: false, name: 'First Todo' },
{ done: false, name: 'Second Todo' },
],
nullableNumber: null,
});

@@ -133,3 +151,3 @@

todos: [{ done: false, name: 'First Todo' }],
nullableNumber: null
nullableNumber: null,
});

@@ -151,4 +169,5 @@

it('throws if key does not exist', () => {
expect(() => s.setState(s.rootCursor, {}))
.toThrowError('Default state must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).');
expect(() => s.setState(s.rootCursor, {})).toThrowError(
'Default state must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).'
);
});

@@ -159,3 +178,3 @@ });

const rootCursorTestFixture: s.ICursor<IStateTestFixture> = {
key: ''
key: '',
};

@@ -169,4 +188,3 @@

const cursor = { key: 'invalid' };
expect(() => s.setState(cursor, {}))
.toThrowError();
expect(() => s.setState(cursor, {})).toThrowError();
});

@@ -178,3 +196,6 @@

expect(s.getState(s.rootCursor)).toEqual({ key: null, 'invalid': {} });
expect(s.getState(s.rootCursor)).toEqual({
key: null,
invalid: {},
});
});

@@ -186,3 +207,6 @@

expect(s.getState(s.rootCursor)).toEqual({ key: null, 'not': { 'existing': { key: {} } } });
expect(s.getState(s.rootCursor)).toEqual({
key: null,
not: { existing: { key: {} } },
});
});

@@ -204,3 +228,5 @@

expect((s.getState(rootCursorTestFixture)).some.nested.state).toBe('newValue');
expect(
s.getState(rootCursorTestFixture).some.nested.state
).toBe('newValue');
});

@@ -213,3 +239,5 @@

expect((s.getState(rootCursorTestFixture)).some.nested.state).toBe('newValue');
expect(
s.getState(rootCursorTestFixture).some.nested.state
).toBe('newValue');
});

@@ -232,3 +260,6 @@

expect(debugCallback).toHaveBeenCalledWith('Current state:', s.getState(s.rootCursor));
expect(debugCallback).toHaveBeenCalledWith(
'Current state:',
s.getState(s.rootCursor)
);
});

@@ -251,3 +282,6 @@

s.setState({ key: 'some.nested.optional', isUndefinable: true }, 'newValue');
s.setState(
{ key: 'some.nested.optional', isUndefinable: true },
'newValue'
);

@@ -266,4 +300,7 @@ const newState = s.getState(rootCursorTestFixture);

givenTodoStore({
todos: [{ done: false, name: 'First Todo' }, { done: false, name: 'Second Todo' }],
nullableNumber: null
todos: [
{ done: false, name: 'First Todo' },
{ done: false, name: 'Second Todo' },
],
nullableNumber: null,
});

@@ -273,3 +310,5 @@

expect(s.getState({ key: 'todos.1.name' })).toBe('New Todo Name');
expect(s.getState({ key: 'todos.1.name' })).toBe(
'New Todo Name'
);
});

@@ -280,15 +319,24 @@

todos: [{ done: false, name: 'First Todo' }],
nullableNumber: null
nullableNumber: null,
});
s.setState({ key: 'todos.0' }, { done: false, name: 'Second Todo' });
s.setState(
{ key: 'todos.0' },
{ done: false, name: 'Second Todo' }
);
expect(s.getState({ key: 'todos.0' })).toEqual({ done: false, name: 'Second Todo' });
expect(s.getState({ key: 'todos.0' })).toEqual({
done: false,
name: 'Second Todo',
});
});
it('sets new instance of array when nested item has been changed', () => {
const storedTodos = [{ done: false, name: 'First Todo' }, { done: false, name: 'Second Todo' }];
const storedTodos = [
{ done: false, name: 'First Todo' },
{ done: false, name: 'Second Todo' },
];
givenTodoStore({
todos: storedTodos,
nullableNumber: null
nullableNumber: null,
});

@@ -304,6 +352,9 @@

todos: [{ done: false, name: 'First Todo' }],
nullableNumber: null
nullableNumber: null,
});
s.setState(tds.todosCursor, [{ done: false, name: 'Second Todo' }, { done: false, name: 'Third Todo' }]);
s.setState(tds.todosCursor, [
{ done: false, name: 'Second Todo' },
{ done: false, name: 'Third Todo' },
]);

@@ -340,3 +391,3 @@ expect(s.getState(tds.todosCursor).length).toBe(2);

todos: [{ done: false, name: 'First Todo' }],
nullableNumber: 10
nullableNumber: 10,
});

@@ -365,4 +416,4 @@

optional?: string;
}
};
};
}

@@ -11,22 +11,22 @@ import * as s from '../src/store';

todos: [],
nullableNumber: null
}
}
nullableNumber: null,
};
};
export interface ITodo {
done: boolean
name: string
done: boolean;
name: string;
}
export interface ITodoParams {
todo: ITodo
index: number
todo: ITodo;
index: number;
}
export const todosCursor: s.ICursor<ITodo[]> = {
key: 'todos'
}
key: 'todos',
};
export const nullableNumberCursor: s.ICursor<number | null> = {
key: 'nullableNumber'
}
key: 'nullableNumber',
};

@@ -8,3 +8,6 @@ import * as s from './store';

export const bootstrap = (onStateChanged: (() => void) | null, withExceptionHandling: boolean | (() => boolean) = false) => {
export const bootstrap = (
onStateChanged: (() => void) | null,
withExceptionHandling: boolean | (() => boolean) = false
) => {
stateChanged = onStateChanged;

@@ -24,3 +27,6 @@ queueOfHandlers = [];

export type IActionHandler<TState, TParams> = (state: TState, t: TParams) => TState;
export type IActionHandler<TState, TParams> = (
state: TState,
t: TParams
) => TState;

@@ -31,6 +37,9 @@ export type IParamLessActionHandler<TState> = (state: TState) => TState;

const renderCallbackMustBeSetBefore = 'Render callback must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).';
const renderCallbackMustBeSetBefore =
'Render callback must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).';
export const createAction = <TState, TParams>(cursor: s.CursorType<TState> | s.ICursorFactory<TState, TParams>, handler: IActionHandler<TState, TParams>)
: IAction<TParams> => {
export const createAction = <TState, TParams>(
cursor: s.CursorType<TState> | s.ICursorFactory<TState, TParams>,
handler: IActionHandler<TState, TParams>
): IAction<TParams> => {
return (params: TParams): void => {

@@ -40,3 +49,7 @@ if (stateChanged === null)

if (changeStateWithQueue(unifyCursor(cursor, params), (state) => handler(state, params))) {
if (
changeStateWithQueue(unifyCursor(cursor, params), (state) =>
handler(state, params)
)
) {
stateChanged();

@@ -48,9 +61,12 @@ d.log('Rendering invoked...');

export const createReplaceAction = <TState>(cursor: s.CursorType<TState> | s.ICursorFactory<TState, TState>)
: IAction<TState> => {
export const createReplaceAction = <TState>(
cursor: s.CursorType<TState> | s.ICursorFactory<TState, TState>
): IAction<TState> => {
return createAction(cursor, (_state, params) => params);
};
export const createParamLessAction = <TState>(cursor: s.CursorType<TState> | s.ICursorFactory<TState, TState>, handler: IParamLessActionHandler<TState>)
: IParamLessAction => {
export const createParamLessAction = <TState>(
cursor: s.CursorType<TState> | s.ICursorFactory<TState, TState>,
handler: IParamLessActionHandler<TState>
): IParamLessAction => {
return (): void => {

@@ -67,6 +83,13 @@ if (stateChanged === null)

function unifyCursor<TState, TParams>(cursor: s.CursorType<TState> | s.ICursorFactory<TState, TParams>, params: TParams): s.ICursor<TState> {
function unifyCursor<TState, TParams>(
cursor: s.CursorType<TState> | s.ICursorFactory<TState, TParams>,
params: TParams
): s.ICursor<TState> {
return (<any>cursor).create instanceof Function
? <s.ICursor<TState>>(<any>cursor).create(params)
: <s.ICursor<TState>>(s.isCursorFunction(<s.CursorType<TState>>cursor) ? (<() => s.ICursor<TState>>cursor)() : cursor);
: <s.ICursor<TState>>(
(s.isCursorFunction(<s.CursorType<TState>>cursor)
? (<() => s.ICursor<TState>>cursor)()
: cursor)
);
}

@@ -76,6 +99,8 @@

cursor: s.ICursor<TState>;
handler: (state: TState, t: TParam) => TState
handler: (state: TState, t: TParam) => TState;
}
export const createActions = <TState, TParams>(...pairs: IPair<TState, TParams>[]): IAction<TParams> => {
export const createActions = <TState, TParams>(
...pairs: IPair<TState, TParams>[]
): IAction<TParams> => {
return (params: TParams) => {

@@ -89,3 +114,7 @@ if (stateChanged === null)

let pair = pairs[i];
if (changeStateWithQueue(pair.cursor, (state) => pair.handler(state, params)))
if (
changeStateWithQueue(pair.cursor, (state) =>
pair.handler(state, params)
)
)
changed = true;

@@ -99,6 +128,8 @@ }

cursor: s.ICursor<TState>;
handler: (state: TState) => TState
handler: (state: TState) => TState;
}
export const createParamLessActions = <TState>(...pairs: IParamLessPair<TState>[]): IParamLessAction => {
export const createParamLessActions = <TState>(
...pairs: IParamLessPair<TState>[]
): IParamLessAction => {
return () => {

@@ -124,13 +155,19 @@ if (stateChanged === null)

let queueOfHandlers: IQueuedHandling<any>[] = [];
function changeStateWithQueue<TState>(cursor: s.ICursor<TState>, handler: IInternalActionHandler<TState>)
: boolean {
function changeStateWithQueue<TState>(
cursor: s.ICursor<TState>,
handler: IInternalActionHandler<TState>
): boolean {
queueOfHandlers.push({ cursor, handler });
if (queueOfHandlers.length > 1)
return false;
if (queueOfHandlers.length > 1) return false;
let isStateChanged = false;
while (queueOfHandlers.length > 0) {
let n = queueOfHandlers[0];
if (h.isFunction(exceptionHandling) ? exceptionHandling() : exceptionHandling)
if (
h.isFunction(exceptionHandling)
? exceptionHandling()
: exceptionHandling
)
try {
isStateChanged = changeState(n.cursor, n.handler) || isStateChanged;
isStateChanged =
changeState(n.cursor, n.handler) || isStateChanged;
} catch (error) {

@@ -148,10 +185,11 @@ d.log('Error in action handling: ', error);

function changeState<TState>(cursor: s.ICursor<TState>, handler: IInternalActionHandler<TState>)
: boolean {
function changeState<TState>(
cursor: s.ICursor<TState>,
handler: IInternalActionHandler<TState>
): boolean {
let oldState = s.getState(cursor);
let newState = handler(oldState);
if (oldState === newState)
return false;
if (oldState === newState) return false;
s.setState(cursor, newState);
return true;
}

@@ -0,4 +1,3 @@

export type debugCallbackType = (message: string, params?: any) => void;
export type debugCallbackType = (message: string, params?: any) => void
export let debug: debugCallbackType | undefined = undefined;

@@ -12,2 +11,2 @@

debug && debug(message, params);
}
};

@@ -1,4 +0,7 @@

import { debug } from "./debug";
import { debug } from './debug';
export function shallowCopy<T>(source: T, callback?: (target: T) => void | T): T {
export function shallowCopy<T>(
source: T,
callback?: (target: T) => void | T
): T {
if (source instanceof Array) {

@@ -9,9 +12,14 @@ source = [...source] as unknown as T;

}
if (typeof source === "object") {
return objectShallowCopy(source, callback);
if (isObjectType(source)) {
return objectShallowCopy<T & Object>(
source,
callback ? (t) => callback(t) as T & Object : undefined
);
}
if (debug) {
debug("Shallow copy should not be used for primitive types.");
debug('Shallow copy should not be used for primitive types.');
}
const result = callback && callback(source);

@@ -21,3 +29,10 @@ return result || source;

export function objectShallowCopy<T extends Object>(source: T, callback: (target: T) => void | T = (_t: T) => { }): T {
function isObjectType(source: unknown): source is Object {
return typeof source === 'object';
}
export function objectShallowCopy<T extends Object>(
source: T,
callback: (target: T) => void | T = (_t: T) => {}
): T {
const target = <T>{};

@@ -30,3 +45,3 @@ for (var property in source)

return <T>result || target;
};
}

@@ -37,6 +52,9 @@ export function deepFreeze<T extends Object>(source: T): T {

const sourceProperty = (<any>source)[property];
if (source.hasOwnProperty(property)
&& sourceProperty !== null
&& (typeof sourceProperty === "object" || typeof sourceProperty === "function")
&& !Object.isFrozen(sourceProperty))
if (
source.hasOwnProperty(property) &&
sourceProperty !== null &&
(typeof sourceProperty === 'object' ||
typeof sourceProperty === 'function') &&
!Object.isFrozen(sourceProperty)
)
deepFreeze(sourceProperty);

@@ -46,6 +64,6 @@ }

return source;
};
}
export function isFunction(val: any): val is Function {
return typeof val == "function";
}
return typeof val == 'function';
}
import * as h from './helpers';
import * as d from './debug';
export interface IState {
}
export interface IState {}
export type CursorType<TState extends IState> = (() => ICursor<TState>) | ICursor<TState>;
export function createNestedCursor<TState extends IState, TNestedState extends IState>(cursor: CursorType<TState>, nestedStateKey: string): CursorType<TNestedState> {
export type CursorType<TState> = (() => ICursor<TState>) | ICursor<TState>;
export function createNestedCursor<
TState extends IState,
TNestedState extends IState
>(
cursor: CursorType<TState>,
nestedStateKey: string
): CursorType<TNestedState> {
if (isCursorFunction(cursor))
return () => { return { key: cursor().key + stateSeparator + nestedStateKey } }
else
return { key: cursor.key + stateSeparator + nestedStateKey };
return () => {
return { key: cursor().key + stateSeparator + nestedStateKey };
};
else return { key: cursor.key + stateSeparator + nestedStateKey };
}
export function createNestedCursorFactory<TRootState extends IState, TNestedState extends IState>(key: string) {
return (cursor: CursorType<TRootState>) => createNestedCursor<TRootState, TNestedState>(cursor, key);
export function createNestedCursorFactory<
TRootState extends IState,
TNestedState extends IState
>(key: string) {
return (cursor: CursorType<TRootState>) =>
createNestedCursor<TRootState, TNestedState>(cursor, key);
}
export function isCursorFunction<TState extends IState>(cursor: CursorType<TState>): cursor is () => ICursor<TState> {
return typeof cursor == "function";
export function isCursorFunction<TState>(
cursor: CursorType<TState>
): cursor is () => ICursor<TState> {
return typeof cursor == 'function';
}

@@ -39,6 +51,10 @@

export const rootCursor: ICursor<IState> = {
key: rootStateKey
key: rootStateKey,
};
export const bootstrap = (defaultState: IState | null, withStateFreezing: boolean | (() => boolean) = false, subStateSeparator: string = '.') => {
export const bootstrap = (
defaultState: IState | null,
withStateFreezing: boolean | (() => boolean) = false,
subStateSeparator: string = '.'
) => {
stateSeparator = subStateSeparator;

@@ -49,5 +65,10 @@ state = defaultState;

export const isExistingCursor = <TState>(cursor: CursorType<TState>): boolean => {
export const isExistingCursor = <TState>(
cursor: CursorType<TState>
): boolean => {
cursor = isCursorFunction(cursor) ? cursor() : cursor;
const hasExistingInnerStateInArray = (innerState: IState[], path: string[]): boolean => {
const hasExistingInnerStateInArray = (
innerState: IState[],
path: string[]
): boolean => {
const index = Number(path.shift());

@@ -57,11 +78,11 @@ return index < innerState.length

: false;
}
const hasExistingInnerState = (innerState: IState, path: string[]): boolean => {
if (path.length === 0)
return true;
};
const hasExistingInnerState = (
innerState: IState,
path: string[]
): boolean => {
if (path.length === 0) return true;
const subPath = path.shift();
if (!subPath)
return true;
if ((<any>innerState)[subPath] === undefined)
return false;
if (!subPath) return true;
if ((<any>innerState)[subPath] === undefined) return false;
const prop = (<any>innerState)[subPath];

@@ -79,3 +100,3 @@ return Array.isArray(prop)

: hasExistingInnerState(state, cursor.key.split(stateSeparator));
}
};

@@ -85,7 +106,5 @@ export const getState = <TState>(cursor: CursorType<TState>): TState => {

const getInnerState = (innerState: IState, path: string[]): IState => {
if (path.length === 0)
return innerState;
if (path.length === 0) return innerState;
const subPath = path.shift();
if (!subPath)
return innerState;
if (!subPath) return innerState;
checkSubstate(innerState, subPath, path, cursorValue);

@@ -101,20 +120,25 @@ const prop = (<any>innerState)[subPath];

return <TState>(cursorValue.key === rootStateKey
? state
: getInnerState(state, cursorValue.key.split(stateSeparator)));
return <TState>(
(cursorValue.key === rootStateKey
? state
: getInnerState(state, cursorValue.key.split(stateSeparator)))
);
};
export const setState = <TState>(cursor: CursorType<TState>, updatedState: TState, canCreateObjectsOnPath = false) => {
export const setState = <TState>(
cursor: CursorType<TState>,
updatedState: TState,
canCreateObjectsOnPath = false
) => {
const cursorValue = isCursorFunction(cursor) ? cursor() : cursor;
const setInnerState = <TInnerState>(innerState: TInnerState, path: string[]): TInnerState => {
if (path.length === 0)
return <any>updatedState;
const setInnerState = <TInnerState>(
innerState: TInnerState,
path: string[]
): TInnerState => {
if (path.length === 0) return <any>updatedState;
const subPath = path.shift();
if (!subPath)
return <any>updatedState;
if (!subPath) return <any>updatedState;
if (canCreateObjectsOnPath)
createSubstate(innerState, subPath);
else
checkSubstate(innerState, subPath, path, cursorValue);
if (canCreateObjectsOnPath) createSubstate(innerState, subPath);
else checkSubstate(innerState, subPath, path, cursorValue);
const prop = (<any>innerState)[subPath];

@@ -125,9 +149,9 @@ let newSubState: Object | Array<IState> | null = null;

newSubState = [...prop];
(<any>newSubState)[index] = setInnerState((<any>newSubState)[index], path);
}
else
newSubState = setInnerState(prop, path);
(<any>newSubState)[index] = setInnerState(
(<any>newSubState)[index],
path
);
} else newSubState = setInnerState(prop, path);
if (newSubState === prop)
return innerState;
if (newSubState === prop) return innerState;

@@ -142,14 +166,21 @@ const newState = h.shallowCopy(innerState);

state =
cursorValue.key === rootStateKey
? updatedState
: setInnerState(state, cursorValue.key.split(stateSeparator));
if (h.isFunction(freezing) ? freezing() : freezing)
h.deepFreeze(state);
state = isRootCursor(updatedState, cursorValue)
? updatedState
: setInnerState(state, cursorValue.key.split(stateSeparator));
if (h.isFunction(freezing) ? freezing() : freezing) h.deepFreeze(state);
d.log('Current state:', state);
};
function checkSubstate<TCurrentState, TTargetState>(s: TCurrentState, subPath: string, remainingPath: string[], cursor: ICursor<TTargetState>) {
if (remainingPath.length === 0 && cursor.isUndefinable)
return;
function isRootCursor(_: unknown, cursorValue: ICursor<unknown>): _ is IState {
return cursorValue.key === rootStateKey;
}
function checkSubstate<TCurrentState, TTargetState>(
s: TCurrentState,
subPath: string,
remainingPath: string[],
cursor: ICursor<TTargetState>
) {
if (remainingPath.length === 0 && cursor.isUndefinable) return;
if ((<any>s)[subPath] === undefined)

@@ -160,4 +191,3 @@ throw new Error(`State for cursor key (${cursor.key}) does not exist.`);

function createSubstate<TState>(s: TState, subPath: string) {
if ((<any>s)[subPath] === undefined)
(<any>s)[subPath] = {};
if ((<any>s)[subPath] === undefined) (<any>s)[subPath] = {};
}

@@ -167,3 +197,5 @@

if (state === null)
throw new Error('Default state must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).');
throw new Error(
'Default state must be set before first usage through bootstrap(defaultState, () => { yourRenderCallback(); }).'
);
return true;

@@ -173,5 +205,4 @@ }

function isValidCursorKey<TState>(cursor: ICursor<TState>): boolean {
if (cursor.key === null)
throw new Error( 'Cursor key cannot be null.');
if (cursor.key === null) throw new Error('Cursor key cannot be null.');
return true;
}
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc