@hookstate/core
Advanced tools
Comparing version 4.0.0-rc.4 to 4.0.0-rc10
@@ -37,8 +37,2 @@ import React from 'react'; | ||
/** | ||
* Special symbol which might be returned by onPromised callback of [StateMethods.map](#map) function. | ||
* | ||
* [Learn more...](https://hookstate.js.org/docs/asynchronous-state#executing-an-action-when-state-is-loaded) | ||
*/ | ||
export declare const postpone: unique symbol; | ||
/** | ||
* Special symbol which might be used to delete properties | ||
@@ -61,3 +55,3 @@ * from an object calling [StateMethods.set](#set) or [StateMethods.merge](#merge). | ||
*/ | ||
export declare type InferredStateOrnullType<S> = S extends undefined ? undefined : S extends null ? null : State<S>; | ||
export declare type InferredStateOrnullType<S, E> = S extends undefined ? undefined : S extends null ? null : State<S, E>; | ||
/** | ||
@@ -100,3 +94,3 @@ * For plugin developers only. | ||
*/ | ||
export interface StateMethods<S> { | ||
export interface StateMethods<S, E = {}> extends State_<S, E> { | ||
/** | ||
@@ -160,4 +154,17 @@ * 'Javascript' object 'path' to an element relative to the root object | ||
* It returns the same result as [StateMethods.value](#readonly-value) method. | ||
* | ||
* If the additional option `noproxy` is set, the method will return | ||
* the original data object without wrapping it by proxy. | ||
* All properties of the object will be marked as used and on change will trigger the rerender. | ||
* | ||
* If the additional option `stealth` is set, the method will not mark | ||
* the object as used and it will not trigger the rerender if it is changed. | ||
* It might be helpful to use it during debugging, for example: | ||
* `console.log(state.get({ stealth: true }))`. | ||
* If you use it, make sure you know what you are doing. | ||
*/ | ||
get(): S; | ||
get(options?: { | ||
noproxy?: boolean; | ||
stealth?: boolean; | ||
}): S; | ||
/** | ||
@@ -199,16 +206,4 @@ * Sets new value for a state. | ||
*/ | ||
nested<K extends keyof S>(key: K): State<S[K]>; | ||
nested<K extends keyof S>(key: K): State<S[K], E>; | ||
/** | ||
* Runs the provided action callback with optimised re-rendering. | ||
* Updating state within a batch action does not trigger immediate rerendering. | ||
* Instead, all required rerendering is done once the batch is finished. | ||
* | ||
* [Learn more about batching...](https://hookstate.js.org/docs/performance-batched-updates | ||
* | ||
* @param action callback function to execute in a batch | ||
* | ||
* @param context custom user's value, which is passed to plugins | ||
*/ | ||
batch<R, C>(action: (s: State<S>) => R, context?: Exclude<C, Function>): R; | ||
/** | ||
* If state value is null or undefined, returns state value. | ||
@@ -220,3 +215,3 @@ * Otherwise, it returns this state instance but | ||
*/ | ||
ornull: InferredStateOrnullType<S>; | ||
ornull: InferredStateOrnullType<S, E>; | ||
/** | ||
@@ -227,3 +222,3 @@ * Adds plugin to the state. | ||
*/ | ||
attach(plugin: () => Plugin): State<S>; | ||
attach(plugin: () => Plugin): State<S, E>; | ||
/** | ||
@@ -253,2 +248,16 @@ * For plugin developers only. | ||
/** | ||
* Returns an interface stripped of all keys that don't resolve to U, defaulting | ||
* to a non-strict comparison of T[key] extends U. Setting B to true performs | ||
* a strict type comparison of T[key] extends U & U extends T[key] | ||
*/ | ||
declare type KeysOfType<T, U, B = false> = { | ||
[P in keyof T]: B extends true ? T[P] extends U ? (U extends T[P] ? P : never) : never : T[P] extends U ? P : never; | ||
}[keyof T]; | ||
export declare const __state: unique symbol; | ||
export interface State_<S, E> { | ||
[__state]: (s: S, e: E) => never; | ||
} | ||
export declare type StateValue<V> = V extends State_<(infer S), (infer _)> ? S : V; | ||
export declare type StateExtension<V> = V extends State_<(infer _), (infer E)> ? E : V; | ||
/** | ||
* Type of a result of [createState](#createstate) and [useState](#usestate) functions | ||
@@ -262,5 +271,5 @@ * | ||
*/ | ||
export declare type State<S> = StateMethods<S> & (S extends ReadonlyArray<(infer U)> ? ReadonlyArray<State<U>> : S extends object ? Omit<{ | ||
readonly [K in keyof Required<S>]: State<S[K]>; | ||
}, keyof StateMethods<S> | keyof StateMethodsDestroy> : {}); | ||
export declare type State<S, E = {}> = StateMethods<S, E> & E & (S extends ReadonlyArray<(infer U)> ? ReadonlyArray<State<U, E>> : S extends object ? Omit<{ | ||
readonly [K in keyof Required<S>]: State<S[K], E>; | ||
}, keyof StateMethods<S, E> | keyof StateMethodsDestroy | KeysOfType<S, Function> | keyof E> : {}); | ||
/** | ||
@@ -300,2 +309,10 @@ * For plugin developers only. | ||
* For plugin developers only. | ||
* Extension.onSet argument type. | ||
*/ | ||
export interface ExtensionOnSetArgument { | ||
readonly state: State<StateValueAtRoot, {}>; | ||
readonly descriptor: SetActionDescriptor; | ||
} | ||
/** | ||
* For plugin developers only. | ||
* PluginCallbacks.onSet argument type. | ||
@@ -315,4 +332,3 @@ */ | ||
* | ||
* As a workaround, you can [batch state updates](https://hookstate.js.org/docs/performance-batched-updates) or | ||
* replace merge calls with the immutable-style set operation like so: | ||
* As a workaround, you can replace merge calls with the immutable-style set operation like so: | ||
* | ||
@@ -341,11 +357,2 @@ * ``` | ||
* For plugin developers only. | ||
* PluginCallbacks.onBatchStart/Finish argument type. | ||
*/ | ||
export interface PluginCallbacksOnBatchArgument { | ||
readonly path: Path; | ||
readonly state?: StateValueAtRoot; | ||
readonly context?: AnyContext; | ||
} | ||
/** | ||
* For plugin developers only. | ||
* Set of callbacks, a plugin may subscribe to. | ||
@@ -358,4 +365,2 @@ * | ||
readonly onDestroy?: (arg: PluginCallbacksOnDestroyArgument) => void; | ||
readonly onBatchStart?: (arg: PluginCallbacksOnBatchArgument) => void; | ||
readonly onBatchFinish?: (arg: PluginCallbacksOnBatchArgument) => void; | ||
} | ||
@@ -376,4 +381,11 @@ /** | ||
*/ | ||
readonly init?: (state: State<StateValueAtRoot>) => PluginCallbacks; | ||
readonly init?: (state: State<StateValueAtRoot, {}>) => PluginCallbacks; | ||
} | ||
export interface Extension<E extends {}> { | ||
readonly onInit: (state: () => State<StateValueAtRoot, {}>) => { | ||
readonly [K in keyof Required<E>]: (state: State<StateValueAtPath, {}>) => E[K]; | ||
}; | ||
readonly onSet?: (state: State<StateValueAtRoot, {}>, descriptor: SetActionDescriptor) => void; | ||
readonly onDestroy?: (state: State<StateValueAtRoot, {}>) => void; | ||
} | ||
/** | ||
@@ -410,4 +422,12 @@ * Creates new state and returns it. | ||
*/ | ||
export declare function createState<S>(initial: SetInitialStateAction<S>): State<S> & StateMethodsDestroy; | ||
export declare function createState<S>(initial: SetInitialStateAction<S>): State<S, {}> & StateMethodsDestroy; | ||
export declare function createHookstate<S, E>(initial: SetInitialStateAction<S>, extension?: () => Extension<E>): State<S, E> & StateMethodsDestroy; | ||
/** | ||
* @warning Initializing a local state to a promise without using | ||
* an initializer callback function, which returns a Promise, | ||
* is almost always a mistake. So, it is blocked. | ||
* Use `useState(() => your_promise)` instead of `useState(your_promise)`. | ||
*/ | ||
export declare function useState<S>(source: Promise<S>): never; | ||
/** | ||
* Enables a functional React component to use a state, | ||
@@ -439,3 +459,3 @@ * either created by [createState](#createstate) (*global* state) or | ||
*/ | ||
export declare function useState<S>(source: State<S>): State<S>; | ||
export declare function useState<S>(source: State<S, {}>): State<S, {}>; | ||
/** | ||
@@ -468,8 +488,23 @@ * This function enables a functional React component to use a state, | ||
*/ | ||
export declare function useState<S>(source: SetInitialStateAction<S>): State<S>; | ||
export declare function useState<S>(source: SetInitialStateAction<S>): State<S, {}>; | ||
export declare function extend<E1 extends {} = {}, E2 extends {} = {}, E3 extends {} = {}, E4 extends {} = {}, E5 extends {} = {}>(extensions: [ | ||
Extension<E1>, | ||
Extension<E2>?, | ||
Extension<E3>?, | ||
Extension<E4>?, | ||
Extension<E5>? | ||
]): Extension<E5 & E4 & E3 & E2 & E1>; | ||
/** | ||
* @warning Initializing a local state to a promise without using | ||
* an initializer callback function, which returns a Promise, | ||
* is almost always a mistake. So, it is blocked. | ||
* Use `useHookstate(() => your_promise)` instead of `useHookstate(your_promise)`. | ||
*/ | ||
export declare function useHookstate<S, E>(source: Promise<S>, extension?: () => Extension<E>): never; | ||
export declare function useHookstate<S, E, E2>(source: State_<S, E>, extension: () => Extension<E2>): never; | ||
/** | ||
* Alias to [useState](#usestate) which provides a workaround | ||
* for [React 20613 bug](https://github.com/facebook/react/issues/20613) | ||
*/ | ||
export declare function useHookstate<S>(source: State<S>): State<S>; | ||
export declare function useHookstate<S, E>(source: State_<S, E>): State<S, E>; | ||
/** | ||
@@ -479,3 +514,8 @@ * Alias to [useState](#usestate) which provides a workaround | ||
*/ | ||
export declare function useHookstate<S>(source: SetInitialStateAction<S>): State<S>; | ||
export declare function useHookstate<S, E>(source: SetInitialStateAction<S>, extension?: () => Extension<E>): State<S, E>; | ||
export declare function StateFragment<S, E>(props: { | ||
state: State_<S, E>; | ||
extension: () => Extension<E>; | ||
children: (state: State<S, E>) => React.ReactElement; | ||
}): never; | ||
/** | ||
@@ -490,5 +530,5 @@ * Allows to use a state without defining a functional react component. | ||
*/ | ||
export declare function StateFragment<S>(props: { | ||
state: State<S>; | ||
children: (state: State<S>) => React.ReactElement; | ||
export declare function StateFragment<S, E>(props: { | ||
state: State_<S, E>; | ||
children: (state: State<S, E>) => React.ReactElement; | ||
}): React.ReactElement; | ||
@@ -503,5 +543,6 @@ /** | ||
*/ | ||
export declare function StateFragment<S>(props: { | ||
export declare function StateFragment<S, E>(props: { | ||
state: SetInitialStateAction<S>; | ||
children: (state: State<S>) => React.ReactElement; | ||
extension?: () => Extension<E>; | ||
children: (state: State<S, E>) => React.ReactElement; | ||
}): React.ReactElement; | ||
@@ -552,3 +593,21 @@ /** | ||
*/ | ||
export declare function DevTools<S>(state: State<S>): DevToolsExtensions; | ||
export declare function DevTools<S, E>(state: State<S, E>): DevToolsExtensions; | ||
export interface SetActionDescriptor { | ||
path: Path; | ||
actions?: Record<string | number, "I" | "U" | "D">; | ||
} | ||
export interface Configuration { | ||
interceptDependencyListsMode: 'always' | 'development' | 'never'; | ||
isDevelopmentMode: boolean; | ||
promiseDetector: (p: any) => boolean; | ||
} | ||
export declare function configure(config: Partial<Configuration>): void; | ||
export declare function useHookEffect(effect: React.EffectCallback, deps?: React.DependencyList): void; | ||
export declare function useHookLayoutEffect(effect: React.EffectCallback, deps?: React.DependencyList): void; | ||
export declare function useHookInsertionEffect(effect: React.EffectCallback, deps?: React.DependencyList): void; | ||
export declare function useHookImperativeHandle<T, R extends T>(ref: React.Ref<T> | undefined, init: () => R, deps?: React.DependencyList): void; | ||
export declare function useHookMemo<T>(factory: () => T, deps: React.DependencyList | undefined): T; | ||
export declare function useHookCallback<T extends Function>(callback: T, deps: React.DependencyList): T; | ||
export declare function hookMemo<T extends React.ComponentType<any>>(Component: T, propsAreEqual?: (prevProps: Readonly<React.ComponentProps<T>>, nextProps: Readonly<React.ComponentProps<T>>) => boolean): React.MemoExoticComponent<T>; | ||
export {}; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -46,8 +46,51 @@ import React from 'react'; | ||
/** | ||
* Special symbol which might be returned by onPromised callback of [StateMethods.map](#map) function. | ||
* | ||
* [Learn more...](https://hookstate.js.org/docs/asynchronous-state#executing-an-action-when-state-is-loaded) | ||
* Copied from fbjs is-shallow-equal | ||
*/ | ||
var postpone = Symbol('postpone'); | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
/** | ||
* inlined Object.is polyfill to avoid requiring consumers ship their own | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | ||
*/ | ||
function is(x, y) { | ||
// SameValue algorithm | ||
if (x === y) { // Steps 1-5, 7-10 | ||
// Steps 6.b-6.e: +0 != -0 | ||
// Added the nonzero y check to make Flow happy, but it is redundant | ||
return x !== 0 || y !== 0 || 1 / x === 1 / y; | ||
} | ||
else { | ||
// Step 6.a: NaN == NaN | ||
return x !== x && y !== y; | ||
} | ||
} | ||
/** | ||
* Performs equality by iterating through keys on an object and returning false | ||
* when any key has values which are not strictly equal between the arguments. | ||
* Returns true when the values of all keys are strictly equal. | ||
*/ | ||
function shallowEqual(objA, objB) { | ||
if (is(objA, objB)) { | ||
return true; | ||
} | ||
if (typeof objA !== 'object' || objA === null || | ||
typeof objB !== 'object' || objB === null) { | ||
return false; | ||
} | ||
var keysA = Object.keys(objA); | ||
var keysB = Object.keys(objB); | ||
if (keysA.length !== keysB.length) { | ||
return false; | ||
} | ||
// Test for A's keys different from B. | ||
for (var i = 0; i < keysA.length; i++) { | ||
if (!hasOwnProperty.call(objB, keysA[i]) || | ||
!is(objA[keysA[i]], objB[keysA[i]])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
var _a; | ||
/** | ||
* Special symbol which might be used to delete properties | ||
@@ -59,2 +102,5 @@ * from an object calling [StateMethods.set](#set) or [StateMethods.merge](#merge). | ||
var none = Symbol('none'); | ||
// type PickByType<T, U, B = false> = Pick<T, KeysOfType<T, U, B>>; | ||
var __state = Symbol('__state'); | ||
// TODO deprecate | ||
/** | ||
@@ -92,3 +138,6 @@ * Creates new state and returns it. | ||
function createState(initial) { | ||
var methods = createStore(initial).toMethods(); | ||
return createHookstate(initial); | ||
} | ||
function createHookstate(initial, extension) { | ||
var methods = createStore(initial, extension).toMethods(); | ||
var devtools = createState[DevToolsID]; | ||
@@ -103,4 +152,38 @@ if (devtools) { | ||
} | ||
function useHookstate(source) { | ||
var parentMethods = typeof source === 'object' && source !== null ? | ||
// TODO document | ||
function extend(extensions) { | ||
var exts = extensions.filter(function (i) { return i; }); | ||
var onSetCbs = exts.map(function (i) { return i.onSet; }).filter(function (i) { return i; }); | ||
var onDestroyCbs = exts.map(function (i) { return i.onDestroy; }).filter(function (i) { return i; }); | ||
var result = { | ||
onInit: function (instanceFactory) { | ||
var combinedMethods = {}; | ||
for (var _i = 0, exts_1 = exts; _i < exts_1.length; _i++) { | ||
var ext = exts_1[_i]; | ||
var extMethods = ext.onInit(instanceFactory); | ||
combinedMethods = __assign(__assign({}, combinedMethods), extMethods); | ||
} | ||
return combinedMethods; | ||
} | ||
}; | ||
if (onSetCbs.length > 0) { | ||
result.onSet = function (s, d) { | ||
for (var _i = 0, onSetCbs_1 = onSetCbs; _i < onSetCbs_1.length; _i++) { | ||
var cb = onSetCbs_1[_i]; | ||
cb(s, d); | ||
} | ||
}; | ||
} | ||
if (onDestroyCbs.length > 0) { | ||
result.onDestroy = function (s) { | ||
for (var _i = 0, onDestroyCbs_1 = onDestroyCbs; _i < onDestroyCbs_1.length; _i++) { | ||
var cb = onDestroyCbs_1[_i]; | ||
cb(s); | ||
} | ||
}; | ||
} | ||
return result; | ||
} | ||
function useHookstate(source, extension) { | ||
var parentMethods = Object(source) === source ? | ||
source[self] : | ||
@@ -126,3 +209,3 @@ undefined; | ||
}; | ||
var _a = React.useState(initializer), value_1 = _a[0], setValue_1 = _a[1]; | ||
var _b = React.useState(initializer), value_1 = _b[0], setValue_1 = _b[1]; | ||
value_1.state.reconstruct(parentMethods.path, value_1.store.get(parentMethods.path), value_1.store.edition, | ||
@@ -159,3 +242,3 @@ // parent state object has changed its reference object | ||
}; | ||
var _b = React.useState(initializer), value_2 = _b[0], setValue_2 = _b[1]; | ||
var _c = React.useState(initializer), value_2 = _c[0], setValue_2 = _c[1]; | ||
value_2.state.reconstruct(RootPath, value_2.store.get(RootPath), value_2.store.edition, | ||
@@ -184,3 +267,3 @@ // parent state object has changed its reference object | ||
var initializer = function () { | ||
var store = createStore(source); | ||
var store = createStore(source, extension); | ||
var onSetUsedCallback = function () { return setValue_3({ | ||
@@ -196,3 +279,3 @@ store: store, | ||
}; | ||
var _c = React.useState(initializer), value_3 = _c[0], setValue_3 = _c[1]; | ||
var _d = React.useState(initializer), value_3 = _d[0], setValue_3 = _d[1]; | ||
value_3.state.reconstruct(RootPath, value_3.store.get(RootPath), value_3.store.edition, false); | ||
@@ -206,3 +289,3 @@ value_3.store.subscribe(value_3.state); // in sync here, not in effect | ||
}, []); | ||
if (isDevelopmentMode) { | ||
if (configuration.isDevelopmentMode) { | ||
// This is a workaround for the issue: | ||
@@ -232,5 +315,6 @@ // https://github.com/avkonst/hookstate/issues/109 | ||
function StateFragment(props) { | ||
var scoped = useState(props.state); | ||
var scoped = useHookstate(props.state, props.extension); | ||
return props.children(scoped); | ||
} | ||
// TODO deprecate | ||
/** | ||
@@ -280,5 +364,2 @@ * A plugin which allows to opt-out from usage of Javascript proxies for | ||
/// | ||
var isDevelopmentMode = typeof process === 'object' && | ||
typeof process.env === 'object' && | ||
process.env.NODE_ENV === 'development'; | ||
var self = Symbol('self'); | ||
@@ -291,2 +372,4 @@ var EmptyDevToolsExtensions = { | ||
(function (ErrorId) { | ||
// TODO document | ||
ErrorId[ErrorId["StateUsedInDependencyList"] = 100] = "StateUsedInDependencyList"; | ||
ErrorId[ErrorId["InitStateToValueFromState"] = 101] = "InitStateToValueFromState"; | ||
@@ -300,2 +383,4 @@ ErrorId[ErrorId["SetStateToValueFromState"] = 102] = "SetStateToValueFromState"; | ||
ErrorId[ErrorId["ToJson_State"] = 109] = "ToJson_State"; | ||
// TODO document | ||
ErrorId[ErrorId["GetProperty_Function"] = 110] = "GetProperty_Function"; | ||
ErrorId[ErrorId["GetUnknownPlugin"] = 120] = "GetUnknownPlugin"; | ||
@@ -316,2 +401,4 @@ ErrorId[ErrorId["SetProperty_State"] = 201] = "SetProperty_State"; | ||
ErrorId[ErrorId["Apply_Value"] = 214] = "Apply_Value"; | ||
// TODO document | ||
ErrorId[ErrorId["InternalError"] = 0] = "InternalError"; | ||
})(ErrorId || (ErrorId = {})); | ||
@@ -326,12 +413,3 @@ var StateInvalidUsageError = /** @class */ (function (_super) { | ||
}(Error)); | ||
function isNoProxyInitializer() { | ||
try { | ||
var used = new Proxy({}, {}); | ||
return false; | ||
} | ||
catch (e) { | ||
return true; | ||
} | ||
} | ||
var IsNoProxy = isNoProxyInitializer(); | ||
// TODO deprecate | ||
var DowngradedID = Symbol('Downgraded'); | ||
@@ -342,4 +420,7 @@ var SelfMethodsID = Symbol('ProxyMarker'); | ||
var Store = /** @class */ (function () { | ||
function Store(_value) { | ||
function Store(_value, _extension) { | ||
var _this = this; | ||
var _b; | ||
this._value = _value; | ||
this._extension = _extension; | ||
this._edition = 0; | ||
@@ -349,39 +430,57 @@ this._subscribers = new Set(); | ||
this._destroySubscribers = new Set(); | ||
this._batchStartSubscribers = new Set(); | ||
this._batchFinishSubscribers = new Set(); | ||
this._plugins = new Map(); | ||
this._batches = 0; | ||
if (typeof _value === 'object' && | ||
Promise.resolve(_value) === _value) { | ||
this._promised = this.createPromised(_value); | ||
this._value = none; | ||
if (Object(_value) === _value && | ||
configuration.promiseDetector(_value)) { | ||
this.setPromised(_value); | ||
} | ||
else if (_value === none) { | ||
this._promised = this.createPromised(undefined); | ||
this.setPromised(undefined); | ||
} | ||
var onSetUsedStoreStateMethods = function () { | ||
_this._stateMethods.reconstruct(RootPath, _this.get(RootPath), _this.edition, false); | ||
}; | ||
onSetUsedStoreStateMethods[UnmountedMarker] = true; | ||
this._stateMethods = new StateMethodsImpl(this, RootPath, this.get(RootPath), this.edition, onSetUsedStoreStateMethods); | ||
this.subscribe(this._stateMethods); | ||
this._extensionMethods = (_b = this._extension) === null || _b === void 0 ? void 0 : _b.onInit(function () { return _this.toMethods().self; }); | ||
} | ||
Store.prototype.createPromised = function (newValue) { | ||
Store.prototype.setPromised = function (promise) { | ||
var _this = this; | ||
var promised = new Promised(newValue ? Promise.resolve(newValue) : undefined, function (r) { | ||
if (_this.promised === promised && _this.edition !== DestroyedEdition) { | ||
_this._promised = undefined; | ||
_this.set(RootPath, r, undefined); | ||
_this.update([RootPath]); | ||
this._value = none; | ||
this._promiseError = undefined; | ||
this._promiseResolver = undefined; | ||
if (!promise) { | ||
this._promise = new Promise(function (resolve) { | ||
_this._promiseResolver = resolve; | ||
}); | ||
return; | ||
} | ||
promise = promise | ||
.then(function (r) { | ||
if (_this._promise === promise && _this.edition !== DestroyedEdition) { | ||
_this._promise = undefined; | ||
_this._promiseError = undefined; | ||
_this._promiseResolver === undefined; | ||
_this.update(_this.set(RootPath, r, undefined)); | ||
} | ||
}, function () { | ||
if (_this.promised === promised && _this.edition !== DestroyedEdition) { | ||
}) | ||
.catch(function (err) { | ||
if (_this._promise === promise && _this.edition !== DestroyedEdition) { | ||
_this._promise = undefined; | ||
_this._promiseResolver = undefined; | ||
_this._promiseError = err; | ||
_this._edition += 1; | ||
_this.update([RootPath]); | ||
var ad = { path: RootPath }; | ||
_this.update(ad); | ||
} | ||
}, function () { | ||
if (_this._batchesPendingActions && | ||
_this._value !== none && | ||
_this.edition !== DestroyedEdition) { | ||
var actions = _this._batchesPendingActions; | ||
_this._batchesPendingActions = undefined; | ||
actions.forEach(function (a) { return a(); }); | ||
} | ||
}); | ||
return promised; | ||
this._promise = promise; | ||
}; | ||
Object.defineProperty(Store.prototype, "extension", { | ||
get: function () { | ||
return this._extensionMethods; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Store.prototype, "edition", { | ||
@@ -394,5 +493,5 @@ get: function () { | ||
}); | ||
Object.defineProperty(Store.prototype, "promised", { | ||
Object.defineProperty(Store.prototype, "promise", { | ||
get: function () { | ||
return this._promised; | ||
return this._promise; | ||
}, | ||
@@ -402,2 +501,9 @@ enumerable: false, | ||
}); | ||
Object.defineProperty(Store.prototype, "promiseError", { | ||
get: function () { | ||
return this._promiseError; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Store.prototype.get = function (path) { | ||
@@ -414,3 +520,5 @@ var result = this._value; | ||
Store.prototype.set = function (path, value, mergeValue) { | ||
var _b, _c; | ||
if (this._edition < 0) { | ||
// TODO convert to console log | ||
throw new StateInvalidUsageError(path, ErrorId.SetStateWhenDestroyed); | ||
@@ -428,8 +536,8 @@ } | ||
if (value === none) { | ||
this._promised = this.createPromised(undefined); | ||
this.setPromised(undefined); | ||
delete onSetArg.value; | ||
delete onSetArg.state; | ||
} | ||
else if (typeof value === 'object' && Promise.resolve(value) === value) { | ||
this._promised = this.createPromised(value); | ||
else if (Object(value) === value && configuration.promiseDetector(value)) { | ||
this.setPromised(value); | ||
value = none; | ||
@@ -439,5 +547,8 @@ delete onSetArg.value; | ||
} | ||
else if (this._promised && (!this._promised.resolver && !this._promised.fullfilled)) { | ||
else if (this._promise && !this._promiseResolver) { | ||
throw new StateInvalidUsageError(path, ErrorId.SetStateWhenPromised); | ||
} | ||
else { | ||
this._promiseError = undefined; | ||
} | ||
var prevValue = this._value; | ||
@@ -449,9 +560,15 @@ if (prevValue === none) { | ||
this.afterSet(onSetArg); | ||
if (prevValue === none && this._value !== none && | ||
this.promised && this.promised.resolver) { | ||
this.promised.resolver(this._value); | ||
if (prevValue === none && this._value !== none && this._promiseResolver) { | ||
this._promise = undefined; | ||
this._promiseError = undefined; | ||
var resolver = this._promiseResolver; | ||
this._promiseResolver === undefined; | ||
resolver(this._value); | ||
} | ||
return path; | ||
return { | ||
path: path | ||
}; | ||
} | ||
if (typeof value === 'object' && Promise.resolve(value) === value) { | ||
if (Object(value) === value && configuration.promiseDetector(value)) { | ||
// TODO this one still can get into the state as nested property, need to check on read instead | ||
throw new StateInvalidUsageError(path, ErrorId.SetStateNestedToPromised); | ||
@@ -476,3 +593,5 @@ } | ||
}); | ||
return path; | ||
return { | ||
path: path | ||
}; | ||
} | ||
@@ -497,3 +616,6 @@ else { | ||
// which is identified by upper path | ||
return path.slice(0, -1); | ||
return { | ||
path: path.slice(0, -1), | ||
actions: (_b = {}, _b[p] = "D", _b) | ||
}; | ||
} | ||
@@ -513,17 +635,30 @@ } | ||
// which is identified by upper path | ||
return path.slice(0, -1); | ||
return { | ||
path: path.slice(0, -1), | ||
actions: (_c = {}, _c[p] = "I", _c) | ||
}; | ||
} | ||
// Non-existing property DELETE case | ||
// no-op | ||
return path; | ||
return { | ||
path: path | ||
}; | ||
}; | ||
Store.prototype.update = function (paths) { | ||
if (this._batches) { | ||
this._batchesPendingPaths = this._batchesPendingPaths || []; | ||
this._batchesPendingPaths = this._batchesPendingPaths.concat(paths); | ||
return; | ||
Store.prototype.update = function (ad) { | ||
var _this = this; | ||
var _b, _c; | ||
(_c = (_b = this._extension) === null || _b === void 0 ? void 0 : _b.onSet) === null || _c === void 0 ? void 0 : _c.call(_b, this.toMethods().self, ad); | ||
var actions = new Set(); | ||
// check if actions descriptor can be unfolded into a number of individual update actions | ||
// this is the case when merge call swaps to properties for example | ||
// so we optimize rerendering only these properties | ||
if (ad.actions && Object.values(ad.actions).findIndex(function (i) { return i !== "U"; }) === -1) { | ||
// all actions are update actions | ||
Object.keys(ad.actions).forEach(function (key) { | ||
_this._subscribers.forEach(function (s) { return s.onSet({ path: ad.path.concat(key) }, actions); }); | ||
}); | ||
} | ||
var actions = []; | ||
this._subscribers.forEach(function (s) { return s.onSet(paths, actions); }); | ||
// TODO action can be duplicate, so we can distinct them before calling | ||
else { | ||
this._subscribers.forEach(function (s) { return s.onSet(ad, actions); }); | ||
} | ||
actions.forEach(function (a) { return a(); }); | ||
@@ -537,39 +672,2 @@ }; | ||
}; | ||
Store.prototype.startBatch = function (path, options) { | ||
this._batches += 1; | ||
var cbArgument = { | ||
path: path | ||
}; | ||
if (options && 'context' in options) { | ||
cbArgument.context = options.context; | ||
} | ||
if (this._value !== none) { | ||
cbArgument.state = this._value; | ||
} | ||
this._batchStartSubscribers.forEach(function (cb) { return cb(cbArgument); }); | ||
}; | ||
Store.prototype.finishBatch = function (path, options) { | ||
var cbArgument = { | ||
path: path | ||
}; | ||
if (options && 'context' in options) { | ||
cbArgument.context = options.context; | ||
} | ||
if (this._value !== none) { | ||
cbArgument.state = this._value; | ||
} | ||
this._batchFinishSubscribers.forEach(function (cb) { return cb(cbArgument); }); | ||
this._batches -= 1; | ||
if (this._batches === 0) { | ||
if (this._batchesPendingPaths) { | ||
var paths = this._batchesPendingPaths; | ||
this._batchesPendingPaths = undefined; | ||
this.update(paths); | ||
} | ||
} | ||
}; | ||
Store.prototype.postponeBatch = function (action) { | ||
this._batchesPendingActions = this._batchesPendingActions || []; | ||
this._batchesPendingActions.push(action); | ||
}; | ||
Store.prototype.getPlugin = function (pluginId) { | ||
@@ -591,11 +689,5 @@ return this._plugins.get(pluginId); | ||
} | ||
if (pluginCallbacks.onBatchStart) { | ||
this._batchStartSubscribers.add(function (p) { return pluginCallbacks.onBatchStart(p); }); | ||
} | ||
if (pluginCallbacks.onBatchFinish) { | ||
this._batchFinishSubscribers.add(function (p) { return pluginCallbacks.onBatchFinish(p); }); | ||
} | ||
}; | ||
Store.prototype.toMethods = function () { | ||
return new StateMethodsImpl(this, RootPath, this.get(RootPath), this.edition, OnSetUsedNoAction); | ||
return this._stateMethods; | ||
}; | ||
@@ -609,4 +701,6 @@ Store.prototype.subscribe = function (l) { | ||
Store.prototype.destroy = function () { | ||
var _this = this; | ||
this._destroySubscribers.forEach(function (cb) { return cb(_this._value !== none ? { state: _this._value } : {}); }); | ||
var _b, _c; | ||
(_c = (_b = this._extension) === null || _b === void 0 ? void 0 : _b.onDestroy) === null || _c === void 0 ? void 0 : _c.call(_b, this.toMethods().self); | ||
var params = this._value !== none ? { state: this._value } : {}; | ||
this._destroySubscribers.forEach(function (cb) { return cb(params); }); | ||
this._edition = DestroyedEdition; | ||
@@ -619,35 +713,10 @@ }; | ||
}()); | ||
var Promised = /** @class */ (function () { | ||
function Promised(promise, onResolve, onReject, onPostResolve) { | ||
var _this = this; | ||
this.promise = promise; | ||
if (!promise) { | ||
promise = new Promise(function (resolve) { | ||
_this.resolver = resolve; | ||
}); | ||
} | ||
this.promise = promise | ||
.then(function (r) { | ||
_this.fullfilled = true; | ||
if (!_this.resolver) { | ||
onResolve(r); | ||
} | ||
}) | ||
.catch(function (err) { | ||
_this.fullfilled = true; | ||
_this.error = err; | ||
onReject(); | ||
}) | ||
.then(function () { return onPostResolve(); }); | ||
} | ||
return Promised; | ||
}()); | ||
// use symbol property to allow for easier reference finding | ||
var ValueUnusedMarker = Symbol('ValueUnusedMarker'); | ||
function OnSetUsedNoAction() { } | ||
// use symbol to mark that a function has no effect anymore | ||
var UnmountedMarker = Symbol('UnmountedMarker'); | ||
OnSetUsedNoAction[UnmountedMarker] = true; | ||
// TODO remove from the docs IE11 support | ||
var StateMethodsImpl = /** @class */ (function () { | ||
function StateMethodsImpl(store, path, valueSource, valueEdition, onSetUsed) { | ||
var _this = this; | ||
this.store = store; | ||
@@ -658,61 +727,50 @@ this.path = path; | ||
this.onSetUsed = onSetUsed; | ||
this.valueCache = ValueUnusedMarker; | ||
this.valueUsed = ValueUnusedMarker; | ||
this[_a] = function () { | ||
// this is impossible (from the typescript point of view) to reach | ||
// to this function and call it from the client side | ||
throw new StateInvalidUsageError(_this.path, ErrorId.InternalError); | ||
}; | ||
} | ||
StateMethodsImpl.prototype.reconstruct = function (path, valueSource, valueEdition, forget) { | ||
StateMethodsImpl.prototype.reconstruct = function (path, valueSource, valueEdition, reset) { | ||
this.path = path; | ||
this.valueSource = valueSource; | ||
this.valueEdition = valueEdition; | ||
this.valueCache = ValueUnusedMarker; | ||
delete this.isDowngraded; | ||
delete this.subscribers; | ||
if (forget) { | ||
delete this.selfCache; | ||
delete this.children; | ||
this.valueUsed = ValueUnusedMarker; | ||
delete this.downgraded; | ||
if (reset) { | ||
delete this.selfUsed; | ||
delete this.childrenCreated; | ||
delete this.childrenUsedPrevious; | ||
} | ||
else { | ||
this.children = this.childrenCache; | ||
this.childrenUsedPrevious = this.childrenUsed; | ||
} | ||
delete this.childrenCache; | ||
delete this.childrenUsed; | ||
// We should not delete subscribers as these are self cleaned up when unmounted | ||
// Theoretically it is possible to reconnect subscribers like we done it for | ||
// children, but it is easier and more efficient to leave subscribers to have independent lifecycle | ||
// If we delete subscribers here, scoped states wrapped in React.memo | ||
// will lose state change propagation and rerendering for scopped states | ||
// delete this.subscribers; | ||
}; | ||
StateMethodsImpl.prototype.reconnect = function () { | ||
this.childrenCache = __assign(__assign({}, this.children), this.childrenCache); | ||
this.childrenUsed = __assign(__assign({}, this.childrenUsedPrevious), this.childrenUsed); | ||
}; | ||
StateMethodsImpl.prototype.getUntracked = function (allowPromised) { | ||
StateMethodsImpl.prototype.getUntracked = function (__internalAllowPromised) { | ||
if (this.valueEdition !== this.store.edition) { | ||
this.valueSource = this.store.get(this.path); | ||
this.valueEdition = this.store.edition; | ||
if (this.isMounted) { | ||
// this link is still mounted to a component | ||
// populate cache again to ensure correct tracking of usage | ||
// when React scans which states to rerender on update | ||
if (this.valueCache !== ValueUnusedMarker) { | ||
this.valueCache = ValueUnusedMarker; | ||
this.get(true); // renew cache to keep it marked used | ||
// let walkedState: StateMethods<StateValueAtPath> = this.onSetUsed[RootStateAccessor]; | ||
// for (let pathElem of this.path) { | ||
// walkedState = walkedState.nested(pathElem) | ||
// } | ||
// walkedState.get() | ||
} | ||
if (this.valueUsed !== ValueUnusedMarker) { | ||
this.valueUsed = ValueUnusedMarker; | ||
this.get({ __internalAllowPromised: true }); // renew cache to keep it marked used | ||
} | ||
else { | ||
// This link is not mounted to a component | ||
// for example, it might be global link or | ||
// a link which has been discarded after rerender | ||
// but still captured by some callback or an effect. | ||
// If we are here and if it was mounted before, | ||
// it means it has not been garbage collected | ||
// when a component unmounted. | ||
// We take this opportunity to clean up caches | ||
// to avoid memory leaks via stale children states cache. | ||
this.valueCache = ValueUnusedMarker; | ||
// TODO what do we need to do with this.children here? | ||
delete this.childrenCache; | ||
delete this.selfCache; | ||
} | ||
} | ||
if (this.valueSource === none && !allowPromised) { | ||
if (this.store.promised && this.store.promised.error) { | ||
throw this.store.promised.error; | ||
} | ||
if (__internalAllowPromised) { | ||
return this.valueSource; | ||
} | ||
if (this.store.promiseError) { | ||
throw this.store.promiseError; | ||
} | ||
if (this.store.promise) { | ||
throw new StateInvalidUsageError(this.path, ErrorId.GetStateWhenPromised); | ||
@@ -722,19 +780,31 @@ } | ||
}; | ||
StateMethodsImpl.prototype.get = function (allowPromised) { | ||
var currentValue = this.getUntracked(allowPromised); | ||
if (this.valueCache === ValueUnusedMarker) { | ||
if (this.isDowngraded) { | ||
this.valueCache = currentValue; | ||
StateMethodsImpl.prototype.get = function (options) { | ||
var _b; | ||
var valueSource = this.getUntracked(options === null || options === void 0 ? void 0 : options.__internalAllowPromised); | ||
if (options === null || options === void 0 ? void 0 : options.stealth) { | ||
return valueSource; | ||
} | ||
if (this.valueUsed === ValueUnusedMarker) { | ||
if (Array.isArray(valueSource)) { | ||
this.valueUsed = this.valueArrayImpl(valueSource); | ||
} | ||
else if (Array.isArray(currentValue)) { | ||
this.valueCache = this.valueArrayImpl(currentValue); | ||
else if (Object(valueSource) === valueSource) { | ||
if (((_b = valueSource.constructor) === null || _b === void 0 ? void 0 : _b.name) === "Object") { | ||
this.valueUsed = this.valueObjectImpl(valueSource); | ||
} | ||
else { | ||
// any other object except Object, for example Date | ||
this.downgraded = true; | ||
this.valueUsed = valueSource; | ||
} | ||
} | ||
else if (typeof currentValue === 'object' && currentValue !== null) { | ||
this.valueCache = this.valueObjectImpl(currentValue); | ||
} | ||
else { | ||
this.valueCache = currentValue; | ||
this.valueUsed = valueSource; | ||
} | ||
} | ||
return this.valueCache; | ||
if (options === null || options === void 0 ? void 0 : options.noproxy) { | ||
this.downgraded = true; | ||
return valueSource; | ||
} | ||
return this.valueUsed; | ||
}; | ||
@@ -749,6 +819,14 @@ Object.defineProperty(StateMethodsImpl.prototype, "value", { | ||
StateMethodsImpl.prototype.setUntracked = function (newValue, mergeValue) { | ||
var r = this.setUntrackedV4(newValue, mergeValue); | ||
if (r) { | ||
return [r.path]; | ||
} | ||
return []; | ||
}; | ||
StateMethodsImpl.prototype.setUntrackedV4 = function (newValue, mergeValue) { | ||
if (typeof newValue === 'function') { | ||
newValue = newValue(this.getUntracked()); | ||
} | ||
if (typeof newValue === 'object' && newValue !== null && newValue[SelfMethodsID]) { | ||
if (Object(newValue) === newValue && newValue[SelfMethodsID]) { | ||
// TODO check on read instead as it might escape as nested on set anyway | ||
throw new StateInvalidUsageError(this.path, ErrorId.SetStateToValueFromState); | ||
@@ -759,10 +837,20 @@ } | ||
// so skip this set call as it does not make an effect | ||
return []; | ||
return null; | ||
} | ||
return [this.store.set(this.path, newValue, mergeValue)]; | ||
return this.store.set(this.path, newValue, mergeValue); | ||
}; | ||
StateMethodsImpl.prototype.set = function (newValue) { | ||
this.store.update(this.setUntracked(newValue)); | ||
var ad = this.setUntrackedV4(newValue); | ||
if (ad) { | ||
this.store.update(ad); | ||
} | ||
}; | ||
StateMethodsImpl.prototype.mergeUntracked = function (sourceValue) { | ||
var r = this.mergeUntrackedV4(sourceValue); | ||
if (r) { | ||
return [r.path]; | ||
} | ||
return []; | ||
}; | ||
StateMethodsImpl.prototype.mergeUntrackedV4 = function (sourceValue) { | ||
var currentValue = this.getUntracked(); | ||
@@ -772,9 +860,16 @@ if (typeof sourceValue === 'function') { | ||
} | ||
var updatedPaths; | ||
var deletedOrInsertedProps = false; | ||
if (Array.isArray(currentValue)) { | ||
if (Array.isArray(sourceValue)) { | ||
return this.setUntracked(currentValue.concat(sourceValue), sourceValue); | ||
var ad_1 = { path: this.path, actions: {} }; | ||
sourceValue.forEach(function (e, i) { | ||
ad_1.actions[currentValue.push(e) - 1] = "I"; | ||
}); | ||
if (Object.keys(ad_1.actions).length > 0) { | ||
this.setUntrackedV4(currentValue, sourceValue); | ||
return ad_1; | ||
} | ||
return null; | ||
} | ||
else { | ||
var ad_2 = { path: this.path, actions: {} }; | ||
var deletedIndexes_1 = []; | ||
@@ -785,7 +880,12 @@ Object.keys(sourceValue).sort().forEach(function (i) { | ||
if (newPropValue === none) { | ||
deletedOrInsertedProps = true; | ||
ad_2.actions[index] = "D"; | ||
deletedIndexes_1.push(index); | ||
} | ||
else { | ||
deletedOrInsertedProps = deletedOrInsertedProps || !(index in currentValue); | ||
if (index in currentValue) { | ||
ad_2.actions[index] = "U"; | ||
} | ||
else { | ||
ad_2.actions[index] = "I"; | ||
} | ||
currentValue[index] = newPropValue; | ||
@@ -800,33 +900,45 @@ } | ||
}); | ||
updatedPaths = this.setUntracked(currentValue, sourceValue); | ||
if (Object.keys(ad_2.actions).length > 0) { | ||
this.setUntrackedV4(currentValue, sourceValue); | ||
return ad_2; | ||
} | ||
return null; | ||
} | ||
} | ||
else if (typeof currentValue === 'object' && currentValue !== null) { | ||
else if (Object(currentValue) === currentValue) { | ||
var ad_3 = { path: this.path, actions: {} }; | ||
Object.keys(sourceValue).forEach(function (key) { | ||
var newPropValue = sourceValue[key]; | ||
if (newPropValue === none) { | ||
deletedOrInsertedProps = true; | ||
ad_3.actions[key] = "D"; | ||
delete currentValue[key]; | ||
} | ||
else { | ||
deletedOrInsertedProps = deletedOrInsertedProps || !(key in currentValue); | ||
if (key in currentValue) { | ||
ad_3.actions[key] = "U"; | ||
} | ||
else { | ||
ad_3.actions[key] = "I"; | ||
} | ||
currentValue[key] = newPropValue; | ||
} | ||
}); | ||
updatedPaths = this.setUntracked(currentValue, sourceValue); | ||
if (Object.keys(ad_3.actions).length > 0) { | ||
this.setUntrackedV4(currentValue, sourceValue); | ||
return ad_3; | ||
} | ||
return null; | ||
} | ||
else if (typeof currentValue === 'string') { | ||
return this.setUntracked((currentValue + String(sourceValue)), sourceValue); | ||
return this.setUntrackedV4((currentValue + String(sourceValue)), sourceValue); | ||
} | ||
else { | ||
return this.setUntracked(sourceValue); | ||
return this.setUntrackedV4(sourceValue); | ||
} | ||
if (updatedPaths.length !== 1 || updatedPaths[0] !== this.path || deletedOrInsertedProps) { | ||
return updatedPaths; | ||
} | ||
var updatedPath = updatedPaths[0]; | ||
return Object.keys(sourceValue).map(function (p) { return updatedPath.slice().concat(p); }); | ||
}; | ||
StateMethodsImpl.prototype.merge = function (sourceValue) { | ||
this.store.update(this.mergeUntracked(sourceValue)); | ||
var r = this.mergeUntrackedV4(sourceValue); | ||
if (r) { | ||
this.store.update(r); | ||
} | ||
}; | ||
@@ -837,3 +949,6 @@ StateMethodsImpl.prototype.nested = function (key) { | ||
StateMethodsImpl.prototype.rerender = function (paths) { | ||
this.store.update(paths); | ||
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { | ||
var path = paths_1[_i]; | ||
this.store.update({ path: path }); | ||
} | ||
}; | ||
@@ -867,32 +982,60 @@ StateMethodsImpl.prototype.destroy = function () { | ||
}; | ||
StateMethodsImpl.prototype.onSet = function (paths, actions) { | ||
StateMethodsImpl.prototype.onSet = function (ad, actions) { | ||
var _this = this; | ||
var update = function () { | ||
if (_this.isDowngraded && _this.valueCache !== ValueUnusedMarker) { | ||
actions.push(_this.onSetUsed); | ||
delete _this.selfCache; | ||
return true; | ||
var _b; | ||
var isAffected = false; | ||
if (_this.downgraded | ||
// TODO this condition becomes redundant when Downgraded plugins is deleted | ||
&& _this.valueUsed !== ValueUnusedMarker) { | ||
actions.add(_this.onSetUsed); | ||
delete _this.selfUsed; | ||
isAffected = true; | ||
} | ||
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { | ||
var path = paths_1[_i]; | ||
var nextChildKey = path[_this.path.length]; | ||
if (nextChildKey === undefined) { | ||
// There is no next child to dive into | ||
// So it is this one which was updated | ||
if (_this.valueCache !== ValueUnusedMarker) { | ||
actions.push(_this.onSetUsed); | ||
delete _this.selfCache; | ||
delete _this.childrenCache; | ||
return true; | ||
var path = ad.path; | ||
var nextChildKey = path[_this.path.length]; | ||
if (nextChildKey === undefined) { | ||
// There is no next child to dive into | ||
// So it is this one which was updated | ||
if (_this.valueUsed !== ValueUnusedMarker) { | ||
actions.add(_this.onSetUsed); | ||
delete _this.selfUsed; | ||
delete _this.childrenUsed; | ||
if (ad.actions && _this.childrenCreated) { | ||
// TODO add automated unit tests for this part | ||
if (Array.isArray(_this.valueSource) | ||
&& Object.values(ad.actions).includes("D")) { | ||
// this is an array and some elements were removed | ||
// so invalidate cache for all children after the first deleted | ||
var firstDeletedIndex = Object.keys(ad.actions) | ||
.map(function (i) { return Number(i); }) | ||
.sort() | ||
.find(function (i) { var _b; return ((_b = ad.actions) === null || _b === void 0 ? void 0 : _b[i]) === "D"; }); | ||
for (var childKey in _this.childrenCreated) { | ||
if (Number(childKey) >= firstDeletedIndex || | ||
childKey in ad.actions) { | ||
delete _this.childrenCreated[childKey]; | ||
} | ||
} | ||
} | ||
else { | ||
for (var childKey in ad.actions) { | ||
delete _this.childrenCreated[childKey]; | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
var nextChild = _this.childrenCache && _this.childrenCache[nextChildKey]; | ||
if (nextChild && nextChild.onSet(paths, actions)) { | ||
delete _this.selfCache; | ||
return true; | ||
else { | ||
delete _this.childrenCreated; | ||
} | ||
return true; | ||
} | ||
} | ||
return false; | ||
else { | ||
var nextChild = (_b = _this.childrenUsed) === null || _b === void 0 ? void 0 : _b[nextChildKey]; | ||
if (nextChild && nextChild.onSet(ad, actions)) { | ||
delete _this.selfUsed; | ||
return true; | ||
} | ||
} | ||
return isAffected; | ||
}; | ||
@@ -902,4 +1045,4 @@ var updated = update(); | ||
this.subscribers.forEach(function (s) { | ||
if (s.onSet(paths, actions)) { | ||
delete _this.selfCache; | ||
if (s.onSet(ad, actions)) { | ||
delete _this.selfUsed; | ||
} | ||
@@ -916,3 +1059,3 @@ }); | ||
} | ||
if (typeof value === 'object' && value !== null) { | ||
if (Object(value) === value) { | ||
return Object.keys(value); | ||
@@ -926,28 +1069,28 @@ } | ||
StateMethodsImpl.prototype.child = function (key) { | ||
// if this state is not mounted to a hook, | ||
// we do not cache children to avoid unnecessary memory leaks | ||
if (this.isMounted) { | ||
this.childrenCache = this.childrenCache || {}; | ||
var cachedChild = this.childrenCache[key]; | ||
if (cachedChild) { | ||
return cachedChild; | ||
} | ||
this.childrenUsed = this.childrenUsed || {}; | ||
var cachedChild = this.childrenUsed.hasOwnProperty(key) && this.childrenUsed[key]; | ||
if (cachedChild) { | ||
return cachedChild; | ||
} | ||
this.children = this.children || {}; | ||
var child = this.children[key]; | ||
var valueSource = this.valueSource[key]; | ||
if (typeof valueSource === 'function') { | ||
// hitting a method of a custom type, should be no-op | ||
throw new StateInvalidUsageError(this.path, ErrorId.GetProperty_Function); | ||
} | ||
this.childrenCreated = this.childrenCreated || {}; | ||
var child = this.childrenCreated[key]; | ||
var r; | ||
if (child) { | ||
child.reconstruct(this.path.slice().concat(key), this.valueSource[key], this.valueEdition, false); | ||
child.reconstruct(this.path.concat(key), valueSource, this.valueEdition, false); | ||
r = child; | ||
} | ||
else { | ||
r = new StateMethodsImpl(this.store, this.path.slice().concat(key), this.valueSource[key], this.valueEdition, this.onSetUsed); | ||
this.children[key] = r; | ||
r = new StateMethodsImpl(this.store, this.path.concat(key), valueSource, this.valueEdition, this.onSetUsed); | ||
this.childrenCreated[key] = r; | ||
} | ||
if (this.isDowngraded) { | ||
r.isDowngraded = true; | ||
if (this.downgraded) { | ||
// TODO this is redundant when Downgraded plugin is deleted | ||
r.downgraded = true; | ||
} | ||
if (this.childrenCache) { | ||
this.childrenCache[key] = r; | ||
} | ||
this.childrenUsed[key] = r; | ||
return r; | ||
@@ -957,6 +1100,2 @@ }; | ||
var _this = this; | ||
if (IsNoProxy) { | ||
this.isDowngraded = true; | ||
return currentValue; | ||
} | ||
return proxyWrap(this.path, currentValue, function () { return currentValue; }, function (target, key) { | ||
@@ -992,7 +1131,6 @@ if (key === 'length') { | ||
var _this = this; | ||
if (IsNoProxy) { | ||
this.isDowngraded = true; | ||
return currentValue; | ||
} | ||
return proxyWrap(this.path, currentValue, function () { return currentValue; }, function (target, key) { | ||
if (key in Object.prototype) { | ||
return Object.prototype[key]; | ||
} | ||
if (key === SelfMethodsID) { | ||
@@ -1018,4 +1156,4 @@ return _this; | ||
var _this = this; | ||
if (this.selfCache) { | ||
return this.selfCache; | ||
if (this.selfUsed) { | ||
return this.selfUsed; | ||
} | ||
@@ -1033,7 +1171,14 @@ var getter = function (_, key) { | ||
var nestedGetter = function (prop) { | ||
var currentDowngraded = _this.isDowngraded; // relevant for IE11 only | ||
var currentValue = _this.get(); // IE11 marks this as downgraded | ||
_this.isDowngraded = currentDowngraded; // relevant for IE11 only | ||
var currentValue = _this.get(); | ||
if (prop in Object.prototype) { | ||
// Mark it used entirely, so changes to the value | ||
// invalidate and rerender results for Object.prototype.toString(), | ||
// for example. | ||
// We check for Object prototype functions | ||
// even for primitive values, because primitive values still | ||
// can have object methods. | ||
return Object.prototype[prop]; | ||
} | ||
if ( // if currentValue is primitive type | ||
(typeof currentValue !== 'object' || currentValue === null) && | ||
(Object(currentValue) !== currentValue) && | ||
// if promised, it will be none | ||
@@ -1079,3 +1224,3 @@ currentValue !== none) { | ||
case 'get': | ||
return function () { return _this.get(); }; | ||
return function (opts) { return _this.get(opts); }; | ||
case 'set': | ||
@@ -1087,36 +1232,17 @@ return function (p) { return _this.set(p); }; | ||
return function (p) { return nestedGetter(p); }; | ||
case 'batch': | ||
// tslint:disable-next-line: no-any | ||
return function (action, context) { return _this.batch(action, context); }; | ||
case 'attach': | ||
return function (p) { return _this.attach(p); }; | ||
case 'destroy': | ||
case 'destroy': // TODO move destroy to the state, otherwise State type hides this well existing property | ||
return function () { return _this.destroy(); }; | ||
default: | ||
// check if extension method | ||
var ext = _this.store.extension; | ||
if (ext && key in ext) { | ||
return ext[key](_this.self); | ||
} | ||
// otherwise nested child | ||
return nestedGetter(key); | ||
} | ||
}; | ||
if (IsNoProxy) { | ||
// minimal support for IE11 | ||
var result_1 = (Array.isArray(this.valueSource) ? [] : {}); | ||
[self, 'toJSON', 'path', 'keys', 'value', 'ornull', | ||
'promised', 'error', 'get', 'set', 'merge', | ||
'nested', 'batch', 'attach', 'destroy'] | ||
.forEach(function (key) { | ||
Object.defineProperty(result_1, key, { | ||
get: function () { return getter(result_1, key); } | ||
}); | ||
}); | ||
if (typeof this.valueSource === 'object' && this.valueSource !== null) { | ||
Object.keys(this.valueSource).forEach(function (key) { | ||
Object.defineProperty(result_1, key, { | ||
enumerable: true, | ||
get: function () { return getter(result_1, key); } | ||
}); | ||
}); | ||
} | ||
this.selfCache = result_1; | ||
return this.selfCache; | ||
} | ||
this.selfCache = proxyWrap(this.path, this.valueSource, function () { | ||
this.selfUsed = proxyWrap(this.path, this.valueSource, function () { | ||
return _this.get(); | ||
@@ -1126,3 +1252,3 @@ }, getter, function (_, key, value) { | ||
}, false); | ||
return this.selfCache; | ||
return this.selfUsed; | ||
}, | ||
@@ -1134,7 +1260,4 @@ enumerable: false, | ||
get: function () { | ||
var currentValue = this.get(true); // marks used | ||
if (currentValue === none && this.store.promised && !this.store.promised.fullfilled) { | ||
return true; | ||
} | ||
return false; | ||
this.get({ __internalAllowPromised: true }); // marks used | ||
return !!this.store.promise; | ||
}, | ||
@@ -1146,10 +1269,4 @@ enumerable: false, | ||
get: function () { | ||
var currentValue = this.get(true); // marks used | ||
if (currentValue === none) { | ||
if (this.store.promised && this.store.promised.fullfilled) { | ||
return this.store.promised.error; | ||
} | ||
this.get(); // will throw 'read while promised' exception | ||
} | ||
return undefined; | ||
this.get({ __internalAllowPromised: !!this.store.promiseError }); // marks used | ||
return this.store.promiseError; | ||
}, | ||
@@ -1159,17 +1276,2 @@ enumerable: false, | ||
}); | ||
StateMethodsImpl.prototype.batch = function (action, context) { | ||
var _this = this; | ||
var opts = { context: context }; | ||
try { | ||
this.store.startBatch(this.path, opts); | ||
var result = action(this.self); | ||
if (result === postpone) { | ||
this.store.postponeBatch(function () { return _this.batch(action, context); }); | ||
} | ||
return result; | ||
} | ||
finally { | ||
this.store.finishBatch(this.path, opts); | ||
} | ||
}; | ||
Object.defineProperty(StateMethodsImpl.prototype, "ornull", { | ||
@@ -1190,6 +1292,6 @@ get: function () { | ||
if (pluginMeta.id === DowngradedID) { | ||
this.isDowngraded = true; | ||
if (this.valueCache !== ValueUnusedMarker) { | ||
this.downgraded = true; | ||
if (this.valueUsed !== ValueUnusedMarker) { | ||
var currentValue = this.getUntracked(true); | ||
this.valueCache = currentValue; | ||
this.valueUsed = currentValue; | ||
} | ||
@@ -1211,2 +1313,3 @@ return this.self; | ||
}()); | ||
_a = __state; | ||
function proxyWrap(path, | ||
@@ -1224,3 +1327,3 @@ // tslint:disable-next-line: no-any | ||
}; | ||
if (typeof targetBootstrap !== 'object' || targetBootstrap === null) { | ||
if (Object(targetBootstrap) !== targetBootstrap) { | ||
targetBootstrap = {}; | ||
@@ -1275,3 +1378,3 @@ } | ||
var targetReal = targetGetter(); | ||
if (typeof targetReal === 'object' && targetReal !== null) { | ||
if (Object(targetReal) === targetReal) { | ||
return p in targetReal; | ||
@@ -1315,3 +1418,3 @@ } | ||
} | ||
function createStore(initial) { | ||
function createStore(initial, extensions) { | ||
var initialValue = initial; | ||
@@ -1321,16 +1424,66 @@ if (typeof initial === 'function') { | ||
} | ||
if (typeof initialValue === 'object' && initialValue !== null && initialValue[SelfMethodsID]) { | ||
if (Object(initialValue) === initialValue && initialValue[SelfMethodsID]) { | ||
throw new StateInvalidUsageError(RootPath, ErrorId.InitStateToValueFromState); | ||
} | ||
return new Store(initialValue); | ||
return new Store(initialValue, extensions === null || extensions === void 0 ? void 0 : extensions()); | ||
} | ||
// Do not try to use useLayoutEffect if DOM not available (SSR) | ||
var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect; | ||
var useEffectOrigin; | ||
function useHookEffect(effect, deps) { | ||
for (var _i = 0, _a = deps || []; _i < _a.length; _i++) { | ||
var i = _a[_i]; | ||
var configuration = { | ||
interceptDependencyListsMode: 'always', | ||
// TODO this does not always work, so it is better if it is set by the app explictly. Document this | ||
isDevelopmentMode: typeof process === 'object' && | ||
typeof process.env === 'object' && | ||
process.env.NODE_ENV === 'development', | ||
promiseDetector: function (p) { return Promise.resolve(p) === p; }, | ||
hiddenInterceptDependencyListsModeDebug: false | ||
}; | ||
// TODO document | ||
function configure(config) { | ||
var _b, _c, _d; | ||
configuration = { | ||
interceptDependencyListsMode: (_b = config.interceptDependencyListsMode) !== null && _b !== void 0 ? _b : configuration.interceptDependencyListsMode, | ||
isDevelopmentMode: (_c = config.isDevelopmentMode) !== null && _c !== void 0 ? _c : configuration.isDevelopmentMode, | ||
promiseDetector: (_d = config.promiseDetector) !== null && _d !== void 0 ? _d : configuration.promiseDetector, | ||
hiddenInterceptDependencyListsModeDebug: false | ||
}; | ||
interceptReactHooks(); // not really required, but for safety | ||
if (configuration.interceptDependencyListsMode === 'never') { | ||
configuration.hiddenInterceptDependencyListsModeDebug = false; | ||
React['useEffect'] = useEffectOrigin; | ||
React['useLayoutEffect'] = useLayoutEffectOrigin; | ||
React['useInsertionEffect'] = useInsertionEffectOrigin; | ||
React['useImperativeHandle'] = useImperativeHandleOrigin; | ||
React['useMemo'] = useMemoOrigin; | ||
React['useCallback'] = useCallbackOrigin; | ||
// the following does not make an effect as memo calls happen on module load | ||
// so it is always set to memoIntercept | ||
React['memo'] = memoOrigin; | ||
} | ||
else { | ||
React['useEffect'] = useEffectIntercept; | ||
React['useLayoutEffect'] = useLayoutEffectIntercept; | ||
React['useInsertionEffect'] = useInsertionEffectIntercept; | ||
React['useImperativeHandle'] = useImperativeHandleIntercept; | ||
React['useMemo'] = useMemoIntercept; | ||
React['useCallback'] = useCallbackIntercept; | ||
// the following does not make an effect as memo calls happen on module load | ||
// so it is always set to memoIntercept | ||
React['memo'] = memoIntercept; | ||
if (configuration.interceptDependencyListsMode === 'development' | ||
&& configuration.isDevelopmentMode) { | ||
configuration.hiddenInterceptDependencyListsModeDebug = true; | ||
} | ||
} | ||
} | ||
function reconnectDependencies(deps, fromIntercept) { | ||
for (var _i = 0, _b = deps || []; _i < _b.length; _i++) { | ||
var i = _b[_i]; | ||
if (i === Object(i)) { | ||
var state = i[self]; | ||
if (state) { | ||
if (fromIntercept && configuration.hiddenInterceptDependencyListsModeDebug) { | ||
// TODO document this exception | ||
throw new StateInvalidUsageError(state.path, ErrorId.StateUsedInDependencyList); | ||
} | ||
state.reconnect(); | ||
@@ -1340,13 +1493,104 @@ } | ||
} | ||
return deps; | ||
} | ||
var useEffectOrigin; | ||
function useHookEffect(effect, deps) { | ||
reconnectDependencies(deps); | ||
return useEffectOrigin(effect, deps); | ||
} | ||
function interceptUseEffect() { | ||
function useEffectIntercept(effect, deps) { | ||
reconnectDependencies(deps, true); | ||
return useEffectOrigin(effect, deps); | ||
} | ||
var useLayoutEffectOrigin; | ||
function useHookLayoutEffect(effect, deps) { | ||
reconnectDependencies(deps); | ||
return useLayoutEffectOrigin(effect, deps); | ||
} | ||
function useLayoutEffectIntercept(effect, deps) { | ||
reconnectDependencies(deps, true); | ||
return useLayoutEffectOrigin(effect, deps); | ||
} | ||
var useInsertionEffectOrigin; | ||
function useHookInsertionEffect(effect, deps) { | ||
reconnectDependencies(deps); | ||
return useInsertionEffectOrigin(effect, deps); | ||
} | ||
function useInsertionEffectIntercept(effect, deps) { | ||
reconnectDependencies(deps, true); | ||
return useInsertionEffectOrigin(effect, deps); | ||
} | ||
var useImperativeHandleOrigin; | ||
function useHookImperativeHandle(ref, init, deps) { | ||
reconnectDependencies(deps); | ||
return useImperativeHandleOrigin(ref, init, deps); | ||
} | ||
function useImperativeHandleIntercept(ref, init, deps) { | ||
reconnectDependencies(deps, true); | ||
return useImperativeHandleOrigin(ref, init, deps); | ||
} | ||
var useMemoOrigin; | ||
function useHookMemo(factory, deps) { | ||
reconnectDependencies(deps); | ||
return useMemoOrigin(factory, deps); | ||
} | ||
function useMemoIntercept(factory, deps) { | ||
reconnectDependencies(deps, true); | ||
return useMemoOrigin(factory, deps); | ||
} | ||
var useCallbackOrigin; | ||
function useHookCallback(callback, deps) { | ||
reconnectDependencies(deps); | ||
return useCallbackOrigin(callback, deps); | ||
} | ||
function useCallbackIntercept(callback, deps) { | ||
reconnectDependencies(deps, true); | ||
return useCallbackOrigin(callback, deps); | ||
} | ||
var memoOrigin; | ||
function hookMemo(Component, propsAreEqual) { | ||
return memoOrigin(Component, function (prevProps, nextProps) { | ||
reconnectDependencies(Object.keys(nextProps).map(function (i) { return nextProps[i]; })); | ||
return (propsAreEqual || shallowEqual)(prevProps, nextProps); | ||
}); | ||
} | ||
function memoIntercept(Component, propsAreEqual) { | ||
return memoOrigin(Component, function (prevProps, nextProps) { | ||
reconnectDependencies(Object.keys(nextProps).map(function (i) { return nextProps[i]; }), true); | ||
return (propsAreEqual || shallowEqual)(prevProps, nextProps); | ||
}); | ||
} | ||
function interceptReactHooks() { | ||
if (!useEffectOrigin) { | ||
useEffectOrigin = React['useEffect']; | ||
React['useEffect'] = useHookEffect; | ||
React['useEffect'] = useEffectIntercept; | ||
} | ||
if (!useLayoutEffectOrigin) { | ||
useLayoutEffectOrigin = React['useLayoutEffect']; | ||
React['useLayoutEffect'] = useLayoutEffectIntercept; | ||
} | ||
if (!useInsertionEffectOrigin) { | ||
useInsertionEffectOrigin = React['useInsertionEffect']; | ||
React['useInsertionEffect'] = useInsertionEffectIntercept; | ||
} | ||
if (!useImperativeHandleOrigin) { | ||
useImperativeHandleOrigin = React['useImperativeHandle']; | ||
React['useImperativeHandle'] = useImperativeHandleIntercept; | ||
} | ||
if (!useMemoOrigin) { | ||
useMemoOrigin = React['useMemo']; | ||
React['useMemo'] = useMemoIntercept; | ||
} | ||
if (!useCallbackOrigin) { | ||
useCallbackOrigin = React['useCallback']; | ||
React['useCallback'] = useCallbackIntercept; | ||
} | ||
if (!memoOrigin) { | ||
memoOrigin = React['memo']; | ||
React['memo'] = memoIntercept; | ||
} | ||
} | ||
interceptUseEffect(); | ||
interceptReactHooks(); | ||
export { DevTools, DevToolsID, Downgraded, StateFragment, createState, none, postpone, useHookstate, useState }; | ||
export { DevTools, DevToolsID, Downgraded, StateFragment, __state, configure, createHookstate, createState, extend, hookMemo, none, useHookCallback, useHookEffect, useHookImperativeHandle, useHookInsertionEffect, useHookLayoutEffect, useHookMemo, useHookstate, useState }; | ||
//# sourceMappingURL=index.es.js.map |
@@ -54,8 +54,51 @@ 'use strict'; | ||
/** | ||
* Special symbol which might be returned by onPromised callback of [StateMethods.map](#map) function. | ||
* | ||
* [Learn more...](https://hookstate.js.org/docs/asynchronous-state#executing-an-action-when-state-is-loaded) | ||
* Copied from fbjs is-shallow-equal | ||
*/ | ||
var postpone = Symbol('postpone'); | ||
var hasOwnProperty = Object.prototype.hasOwnProperty; | ||
/** | ||
* inlined Object.is polyfill to avoid requiring consumers ship their own | ||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | ||
*/ | ||
function is(x, y) { | ||
// SameValue algorithm | ||
if (x === y) { // Steps 1-5, 7-10 | ||
// Steps 6.b-6.e: +0 != -0 | ||
// Added the nonzero y check to make Flow happy, but it is redundant | ||
return x !== 0 || y !== 0 || 1 / x === 1 / y; | ||
} | ||
else { | ||
// Step 6.a: NaN == NaN | ||
return x !== x && y !== y; | ||
} | ||
} | ||
/** | ||
* Performs equality by iterating through keys on an object and returning false | ||
* when any key has values which are not strictly equal between the arguments. | ||
* Returns true when the values of all keys are strictly equal. | ||
*/ | ||
function shallowEqual(objA, objB) { | ||
if (is(objA, objB)) { | ||
return true; | ||
} | ||
if (typeof objA !== 'object' || objA === null || | ||
typeof objB !== 'object' || objB === null) { | ||
return false; | ||
} | ||
var keysA = Object.keys(objA); | ||
var keysB = Object.keys(objB); | ||
if (keysA.length !== keysB.length) { | ||
return false; | ||
} | ||
// Test for A's keys different from B. | ||
for (var i = 0; i < keysA.length; i++) { | ||
if (!hasOwnProperty.call(objB, keysA[i]) || | ||
!is(objA[keysA[i]], objB[keysA[i]])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
var _a; | ||
/** | ||
* Special symbol which might be used to delete properties | ||
@@ -67,2 +110,5 @@ * from an object calling [StateMethods.set](#set) or [StateMethods.merge](#merge). | ||
var none = Symbol('none'); | ||
// type PickByType<T, U, B = false> = Pick<T, KeysOfType<T, U, B>>; | ||
var __state = Symbol('__state'); | ||
// TODO deprecate | ||
/** | ||
@@ -100,3 +146,6 @@ * Creates new state and returns it. | ||
function createState(initial) { | ||
var methods = createStore(initial).toMethods(); | ||
return createHookstate(initial); | ||
} | ||
function createHookstate(initial, extension) { | ||
var methods = createStore(initial, extension).toMethods(); | ||
var devtools = createState[DevToolsID]; | ||
@@ -111,4 +160,38 @@ if (devtools) { | ||
} | ||
function useHookstate(source) { | ||
var parentMethods = typeof source === 'object' && source !== null ? | ||
// TODO document | ||
function extend(extensions) { | ||
var exts = extensions.filter(function (i) { return i; }); | ||
var onSetCbs = exts.map(function (i) { return i.onSet; }).filter(function (i) { return i; }); | ||
var onDestroyCbs = exts.map(function (i) { return i.onDestroy; }).filter(function (i) { return i; }); | ||
var result = { | ||
onInit: function (instanceFactory) { | ||
var combinedMethods = {}; | ||
for (var _i = 0, exts_1 = exts; _i < exts_1.length; _i++) { | ||
var ext = exts_1[_i]; | ||
var extMethods = ext.onInit(instanceFactory); | ||
combinedMethods = __assign(__assign({}, combinedMethods), extMethods); | ||
} | ||
return combinedMethods; | ||
} | ||
}; | ||
if (onSetCbs.length > 0) { | ||
result.onSet = function (s, d) { | ||
for (var _i = 0, onSetCbs_1 = onSetCbs; _i < onSetCbs_1.length; _i++) { | ||
var cb = onSetCbs_1[_i]; | ||
cb(s, d); | ||
} | ||
}; | ||
} | ||
if (onDestroyCbs.length > 0) { | ||
result.onDestroy = function (s) { | ||
for (var _i = 0, onDestroyCbs_1 = onDestroyCbs; _i < onDestroyCbs_1.length; _i++) { | ||
var cb = onDestroyCbs_1[_i]; | ||
cb(s); | ||
} | ||
}; | ||
} | ||
return result; | ||
} | ||
function useHookstate(source, extension) { | ||
var parentMethods = Object(source) === source ? | ||
source[self] : | ||
@@ -134,3 +217,3 @@ undefined; | ||
}; | ||
var _a = React__default["default"].useState(initializer), value_1 = _a[0], setValue_1 = _a[1]; | ||
var _b = React__default["default"].useState(initializer), value_1 = _b[0], setValue_1 = _b[1]; | ||
value_1.state.reconstruct(parentMethods.path, value_1.store.get(parentMethods.path), value_1.store.edition, | ||
@@ -167,3 +250,3 @@ // parent state object has changed its reference object | ||
}; | ||
var _b = React__default["default"].useState(initializer), value_2 = _b[0], setValue_2 = _b[1]; | ||
var _c = React__default["default"].useState(initializer), value_2 = _c[0], setValue_2 = _c[1]; | ||
value_2.state.reconstruct(RootPath, value_2.store.get(RootPath), value_2.store.edition, | ||
@@ -192,3 +275,3 @@ // parent state object has changed its reference object | ||
var initializer = function () { | ||
var store = createStore(source); | ||
var store = createStore(source, extension); | ||
var onSetUsedCallback = function () { return setValue_3({ | ||
@@ -204,3 +287,3 @@ store: store, | ||
}; | ||
var _c = React__default["default"].useState(initializer), value_3 = _c[0], setValue_3 = _c[1]; | ||
var _d = React__default["default"].useState(initializer), value_3 = _d[0], setValue_3 = _d[1]; | ||
value_3.state.reconstruct(RootPath, value_3.store.get(RootPath), value_3.store.edition, false); | ||
@@ -214,3 +297,3 @@ value_3.store.subscribe(value_3.state); // in sync here, not in effect | ||
}, []); | ||
if (isDevelopmentMode) { | ||
if (configuration.isDevelopmentMode) { | ||
// This is a workaround for the issue: | ||
@@ -240,5 +323,6 @@ // https://github.com/avkonst/hookstate/issues/109 | ||
function StateFragment(props) { | ||
var scoped = useState(props.state); | ||
var scoped = useHookstate(props.state, props.extension); | ||
return props.children(scoped); | ||
} | ||
// TODO deprecate | ||
/** | ||
@@ -288,5 +372,2 @@ * A plugin which allows to opt-out from usage of Javascript proxies for | ||
/// | ||
var isDevelopmentMode = typeof process === 'object' && | ||
typeof process.env === 'object' && | ||
process.env.NODE_ENV === 'development'; | ||
var self = Symbol('self'); | ||
@@ -299,2 +380,4 @@ var EmptyDevToolsExtensions = { | ||
(function (ErrorId) { | ||
// TODO document | ||
ErrorId[ErrorId["StateUsedInDependencyList"] = 100] = "StateUsedInDependencyList"; | ||
ErrorId[ErrorId["InitStateToValueFromState"] = 101] = "InitStateToValueFromState"; | ||
@@ -308,2 +391,4 @@ ErrorId[ErrorId["SetStateToValueFromState"] = 102] = "SetStateToValueFromState"; | ||
ErrorId[ErrorId["ToJson_State"] = 109] = "ToJson_State"; | ||
// TODO document | ||
ErrorId[ErrorId["GetProperty_Function"] = 110] = "GetProperty_Function"; | ||
ErrorId[ErrorId["GetUnknownPlugin"] = 120] = "GetUnknownPlugin"; | ||
@@ -324,2 +409,4 @@ ErrorId[ErrorId["SetProperty_State"] = 201] = "SetProperty_State"; | ||
ErrorId[ErrorId["Apply_Value"] = 214] = "Apply_Value"; | ||
// TODO document | ||
ErrorId[ErrorId["InternalError"] = 0] = "InternalError"; | ||
})(ErrorId || (ErrorId = {})); | ||
@@ -334,12 +421,3 @@ var StateInvalidUsageError = /** @class */ (function (_super) { | ||
}(Error)); | ||
function isNoProxyInitializer() { | ||
try { | ||
var used = new Proxy({}, {}); | ||
return false; | ||
} | ||
catch (e) { | ||
return true; | ||
} | ||
} | ||
var IsNoProxy = isNoProxyInitializer(); | ||
// TODO deprecate | ||
var DowngradedID = Symbol('Downgraded'); | ||
@@ -350,4 +428,7 @@ var SelfMethodsID = Symbol('ProxyMarker'); | ||
var Store = /** @class */ (function () { | ||
function Store(_value) { | ||
function Store(_value, _extension) { | ||
var _this = this; | ||
var _b; | ||
this._value = _value; | ||
this._extension = _extension; | ||
this._edition = 0; | ||
@@ -357,39 +438,57 @@ this._subscribers = new Set(); | ||
this._destroySubscribers = new Set(); | ||
this._batchStartSubscribers = new Set(); | ||
this._batchFinishSubscribers = new Set(); | ||
this._plugins = new Map(); | ||
this._batches = 0; | ||
if (typeof _value === 'object' && | ||
Promise.resolve(_value) === _value) { | ||
this._promised = this.createPromised(_value); | ||
this._value = none; | ||
if (Object(_value) === _value && | ||
configuration.promiseDetector(_value)) { | ||
this.setPromised(_value); | ||
} | ||
else if (_value === none) { | ||
this._promised = this.createPromised(undefined); | ||
this.setPromised(undefined); | ||
} | ||
var onSetUsedStoreStateMethods = function () { | ||
_this._stateMethods.reconstruct(RootPath, _this.get(RootPath), _this.edition, false); | ||
}; | ||
onSetUsedStoreStateMethods[UnmountedMarker] = true; | ||
this._stateMethods = new StateMethodsImpl(this, RootPath, this.get(RootPath), this.edition, onSetUsedStoreStateMethods); | ||
this.subscribe(this._stateMethods); | ||
this._extensionMethods = (_b = this._extension) === null || _b === void 0 ? void 0 : _b.onInit(function () { return _this.toMethods().self; }); | ||
} | ||
Store.prototype.createPromised = function (newValue) { | ||
Store.prototype.setPromised = function (promise) { | ||
var _this = this; | ||
var promised = new Promised(newValue ? Promise.resolve(newValue) : undefined, function (r) { | ||
if (_this.promised === promised && _this.edition !== DestroyedEdition) { | ||
_this._promised = undefined; | ||
_this.set(RootPath, r, undefined); | ||
_this.update([RootPath]); | ||
this._value = none; | ||
this._promiseError = undefined; | ||
this._promiseResolver = undefined; | ||
if (!promise) { | ||
this._promise = new Promise(function (resolve) { | ||
_this._promiseResolver = resolve; | ||
}); | ||
return; | ||
} | ||
promise = promise | ||
.then(function (r) { | ||
if (_this._promise === promise && _this.edition !== DestroyedEdition) { | ||
_this._promise = undefined; | ||
_this._promiseError = undefined; | ||
_this._promiseResolver === undefined; | ||
_this.update(_this.set(RootPath, r, undefined)); | ||
} | ||
}, function () { | ||
if (_this.promised === promised && _this.edition !== DestroyedEdition) { | ||
}) | ||
.catch(function (err) { | ||
if (_this._promise === promise && _this.edition !== DestroyedEdition) { | ||
_this._promise = undefined; | ||
_this._promiseResolver = undefined; | ||
_this._promiseError = err; | ||
_this._edition += 1; | ||
_this.update([RootPath]); | ||
var ad = { path: RootPath }; | ||
_this.update(ad); | ||
} | ||
}, function () { | ||
if (_this._batchesPendingActions && | ||
_this._value !== none && | ||
_this.edition !== DestroyedEdition) { | ||
var actions = _this._batchesPendingActions; | ||
_this._batchesPendingActions = undefined; | ||
actions.forEach(function (a) { return a(); }); | ||
} | ||
}); | ||
return promised; | ||
this._promise = promise; | ||
}; | ||
Object.defineProperty(Store.prototype, "extension", { | ||
get: function () { | ||
return this._extensionMethods; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Store.prototype, "edition", { | ||
@@ -402,5 +501,5 @@ get: function () { | ||
}); | ||
Object.defineProperty(Store.prototype, "promised", { | ||
Object.defineProperty(Store.prototype, "promise", { | ||
get: function () { | ||
return this._promised; | ||
return this._promise; | ||
}, | ||
@@ -410,2 +509,9 @@ enumerable: false, | ||
}); | ||
Object.defineProperty(Store.prototype, "promiseError", { | ||
get: function () { | ||
return this._promiseError; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Store.prototype.get = function (path) { | ||
@@ -422,3 +528,5 @@ var result = this._value; | ||
Store.prototype.set = function (path, value, mergeValue) { | ||
var _b, _c; | ||
if (this._edition < 0) { | ||
// TODO convert to console log | ||
throw new StateInvalidUsageError(path, ErrorId.SetStateWhenDestroyed); | ||
@@ -436,8 +544,8 @@ } | ||
if (value === none) { | ||
this._promised = this.createPromised(undefined); | ||
this.setPromised(undefined); | ||
delete onSetArg.value; | ||
delete onSetArg.state; | ||
} | ||
else if (typeof value === 'object' && Promise.resolve(value) === value) { | ||
this._promised = this.createPromised(value); | ||
else if (Object(value) === value && configuration.promiseDetector(value)) { | ||
this.setPromised(value); | ||
value = none; | ||
@@ -447,5 +555,8 @@ delete onSetArg.value; | ||
} | ||
else if (this._promised && (!this._promised.resolver && !this._promised.fullfilled)) { | ||
else if (this._promise && !this._promiseResolver) { | ||
throw new StateInvalidUsageError(path, ErrorId.SetStateWhenPromised); | ||
} | ||
else { | ||
this._promiseError = undefined; | ||
} | ||
var prevValue = this._value; | ||
@@ -457,9 +568,15 @@ if (prevValue === none) { | ||
this.afterSet(onSetArg); | ||
if (prevValue === none && this._value !== none && | ||
this.promised && this.promised.resolver) { | ||
this.promised.resolver(this._value); | ||
if (prevValue === none && this._value !== none && this._promiseResolver) { | ||
this._promise = undefined; | ||
this._promiseError = undefined; | ||
var resolver = this._promiseResolver; | ||
this._promiseResolver === undefined; | ||
resolver(this._value); | ||
} | ||
return path; | ||
return { | ||
path: path | ||
}; | ||
} | ||
if (typeof value === 'object' && Promise.resolve(value) === value) { | ||
if (Object(value) === value && configuration.promiseDetector(value)) { | ||
// TODO this one still can get into the state as nested property, need to check on read instead | ||
throw new StateInvalidUsageError(path, ErrorId.SetStateNestedToPromised); | ||
@@ -484,3 +601,5 @@ } | ||
}); | ||
return path; | ||
return { | ||
path: path | ||
}; | ||
} | ||
@@ -505,3 +624,6 @@ else { | ||
// which is identified by upper path | ||
return path.slice(0, -1); | ||
return { | ||
path: path.slice(0, -1), | ||
actions: (_b = {}, _b[p] = "D", _b) | ||
}; | ||
} | ||
@@ -521,17 +643,30 @@ } | ||
// which is identified by upper path | ||
return path.slice(0, -1); | ||
return { | ||
path: path.slice(0, -1), | ||
actions: (_c = {}, _c[p] = "I", _c) | ||
}; | ||
} | ||
// Non-existing property DELETE case | ||
// no-op | ||
return path; | ||
return { | ||
path: path | ||
}; | ||
}; | ||
Store.prototype.update = function (paths) { | ||
if (this._batches) { | ||
this._batchesPendingPaths = this._batchesPendingPaths || []; | ||
this._batchesPendingPaths = this._batchesPendingPaths.concat(paths); | ||
return; | ||
Store.prototype.update = function (ad) { | ||
var _this = this; | ||
var _b, _c; | ||
(_c = (_b = this._extension) === null || _b === void 0 ? void 0 : _b.onSet) === null || _c === void 0 ? void 0 : _c.call(_b, this.toMethods().self, ad); | ||
var actions = new Set(); | ||
// check if actions descriptor can be unfolded into a number of individual update actions | ||
// this is the case when merge call swaps to properties for example | ||
// so we optimize rerendering only these properties | ||
if (ad.actions && Object.values(ad.actions).findIndex(function (i) { return i !== "U"; }) === -1) { | ||
// all actions are update actions | ||
Object.keys(ad.actions).forEach(function (key) { | ||
_this._subscribers.forEach(function (s) { return s.onSet({ path: ad.path.concat(key) }, actions); }); | ||
}); | ||
} | ||
var actions = []; | ||
this._subscribers.forEach(function (s) { return s.onSet(paths, actions); }); | ||
// TODO action can be duplicate, so we can distinct them before calling | ||
else { | ||
this._subscribers.forEach(function (s) { return s.onSet(ad, actions); }); | ||
} | ||
actions.forEach(function (a) { return a(); }); | ||
@@ -545,39 +680,2 @@ }; | ||
}; | ||
Store.prototype.startBatch = function (path, options) { | ||
this._batches += 1; | ||
var cbArgument = { | ||
path: path | ||
}; | ||
if (options && 'context' in options) { | ||
cbArgument.context = options.context; | ||
} | ||
if (this._value !== none) { | ||
cbArgument.state = this._value; | ||
} | ||
this._batchStartSubscribers.forEach(function (cb) { return cb(cbArgument); }); | ||
}; | ||
Store.prototype.finishBatch = function (path, options) { | ||
var cbArgument = { | ||
path: path | ||
}; | ||
if (options && 'context' in options) { | ||
cbArgument.context = options.context; | ||
} | ||
if (this._value !== none) { | ||
cbArgument.state = this._value; | ||
} | ||
this._batchFinishSubscribers.forEach(function (cb) { return cb(cbArgument); }); | ||
this._batches -= 1; | ||
if (this._batches === 0) { | ||
if (this._batchesPendingPaths) { | ||
var paths = this._batchesPendingPaths; | ||
this._batchesPendingPaths = undefined; | ||
this.update(paths); | ||
} | ||
} | ||
}; | ||
Store.prototype.postponeBatch = function (action) { | ||
this._batchesPendingActions = this._batchesPendingActions || []; | ||
this._batchesPendingActions.push(action); | ||
}; | ||
Store.prototype.getPlugin = function (pluginId) { | ||
@@ -599,11 +697,5 @@ return this._plugins.get(pluginId); | ||
} | ||
if (pluginCallbacks.onBatchStart) { | ||
this._batchStartSubscribers.add(function (p) { return pluginCallbacks.onBatchStart(p); }); | ||
} | ||
if (pluginCallbacks.onBatchFinish) { | ||
this._batchFinishSubscribers.add(function (p) { return pluginCallbacks.onBatchFinish(p); }); | ||
} | ||
}; | ||
Store.prototype.toMethods = function () { | ||
return new StateMethodsImpl(this, RootPath, this.get(RootPath), this.edition, OnSetUsedNoAction); | ||
return this._stateMethods; | ||
}; | ||
@@ -617,4 +709,6 @@ Store.prototype.subscribe = function (l) { | ||
Store.prototype.destroy = function () { | ||
var _this = this; | ||
this._destroySubscribers.forEach(function (cb) { return cb(_this._value !== none ? { state: _this._value } : {}); }); | ||
var _b, _c; | ||
(_c = (_b = this._extension) === null || _b === void 0 ? void 0 : _b.onDestroy) === null || _c === void 0 ? void 0 : _c.call(_b, this.toMethods().self); | ||
var params = this._value !== none ? { state: this._value } : {}; | ||
this._destroySubscribers.forEach(function (cb) { return cb(params); }); | ||
this._edition = DestroyedEdition; | ||
@@ -627,35 +721,10 @@ }; | ||
}()); | ||
var Promised = /** @class */ (function () { | ||
function Promised(promise, onResolve, onReject, onPostResolve) { | ||
var _this = this; | ||
this.promise = promise; | ||
if (!promise) { | ||
promise = new Promise(function (resolve) { | ||
_this.resolver = resolve; | ||
}); | ||
} | ||
this.promise = promise | ||
.then(function (r) { | ||
_this.fullfilled = true; | ||
if (!_this.resolver) { | ||
onResolve(r); | ||
} | ||
}) | ||
.catch(function (err) { | ||
_this.fullfilled = true; | ||
_this.error = err; | ||
onReject(); | ||
}) | ||
.then(function () { return onPostResolve(); }); | ||
} | ||
return Promised; | ||
}()); | ||
// use symbol property to allow for easier reference finding | ||
var ValueUnusedMarker = Symbol('ValueUnusedMarker'); | ||
function OnSetUsedNoAction() { } | ||
// use symbol to mark that a function has no effect anymore | ||
var UnmountedMarker = Symbol('UnmountedMarker'); | ||
OnSetUsedNoAction[UnmountedMarker] = true; | ||
// TODO remove from the docs IE11 support | ||
var StateMethodsImpl = /** @class */ (function () { | ||
function StateMethodsImpl(store, path, valueSource, valueEdition, onSetUsed) { | ||
var _this = this; | ||
this.store = store; | ||
@@ -666,61 +735,50 @@ this.path = path; | ||
this.onSetUsed = onSetUsed; | ||
this.valueCache = ValueUnusedMarker; | ||
this.valueUsed = ValueUnusedMarker; | ||
this[_a] = function () { | ||
// this is impossible (from the typescript point of view) to reach | ||
// to this function and call it from the client side | ||
throw new StateInvalidUsageError(_this.path, ErrorId.InternalError); | ||
}; | ||
} | ||
StateMethodsImpl.prototype.reconstruct = function (path, valueSource, valueEdition, forget) { | ||
StateMethodsImpl.prototype.reconstruct = function (path, valueSource, valueEdition, reset) { | ||
this.path = path; | ||
this.valueSource = valueSource; | ||
this.valueEdition = valueEdition; | ||
this.valueCache = ValueUnusedMarker; | ||
delete this.isDowngraded; | ||
delete this.subscribers; | ||
if (forget) { | ||
delete this.selfCache; | ||
delete this.children; | ||
this.valueUsed = ValueUnusedMarker; | ||
delete this.downgraded; | ||
if (reset) { | ||
delete this.selfUsed; | ||
delete this.childrenCreated; | ||
delete this.childrenUsedPrevious; | ||
} | ||
else { | ||
this.children = this.childrenCache; | ||
this.childrenUsedPrevious = this.childrenUsed; | ||
} | ||
delete this.childrenCache; | ||
delete this.childrenUsed; | ||
// We should not delete subscribers as these are self cleaned up when unmounted | ||
// Theoretically it is possible to reconnect subscribers like we done it for | ||
// children, but it is easier and more efficient to leave subscribers to have independent lifecycle | ||
// If we delete subscribers here, scoped states wrapped in React.memo | ||
// will lose state change propagation and rerendering for scopped states | ||
// delete this.subscribers; | ||
}; | ||
StateMethodsImpl.prototype.reconnect = function () { | ||
this.childrenCache = __assign(__assign({}, this.children), this.childrenCache); | ||
this.childrenUsed = __assign(__assign({}, this.childrenUsedPrevious), this.childrenUsed); | ||
}; | ||
StateMethodsImpl.prototype.getUntracked = function (allowPromised) { | ||
StateMethodsImpl.prototype.getUntracked = function (__internalAllowPromised) { | ||
if (this.valueEdition !== this.store.edition) { | ||
this.valueSource = this.store.get(this.path); | ||
this.valueEdition = this.store.edition; | ||
if (this.isMounted) { | ||
// this link is still mounted to a component | ||
// populate cache again to ensure correct tracking of usage | ||
// when React scans which states to rerender on update | ||
if (this.valueCache !== ValueUnusedMarker) { | ||
this.valueCache = ValueUnusedMarker; | ||
this.get(true); // renew cache to keep it marked used | ||
// let walkedState: StateMethods<StateValueAtPath> = this.onSetUsed[RootStateAccessor]; | ||
// for (let pathElem of this.path) { | ||
// walkedState = walkedState.nested(pathElem) | ||
// } | ||
// walkedState.get() | ||
} | ||
if (this.valueUsed !== ValueUnusedMarker) { | ||
this.valueUsed = ValueUnusedMarker; | ||
this.get({ __internalAllowPromised: true }); // renew cache to keep it marked used | ||
} | ||
else { | ||
// This link is not mounted to a component | ||
// for example, it might be global link or | ||
// a link which has been discarded after rerender | ||
// but still captured by some callback or an effect. | ||
// If we are here and if it was mounted before, | ||
// it means it has not been garbage collected | ||
// when a component unmounted. | ||
// We take this opportunity to clean up caches | ||
// to avoid memory leaks via stale children states cache. | ||
this.valueCache = ValueUnusedMarker; | ||
// TODO what do we need to do with this.children here? | ||
delete this.childrenCache; | ||
delete this.selfCache; | ||
} | ||
} | ||
if (this.valueSource === none && !allowPromised) { | ||
if (this.store.promised && this.store.promised.error) { | ||
throw this.store.promised.error; | ||
} | ||
if (__internalAllowPromised) { | ||
return this.valueSource; | ||
} | ||
if (this.store.promiseError) { | ||
throw this.store.promiseError; | ||
} | ||
if (this.store.promise) { | ||
throw new StateInvalidUsageError(this.path, ErrorId.GetStateWhenPromised); | ||
@@ -730,19 +788,31 @@ } | ||
}; | ||
StateMethodsImpl.prototype.get = function (allowPromised) { | ||
var currentValue = this.getUntracked(allowPromised); | ||
if (this.valueCache === ValueUnusedMarker) { | ||
if (this.isDowngraded) { | ||
this.valueCache = currentValue; | ||
StateMethodsImpl.prototype.get = function (options) { | ||
var _b; | ||
var valueSource = this.getUntracked(options === null || options === void 0 ? void 0 : options.__internalAllowPromised); | ||
if (options === null || options === void 0 ? void 0 : options.stealth) { | ||
return valueSource; | ||
} | ||
if (this.valueUsed === ValueUnusedMarker) { | ||
if (Array.isArray(valueSource)) { | ||
this.valueUsed = this.valueArrayImpl(valueSource); | ||
} | ||
else if (Array.isArray(currentValue)) { | ||
this.valueCache = this.valueArrayImpl(currentValue); | ||
else if (Object(valueSource) === valueSource) { | ||
if (((_b = valueSource.constructor) === null || _b === void 0 ? void 0 : _b.name) === "Object") { | ||
this.valueUsed = this.valueObjectImpl(valueSource); | ||
} | ||
else { | ||
// any other object except Object, for example Date | ||
this.downgraded = true; | ||
this.valueUsed = valueSource; | ||
} | ||
} | ||
else if (typeof currentValue === 'object' && currentValue !== null) { | ||
this.valueCache = this.valueObjectImpl(currentValue); | ||
} | ||
else { | ||
this.valueCache = currentValue; | ||
this.valueUsed = valueSource; | ||
} | ||
} | ||
return this.valueCache; | ||
if (options === null || options === void 0 ? void 0 : options.noproxy) { | ||
this.downgraded = true; | ||
return valueSource; | ||
} | ||
return this.valueUsed; | ||
}; | ||
@@ -757,6 +827,14 @@ Object.defineProperty(StateMethodsImpl.prototype, "value", { | ||
StateMethodsImpl.prototype.setUntracked = function (newValue, mergeValue) { | ||
var r = this.setUntrackedV4(newValue, mergeValue); | ||
if (r) { | ||
return [r.path]; | ||
} | ||
return []; | ||
}; | ||
StateMethodsImpl.prototype.setUntrackedV4 = function (newValue, mergeValue) { | ||
if (typeof newValue === 'function') { | ||
newValue = newValue(this.getUntracked()); | ||
} | ||
if (typeof newValue === 'object' && newValue !== null && newValue[SelfMethodsID]) { | ||
if (Object(newValue) === newValue && newValue[SelfMethodsID]) { | ||
// TODO check on read instead as it might escape as nested on set anyway | ||
throw new StateInvalidUsageError(this.path, ErrorId.SetStateToValueFromState); | ||
@@ -767,10 +845,20 @@ } | ||
// so skip this set call as it does not make an effect | ||
return []; | ||
return null; | ||
} | ||
return [this.store.set(this.path, newValue, mergeValue)]; | ||
return this.store.set(this.path, newValue, mergeValue); | ||
}; | ||
StateMethodsImpl.prototype.set = function (newValue) { | ||
this.store.update(this.setUntracked(newValue)); | ||
var ad = this.setUntrackedV4(newValue); | ||
if (ad) { | ||
this.store.update(ad); | ||
} | ||
}; | ||
StateMethodsImpl.prototype.mergeUntracked = function (sourceValue) { | ||
var r = this.mergeUntrackedV4(sourceValue); | ||
if (r) { | ||
return [r.path]; | ||
} | ||
return []; | ||
}; | ||
StateMethodsImpl.prototype.mergeUntrackedV4 = function (sourceValue) { | ||
var currentValue = this.getUntracked(); | ||
@@ -780,9 +868,16 @@ if (typeof sourceValue === 'function') { | ||
} | ||
var updatedPaths; | ||
var deletedOrInsertedProps = false; | ||
if (Array.isArray(currentValue)) { | ||
if (Array.isArray(sourceValue)) { | ||
return this.setUntracked(currentValue.concat(sourceValue), sourceValue); | ||
var ad_1 = { path: this.path, actions: {} }; | ||
sourceValue.forEach(function (e, i) { | ||
ad_1.actions[currentValue.push(e) - 1] = "I"; | ||
}); | ||
if (Object.keys(ad_1.actions).length > 0) { | ||
this.setUntrackedV4(currentValue, sourceValue); | ||
return ad_1; | ||
} | ||
return null; | ||
} | ||
else { | ||
var ad_2 = { path: this.path, actions: {} }; | ||
var deletedIndexes_1 = []; | ||
@@ -793,7 +888,12 @@ Object.keys(sourceValue).sort().forEach(function (i) { | ||
if (newPropValue === none) { | ||
deletedOrInsertedProps = true; | ||
ad_2.actions[index] = "D"; | ||
deletedIndexes_1.push(index); | ||
} | ||
else { | ||
deletedOrInsertedProps = deletedOrInsertedProps || !(index in currentValue); | ||
if (index in currentValue) { | ||
ad_2.actions[index] = "U"; | ||
} | ||
else { | ||
ad_2.actions[index] = "I"; | ||
} | ||
currentValue[index] = newPropValue; | ||
@@ -808,33 +908,45 @@ } | ||
}); | ||
updatedPaths = this.setUntracked(currentValue, sourceValue); | ||
if (Object.keys(ad_2.actions).length > 0) { | ||
this.setUntrackedV4(currentValue, sourceValue); | ||
return ad_2; | ||
} | ||
return null; | ||
} | ||
} | ||
else if (typeof currentValue === 'object' && currentValue !== null) { | ||
else if (Object(currentValue) === currentValue) { | ||
var ad_3 = { path: this.path, actions: {} }; | ||
Object.keys(sourceValue).forEach(function (key) { | ||
var newPropValue = sourceValue[key]; | ||
if (newPropValue === none) { | ||
deletedOrInsertedProps = true; | ||
ad_3.actions[key] = "D"; | ||
delete currentValue[key]; | ||
} | ||
else { | ||
deletedOrInsertedProps = deletedOrInsertedProps || !(key in currentValue); | ||
if (key in currentValue) { | ||
ad_3.actions[key] = "U"; | ||
} | ||
else { | ||
ad_3.actions[key] = "I"; | ||
} | ||
currentValue[key] = newPropValue; | ||
} | ||
}); | ||
updatedPaths = this.setUntracked(currentValue, sourceValue); | ||
if (Object.keys(ad_3.actions).length > 0) { | ||
this.setUntrackedV4(currentValue, sourceValue); | ||
return ad_3; | ||
} | ||
return null; | ||
} | ||
else if (typeof currentValue === 'string') { | ||
return this.setUntracked((currentValue + String(sourceValue)), sourceValue); | ||
return this.setUntrackedV4((currentValue + String(sourceValue)), sourceValue); | ||
} | ||
else { | ||
return this.setUntracked(sourceValue); | ||
return this.setUntrackedV4(sourceValue); | ||
} | ||
if (updatedPaths.length !== 1 || updatedPaths[0] !== this.path || deletedOrInsertedProps) { | ||
return updatedPaths; | ||
} | ||
var updatedPath = updatedPaths[0]; | ||
return Object.keys(sourceValue).map(function (p) { return updatedPath.slice().concat(p); }); | ||
}; | ||
StateMethodsImpl.prototype.merge = function (sourceValue) { | ||
this.store.update(this.mergeUntracked(sourceValue)); | ||
var r = this.mergeUntrackedV4(sourceValue); | ||
if (r) { | ||
this.store.update(r); | ||
} | ||
}; | ||
@@ -845,3 +957,6 @@ StateMethodsImpl.prototype.nested = function (key) { | ||
StateMethodsImpl.prototype.rerender = function (paths) { | ||
this.store.update(paths); | ||
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { | ||
var path = paths_1[_i]; | ||
this.store.update({ path: path }); | ||
} | ||
}; | ||
@@ -875,32 +990,60 @@ StateMethodsImpl.prototype.destroy = function () { | ||
}; | ||
StateMethodsImpl.prototype.onSet = function (paths, actions) { | ||
StateMethodsImpl.prototype.onSet = function (ad, actions) { | ||
var _this = this; | ||
var update = function () { | ||
if (_this.isDowngraded && _this.valueCache !== ValueUnusedMarker) { | ||
actions.push(_this.onSetUsed); | ||
delete _this.selfCache; | ||
return true; | ||
var _b; | ||
var isAffected = false; | ||
if (_this.downgraded | ||
// TODO this condition becomes redundant when Downgraded plugins is deleted | ||
&& _this.valueUsed !== ValueUnusedMarker) { | ||
actions.add(_this.onSetUsed); | ||
delete _this.selfUsed; | ||
isAffected = true; | ||
} | ||
for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) { | ||
var path = paths_1[_i]; | ||
var nextChildKey = path[_this.path.length]; | ||
if (nextChildKey === undefined) { | ||
// There is no next child to dive into | ||
// So it is this one which was updated | ||
if (_this.valueCache !== ValueUnusedMarker) { | ||
actions.push(_this.onSetUsed); | ||
delete _this.selfCache; | ||
delete _this.childrenCache; | ||
return true; | ||
var path = ad.path; | ||
var nextChildKey = path[_this.path.length]; | ||
if (nextChildKey === undefined) { | ||
// There is no next child to dive into | ||
// So it is this one which was updated | ||
if (_this.valueUsed !== ValueUnusedMarker) { | ||
actions.add(_this.onSetUsed); | ||
delete _this.selfUsed; | ||
delete _this.childrenUsed; | ||
if (ad.actions && _this.childrenCreated) { | ||
// TODO add automated unit tests for this part | ||
if (Array.isArray(_this.valueSource) | ||
&& Object.values(ad.actions).includes("D")) { | ||
// this is an array and some elements were removed | ||
// so invalidate cache for all children after the first deleted | ||
var firstDeletedIndex = Object.keys(ad.actions) | ||
.map(function (i) { return Number(i); }) | ||
.sort() | ||
.find(function (i) { var _b; return ((_b = ad.actions) === null || _b === void 0 ? void 0 : _b[i]) === "D"; }); | ||
for (var childKey in _this.childrenCreated) { | ||
if (Number(childKey) >= firstDeletedIndex || | ||
childKey in ad.actions) { | ||
delete _this.childrenCreated[childKey]; | ||
} | ||
} | ||
} | ||
else { | ||
for (var childKey in ad.actions) { | ||
delete _this.childrenCreated[childKey]; | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
var nextChild = _this.childrenCache && _this.childrenCache[nextChildKey]; | ||
if (nextChild && nextChild.onSet(paths, actions)) { | ||
delete _this.selfCache; | ||
return true; | ||
else { | ||
delete _this.childrenCreated; | ||
} | ||
return true; | ||
} | ||
} | ||
return false; | ||
else { | ||
var nextChild = (_b = _this.childrenUsed) === null || _b === void 0 ? void 0 : _b[nextChildKey]; | ||
if (nextChild && nextChild.onSet(ad, actions)) { | ||
delete _this.selfUsed; | ||
return true; | ||
} | ||
} | ||
return isAffected; | ||
}; | ||
@@ -910,4 +1053,4 @@ var updated = update(); | ||
this.subscribers.forEach(function (s) { | ||
if (s.onSet(paths, actions)) { | ||
delete _this.selfCache; | ||
if (s.onSet(ad, actions)) { | ||
delete _this.selfUsed; | ||
} | ||
@@ -924,3 +1067,3 @@ }); | ||
} | ||
if (typeof value === 'object' && value !== null) { | ||
if (Object(value) === value) { | ||
return Object.keys(value); | ||
@@ -934,28 +1077,28 @@ } | ||
StateMethodsImpl.prototype.child = function (key) { | ||
// if this state is not mounted to a hook, | ||
// we do not cache children to avoid unnecessary memory leaks | ||
if (this.isMounted) { | ||
this.childrenCache = this.childrenCache || {}; | ||
var cachedChild = this.childrenCache[key]; | ||
if (cachedChild) { | ||
return cachedChild; | ||
} | ||
this.childrenUsed = this.childrenUsed || {}; | ||
var cachedChild = this.childrenUsed.hasOwnProperty(key) && this.childrenUsed[key]; | ||
if (cachedChild) { | ||
return cachedChild; | ||
} | ||
this.children = this.children || {}; | ||
var child = this.children[key]; | ||
var valueSource = this.valueSource[key]; | ||
if (typeof valueSource === 'function') { | ||
// hitting a method of a custom type, should be no-op | ||
throw new StateInvalidUsageError(this.path, ErrorId.GetProperty_Function); | ||
} | ||
this.childrenCreated = this.childrenCreated || {}; | ||
var child = this.childrenCreated[key]; | ||
var r; | ||
if (child) { | ||
child.reconstruct(this.path.slice().concat(key), this.valueSource[key], this.valueEdition, false); | ||
child.reconstruct(this.path.concat(key), valueSource, this.valueEdition, false); | ||
r = child; | ||
} | ||
else { | ||
r = new StateMethodsImpl(this.store, this.path.slice().concat(key), this.valueSource[key], this.valueEdition, this.onSetUsed); | ||
this.children[key] = r; | ||
r = new StateMethodsImpl(this.store, this.path.concat(key), valueSource, this.valueEdition, this.onSetUsed); | ||
this.childrenCreated[key] = r; | ||
} | ||
if (this.isDowngraded) { | ||
r.isDowngraded = true; | ||
if (this.downgraded) { | ||
// TODO this is redundant when Downgraded plugin is deleted | ||
r.downgraded = true; | ||
} | ||
if (this.childrenCache) { | ||
this.childrenCache[key] = r; | ||
} | ||
this.childrenUsed[key] = r; | ||
return r; | ||
@@ -965,6 +1108,2 @@ }; | ||
var _this = this; | ||
if (IsNoProxy) { | ||
this.isDowngraded = true; | ||
return currentValue; | ||
} | ||
return proxyWrap(this.path, currentValue, function () { return currentValue; }, function (target, key) { | ||
@@ -1000,7 +1139,6 @@ if (key === 'length') { | ||
var _this = this; | ||
if (IsNoProxy) { | ||
this.isDowngraded = true; | ||
return currentValue; | ||
} | ||
return proxyWrap(this.path, currentValue, function () { return currentValue; }, function (target, key) { | ||
if (key in Object.prototype) { | ||
return Object.prototype[key]; | ||
} | ||
if (key === SelfMethodsID) { | ||
@@ -1026,4 +1164,4 @@ return _this; | ||
var _this = this; | ||
if (this.selfCache) { | ||
return this.selfCache; | ||
if (this.selfUsed) { | ||
return this.selfUsed; | ||
} | ||
@@ -1041,7 +1179,14 @@ var getter = function (_, key) { | ||
var nestedGetter = function (prop) { | ||
var currentDowngraded = _this.isDowngraded; // relevant for IE11 only | ||
var currentValue = _this.get(); // IE11 marks this as downgraded | ||
_this.isDowngraded = currentDowngraded; // relevant for IE11 only | ||
var currentValue = _this.get(); | ||
if (prop in Object.prototype) { | ||
// Mark it used entirely, so changes to the value | ||
// invalidate and rerender results for Object.prototype.toString(), | ||
// for example. | ||
// We check for Object prototype functions | ||
// even for primitive values, because primitive values still | ||
// can have object methods. | ||
return Object.prototype[prop]; | ||
} | ||
if ( // if currentValue is primitive type | ||
(typeof currentValue !== 'object' || currentValue === null) && | ||
(Object(currentValue) !== currentValue) && | ||
// if promised, it will be none | ||
@@ -1087,3 +1232,3 @@ currentValue !== none) { | ||
case 'get': | ||
return function () { return _this.get(); }; | ||
return function (opts) { return _this.get(opts); }; | ||
case 'set': | ||
@@ -1095,36 +1240,17 @@ return function (p) { return _this.set(p); }; | ||
return function (p) { return nestedGetter(p); }; | ||
case 'batch': | ||
// tslint:disable-next-line: no-any | ||
return function (action, context) { return _this.batch(action, context); }; | ||
case 'attach': | ||
return function (p) { return _this.attach(p); }; | ||
case 'destroy': | ||
case 'destroy': // TODO move destroy to the state, otherwise State type hides this well existing property | ||
return function () { return _this.destroy(); }; | ||
default: | ||
// check if extension method | ||
var ext = _this.store.extension; | ||
if (ext && key in ext) { | ||
return ext[key](_this.self); | ||
} | ||
// otherwise nested child | ||
return nestedGetter(key); | ||
} | ||
}; | ||
if (IsNoProxy) { | ||
// minimal support for IE11 | ||
var result_1 = (Array.isArray(this.valueSource) ? [] : {}); | ||
[self, 'toJSON', 'path', 'keys', 'value', 'ornull', | ||
'promised', 'error', 'get', 'set', 'merge', | ||
'nested', 'batch', 'attach', 'destroy'] | ||
.forEach(function (key) { | ||
Object.defineProperty(result_1, key, { | ||
get: function () { return getter(result_1, key); } | ||
}); | ||
}); | ||
if (typeof this.valueSource === 'object' && this.valueSource !== null) { | ||
Object.keys(this.valueSource).forEach(function (key) { | ||
Object.defineProperty(result_1, key, { | ||
enumerable: true, | ||
get: function () { return getter(result_1, key); } | ||
}); | ||
}); | ||
} | ||
this.selfCache = result_1; | ||
return this.selfCache; | ||
} | ||
this.selfCache = proxyWrap(this.path, this.valueSource, function () { | ||
this.selfUsed = proxyWrap(this.path, this.valueSource, function () { | ||
return _this.get(); | ||
@@ -1134,3 +1260,3 @@ }, getter, function (_, key, value) { | ||
}, false); | ||
return this.selfCache; | ||
return this.selfUsed; | ||
}, | ||
@@ -1142,7 +1268,4 @@ enumerable: false, | ||
get: function () { | ||
var currentValue = this.get(true); // marks used | ||
if (currentValue === none && this.store.promised && !this.store.promised.fullfilled) { | ||
return true; | ||
} | ||
return false; | ||
this.get({ __internalAllowPromised: true }); // marks used | ||
return !!this.store.promise; | ||
}, | ||
@@ -1154,10 +1277,4 @@ enumerable: false, | ||
get: function () { | ||
var currentValue = this.get(true); // marks used | ||
if (currentValue === none) { | ||
if (this.store.promised && this.store.promised.fullfilled) { | ||
return this.store.promised.error; | ||
} | ||
this.get(); // will throw 'read while promised' exception | ||
} | ||
return undefined; | ||
this.get({ __internalAllowPromised: !!this.store.promiseError }); // marks used | ||
return this.store.promiseError; | ||
}, | ||
@@ -1167,17 +1284,2 @@ enumerable: false, | ||
}); | ||
StateMethodsImpl.prototype.batch = function (action, context) { | ||
var _this = this; | ||
var opts = { context: context }; | ||
try { | ||
this.store.startBatch(this.path, opts); | ||
var result = action(this.self); | ||
if (result === postpone) { | ||
this.store.postponeBatch(function () { return _this.batch(action, context); }); | ||
} | ||
return result; | ||
} | ||
finally { | ||
this.store.finishBatch(this.path, opts); | ||
} | ||
}; | ||
Object.defineProperty(StateMethodsImpl.prototype, "ornull", { | ||
@@ -1198,6 +1300,6 @@ get: function () { | ||
if (pluginMeta.id === DowngradedID) { | ||
this.isDowngraded = true; | ||
if (this.valueCache !== ValueUnusedMarker) { | ||
this.downgraded = true; | ||
if (this.valueUsed !== ValueUnusedMarker) { | ||
var currentValue = this.getUntracked(true); | ||
this.valueCache = currentValue; | ||
this.valueUsed = currentValue; | ||
} | ||
@@ -1219,2 +1321,3 @@ return this.self; | ||
}()); | ||
_a = __state; | ||
function proxyWrap(path, | ||
@@ -1232,3 +1335,3 @@ // tslint:disable-next-line: no-any | ||
}; | ||
if (typeof targetBootstrap !== 'object' || targetBootstrap === null) { | ||
if (Object(targetBootstrap) !== targetBootstrap) { | ||
targetBootstrap = {}; | ||
@@ -1283,3 +1386,3 @@ } | ||
var targetReal = targetGetter(); | ||
if (typeof targetReal === 'object' && targetReal !== null) { | ||
if (Object(targetReal) === targetReal) { | ||
return p in targetReal; | ||
@@ -1323,3 +1426,3 @@ } | ||
} | ||
function createStore(initial) { | ||
function createStore(initial, extensions) { | ||
var initialValue = initial; | ||
@@ -1329,16 +1432,66 @@ if (typeof initial === 'function') { | ||
} | ||
if (typeof initialValue === 'object' && initialValue !== null && initialValue[SelfMethodsID]) { | ||
if (Object(initialValue) === initialValue && initialValue[SelfMethodsID]) { | ||
throw new StateInvalidUsageError(RootPath, ErrorId.InitStateToValueFromState); | ||
} | ||
return new Store(initialValue); | ||
return new Store(initialValue, extensions === null || extensions === void 0 ? void 0 : extensions()); | ||
} | ||
// Do not try to use useLayoutEffect if DOM not available (SSR) | ||
var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React__default["default"].useLayoutEffect : React__default["default"].useEffect; | ||
var useEffectOrigin; | ||
function useHookEffect(effect, deps) { | ||
for (var _i = 0, _a = deps || []; _i < _a.length; _i++) { | ||
var i = _a[_i]; | ||
var configuration = { | ||
interceptDependencyListsMode: 'always', | ||
// TODO this does not always work, so it is better if it is set by the app explictly. Document this | ||
isDevelopmentMode: typeof process === 'object' && | ||
typeof process.env === 'object' && | ||
process.env.NODE_ENV === 'development', | ||
promiseDetector: function (p) { return Promise.resolve(p) === p; }, | ||
hiddenInterceptDependencyListsModeDebug: false | ||
}; | ||
// TODO document | ||
function configure(config) { | ||
var _b, _c, _d; | ||
configuration = { | ||
interceptDependencyListsMode: (_b = config.interceptDependencyListsMode) !== null && _b !== void 0 ? _b : configuration.interceptDependencyListsMode, | ||
isDevelopmentMode: (_c = config.isDevelopmentMode) !== null && _c !== void 0 ? _c : configuration.isDevelopmentMode, | ||
promiseDetector: (_d = config.promiseDetector) !== null && _d !== void 0 ? _d : configuration.promiseDetector, | ||
hiddenInterceptDependencyListsModeDebug: false | ||
}; | ||
interceptReactHooks(); // not really required, but for safety | ||
if (configuration.interceptDependencyListsMode === 'never') { | ||
configuration.hiddenInterceptDependencyListsModeDebug = false; | ||
React__default["default"]['useEffect'] = useEffectOrigin; | ||
React__default["default"]['useLayoutEffect'] = useLayoutEffectOrigin; | ||
React__default["default"]['useInsertionEffect'] = useInsertionEffectOrigin; | ||
React__default["default"]['useImperativeHandle'] = useImperativeHandleOrigin; | ||
React__default["default"]['useMemo'] = useMemoOrigin; | ||
React__default["default"]['useCallback'] = useCallbackOrigin; | ||
// the following does not make an effect as memo calls happen on module load | ||
// so it is always set to memoIntercept | ||
React__default["default"]['memo'] = memoOrigin; | ||
} | ||
else { | ||
React__default["default"]['useEffect'] = useEffectIntercept; | ||
React__default["default"]['useLayoutEffect'] = useLayoutEffectIntercept; | ||
React__default["default"]['useInsertionEffect'] = useInsertionEffectIntercept; | ||
React__default["default"]['useImperativeHandle'] = useImperativeHandleIntercept; | ||
React__default["default"]['useMemo'] = useMemoIntercept; | ||
React__default["default"]['useCallback'] = useCallbackIntercept; | ||
// the following does not make an effect as memo calls happen on module load | ||
// so it is always set to memoIntercept | ||
React__default["default"]['memo'] = memoIntercept; | ||
if (configuration.interceptDependencyListsMode === 'development' | ||
&& configuration.isDevelopmentMode) { | ||
configuration.hiddenInterceptDependencyListsModeDebug = true; | ||
} | ||
} | ||
} | ||
function reconnectDependencies(deps, fromIntercept) { | ||
for (var _i = 0, _b = deps || []; _i < _b.length; _i++) { | ||
var i = _b[_i]; | ||
if (i === Object(i)) { | ||
var state = i[self]; | ||
if (state) { | ||
if (fromIntercept && configuration.hiddenInterceptDependencyListsModeDebug) { | ||
// TODO document this exception | ||
throw new StateInvalidUsageError(state.path, ErrorId.StateUsedInDependencyList); | ||
} | ||
state.reconnect(); | ||
@@ -1348,11 +1501,102 @@ } | ||
} | ||
return deps; | ||
} | ||
var useEffectOrigin; | ||
function useHookEffect(effect, deps) { | ||
reconnectDependencies(deps); | ||
return useEffectOrigin(effect, deps); | ||
} | ||
function interceptUseEffect() { | ||
function useEffectIntercept(effect, deps) { | ||
reconnectDependencies(deps, true); | ||
return useEffectOrigin(effect, deps); | ||
} | ||
var useLayoutEffectOrigin; | ||
function useHookLayoutEffect(effect, deps) { | ||
reconnectDependencies(deps); | ||
return useLayoutEffectOrigin(effect, deps); | ||
} | ||
function useLayoutEffectIntercept(effect, deps) { | ||
reconnectDependencies(deps, true); | ||
return useLayoutEffectOrigin(effect, deps); | ||
} | ||
var useInsertionEffectOrigin; | ||
function useHookInsertionEffect(effect, deps) { | ||
reconnectDependencies(deps); | ||
return useInsertionEffectOrigin(effect, deps); | ||
} | ||
function useInsertionEffectIntercept(effect, deps) { | ||
reconnectDependencies(deps, true); | ||
return useInsertionEffectOrigin(effect, deps); | ||
} | ||
var useImperativeHandleOrigin; | ||
function useHookImperativeHandle(ref, init, deps) { | ||
reconnectDependencies(deps); | ||
return useImperativeHandleOrigin(ref, init, deps); | ||
} | ||
function useImperativeHandleIntercept(ref, init, deps) { | ||
reconnectDependencies(deps, true); | ||
return useImperativeHandleOrigin(ref, init, deps); | ||
} | ||
var useMemoOrigin; | ||
function useHookMemo(factory, deps) { | ||
reconnectDependencies(deps); | ||
return useMemoOrigin(factory, deps); | ||
} | ||
function useMemoIntercept(factory, deps) { | ||
reconnectDependencies(deps, true); | ||
return useMemoOrigin(factory, deps); | ||
} | ||
var useCallbackOrigin; | ||
function useHookCallback(callback, deps) { | ||
reconnectDependencies(deps); | ||
return useCallbackOrigin(callback, deps); | ||
} | ||
function useCallbackIntercept(callback, deps) { | ||
reconnectDependencies(deps, true); | ||
return useCallbackOrigin(callback, deps); | ||
} | ||
var memoOrigin; | ||
function hookMemo(Component, propsAreEqual) { | ||
return memoOrigin(Component, function (prevProps, nextProps) { | ||
reconnectDependencies(Object.keys(nextProps).map(function (i) { return nextProps[i]; })); | ||
return (propsAreEqual || shallowEqual)(prevProps, nextProps); | ||
}); | ||
} | ||
function memoIntercept(Component, propsAreEqual) { | ||
return memoOrigin(Component, function (prevProps, nextProps) { | ||
reconnectDependencies(Object.keys(nextProps).map(function (i) { return nextProps[i]; }), true); | ||
return (propsAreEqual || shallowEqual)(prevProps, nextProps); | ||
}); | ||
} | ||
function interceptReactHooks() { | ||
if (!useEffectOrigin) { | ||
useEffectOrigin = React__default["default"]['useEffect']; | ||
React__default["default"]['useEffect'] = useHookEffect; | ||
React__default["default"]['useEffect'] = useEffectIntercept; | ||
} | ||
if (!useLayoutEffectOrigin) { | ||
useLayoutEffectOrigin = React__default["default"]['useLayoutEffect']; | ||
React__default["default"]['useLayoutEffect'] = useLayoutEffectIntercept; | ||
} | ||
if (!useInsertionEffectOrigin) { | ||
useInsertionEffectOrigin = React__default["default"]['useInsertionEffect']; | ||
React__default["default"]['useInsertionEffect'] = useInsertionEffectIntercept; | ||
} | ||
if (!useImperativeHandleOrigin) { | ||
useImperativeHandleOrigin = React__default["default"]['useImperativeHandle']; | ||
React__default["default"]['useImperativeHandle'] = useImperativeHandleIntercept; | ||
} | ||
if (!useMemoOrigin) { | ||
useMemoOrigin = React__default["default"]['useMemo']; | ||
React__default["default"]['useMemo'] = useMemoIntercept; | ||
} | ||
if (!useCallbackOrigin) { | ||
useCallbackOrigin = React__default["default"]['useCallback']; | ||
React__default["default"]['useCallback'] = useCallbackIntercept; | ||
} | ||
if (!memoOrigin) { | ||
memoOrigin = React__default["default"]['memo']; | ||
React__default["default"]['memo'] = memoIntercept; | ||
} | ||
} | ||
interceptUseEffect(); | ||
interceptReactHooks(); | ||
@@ -1363,7 +1607,17 @@ exports.DevTools = DevTools; | ||
exports.StateFragment = StateFragment; | ||
exports.__state = __state; | ||
exports.configure = configure; | ||
exports.createHookstate = createHookstate; | ||
exports.createState = createState; | ||
exports.extend = extend; | ||
exports.hookMemo = hookMemo; | ||
exports.none = none; | ||
exports.postpone = postpone; | ||
exports.useHookCallback = useHookCallback; | ||
exports.useHookEffect = useHookEffect; | ||
exports.useHookImperativeHandle = useHookImperativeHandle; | ||
exports.useHookInsertionEffect = useHookInsertionEffect; | ||
exports.useHookLayoutEffect = useHookLayoutEffect; | ||
exports.useHookMemo = useHookMemo; | ||
exports.useHookstate = useHookstate; | ||
exports.useState = useState; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@hookstate/core", | ||
"version": "4.0.0-rc.4", | ||
"version": "4.0.0-rc10", | ||
"description": "The flexible, fast and extendable state management for React that is based on hooks and state usage tracking.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
470527
11
3680