deep-storage
Advanced tools
Comparing version 4.0.1 to 5.0.0
export declare type StateUpdateCallback = <DeepState>(path: Path, newState: DeepState, oldState: DeepState) => void; | ||
export interface DeepSubscriptions { | ||
export interface DeepStorage<State, RootState = {}> { | ||
/** | ||
* Returns a new subscription that can subscribeTo paths in state. Note, | ||
* the subscription must be cancelled when no longer in use. | ||
*/ | ||
subscription: (callback: StateUpdateCallback) => DeepSubscription; | ||
} | ||
export interface DeepStorage<State, RootState = {}> extends DeepSubscriptions { | ||
/** | ||
* sets a value in deep storage and notifies subscribers. shortcut for | ||
@@ -46,2 +39,4 @@ * update where the old value is ignored | ||
prop: <Key extends keyof State>(name: Key) => State[Key]; | ||
addSubscriber: (subscriber: Subscriber) => void; | ||
removeSubscriber: (subscriber: Subscriber) => void; | ||
} | ||
@@ -60,17 +55,14 @@ /** | ||
export declare function isPathMatch<T>(stateChangePath: T[], subscriptionPath: T[]): boolean; | ||
/** | ||
* A cancelable way to subscribe to paths in state | ||
*/ | ||
export interface DeepSubscription { | ||
subscribeTo: (...path: Path) => void; | ||
cancel: () => void; | ||
} | ||
export interface UsesDeepStorage<State> { | ||
storage: DeepStorage<State>; | ||
} | ||
export declare type stringNumberOrSymbol = string | number | symbol; | ||
export declare type Path = stringNumberOrSymbol[]; | ||
export declare class Subscriber { | ||
id: number; | ||
private static idGenerator; | ||
private callback; | ||
constructor(); | ||
onChange(callback: StateUpdateCallback): void; | ||
change<DeepState>(path: Path, newState: DeepState, oldState: DeepState): void; | ||
} | ||
export declare class DefaultDeepStorage<State> implements DeepStorage<State, State> { | ||
state: State; | ||
private id; | ||
private subscriptions; | ||
@@ -86,6 +78,6 @@ constructor(state: State); | ||
deep: <DeepState>(name: string | number | symbol) => DeepStorage<DeepState, State>; | ||
subscription: (callback: StateUpdateCallback) => { | ||
subscribeTo: (...path: (string | number | symbol)[]) => void; | ||
cancel: () => void; | ||
}; | ||
addSubscriber: (subscriber: Subscriber) => void; | ||
addSubscriberIn: (...path: (string | number | symbol)[]) => (subscriber: Subscriber) => void; | ||
removeSubscriber: (subscriber: Subscriber) => void; | ||
removeSubscriberIn: (...path: (string | number | symbol)[]) => (subscriber: Subscriber) => void; | ||
root: () => this; | ||
@@ -107,6 +99,7 @@ path: Path; | ||
deep: <DeepState>(...path: (string | number | symbol)[]) => DeepStorage<DeepState, RootState>; | ||
subscription: (callback: StateUpdateCallback) => DeepSubscription; | ||
root: () => DefaultDeepStorage<RootState>; | ||
readonly props: { [P in keyof State]: DeepStorage<State[P], {}>; }; | ||
prop: <Key extends keyof State>(name: Key) => State[Key]; | ||
addSubscriber: (subscriber: Subscriber) => void; | ||
removeSubscriber: (subscriber: Subscriber) => void; | ||
} | ||
@@ -113,0 +106,0 @@ export declare function parsePath(path: Path | stringNumberOrSymbol): Path; |
@@ -38,2 +38,31 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
function arraysEqual(a, b) { | ||
if (a === b) | ||
return true; | ||
if (a == null || b == null) | ||
return false; | ||
if (a.length != b.length) | ||
return false; | ||
// If you don't care about the order of the elements inside | ||
// the array, you should sort both arrays here. | ||
for (var i = 0; i < a.length; ++i) { | ||
if (a[i] !== b[i]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
function removePath(pathToRemove, paths) { | ||
var found = false; | ||
var result = []; | ||
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { | ||
var path = paths_1[_i]; | ||
if (arraysEqual(path, pathToRemove) && !found) { | ||
found = true; | ||
} | ||
else { | ||
result.push(path); | ||
} | ||
} | ||
return result; | ||
} | ||
/** | ||
@@ -58,2 +87,18 @@ * Is one array a prefix on another e.g. | ||
exports.isPathMatch = isPathMatch; | ||
var Subscriber = /** @class */ (function () { | ||
function Subscriber() { | ||
this.id = Subscriber.idGenerator++; | ||
} | ||
Subscriber.prototype.onChange = function (callback) { | ||
this.callback = callback; | ||
}; | ||
Subscriber.prototype.change = function (path, newState, oldState) { | ||
if (this.callback) { | ||
this.callback(path, newState, oldState); | ||
} | ||
}; | ||
Subscriber.idGenerator = 0; | ||
return Subscriber; | ||
}()); | ||
exports.Subscriber = Subscriber; | ||
var DefaultDeepStorage = /** @class */ (function () { | ||
@@ -63,3 +108,2 @@ function DefaultDeepStorage(state) { | ||
this.state = state; | ||
this.id = 0; | ||
this.subscriptions = {}; | ||
@@ -112,3 +156,3 @@ this.update = function (callback) { | ||
if (subscriber.paths.some(function (subscriberPath) { return isPathMatch(stateChangePath, subscriberPath); })) { | ||
subscriber.callback(stateChangePath, newState, oldState); | ||
subscriber.subscriber.change(stateChangePath, newState, oldState); | ||
} | ||
@@ -147,23 +191,37 @@ } | ||
}; | ||
this.subscription = function (callback) { | ||
var subscriberId = _this.id++; | ||
_this.subscriptions[subscriberId] = { | ||
callback: callback, | ||
paths: [] | ||
this.addSubscriber = function (subscriber) { | ||
_this.addSubscriberIn.apply(_this, _this.path)(subscriber); | ||
}; | ||
this.addSubscriberIn = function () { | ||
var path = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
path[_i] = arguments[_i]; | ||
} | ||
return function (subscriber) { | ||
var subscription = _this.subscriptions[subscriber.id]; | ||
if (subscription) { | ||
subscription.paths.push(path); | ||
} | ||
else { | ||
_this.subscriptions[subscriber.id] = { | ||
paths: [path], | ||
subscriber: subscriber | ||
}; | ||
} | ||
}; | ||
var cancel = function () { | ||
delete _this.subscriptions[subscriberId]; | ||
}; | ||
this.removeSubscriber = function (subscriber) { | ||
_this.removeSubscriberIn.apply(_this, _this.path)(subscriber); | ||
}; | ||
this.removeSubscriberIn = function () { | ||
var path = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
path[_i] = arguments[_i]; | ||
} | ||
return function (subscriber) { | ||
var subscription = _this.subscriptions[subscriber.id]; | ||
if (subscription) { | ||
subscription.paths = removePath(path, subscription.paths); | ||
} | ||
}; | ||
return { | ||
subscribeTo: function () { | ||
var path = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
path[_i] = arguments[_i]; | ||
} | ||
if (subscriberId in _this.subscriptions) { | ||
_this.subscriptions[subscriberId].paths.push(path); | ||
} | ||
}, | ||
cancel: cancel | ||
}; | ||
}; | ||
@@ -240,17 +298,2 @@ this.root = function () { return _this; }; | ||
}; | ||
this.subscription = function (callback) { | ||
var rootSubscription = _this.rootStorage.subscription(function (path, newState, oldState) { | ||
callback(path.slice(path.length - _this.path.length, path.length), newState, oldState); | ||
}); | ||
return { | ||
subscribeTo: function () { | ||
var path = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
path[_i] = arguments[_i]; | ||
} | ||
return rootSubscription.subscribeTo.apply(rootSubscription, _this.path.concat(path)); | ||
}, | ||
cancel: rootSubscription.cancel | ||
}; | ||
}; | ||
this.root = function () { return _this.rootStorage; }; | ||
@@ -260,2 +303,10 @@ this.prop = function (name) { | ||
}; | ||
this.addSubscriber = function (subscriber) { | ||
var _a; | ||
(_a = _this.rootStorage).addSubscriberIn.apply(_a, _this.path)(subscriber); | ||
}; | ||
this.removeSubscriber = function (subscriber) { | ||
var _a; | ||
(_a = _this.rootStorage).removeSubscriberIn.apply(_a, _this.path)(subscriber); | ||
}; | ||
} | ||
@@ -262,0 +313,0 @@ Object.defineProperty(NestedDeepStorage.prototype, "state", { |
{ | ||
"name": "deep-storage", | ||
"version": "4.0.1", | ||
"version": "5.0.0", | ||
"description": "Simple observable state management for reactive applications", | ||
@@ -21,5 +21,5 @@ "main": "./lib/index.js", | ||
"build": "rimraf lib *.d.ts && tsc -p tsconfig.prod.json", | ||
"publish:patch": "yarn build && npm version patch && git push && npm publish", | ||
"publish:minor": "yarn build && npm version minor && git push && npm publish", | ||
"publish:major": "yarn build && npm version major && git push && npm publish" | ||
"publish:patch": "yarn build && npm version patch && git push", | ||
"publish:minor": "yarn build && npm version minor && git push", | ||
"publish:major": "yarn build && npm version major && git push" | ||
}, | ||
@@ -26,0 +26,0 @@ "dependencies": {}, |
import { deepStorage, isPathMatch } from '../'; | ||
import { Subscriber } from '../storage'; | ||
@@ -50,3 +51,5 @@ test('stateIn', () => { | ||
}); | ||
const subscription = storage.subscription((path, newState, oldState) => { | ||
const subscriber = new Subscriber(); | ||
subscriber.onChange((path, newState, oldState) => { | ||
expect(path).toEqual(['todos', 'abc', 'title']); | ||
@@ -57,5 +60,5 @@ expect(newState).toBe('test'); | ||
}); | ||
subscription.subscribeTo('todos'); | ||
storage.addSubscriber(subscriber); | ||
storage.deep('todos').deep('abc').deep('title').set('test'); | ||
subscription.cancel(); | ||
storage.removeSubscriber(subscriber); | ||
}); | ||
@@ -84,3 +87,4 @@ | ||
}); | ||
const subscription = storage.subscription((path, newState, oldState) => { | ||
const subscriber = new Subscriber(); | ||
subscriber.onChange((path, newState, oldState) => { | ||
expect(path).toEqual([]); | ||
@@ -91,5 +95,5 @@ expect(newState).toEqual({ todos: [1] }); | ||
}); | ||
subscription.subscribeTo('todos'); | ||
storage.deep('todos').addSubscriber(subscriber); | ||
storage.update(prevState => ({ ...prevState, todos: [1] })); | ||
subscription.cancel(); | ||
storage.deep('todos').removeSubscriber(subscriber); | ||
}); |
export type StateUpdateCallback = <DeepState>(path: Path, newState: DeepState, oldState: DeepState) => void; | ||
export interface DeepSubscriptions { | ||
/** | ||
* Returns a new subscription that can subscribeTo paths in state. Note, | ||
* the subscription must be cancelled when no longer in use. | ||
*/ | ||
subscription: (callback: StateUpdateCallback) => DeepSubscription; | ||
function arraysEqual(a: any, b: any) { | ||
if (a === b) return true; | ||
if (a == null || b == null) return false; | ||
if (a.length != b.length) return false; | ||
// If you don't care about the order of the elements inside | ||
// the array, you should sort both arrays here. | ||
for (var i = 0; i < a.length; ++i) { | ||
if (a[i] !== b[i]) return false; | ||
} | ||
return true; | ||
} | ||
export interface DeepStorage<State, RootState = {}> extends DeepSubscriptions { | ||
function removePath(pathToRemove: Path, paths: Path[]): Path[] { | ||
let found = false; | ||
const result: Path[] = []; | ||
for(const path of paths) { | ||
if(arraysEqual(path, pathToRemove) && !found) { | ||
found = true; | ||
} else { | ||
result.push(path); | ||
} | ||
} | ||
return result; | ||
} | ||
export interface DeepStorage<State, RootState = {}> { | ||
/** | ||
@@ -54,2 +73,5 @@ * sets a value in deep storage and notifies subscribers. shortcut for | ||
prop: <Key extends keyof State>(name: Key) => State[Key]; | ||
addSubscriber: (subscriber: Subscriber) => void; | ||
removeSubscriber: (subscriber: Subscriber) => void; | ||
} | ||
@@ -75,22 +97,25 @@ | ||
/** | ||
* A cancelable way to subscribe to paths in state | ||
*/ | ||
export interface DeepSubscription { | ||
subscribeTo: (...path: Path) => void; | ||
cancel: () => void; | ||
} | ||
export type stringNumberOrSymbol = string | number | symbol; | ||
export type Path = stringNumberOrSymbol[]; | ||
export interface UsesDeepStorage<State> { | ||
storage: DeepStorage<State>; | ||
export class Subscriber { | ||
public id: number; | ||
private static idGenerator: number = 0; | ||
private callback: StateUpdateCallback | undefined; | ||
constructor() { | ||
this.id = Subscriber.idGenerator++; | ||
} | ||
onChange(callback: StateUpdateCallback) { | ||
this.callback = callback; | ||
} | ||
change<DeepState>(path: Path, newState: DeepState, oldState: DeepState) { | ||
if(this.callback) { | ||
this.callback(path, newState, oldState); | ||
} | ||
} | ||
} | ||
export type stringNumberOrSymbol = string | number | symbol; | ||
export type Path = stringNumberOrSymbol[]; | ||
export class DefaultDeepStorage<State> implements DeepStorage<State, State> { | ||
private id: number = 0; | ||
private subscriptions: { [key: number]: { paths: Path[], callback: StateUpdateCallback } } = {}; | ||
private subscriptions: { [key: number]: { paths: Path[], subscriber: Subscriber } } = {}; | ||
constructor(public state: State) { | ||
@@ -131,3 +156,3 @@ } | ||
if (subscriber.paths.some(subscriberPath => isPathMatch(stateChangePath, subscriberPath))) { | ||
subscriber.callback(stateChangePath, newState, oldState) | ||
subscriber.subscriber.change(stateChangePath, newState, oldState) | ||
} | ||
@@ -155,18 +180,23 @@ } | ||
} | ||
subscription = (callback: StateUpdateCallback) => { | ||
const subscriberId = this.id++; | ||
this.subscriptions[subscriberId] = { | ||
callback, | ||
paths: [] | ||
} as any; | ||
const cancel = () => { | ||
delete this.subscriptions[subscriberId]; | ||
addSubscriber = (subscriber: Subscriber) => { | ||
this.addSubscriberIn(...this.path)(subscriber); | ||
} | ||
addSubscriberIn = (...path: Path) => (subscriber: Subscriber) => { | ||
const subscription = this.subscriptions[subscriber.id]; | ||
if(subscription) { | ||
subscription.paths.push(path); | ||
} else { | ||
this.subscriptions[subscriber.id] = { | ||
paths: [path], | ||
subscriber | ||
} | ||
} | ||
return { | ||
subscribeTo: (...path: Path) => { | ||
if (subscriberId in this.subscriptions) { | ||
this.subscriptions[subscriberId].paths.push(path); | ||
} | ||
}, | ||
cancel | ||
} | ||
removeSubscriber = (subscriber: Subscriber) => { | ||
this.removeSubscriberIn(...this.path)(subscriber); | ||
} | ||
removeSubscriberIn = (...path: Path) => (subscriber: Subscriber) => { | ||
const subscription = this.subscriptions[subscriber.id]; | ||
if(subscription) { | ||
subscription.paths = removePath(path, subscription.paths); | ||
} | ||
@@ -217,13 +247,2 @@ } | ||
} | ||
subscription = (callback: StateUpdateCallback): DeepSubscription => { | ||
const rootSubscription = this.rootStorage.subscription((path, newState, oldState) => { | ||
callback(path.slice(path.length - this.path.length, path.length), newState, oldState); | ||
}); | ||
return { | ||
subscribeTo: (...path: Path) => { | ||
return rootSubscription.subscribeTo(...this.path.concat(path)); | ||
}, | ||
cancel: rootSubscription.cancel | ||
} | ||
} | ||
root = () => this.rootStorage; | ||
@@ -240,2 +259,8 @@ get props() { | ||
} | ||
addSubscriber = (subscriber: Subscriber) => { | ||
this.rootStorage.addSubscriberIn(...this.path)(subscriber); | ||
} | ||
removeSubscriber = (subscriber: Subscriber) => { | ||
this.rootStorage.removeSubscriberIn(...this.path)(subscriber); | ||
} | ||
} | ||
@@ -242,0 +267,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
183788
1271