@cervello/react 1.2.0 β 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -23
- package/dist/cervello.cjs +1 -1
- package/dist/cervello.cjs.map +1 -1
- package/dist/cervello.esm.js +1 -1
- package/dist/cervello.esm.js.map +1 -1
- package/dist/cervello.umd.js +1 -1
- package/dist/cervello.umd.js.map +1 -1
- package/package.json +13 -14
- package/types/index.d.ts +2 -2
- package/types/lib/helpers/constants.d.ts +1 -1
- package/types/lib/helpers/create-store-selector-hook.d.ts +3 -3
- package/types/lib/helpers/index.d.ts +2 -2
- package/types/lib/helpers/proxify-store.d.ts +2 -2
- package/types/lib/store/index.d.ts +58 -43
- package/types/lib/utils/index.d.ts +3 -2
- package/types/lib/utils/object.d.ts +38 -38
- package/types/lib/utils/string.d.ts +2 -0
- package/types/lib/utils/subject.d.ts +13 -13
- package/types/types/shared.d.ts +10 -10
package/README.md
CHANGED
|
@@ -3,19 +3,15 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@cervello/react)
|
|
4
4
|
[](https://bundlephobia.com/package/@cervello/react@latest)
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
<a href="https://www.cervello.dev">
|
|
8
7
|
<img src="https://github.com/chempogonzalez/cervello/blob/main/assets/emoji-logo.png" style="display:block;">
|
|
9
8
|
</a>
|
|
10
9
|
|
|
11
|
-
|
|
12
10
|
> π€― Simplest and truly reactive state manager for React _(just 1.5kb)_
|
|
13
11
|
|
|
14
|
-
|
|
15
12
|
<br>
|
|
16
13
|
<br>
|
|
17
14
|
|
|
18
|
-
|
|
19
15
|
<a href="https://www.cervello.dev">
|
|
20
16
|
<p align="center">
|
|
21
17
|
<strong>π Documentation website</strong>
|
|
@@ -24,9 +20,9 @@
|
|
|
24
20
|
|
|
25
21
|
<br>
|
|
26
22
|
|
|
27
|
-
|
|
28
23
|
## π **Features**
|
|
29
|
-
|
|
24
|
+
|
|
25
|
+
- βοΈ Truly reactive on store change like normal objects without functions **_(nested properties too π!!)_**
|
|
30
26
|
- β
Super simple and minimalistic API
|
|
31
27
|
- π¨ Listen properties lazily
|
|
32
28
|
- π No unnecessary re-renders
|
|
@@ -34,21 +30,22 @@
|
|
|
34
30
|
- π Typescript support
|
|
35
31
|
|
|
36
32
|
## π¦ **Install**
|
|
33
|
+
|
|
37
34
|
```bash
|
|
38
35
|
# NPM
|
|
39
36
|
npm install @cervello/react
|
|
40
37
|
|
|
41
|
-
# YARN
|
|
42
|
-
yarn add @cervello/react
|
|
43
|
-
|
|
44
38
|
# PNPM
|
|
45
39
|
pnpm add @cervello/react
|
|
46
|
-
```
|
|
47
40
|
|
|
41
|
+
# YARN
|
|
42
|
+
yarn add @cervello/react
|
|
43
|
+
```
|
|
48
44
|
|
|
49
45
|
## π» **Quick Usage**
|
|
46
|
+
|
|
50
47
|
The `cervello` function allows you to create a new store in an easy way.
|
|
51
|
-
Just set the initial value _`(the type will be inferred based on this value)`_ and you have it
|
|
48
|
+
Just set the initial value _`(the type will be inferred based on this value)`_ and you have it!
|
|
52
49
|
|
|
53
50
|
```ts
|
|
54
51
|
// - store-example.ts
|
|
@@ -59,11 +56,15 @@ export const {
|
|
|
59
56
|
useStore, // Hook to listen for store changes
|
|
60
57
|
useSelector, // Hook to listen for changes on parts of the store
|
|
61
58
|
reset, // Function to reset the store to initial value
|
|
62
|
-
} = cervello({
|
|
59
|
+
} = cervello({
|
|
60
|
+
count: 0,
|
|
61
|
+
isModalOpen: false,
|
|
62
|
+
/* ... */
|
|
63
|
+
})
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
// With the store object you can use it outside of React components
|
|
66
|
-
const increment = () => store.count++
|
|
67
|
+
const increment = () => { store.count++ }
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
const Button = () => {
|
|
@@ -76,7 +77,7 @@ const Button = () => {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
const ButtonWithSelector = () => {
|
|
79
|
-
const { count } = useSelector(['count']) // Listen for changes just on count property
|
|
80
|
+
const { count } = useSelector(['count']) // Listen for changes just on 'count' property
|
|
80
81
|
return (
|
|
81
82
|
<button onClick={increment}>
|
|
82
83
|
{count}
|
|
@@ -84,21 +85,16 @@ const ButtonWithSelector = () => {
|
|
|
84
85
|
)
|
|
85
86
|
}
|
|
86
87
|
```
|
|
88
|
+
|
|
87
89
|
<br>
|
|
88
90
|
<br>
|
|
89
91
|
|
|
90
|
-
|
|
91
92
|
---------
|
|
92
|
-
|
|
93
|
+
|
|
94
|
+
### To see more in depth explanations or API references and more examples: π [Documentation website](https://www.cervello.dev)
|
|
95
|
+
|
|
93
96
|
--------
|
|
94
97
|
|
|
95
98
|
<br>
|
|
96
99
|
|
|
97
|
-
## π€ Happy Code
|
|
98
|
-
|
|
99
100
|
> Created with Typescript! β‘ and latin music πΊπ΅
|
|
100
|
-
|
|
101
|
-
### This README.md file has been written keeping in mind
|
|
102
|
-
|
|
103
|
-
- [GitHub Markdown](https://guides.github.com/features/mastering-markdown/)
|
|
104
|
-
- [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/)
|
package/dist/cervello.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=require("react")
|
|
1
|
+
var e=require("react");function t(e){if(!e||"object"!=typeof e)return e;var r={};return Array.isArray(e)?r=e.map(function(e){return t(e)}):Object.entries(e).forEach(function(e){r[e[0]]=t(e[1])}),r}var r=function(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)},n=function(e){var t;return null!=(t=e.$$value$$)?t:e};function u(e,t){return e.reduce(function(e,r){return e[r]=t[r],e},{})}var o=function(e){return JSON.stringify(e)},c=function(e,t){return o(e)===o(t)};function i(e){return e.charAt(0).toUpperCase()+e.slice(1).toLocaleLowerCase()}"hasOwn"in Object||(Object.hasOwn=Object.call.bind(Object.hasOwnProperty));var a=Symbol.for("nestedPath");function s(e,u,o,c){if("object"!=typeof u)throw new Error("Store must be an object");return new Proxy(u,{get:function(t,u){var i=n(t);if("toJSON"===u)return function(){return i};var f=i[u];if(c&&r(f)&&"symbol"!=typeof u){var l=Object.hasOwn(t,"$$value$$")?u:i[a]+"."+u,v=o[l];if(v)return v;f[a]=l;var b=s(e,f,o,c);return o[l]=b,b}return f},set:function(u,i,f){var l=Object.hasOwn(u,"$$value$$");if("$value"===i&&l){var v,b=t(f);return o={},Reflect.set(u,"$$value$$",b),e.next(((v={}).$$value$$=b,v)),!0}var $=n(u);if($[i]===f)return!0;if(c&&r(f)&&"symbol"!=typeof i){var y=l?i:$[a]+"."+i,O=t(f);O[a]=y;var h=s(e,O,o,c);Object.keys(o).forEach(function(e){e.startsWith(y)&&delete o[e]}),o[y]=h,$[i]=O}else $[i]=f;return e.next(Object.assign({},e.getValue())),!0},has:function(e,t){return Reflect.has(n(e),t)},ownKeys:function(e){return Reflect.ownKeys(n(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(n(e),t)}})}var f=Symbol.for("_isServer");function l(r,n,o){var i,a=Object.assign({},r.getValue().$$value$$,((i={})[f]=!0,i));return function(i,s){var l,v=e.useRef("full"===o?null:u(i,null==(l=r.getValue())?void 0:l.$$value$$)),b=e.useSyncExternalStore(function(e){var n=r.subscribe({next:function(r){if("full"!==o){var n=u(i,null==r?void 0:r.$$value$$);(null!=s?s:c)(v.current,n)||(v.current=t(n),e())}else e()}});return function(){n.unsubscribe()}},function(){return r.getValue()},function(){return a});return Object.getOwnPropertySymbols(b).includes(f)?b:n}}exports.cervello=function(e,r){var o,a;void 0===r&&(r={reactiveNestedObjects:!0});var f=r.name,v=r.reactiveNestedObjects,b=void 0===v||v;if(f&&!/^[a-z]+([A-Z][a-z]*)*$/.test(f))throw new Error('[cervello]: options.name must be in camelCase and "'+f+"\" doesn't respect this rule.");var $="function"==typeof e?e():e,y=((o={}).$$value$$=t($),o),O=function(e,t){void 0===t&&(t=!0);var r=[],n=e;return{getValue:function(){return n},next:function(e){n=e,r.forEach(function(t){t.next(e)})},subscribe:function(e){return t&&e.next(n),r.push(e),{unsubscribe:function(){r=r.filter(function(t){return t!==e})}}},unSubscribeAll:function(){r=[]}}}(y,!1),h=s(O,y,{},b),p=((a={})[f?f+"Store":"store"]=h,a[f?"reset"+i(f)+"Store":"reset"]=function(){h.$value=$},a[f?"use"+i(f)+"Store":"useStore"]=l(O,h,"full"),a[f?"use"+i(f)+"Selector":"useSelector"]=l(O,h,"slice"),a.use=function(){return[].slice.call(arguments).forEach(function(e){e({onChange:function(e){O.subscribe({next:function(t){e(h)}})},onPartialChange:function(e,r){var o=u(e,n(y));O.subscribe({next:function(i){var a=n(i),s=u(e,a);c(o,s)||(r(h),o=t(s))}})}})}),this},a);return p};
|
|
2
2
|
//# sourceMappingURL=cervello.cjs.map
|
package/dist/cervello.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cervello.cjs","sources":["../src/lib/utils/object.ts","../src/lib/helpers/proxify-store.ts","../src/lib/helpers/constants.ts","../src/lib/helpers/create-store-selector-hook.ts","../src/lib/store/index.ts","../src/lib/utils/subject.ts"],"sourcesContent":["import { INTERNAL_VALUE_PROP } from '../helpers/constants'\n\n\n/**\n * Clones all the provided object and nested properties and it also\n * iterates nested arrays to deepClone them\n *\n * @param obj - base object to be cloned\n * @returns new cloned object with new reference\n */\nexport function deepClone <T> (obj: T): T {\n if (!obj || typeof obj !== 'object')\n return obj\n\n let newObj = {} as any\n if (Array.isArray(obj)) {\n newObj = obj.map(item => deepClone(item))\n } else {\n Object.entries(obj).forEach(([key, value]) => {\n newObj[key] = deepClone(value)\n })\n }\n return newObj as T\n}\n\n\n/**\n * Guard to check is variable is an object\n * @param obj - variable to be checked\n * @returns boolean\n */\nexport const isObject = (obj: unknown): obj is Record<string | symbol, unknown> => (\n obj !== null\n && typeof obj === 'object'\n && !Array.isArray(obj)\n)\n\n\n\n\n/**\n * Returns the actual target value instead of root\n * object with internals props like $$value$$\n *\n * @param target - object from subscription or proxy\n * @returns actual value\n */\nexport const okTarget = (target: any): any => target[INTERNAL_VALUE_PROP] ?? target\n\n\n\n\n/**\n * Gets an object with just the properties requested\n * instead of the full object\n *\n * @param properties - object properties\n * @param obj - target to get the properties\n * @returns New object with just the requested properties\n */\nexport function getPartialObjectFromProperties<T> (properties: Array<keyof T>, obj: T): any {\n return properties.reduce<any>((acc, curr) => {\n acc[curr] = obj[curr]\n return acc\n }, {})\n}\n\n\n\nconst stringify = (obj: any): string => JSON.stringify(obj)\n/**\n * Compare 2 provided objects by stringing them\n * @param a - first object\n * @param b - second object\n * @returns boolean\n */\nexport const contentComparer = (a: any, b: any): boolean => stringify(a) === stringify(b)\n","/* eslint-disable @typescript-eslint/restrict-template-expressions */\n/* eslint-disable @typescript-eslint/no-dynamic-delete */\nimport { deepClone, isObject, okTarget } from '../utils'\nimport { INTERNAL_VALUE_PROP } from './constants'\n\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst nestedPath = Symbol.for('nestedPath')\n\n\n\n\nexport function proxifyStore <T> (store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T\n\nexport function proxifyStore <T extends Record<string | symbol, any>> (\n store$$: CacheableSubject<T>,\n objectToProxify: T,\n proxiedNestedObjectMap: any,\n shouldProxifyNestedObj: boolean,\n): T {\n if (typeof objectToProxify !== 'object') throw new Error('Store must be an object')\n\n /** Proxify object and push value to subject */\n return new Proxy(objectToProxify, {\n get (t, prop) {\n const target = okTarget(t)\n\n /**\n * Return actual object when toJSON\n * is called instead of $$value$$\n */\n if (prop === 'toJSON') return () => target\n\n const propertyValue = target[prop]\n\n if (shouldProxifyNestedObj && isObject(propertyValue) && typeof prop !== 'symbol') {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n\n /** Check if cached to return instead of re-create new proxy */\n const cachedValue = proxiedNestedObjectMap[propNestedPath]\n if (cachedValue) return cachedValue\n\n /** Set the nestedPath symbol to keep track of proxied */\n propertyValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, propertyValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n\n /** Store in cache the proxiedObject */\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n return proxied\n }\n\n\n return propertyValue\n },\n\n\n\n set (t, prop, newValue) {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n\n /** Reset store */\n if (prop === '$value' && isRootTarget) {\n const newClonedValue = deepClone(newValue)\n proxiedNestedObjectMap = {}\n Reflect.set(t, INTERNAL_VALUE_PROP, newClonedValue)\n store$$.next({ [INTERNAL_VALUE_PROP]: newClonedValue } as unknown as T)\n return true\n }\n\n\n const target = okTarget(t)\n const propValue = target[prop]\n\n /** If new value is the same as the current one */\n if (propValue === newValue) return true\n\n /**\n * Override nested proxied object in\n * cache proxying the new value passed in\n */\n if (shouldProxifyNestedObj && isObject(newValue) && typeof prop !== 'symbol') {\n // TODO: check if --- proxiedNestedObjectMap[prop].$value = value --- is possible instead of recreating new nested\n // proxiedNestedObjectMap[prop].$value = value\n\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n const newClonedValue = deepClone(newValue)\n\n newClonedValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, newClonedValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n // Remove nested object props from cache\n // nestedPath: 'a.b.c': remove -> 'a.b.c.d' -> 'a.b.c.d.e'\n Object.keys(proxiedNestedObjectMap).forEach((cachedProxyKey: string) => {\n if (cachedProxyKey.startsWith(propNestedPath)) delete proxiedNestedObjectMap[cachedProxyKey]\n })\n\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n target[prop] = newClonedValue\n } else {\n target[prop] = newValue\n }\n\n /** Push new store object (new reference for immutability) */\n store$$.next(Object.assign({}, store$$.getValue()))\n\n return true\n },\n\n\n\n\n has (t, p) {\n return Reflect.has(okTarget(t), p)\n },\n\n ownKeys (t) {\n return Reflect.ownKeys(okTarget(t))\n },\n\n getOwnPropertyDescriptor (t, p) {\n return Reflect.getOwnPropertyDescriptor(okTarget(t), p)\n },\n\n\n\n }) as any\n}\n","export const INTERNAL_VALUE_PROP = '$$value$$'\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { useRef } from 'react'\nimport { useSyncExternalStore } from 'use-sync-external-store/shim/index.js'\n\nimport { contentComparer, getPartialObjectFromProperties } from '../utils'\nimport { INTERNAL_VALUE_PROP } from './constants'\n\nimport type { ObjectFromAttributes, UseSelector, WithoutType } from '../../types/shared'\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst isServerSymbol = Symbol.for('_isServer')\n\n\nexport function createStoreSelectorHook<T> (store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T> {\n const serverInitialValue = Object.assign({}, (store$$.getValue() as any)[INTERNAL_VALUE_PROP], { [isServerSymbol]: true })\n\n\n // SELECTOR HOOK -------------------------------------------------\n const useStoreWithSelectors = <Attributes extends Array<keyof WithoutType<T, Function>>>(\n selectors: Attributes,\n isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean,\n ): T => {\n const prevSlice = useRef< ObjectFromAttributes<T, Attributes> | null >(null)\n\n const store = useSyncExternalStore(\n (onStoreChange) => {\n const subscription = store$$.subscribe({\n next: (v) => {\n if (type === 'full') return onStoreChange()\n\n const currentSlice = getPartialObjectFromProperties(selectors, (v as any)?.[INTERNAL_VALUE_PROP])\n\n const compareWithPreviousStore = (isEqualFunction ?? contentComparer as any)\n\n const isEquals = compareWithPreviousStore(prevSlice.current, currentSlice)\n if (!isEquals) {\n prevSlice.current = currentSlice\n onStoreChange()\n }\n },\n })\n\n return () => { subscription.unsubscribe() }\n },\n () => store$$.getValue(),\n () => serverInitialValue,\n )\n\n\n const isServerStore = Object.getOwnPropertySymbols(store).includes(isServerSymbol)\n\n return isServerStore ? store : proxiedStore\n }\n\n return useStoreWithSelectors\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { createStoreSelectorHook, proxifyStore } from '../helpers'\nimport { INTERNAL_VALUE_PROP } from '../helpers/constants'\nimport {\n contentComparer,\n deepClone,\n getPartialObjectFromProperties,\n createCacheableSubject,\n okTarget,\n} from '../utils'\n\nimport type { Maybe, UseSelector, WithoutType } from '../../types/shared'\n\n\n\ninterface CervelloStoreUseParam<StoreType> {\n onChange: (cb: (store: StoreType) => void) => void\n onPartialChange: <\n Attributes extends keyof WithoutType<StoreType, Function>,\n R extends Exclude<Attributes, '$value'>,\n ArrayOfAttributes extends Array <R>,\n > (selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void\n}\n\n/**\n * Use function which allows you to listen for changes in the store.\n */\nexport type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void\n\n\ninterface CervelloStore<StoreType> {\n /** Reactive store */\n store: StoreType\n /** Hook to react to all changes done to 'store' */\n useStore: () => StoreType\n /** Hook to react to changes done to 'store' in all the attributes provided */\n useSelector: UseSelector<StoreType>\n /** Resets the value to the initial value passed in */\n reset: () => void\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType>\n}\n\n\nexport interface CervelloOptions {\n reactiveNestedObjects: boolean\n}\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Object with the default values for the store\n *\n * @returns - \\{ store, useStore, useSelector \\}\n */\nexport function cervello <T> (initialValue: T, options?: CervelloOptions): CervelloStore<T & { $value: Maybe<T> }>\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Function which returns the default values for the store\n *\n * @returns \\{ store, useStore, useSelector \\}\n */\nexport function cervello <T> (initialValue: () => T, options?: CervelloOptions): CervelloStore<T & { $value: Maybe<T> }>\n\n\n/** @internal */\nexport function cervello <T> (\n initialValue: T | (() => T),\n options: CervelloOptions = { reactiveNestedObjects: true },\n): CervelloStore<T & { $value: Maybe<T> }> {\n const defaultValue: T = typeof initialValue === 'function' ? (initialValue as any)() : initialValue\n\n\n const clonedInitialValue = { [INTERNAL_VALUE_PROP]: deepClone(defaultValue) } as any as T\n\n /**\n * Object map to keep reference of nested object proxies created\n * to avoid problems with reference equality (i.e.: useEffect dependencies array)\n */\n const proxiedNestedObjectMap: any = {}\n\n const store$$ = createCacheableSubject(clonedInitialValue, false) as any\n const proxiedStore = proxifyStore<T>(store$$, clonedInitialValue, proxiedNestedObjectMap, options.reactiveNestedObjects)\n\n const cervelloStore = {\n store: proxiedStore,\n reset () {\n (proxiedStore as any).$value = defaultValue\n },\n\n // TO BE SEPARATED IN cervello/react package --------------------------------\n useStore: createStoreSelectorHook<T>(store$$, proxiedStore, 'full'),\n useSelector: createStoreSelectorHook<T>(store$$, proxiedStore, 'slice'),\n // --------------------------------------------------------------------------\n\n use (...functionList: any) {\n functionList.forEach((func: any) => {\n func({\n onChange (cb: any) {\n store$$.subscribe({ next: (_: any) => { cb(proxiedStore) } })\n },\n onPartialChange (attrs: Array<keyof T>, cb: any) {\n let prevSlice: any = getPartialObjectFromProperties(attrs, okTarget(clonedInitialValue))\n store$$.subscribe({\n next: (v: T) => {\n const target = okTarget(v)\n const currentSlice = getPartialObjectFromProperties(attrs, target)\n\n const isEquals = contentComparer(prevSlice, currentSlice)\n if (!isEquals) {\n cb(proxiedStore)\n prevSlice = deepClone(currentSlice)\n }\n },\n })\n },\n })\n })\n return this\n },\n } as any\n\n\n\n return cervelloStore as CervelloStore<T & { $value: Maybe<T> }>\n}\n","\nexport type Noop = () => void\n\n\nexport interface Observer<T> {\n next: (value: T) => void\n}\n\nexport interface Subscription {\n unsubscribe: Noop\n}\n\nexport type CacheableSubject<T> = {\n getValue: () => T\n subscribe: (value: Observer<T>) => Subscription\n unSubscribeAll: Noop\n} & Observer<T>\n\n\n\nexport function createCacheableSubject<T> (defaultValue: T, getValueOnSubscribe = true): CacheableSubject<T> {\n let observerList: Array<Observer<T>> = []\n let value = defaultValue\n\n const getValue = (): T => value\n\n const next = (newValue: T): void => {\n value = newValue\n observerList.forEach((o) => { o.next(newValue) })\n }\n\n const subscribe = (observer: Observer<T>): Subscription => {\n // Push cached value on subscribe\n if (getValueOnSubscribe) observer.next(value)\n // Store the observer to be notified\n observerList.push(observer)\n return {\n unsubscribe: () => {\n observerList = observerList.filter(o => o !== observer)\n },\n }\n }\n\n const unSubscribeAll = (): void => {\n observerList = []\n }\n\n return {\n getValue,\n next,\n subscribe,\n unSubscribeAll,\n }\n}\n"],"names":["deepClone","obj","newObj","Array","isArray","map","item","Object","entries","forEach","_ref","okTarget","target","_target$INTERNAL_VALU","getPartialObjectFromProperties","properties","reduce","acc","curr","stringify","JSON","contentComparer","a","b","nestedPath","Symbol","proxifyStore","store$$","objectToProxify","proxiedNestedObjectMap","shouldProxifyNestedObj","Error","Proxy","get","t","prop","propertyValue","isObject","propNestedPath","hasOwn","cachedValue","proxied","set","newValue","isRootTarget","_store$$$next","newClonedValue","Reflect","next","INTERNAL_VALUE_PROP","_newClonedValue","keys","cachedProxyKey","startsWith","assign","getValue","has","p","ownKeys","getOwnPropertyDescriptor","isServerSymbol","createStoreSelectorHook","proxiedStore","type","_Object$assign","serverInitialValue","selectors","isEqualFunction","prevSlice","useRef","store","useSyncExternalStore","onStoreChange","subscription","subscribe","v","currentSlice","current","unsubscribe","getOwnPropertySymbols","includes","cervello","initialValue","options","reactiveNestedObjects","defaultValue","clonedInitialValue","_clonedInitialValue","createCacheableSubject","getValueOnSubscribe","observerList","value","o","observer","push","filter","unSubscribeAll","cervelloStore","reset","$value","useStore","useSelector","use","func","onChange","cb","_","onPartialChange","attrs","this"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cervello.cjs","sources":["../src/lib/utils/object.ts","../src/lib/utils/string.ts","../src/lib/helpers/proxify-store.ts","../src/lib/helpers/constants.ts","../src/lib/helpers/create-store-selector-hook.ts","../src/lib/store/index.ts","../src/lib/utils/subject.ts"],"sourcesContent":["import { INTERNAL_VALUE_PROP } from '../helpers/constants'\n\n\n/**\n * Clones all the provided object and nested properties and it also\n * iterates nested arrays to deepClone them\n *\n * @param obj - base object to be cloned\n * @returns new cloned object with new reference\n */\nexport function deepClone <T> (obj: T): T {\n if (!obj || typeof obj !== 'object')\n return obj\n\n let newObj = {} as any\n if (Array.isArray(obj)) {\n newObj = obj.map(item => deepClone(item))\n } else {\n Object.entries(obj).forEach(([key, value]) => {\n newObj[key] = deepClone(value)\n })\n }\n return newObj as T\n}\n\n\n/**\n * Guard to check is variable is an object\n * @param obj - variable to be checked\n * @returns boolean\n */\nexport const isObject = (obj: unknown): obj is Record<string | symbol, unknown> => (\n obj !== null\n && typeof obj === 'object'\n && !Array.isArray(obj)\n)\n\n\n\n\n/**\n * Returns the actual target value instead of root\n * object with internals props like $$value$$\n *\n * @param target - object from subscription or proxy\n * @returns actual value\n */\nexport const okTarget = (target: any): any => target[INTERNAL_VALUE_PROP] ?? target\n\n\n\n\n/**\n * Gets an object with just the properties requested\n * instead of the full object\n *\n * @param properties - object properties\n * @param obj - target to get the properties\n * @returns New object with just the requested properties\n */\nexport function getPartialObjectFromProperties<T> (properties: Array<keyof T>, obj: T): any {\n return properties.reduce<any>((acc, curr) => {\n acc[curr] = obj[curr]\n return acc\n }, {})\n}\n\n\n\nconst stringify = (obj: any): string => JSON.stringify(obj)\n/**\n * Compare 2 provided objects by stringing them\n * @param a - first object\n * @param b - second object\n * @returns boolean\n */\nexport const contentComparer = (a: any, b: any): boolean => stringify(a) === stringify(b)\n","\nexport function capitalize (str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1).toLocaleLowerCase()\n}\n\nexport function isCamelCase (str: string): boolean {\n const regex = /^[a-z]+([A-Z][a-z]*)*$/\n\n return regex.test(str)\n}\n","\n/* eslint-disable @typescript-eslint/no-dynamic-delete */\nimport { INTERNAL_VALUE_PROP } from './constants'\nimport { deepClone, isObject, okTarget } from '../utils'\n\nimport type { CacheableSubject } from '../utils/subject'\n\n\n// @ts-expect-error - Object.hasOwn is not defined in older Safari's browsers\n('hasOwn' in Object) || (Object.hasOwn = Object.call.bind(Object.hasOwnProperty))\n\nconst nestedPath = Symbol.for('nestedPath')\n\n\n\n\nexport function proxifyStore <T> (store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T\n\nexport function proxifyStore <T extends Record<string | symbol, any>> (\n store$$: CacheableSubject<T>,\n objectToProxify: T,\n proxiedNestedObjectMap: any,\n shouldProxifyNestedObj: boolean,\n): T {\n if (typeof objectToProxify !== 'object') throw new Error('Store must be an object')\n\n /** Proxify object and push value to subject */\n return new Proxy(objectToProxify, {\n get (t, prop) {\n const target = okTarget(t)\n\n /**\n * Return actual object when toJSON\n * is called instead of $$value$$\n */\n if (prop === 'toJSON') return () => target\n\n const propertyValue = target[prop]\n\n if (shouldProxifyNestedObj && isObject(propertyValue) && typeof prop !== 'symbol') {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n\n /** Check if cached to return instead of re-create new proxy */\n const cachedValue = proxiedNestedObjectMap[propNestedPath]\n\n if (cachedValue) return cachedValue\n\n /** Set the nestedPath symbol to keep track of proxied */\n propertyValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, propertyValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n\n /** Store in cache the proxiedObject */\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n return proxied\n }\n\n\n return propertyValue\n },\n\n\n\n set (t, prop, newValue) {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n\n /** Reset store */\n if (prop === '$value' && isRootTarget) {\n const newClonedValue = deepClone(newValue)\n\n proxiedNestedObjectMap = {}\n Reflect.set(t, INTERNAL_VALUE_PROP, newClonedValue)\n store$$.next({ [INTERNAL_VALUE_PROP]: newClonedValue } as unknown as T)\n\n return true\n }\n\n\n const target = okTarget(t)\n const propValue = target[prop]\n\n /** If new value is the same as the current one */\n if (propValue === newValue) return true\n\n /**\n * Override nested proxied object in\n * cache proxying the new value passed in\n */\n if (shouldProxifyNestedObj && isObject(newValue) && typeof prop !== 'symbol') {\n // TODO: check if --- proxiedNestedObjectMap[prop].$value = value --- is possible instead of recreating new nested\n // proxiedNestedObjectMap[prop].$value = value\n\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n const newClonedValue = deepClone(newValue)\n\n newClonedValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, newClonedValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n // Remove nested object props from cache\n // nestedPath: 'a.b.c': remove -> 'a.b.c.d' -> 'a.b.c.d.e'\n Object.keys(proxiedNestedObjectMap).forEach((cachedProxyKey: string) => {\n if (cachedProxyKey.startsWith(propNestedPath)) delete proxiedNestedObjectMap[cachedProxyKey]\n })\n\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n target[prop] = newClonedValue\n } else {\n target[prop] = newValue\n }\n\n /** Push new store object (new reference for immutability) */\n store$$.next(Object.assign({}, store$$.getValue()))\n\n return true\n },\n\n\n\n\n has (t, p) {\n return Reflect.has(okTarget(t), p)\n },\n\n ownKeys (t) {\n return Reflect.ownKeys(okTarget(t))\n },\n\n getOwnPropertyDescriptor (t, p) {\n return Reflect.getOwnPropertyDescriptor(okTarget(t), p)\n },\n\n\n\n }) as any\n}\n","export const INTERNAL_VALUE_PROP = '$$value$$'\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { useRef, useSyncExternalStore } from 'react'\n\nimport { INTERNAL_VALUE_PROP } from './constants'\nimport { contentComparer, deepClone, getPartialObjectFromProperties } from '../utils'\n\nimport type { ObjectFromAttributes, UseSelector, WithoutType } from '../../types/shared'\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst isServerSymbol = Symbol.for('_isServer')\n\n\nexport function createStoreSelectorHook<T> (store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T> {\n const serverInitialValue = Object.assign({}, (store$$.getValue() as any)[INTERNAL_VALUE_PROP], { [isServerSymbol]: true })\n\n\n // SELECTOR HOOK -------------------------------------------------\n const useStoreWithSelectors = <Attributes extends Array<keyof WithoutType<T, Function>>>(\n selectors: Attributes,\n isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean,\n ): T => {\n const prevSlice = useRef< ObjectFromAttributes<T, Attributes> | null >(\n type === 'full'\n ? null\n // To avoid the first render to be always different even\n // if store changed other than selected attributes\n : getPartialObjectFromProperties(selectors, (store$$.getValue() as any)?.[INTERNAL_VALUE_PROP]),\n )\n\n const store = useSyncExternalStore(\n (onStoreChange) => {\n const subscription = store$$.subscribe({\n next: (v) => {\n if (type === 'full') {\n onStoreChange()\n\n return\n }\n\n // Partial store section ------------------------------------------------------------------\n const currentSlice = getPartialObjectFromProperties(selectors, (v as any)?.[INTERNAL_VALUE_PROP])\n\n const compareWithPreviousStore = (isEqualFunction ?? contentComparer as any)\n\n const isEquals = compareWithPreviousStore(prevSlice.current, currentSlice)\n\n if (!isEquals) {\n /**\n * Update previous slice with deepClone to avoid storing direct store$$ value reference.\n * This solve reference/value problems when changing the store object\n * (proxy.set is changing the target object which is 'v' here)\n */\n prevSlice.current = deepClone(currentSlice)\n onStoreChange()\n }\n // ----------------------------------------------------------------------------------------\n },\n })\n\n return () => { subscription.unsubscribe() }\n },\n () => store$$.getValue(),\n () => serverInitialValue,\n )\n\n\n const isServerStore = Object.getOwnPropertySymbols(store).includes(isServerSymbol)\n\n return isServerStore ? store : proxiedStore\n }\n\n return useStoreWithSelectors\n}\n","\n/* eslint-disable @typescript-eslint/ban-types */\nimport { createStoreSelectorHook, proxifyStore } from '../helpers'\nimport { INTERNAL_VALUE_PROP } from '../helpers/constants'\nimport {\n contentComparer,\n deepClone,\n getPartialObjectFromProperties,\n createCacheableSubject,\n okTarget,\n capitalize,\n isCamelCase,\n} from '../utils'\n\nimport type { Maybe, UseSelector, WithoutType } from '../../types/shared'\n\n\n\ntype CervelloStoreUseParam<StoreType> = {\n onChange: (cb: (store: StoreType) => void) => void\n onPartialChange: <\n Attributes extends keyof WithoutType<StoreType, Function>,\n R extends Exclude<Attributes, '$value'>,\n ArrayOfAttributes extends Array <R>,\n > (selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void\n}\n\n/**\n * Use function which allows you to listen for changes in the store.\n */\nexport type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void\n\ndeclare type Prettify<T> = {} & {\n [K in keyof T]: T[K];\n}\n\n\n\n\ntype CervelloStore<StoreType, StoreName extends string | undefined = undefined> =\n StoreName extends string\n ? Prettify<{\n // /** Reactive store */\n [K in `${StoreName}Store`]: StoreType\n }\n & {\n /** Hook to react to all changes done to 'store' */\n [K in `use${Capitalize<StoreName>}Store`]: () => StoreType\n }\n & {\n /** Hook to react to all changes done to 'store' */\n [K in `use${Capitalize<StoreName>}Selector`]: UseSelector<StoreType>\n }\n & {\n /** Resets the value to the initial value passed in */\n [K in `reset${Capitalize<StoreName>}Store`]: () => void\n }\n & {\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>\n }>\n :\n {\n /** Reactive store */\n store: StoreType\n /** Hook to react to all changes done to 'store' */\n useStore: () => StoreType\n /** Hook to react to changes done to 'store' in all the attributes provided */\n useSelector: UseSelector<StoreType>\n /** Resets the value to the initial value passed in */\n reset: () => void\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>\n }\n\n\nexport type CervelloOptions<StoreName extends string | undefined = undefined> = {\n name?: StoreName\n reactiveNestedObjects?: boolean\n}\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Function which returns the default values for the store\n *\n * @returns \\{ store, useStore, useSelector \\} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value\n */\nexport function cervello <\nT,\nStoreName extends string | undefined = undefined,\n> (initialValue: () => T, options?: CervelloOptions<StoreName>): Prettify< CervelloStore<Prettify<T & { $value: Maybe<T> }>, StoreName> >\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Object with the default values for the store\n *\n * @returns - \\{ store, useStore, useSelector \\} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value\n */\nexport function cervello <\nT extends Record<string, unknown>,\nStoreName extends string | undefined = undefined,\n> (initialValue: T, options?: CervelloOptions<StoreName>): Prettify< CervelloStore<Prettify<T & { $value: Maybe<T> }>, StoreName> >\n\n\n\n/** @internal */\nexport function cervello <T, StoreName extends string | undefined = undefined> (\n initialValue: T | (() => T),\n options: CervelloOptions<StoreName> = { reactiveNestedObjects: true },\n): CervelloStore<T & { $value: Maybe<T> }, StoreName> {\n const { name: optionsName, reactiveNestedObjects = true } = options\n\n if (optionsName && !isCamelCase(optionsName))\n throw new Error(`[cervello]: options.name must be in camelCase and \"${optionsName}\" doesn't respect this rule.`)\n\n\n const defaultValue: T = typeof initialValue === 'function' ? (initialValue as any)() : initialValue\n\n\n const clonedInitialValue = { [INTERNAL_VALUE_PROP]: deepClone(defaultValue) } as any as T\n\n /**\n * Object map to keep reference of nested object proxies created\n * to avoid problems with reference equality (i.e.: useEffect dependencies array)\n */\n const proxiedNestedObjectMap: any = {}\n\n const store$$ = createCacheableSubject(clonedInitialValue, false) as any\n const proxiedStore = proxifyStore<T>(store$$, clonedInitialValue, proxiedNestedObjectMap, reactiveNestedObjects)\n\n const cervelloStore = {\n [optionsName\n ? `${optionsName}Store`\n : 'store'\n ]: proxiedStore,\n\n [optionsName\n ? `reset${capitalize(optionsName)}Store`\n : 'reset'\n ] () {\n (proxiedStore as any).$value = defaultValue\n },\n\n // TO BE SEPARATED IN cervello/react package --------------------------------\n [optionsName\n ? `use${capitalize(optionsName)}Store`\n : 'useStore'\n ]: createStoreSelectorHook<T>(store$$, proxiedStore, 'full'),\n\n [optionsName\n ? `use${capitalize(optionsName)}Selector`\n : 'useSelector'\n ]: createStoreSelectorHook<T>(store$$, proxiedStore, 'slice'),\n // --------------------------------------------------------------------------\n\n use (...functionList: any) {\n functionList.forEach((func: any) => {\n func({\n onChange (cb: any) {\n store$$.subscribe({ next: (_: any) => { cb(proxiedStore) } })\n },\n onPartialChange (attrs: Array<keyof T>, cb: any) {\n let prevSlice: any = getPartialObjectFromProperties(attrs, okTarget(clonedInitialValue))\n\n store$$.subscribe({\n next: (v: T) => {\n const target = okTarget(v)\n const currentSlice = getPartialObjectFromProperties(attrs, target)\n\n const isEquals = contentComparer(prevSlice, currentSlice)\n\n if (!isEquals) {\n cb(proxiedStore)\n prevSlice = deepClone(currentSlice)\n }\n },\n })\n },\n })\n })\n\n return this\n },\n } as any\n\n\n\n return cervelloStore\n}\n","\nexport type Noop = () => void\n\n\nexport type Observer<T> = {\n next: (value: T) => void\n}\n\nexport type Subscription = {\n unsubscribe: Noop\n}\n\nexport type CacheableSubject<T> = {\n getValue: () => T\n subscribe: (value: Observer<T>) => Subscription\n unSubscribeAll: Noop\n} & Observer<T>\n\n\n\nexport function createCacheableSubject<T> (defaultValue: T, getValueOnSubscribe = true): CacheableSubject<T> {\n let observerList: Array<Observer<T>> = []\n let value = defaultValue\n\n const getValue = (): T => value\n\n const next = (newValue: T): void => {\n value = newValue\n observerList.forEach((o) => { o.next(newValue) })\n }\n\n const subscribe = (observer: Observer<T>): Subscription => {\n // Push cached value on subscribe\n if (getValueOnSubscribe) observer.next(value)\n // Store the observer to be notified\n observerList.push(observer)\n\n return {\n unsubscribe: () => {\n observerList = observerList.filter(o => o !== observer)\n },\n }\n }\n\n const unSubscribeAll = (): void => {\n observerList = []\n }\n\n return {\n getValue,\n next,\n subscribe,\n unSubscribeAll,\n }\n}\n"],"names":["deepClone","obj","newObj","Array","isArray","map","item","Object","entries","forEach","_ref","okTarget","target","_target$INTERNAL_VALU","getPartialObjectFromProperties","properties","reduce","acc","curr","stringify","JSON","contentComparer","a","b","str","charAt","toUpperCase","slice","toLocaleLowerCase","hasOwn","call","bind","hasOwnProperty","nestedPath","Symbol","proxifyStore","store$$","objectToProxify","proxiedNestedObjectMap","shouldProxifyNestedObj","Error","Proxy","get","t","prop","propertyValue","isObject","propNestedPath","cachedValue","proxied","set","newValue","isRootTarget","_store$$$next","newClonedValue","Reflect","next","keys","cachedProxyKey","startsWith","assign","getValue","has","p","ownKeys","getOwnPropertyDescriptor","isServerSymbol","createStoreSelectorHook","proxiedStore","type","_Object$assign","serverInitialValue","selectors","isEqualFunction","_store$$$getValue","prevSlice","useRef","store","useSyncExternalStore","onStoreChange","subscription","subscribe","v","currentSlice","current","unsubscribe","getOwnPropertySymbols","includes","initialValue","options","reactiveNestedObjects","optionsName","name","_options$reactiveNest","test","defaultValue","clonedInitialValue","_clonedInitialValue","createCacheableSubject","getValueOnSubscribe","observerList","value","o","observer","push","filter","unSubscribeAll","cervelloStore","capitalize","$value","_cervelloStore","use","func","onChange","cb","_","onPartialChange","attrs","this"],"mappings":"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"}
|
package/dist/cervello.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useRef as e
|
|
1
|
+
import{useRef as e,useSyncExternalStore as t}from"react";function n(e){if(!e||"object"!=typeof e)return e;var t={};return Array.isArray(e)?t=e.map(function(e){return n(e)}):Object.entries(e).forEach(function(e){t[e[0]]=n(e[1])}),t}var r=function(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)},u=function(e){var t;return null!=(t=e.$$value$$)?t:e};function o(e,t){return e.reduce(function(e,n){return e[n]=t[n],e},{})}var c=function(e){return JSON.stringify(e)},i=function(e,t){return c(e)===c(t)};function a(e){return e.charAt(0).toUpperCase()+e.slice(1).toLocaleLowerCase()}"hasOwn"in Object||(Object.hasOwn=Object.call.bind(Object.hasOwnProperty));var f=Symbol.for("nestedPath");function s(e,t,o,c){if("object"!=typeof t)throw new Error("Store must be an object");return new Proxy(t,{get:function(t,n){var i=u(t);if("toJSON"===n)return function(){return i};var a=i[n];if(c&&r(a)&&"symbol"!=typeof n){var l=Object.hasOwn(t,"$$value$$")?n:i[f]+"."+n,v=o[l];if(v)return v;a[f]=l;var b=s(e,a,o,c);return o[l]=b,b}return a},set:function(t,i,a){var l=Object.hasOwn(t,"$$value$$");if("$value"===i&&l){var v,b=n(a);return o={},Reflect.set(t,"$$value$$",b),e.next(((v={}).$$value$$=b,v)),!0}var $=u(t);if($[i]===a)return!0;if(c&&r(a)&&"symbol"!=typeof i){var y=l?i:$[f]+"."+i,O=n(a);O[f]=y;var h=s(e,O,o,c);Object.keys(o).forEach(function(e){e.startsWith(y)&&delete o[e]}),o[y]=h,$[i]=O}else $[i]=a;return e.next(Object.assign({},e.getValue())),!0},has:function(e,t){return Reflect.has(u(e),t)},ownKeys:function(e){return Reflect.ownKeys(u(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(u(e),t)}})}var l=Symbol.for("_isServer");function v(r,u,c){var a,f=Object.assign({},r.getValue().$$value$$,((a={})[l]=!0,a));return function(a,s){var v,b=e("full"===c?null:o(a,null==(v=r.getValue())?void 0:v.$$value$$)),$=t(function(e){var t=r.subscribe({next:function(t){if("full"!==c){var r=o(a,null==t?void 0:t.$$value$$);(null!=s?s:i)(b.current,r)||(b.current=n(r),e())}else e()}});return function(){t.unsubscribe()}},function(){return r.getValue()},function(){return f});return Object.getOwnPropertySymbols($).includes(l)?$:u}}function b(e,t){var r,c;void 0===t&&(t={reactiveNestedObjects:!0});var f=t.name,l=t.reactiveNestedObjects,b=void 0===l||l;if(f&&!/^[a-z]+([A-Z][a-z]*)*$/.test(f))throw new Error('[cervello]: options.name must be in camelCase and "'+f+"\" doesn't respect this rule.");var $="function"==typeof e?e():e,y=((r={}).$$value$$=n($),r),O=function(e,t){void 0===t&&(t=!0);var n=[],r=e;return{getValue:function(){return r},next:function(e){r=e,n.forEach(function(t){t.next(e)})},subscribe:function(e){return t&&e.next(r),n.push(e),{unsubscribe:function(){n=n.filter(function(t){return t!==e})}}},unSubscribeAll:function(){n=[]}}}(y,!1),h=s(O,y,{},b),p=((c={})[f?f+"Store":"store"]=h,c[f?"reset"+a(f)+"Store":"reset"]=function(){h.$value=$},c[f?"use"+a(f)+"Store":"useStore"]=v(O,h,"full"),c[f?"use"+a(f)+"Selector":"useSelector"]=v(O,h,"slice"),c.use=function(){return[].slice.call(arguments).forEach(function(e){e({onChange:function(e){O.subscribe({next:function(t){e(h)}})},onPartialChange:function(e,t){var r=o(e,u(y));O.subscribe({next:function(c){var a=u(c),f=o(e,a);i(r,f)||(t(h),r=n(f))}})}})}),this},c);return p}export{b as cervello};
|
|
2
2
|
//# sourceMappingURL=cervello.esm.js.map
|
package/dist/cervello.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cervello.esm.js","sources":["../src/lib/utils/object.ts","../src/lib/helpers/proxify-store.ts","../src/lib/helpers/constants.ts","../src/lib/helpers/create-store-selector-hook.ts","../src/lib/store/index.ts","../src/lib/utils/subject.ts"],"sourcesContent":["import { INTERNAL_VALUE_PROP } from '../helpers/constants'\n\n\n/**\n * Clones all the provided object and nested properties and it also\n * iterates nested arrays to deepClone them\n *\n * @param obj - base object to be cloned\n * @returns new cloned object with new reference\n */\nexport function deepClone <T> (obj: T): T {\n if (!obj || typeof obj !== 'object')\n return obj\n\n let newObj = {} as any\n if (Array.isArray(obj)) {\n newObj = obj.map(item => deepClone(item))\n } else {\n Object.entries(obj).forEach(([key, value]) => {\n newObj[key] = deepClone(value)\n })\n }\n return newObj as T\n}\n\n\n/**\n * Guard to check is variable is an object\n * @param obj - variable to be checked\n * @returns boolean\n */\nexport const isObject = (obj: unknown): obj is Record<string | symbol, unknown> => (\n obj !== null\n && typeof obj === 'object'\n && !Array.isArray(obj)\n)\n\n\n\n\n/**\n * Returns the actual target value instead of root\n * object with internals props like $$value$$\n *\n * @param target - object from subscription or proxy\n * @returns actual value\n */\nexport const okTarget = (target: any): any => target[INTERNAL_VALUE_PROP] ?? target\n\n\n\n\n/**\n * Gets an object with just the properties requested\n * instead of the full object\n *\n * @param properties - object properties\n * @param obj - target to get the properties\n * @returns New object with just the requested properties\n */\nexport function getPartialObjectFromProperties<T> (properties: Array<keyof T>, obj: T): any {\n return properties.reduce<any>((acc, curr) => {\n acc[curr] = obj[curr]\n return acc\n }, {})\n}\n\n\n\nconst stringify = (obj: any): string => JSON.stringify(obj)\n/**\n * Compare 2 provided objects by stringing them\n * @param a - first object\n * @param b - second object\n * @returns boolean\n */\nexport const contentComparer = (a: any, b: any): boolean => stringify(a) === stringify(b)\n","/* eslint-disable @typescript-eslint/restrict-template-expressions */\n/* eslint-disable @typescript-eslint/no-dynamic-delete */\nimport { deepClone, isObject, okTarget } from '../utils'\nimport { INTERNAL_VALUE_PROP } from './constants'\n\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst nestedPath = Symbol.for('nestedPath')\n\n\n\n\nexport function proxifyStore <T> (store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T\n\nexport function proxifyStore <T extends Record<string | symbol, any>> (\n store$$: CacheableSubject<T>,\n objectToProxify: T,\n proxiedNestedObjectMap: any,\n shouldProxifyNestedObj: boolean,\n): T {\n if (typeof objectToProxify !== 'object') throw new Error('Store must be an object')\n\n /** Proxify object and push value to subject */\n return new Proxy(objectToProxify, {\n get (t, prop) {\n const target = okTarget(t)\n\n /**\n * Return actual object when toJSON\n * is called instead of $$value$$\n */\n if (prop === 'toJSON') return () => target\n\n const propertyValue = target[prop]\n\n if (shouldProxifyNestedObj && isObject(propertyValue) && typeof prop !== 'symbol') {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n\n /** Check if cached to return instead of re-create new proxy */\n const cachedValue = proxiedNestedObjectMap[propNestedPath]\n if (cachedValue) return cachedValue\n\n /** Set the nestedPath symbol to keep track of proxied */\n propertyValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, propertyValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n\n /** Store in cache the proxiedObject */\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n return proxied\n }\n\n\n return propertyValue\n },\n\n\n\n set (t, prop, newValue) {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n\n /** Reset store */\n if (prop === '$value' && isRootTarget) {\n const newClonedValue = deepClone(newValue)\n proxiedNestedObjectMap = {}\n Reflect.set(t, INTERNAL_VALUE_PROP, newClonedValue)\n store$$.next({ [INTERNAL_VALUE_PROP]: newClonedValue } as unknown as T)\n return true\n }\n\n\n const target = okTarget(t)\n const propValue = target[prop]\n\n /** If new value is the same as the current one */\n if (propValue === newValue) return true\n\n /**\n * Override nested proxied object in\n * cache proxying the new value passed in\n */\n if (shouldProxifyNestedObj && isObject(newValue) && typeof prop !== 'symbol') {\n // TODO: check if --- proxiedNestedObjectMap[prop].$value = value --- is possible instead of recreating new nested\n // proxiedNestedObjectMap[prop].$value = value\n\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n const newClonedValue = deepClone(newValue)\n\n newClonedValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, newClonedValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n // Remove nested object props from cache\n // nestedPath: 'a.b.c': remove -> 'a.b.c.d' -> 'a.b.c.d.e'\n Object.keys(proxiedNestedObjectMap).forEach((cachedProxyKey: string) => {\n if (cachedProxyKey.startsWith(propNestedPath)) delete proxiedNestedObjectMap[cachedProxyKey]\n })\n\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n target[prop] = newClonedValue\n } else {\n target[prop] = newValue\n }\n\n /** Push new store object (new reference for immutability) */\n store$$.next(Object.assign({}, store$$.getValue()))\n\n return true\n },\n\n\n\n\n has (t, p) {\n return Reflect.has(okTarget(t), p)\n },\n\n ownKeys (t) {\n return Reflect.ownKeys(okTarget(t))\n },\n\n getOwnPropertyDescriptor (t, p) {\n return Reflect.getOwnPropertyDescriptor(okTarget(t), p)\n },\n\n\n\n }) as any\n}\n","export const INTERNAL_VALUE_PROP = '$$value$$'\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { useRef } from 'react'\nimport { useSyncExternalStore } from 'use-sync-external-store/shim/index.js'\n\nimport { contentComparer, getPartialObjectFromProperties } from '../utils'\nimport { INTERNAL_VALUE_PROP } from './constants'\n\nimport type { ObjectFromAttributes, UseSelector, WithoutType } from '../../types/shared'\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst isServerSymbol = Symbol.for('_isServer')\n\n\nexport function createStoreSelectorHook<T> (store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T> {\n const serverInitialValue = Object.assign({}, (store$$.getValue() as any)[INTERNAL_VALUE_PROP], { [isServerSymbol]: true })\n\n\n // SELECTOR HOOK -------------------------------------------------\n const useStoreWithSelectors = <Attributes extends Array<keyof WithoutType<T, Function>>>(\n selectors: Attributes,\n isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean,\n ): T => {\n const prevSlice = useRef< ObjectFromAttributes<T, Attributes> | null >(null)\n\n const store = useSyncExternalStore(\n (onStoreChange) => {\n const subscription = store$$.subscribe({\n next: (v) => {\n if (type === 'full') return onStoreChange()\n\n const currentSlice = getPartialObjectFromProperties(selectors, (v as any)?.[INTERNAL_VALUE_PROP])\n\n const compareWithPreviousStore = (isEqualFunction ?? contentComparer as any)\n\n const isEquals = compareWithPreviousStore(prevSlice.current, currentSlice)\n if (!isEquals) {\n prevSlice.current = currentSlice\n onStoreChange()\n }\n },\n })\n\n return () => { subscription.unsubscribe() }\n },\n () => store$$.getValue(),\n () => serverInitialValue,\n )\n\n\n const isServerStore = Object.getOwnPropertySymbols(store).includes(isServerSymbol)\n\n return isServerStore ? store : proxiedStore\n }\n\n return useStoreWithSelectors\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { createStoreSelectorHook, proxifyStore } from '../helpers'\nimport { INTERNAL_VALUE_PROP } from '../helpers/constants'\nimport {\n contentComparer,\n deepClone,\n getPartialObjectFromProperties,\n createCacheableSubject,\n okTarget,\n} from '../utils'\n\nimport type { Maybe, UseSelector, WithoutType } from '../../types/shared'\n\n\n\ninterface CervelloStoreUseParam<StoreType> {\n onChange: (cb: (store: StoreType) => void) => void\n onPartialChange: <\n Attributes extends keyof WithoutType<StoreType, Function>,\n R extends Exclude<Attributes, '$value'>,\n ArrayOfAttributes extends Array <R>,\n > (selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void\n}\n\n/**\n * Use function which allows you to listen for changes in the store.\n */\nexport type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void\n\n\ninterface CervelloStore<StoreType> {\n /** Reactive store */\n store: StoreType\n /** Hook to react to all changes done to 'store' */\n useStore: () => StoreType\n /** Hook to react to changes done to 'store' in all the attributes provided */\n useSelector: UseSelector<StoreType>\n /** Resets the value to the initial value passed in */\n reset: () => void\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType>\n}\n\n\nexport interface CervelloOptions {\n reactiveNestedObjects: boolean\n}\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Object with the default values for the store\n *\n * @returns - \\{ store, useStore, useSelector \\}\n */\nexport function cervello <T> (initialValue: T, options?: CervelloOptions): CervelloStore<T & { $value: Maybe<T> }>\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Function which returns the default values for the store\n *\n * @returns \\{ store, useStore, useSelector \\}\n */\nexport function cervello <T> (initialValue: () => T, options?: CervelloOptions): CervelloStore<T & { $value: Maybe<T> }>\n\n\n/** @internal */\nexport function cervello <T> (\n initialValue: T | (() => T),\n options: CervelloOptions = { reactiveNestedObjects: true },\n): CervelloStore<T & { $value: Maybe<T> }> {\n const defaultValue: T = typeof initialValue === 'function' ? (initialValue as any)() : initialValue\n\n\n const clonedInitialValue = { [INTERNAL_VALUE_PROP]: deepClone(defaultValue) } as any as T\n\n /**\n * Object map to keep reference of nested object proxies created\n * to avoid problems with reference equality (i.e.: useEffect dependencies array)\n */\n const proxiedNestedObjectMap: any = {}\n\n const store$$ = createCacheableSubject(clonedInitialValue, false) as any\n const proxiedStore = proxifyStore<T>(store$$, clonedInitialValue, proxiedNestedObjectMap, options.reactiveNestedObjects)\n\n const cervelloStore = {\n store: proxiedStore,\n reset () {\n (proxiedStore as any).$value = defaultValue\n },\n\n // TO BE SEPARATED IN cervello/react package --------------------------------\n useStore: createStoreSelectorHook<T>(store$$, proxiedStore, 'full'),\n useSelector: createStoreSelectorHook<T>(store$$, proxiedStore, 'slice'),\n // --------------------------------------------------------------------------\n\n use (...functionList: any) {\n functionList.forEach((func: any) => {\n func({\n onChange (cb: any) {\n store$$.subscribe({ next: (_: any) => { cb(proxiedStore) } })\n },\n onPartialChange (attrs: Array<keyof T>, cb: any) {\n let prevSlice: any = getPartialObjectFromProperties(attrs, okTarget(clonedInitialValue))\n store$$.subscribe({\n next: (v: T) => {\n const target = okTarget(v)\n const currentSlice = getPartialObjectFromProperties(attrs, target)\n\n const isEquals = contentComparer(prevSlice, currentSlice)\n if (!isEquals) {\n cb(proxiedStore)\n prevSlice = deepClone(currentSlice)\n }\n },\n })\n },\n })\n })\n return this\n },\n } as any\n\n\n\n return cervelloStore as CervelloStore<T & { $value: Maybe<T> }>\n}\n","\nexport type Noop = () => void\n\n\nexport interface Observer<T> {\n next: (value: T) => void\n}\n\nexport interface Subscription {\n unsubscribe: Noop\n}\n\nexport type CacheableSubject<T> = {\n getValue: () => T\n subscribe: (value: Observer<T>) => Subscription\n unSubscribeAll: Noop\n} & Observer<T>\n\n\n\nexport function createCacheableSubject<T> (defaultValue: T, getValueOnSubscribe = true): CacheableSubject<T> {\n let observerList: Array<Observer<T>> = []\n let value = defaultValue\n\n const getValue = (): T => value\n\n const next = (newValue: T): void => {\n value = newValue\n observerList.forEach((o) => { o.next(newValue) })\n }\n\n const subscribe = (observer: Observer<T>): Subscription => {\n // Push cached value on subscribe\n if (getValueOnSubscribe) observer.next(value)\n // Store the observer to be notified\n observerList.push(observer)\n return {\n unsubscribe: () => {\n observerList = observerList.filter(o => o !== observer)\n },\n }\n }\n\n const unSubscribeAll = (): void => {\n observerList = []\n }\n\n return {\n getValue,\n next,\n subscribe,\n unSubscribeAll,\n }\n}\n"],"names":["deepClone","obj","newObj","Array","isArray","map","item","Object","entries","forEach","_ref","okTarget","target","_target$INTERNAL_VALU","getPartialObjectFromProperties","properties","reduce","acc","curr","stringify","JSON","contentComparer","a","b","nestedPath","Symbol","proxifyStore","store$$","objectToProxify","proxiedNestedObjectMap","shouldProxifyNestedObj","Error","Proxy","get","t","prop","propertyValue","isObject","propNestedPath","hasOwn","cachedValue","proxied","set","newValue","isRootTarget","_store$$$next","newClonedValue","Reflect","next","INTERNAL_VALUE_PROP","_newClonedValue","keys","cachedProxyKey","startsWith","assign","getValue","has","p","ownKeys","getOwnPropertyDescriptor","isServerSymbol","createStoreSelectorHook","proxiedStore","type","_Object$assign","serverInitialValue","selectors","isEqualFunction","prevSlice","useRef","store","useSyncExternalStore","onStoreChange","subscription","subscribe","v","currentSlice","current","unsubscribe","getOwnPropertySymbols","includes","cervello","initialValue","options","reactiveNestedObjects","defaultValue","clonedInitialValue","_clonedInitialValue","createCacheableSubject","getValueOnSubscribe","observerList","value","o","observer","push","filter","unSubscribeAll","cervelloStore","reset","$value","useStore","useSelector","use","func","onChange","cb","_","onPartialChange","attrs","this"],"mappings":"4GAUgBA,SAAAA,EAAeC,GAC7B,IAAKA,GAAsB,mBACzB,OAAUA,EAEZ,IAAUC,EAAG,CAAS,EAQtB,OAPIC,MAAMC,QAAQH,GAChBC,EAASD,EAAII,IAAI,SAAAC,GAAI,OAAaN,EAACM,EAAK,GAExCC,OAAOC,QAAQP,GAAKQ,QAAQ,YAC1BP,QAAcF,EADwBU,EAAA,GAExC,GAGJR,CAAA,CAQO,MAAiB,SAACD,UACf,OAALA,GACe,iBAARA,IACNE,MAAMC,QAAQH,EAAI,EAaXU,EAAW,SAACC,GAAW,IAAAC,EAAA,cAAAA,EAAUD,EAA0B,aAAKA,CAAM,EAanEE,SAAAA,EAAmCC,EAA4Bd,GAC7E,SAAkBe,OAAY,SAACC,EAAKC,GAElC,OADAD,EAAIC,GAAQjB,EAAIiB,GACTD,CACT,EAAG,GACL,CAIA,IAAeE,EAAG,SAAClB,GAAQ,OAAiBmB,KAACD,UAAUlB,EAAI,EAO/BoB,EAAG,SAACC,EAAQC,GAAM,SAAwBD,KAAOH,EAAUI,EAAE,ECnEnFC,EAAaC,OAAU,IAAC,cAOxB,SAAsBC,EAC1BC,EACAC,EACAC,EACAC,GAEA,GAA+B,iBAApBF,EAA8B,MAAM,IAASG,MAAC,2BAGzD,OAAWC,IAAAA,MAAMJ,EAAiB,CAChCK,IAAKC,SAAAA,EAAGC,GACN,IAAYvB,EAAGD,EAASuB,GAMxB,GAAa,WAATC,EAAmB,yBAAmBvB,CAAA,EAE1C,IAAMwB,EAAgBxB,EAAOuB,GAE7B,GAAIL,GAA0BO,EAASD,IAAkC,iBAATD,EAAmB,CACjF,IACMG,EADe/B,OAAOgC,OAAOL,ECtCR,aDuCWC,EAAUvB,EAAOY,GAAeW,IAAAA,EAGhEK,EAAcX,EAAuBS,GAC3C,GAAIE,EAAa,OAAOA,EAGxBJ,EAAcZ,GAAcc,EAE5B,IAAMG,EAAUf,EAAaC,EAAgBS,EAAeP,EAAwBC,GAMpF,OAFAD,EAAuBS,GAAkBG,EAG1CA,CAAA,CAGD,OAAOL,CACT,EAIAM,IAAKR,SAAAA,EAAGC,EAAMQ,GACZ,IAAkBC,EAAGrC,OAAOgC,OAAOL,EChEN,aDmE7B,GAAa,WAATC,GAAqBS,EAAc,CAAA,IAAAC,EACjBC,EAAG9C,EAAU2C,GAIjC,OAHAd,EAAyB,CAAE,EAC3BkB,QAAQL,IAAIR,ECtEe,YDsESY,GACpCnB,EAAQqB,OAAQC,EAAAA,CAAAA,GAAmB,UAAGH,EAAcD,KAErD,CAAA,CAGD,IAAYjC,EAAGD,EAASuB,GAIxB,GAHkBtB,EAAOuB,KAGPQ,EAAU,OAAW,EAMvC,GAAIb,GAA0BO,EAASM,IAA6B,iBAALR,EAAe,CAI5E,IAAoBG,EAAGM,EAAeT,EAAUvB,EAAOY,GAAW,IAAIW,EAClDe,EAAGlD,EAAU2C,GAEjCG,EAAetB,GAAcc,EAE7B,IAAaG,EAAGf,EAAaC,EAAgBmB,EAAgBjB,EAAwBC,GAIrFvB,OAAO4C,KAAKtB,GAAwBpB,QAAQ,SAAC2C,GACvCA,EAAeC,WAAWf,WAAwBT,EAAuBuB,EAC/E,GAEAvB,EAAuBS,GAAkBG,EAEzC7B,EAAOuB,GAAQW,CAChB,MACClC,EAAOuB,GAAQQ,EAMjB,OAFAhB,EAAQqB,KAAKzC,OAAO+C,OAAO,CAAE,EAAE3B,EAAQ4B,cAGzC,CAAA,EAKAC,IAAKtB,SAAAA,EAAGuB,GACN,OAAcV,QAACS,IAAI7C,EAASuB,GAAIuB,EAClC,EAEAC,QAASxB,SAAAA,GACP,OAAOa,QAAQW,QAAQ/C,EAASuB,GAClC,EAEAyB,kCAA0BzB,EAAGuB,GAC3B,eAAeE,yBAAyBhD,EAASuB,GAAIuB,EACvD,GAKJ,CE1HA,IAAMG,EAAiBnC,WAAW,sBAGKoC,EAAKlC,EAA8BmC,EAAmBC,GAAsB,IAAAC,EAC3GC,EAAqB1D,OAAO+C,OAAO,CAAE,EAAG3B,EAAQ4B,WAAsC,kBAAMK,IAAiB,EAAII,IAwCvH,OApC8B,SAC5BE,EACAC,GAEA,IAAMC,EAAYC,EAAqD,MAEjEC,EAAQC,EACZ,SAACC,GACC,IAAkBC,EAAG9C,EAAQ+C,UAAU,CACrC1B,KAAM,SAAC2B,GACL,GAAa,SAATZ,EAAiB,WAErB,IAAMa,EAAe9D,EAA+BoD,EAAqB,MAATS,OAAS,EAATA,EAA+B,YAE7DR,MAAAA,EAAAA,EAAmB9C,GAEX+C,EAAUS,QAASD,KAE3DR,EAAUS,QAAUD,EACpBJ,IAEJ,IAGF,OAAO,WAAQC,EAAaK,aAAc,CAC5C,EACA,WAAA,SAAcvB,UAAU,EACxB,WAAMU,OAAAA,CAAkB,GAM1B,OAFsB1D,OAAOwE,sBAAsBT,GAAOU,SAASpB,GAE5CU,EAAQR,CACjC,CAGF,CCWgBmB,SAAAA,EACdC,EACAC,GAAAA,IAAAA,OAAAA,IAAAA,IAAAA,EAA2B,CAAEC,uBAAuB,IAEpD,IAAkBC,EAA8B,mBAALH,EAAmBA,IAAyBA,EAG/DI,IAAAC,EAAA,CAAA,GAAyB,UAAGvF,EAAUqF,GAAaE,GAQ9D5D,EC/DC6D,SAA2BH,EAAiBI,QAAAA,IAAAA,IAAAA,GAAsB,GAChF,IAAIC,EAAmC,GAC9BC,EAAGN,EAyBZ,MAAO,CACL9B,SAxBe,WAASoC,OAAAA,CAAK,EAyB7B3C,KAvBW,SAACL,GACZgD,EAAQhD,EACR+C,EAAajF,QAAQ,SAACmF,GAAQA,EAAE5C,KAAKL,EAAU,EACjD,EAqBE+B,UAnBgB,SAACmB,GAKjB,OAHIJ,GAAqBI,EAAS7C,KAAK2C,GAEvCD,EAAaI,KAAKD,GACX,CACLf,YAAa,WACXY,EAAeA,EAAaK,OAAO,SAAAH,UAAMA,IAAKC,CAAQ,EACxD,EAEJ,EAUEG,eARqB,WACrBN,EAAe,EACjB,EAQF,CD8BkBF,CAAuBF,GAAoB,GACrDxB,EAAepC,EAAgBC,EAAS2D,EAHV,CAAA,EAGsDH,EAAQC,uBAE/Ea,EAAG,CACpB3B,MAAOR,EACPoC,MAAK,WACFpC,EAAqBqC,OAASd,CACjC,EAGAe,SAAUvC,EAA2BlC,EAASmC,EAAc,QAC5DuC,YAAaxC,EAA2BlC,EAASmC,EAAc,SAG/DwC,IAAyB,WAuBvB,MAtBA,GAAa7F,MAAAA,KAAAA,WAAAA,QAAQ,SAAC8F,GACpBA,EAAK,CACHC,SAAUC,SAAAA,GACR9E,EAAQ+C,UAAU,CAAE1B,KAAM,SAAC0D,GAAaD,EAAG3C,EAAc,GAC3D,EACA6C,gBAAiBC,SAAAA,EAAuBH,GACtC,IAAarC,EAAQtD,EAA+B8F,EAAOjG,EAAS2E,IACpE3D,EAAQ+C,UAAU,CAChB1B,KAAM,SAAC2B,GACL,IAAM/D,EAASD,EAASgE,GACNC,EAAG9D,EAA+B8F,EAAOhG,GAE1CS,EAAgB+C,EAAWQ,KAE1C6B,EAAG3C,GACHM,EAAYpE,EAAU4E,GAE1B,GAEJ,GAEJ,GAEFiC,IAAA,GAKF,OACFZ,CAAA"}
|
|
1
|
+
{"version":3,"file":"cervello.esm.js","sources":["../src/lib/utils/object.ts","../src/lib/utils/string.ts","../src/lib/helpers/proxify-store.ts","../src/lib/helpers/constants.ts","../src/lib/helpers/create-store-selector-hook.ts","../src/lib/store/index.ts","../src/lib/utils/subject.ts"],"sourcesContent":["import { INTERNAL_VALUE_PROP } from '../helpers/constants'\n\n\n/**\n * Clones all the provided object and nested properties and it also\n * iterates nested arrays to deepClone them\n *\n * @param obj - base object to be cloned\n * @returns new cloned object with new reference\n */\nexport function deepClone <T> (obj: T): T {\n if (!obj || typeof obj !== 'object')\n return obj\n\n let newObj = {} as any\n if (Array.isArray(obj)) {\n newObj = obj.map(item => deepClone(item))\n } else {\n Object.entries(obj).forEach(([key, value]) => {\n newObj[key] = deepClone(value)\n })\n }\n return newObj as T\n}\n\n\n/**\n * Guard to check is variable is an object\n * @param obj - variable to be checked\n * @returns boolean\n */\nexport const isObject = (obj: unknown): obj is Record<string | symbol, unknown> => (\n obj !== null\n && typeof obj === 'object'\n && !Array.isArray(obj)\n)\n\n\n\n\n/**\n * Returns the actual target value instead of root\n * object with internals props like $$value$$\n *\n * @param target - object from subscription or proxy\n * @returns actual value\n */\nexport const okTarget = (target: any): any => target[INTERNAL_VALUE_PROP] ?? target\n\n\n\n\n/**\n * Gets an object with just the properties requested\n * instead of the full object\n *\n * @param properties - object properties\n * @param obj - target to get the properties\n * @returns New object with just the requested properties\n */\nexport function getPartialObjectFromProperties<T> (properties: Array<keyof T>, obj: T): any {\n return properties.reduce<any>((acc, curr) => {\n acc[curr] = obj[curr]\n return acc\n }, {})\n}\n\n\n\nconst stringify = (obj: any): string => JSON.stringify(obj)\n/**\n * Compare 2 provided objects by stringing them\n * @param a - first object\n * @param b - second object\n * @returns boolean\n */\nexport const contentComparer = (a: any, b: any): boolean => stringify(a) === stringify(b)\n","\nexport function capitalize (str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1).toLocaleLowerCase()\n}\n\nexport function isCamelCase (str: string): boolean {\n const regex = /^[a-z]+([A-Z][a-z]*)*$/\n\n return regex.test(str)\n}\n","\n/* eslint-disable @typescript-eslint/no-dynamic-delete */\nimport { INTERNAL_VALUE_PROP } from './constants'\nimport { deepClone, isObject, okTarget } from '../utils'\n\nimport type { CacheableSubject } from '../utils/subject'\n\n\n// @ts-expect-error - Object.hasOwn is not defined in older Safari's browsers\n('hasOwn' in Object) || (Object.hasOwn = Object.call.bind(Object.hasOwnProperty))\n\nconst nestedPath = Symbol.for('nestedPath')\n\n\n\n\nexport function proxifyStore <T> (store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T\n\nexport function proxifyStore <T extends Record<string | symbol, any>> (\n store$$: CacheableSubject<T>,\n objectToProxify: T,\n proxiedNestedObjectMap: any,\n shouldProxifyNestedObj: boolean,\n): T {\n if (typeof objectToProxify !== 'object') throw new Error('Store must be an object')\n\n /** Proxify object and push value to subject */\n return new Proxy(objectToProxify, {\n get (t, prop) {\n const target = okTarget(t)\n\n /**\n * Return actual object when toJSON\n * is called instead of $$value$$\n */\n if (prop === 'toJSON') return () => target\n\n const propertyValue = target[prop]\n\n if (shouldProxifyNestedObj && isObject(propertyValue) && typeof prop !== 'symbol') {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n\n /** Check if cached to return instead of re-create new proxy */\n const cachedValue = proxiedNestedObjectMap[propNestedPath]\n\n if (cachedValue) return cachedValue\n\n /** Set the nestedPath symbol to keep track of proxied */\n propertyValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, propertyValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n\n /** Store in cache the proxiedObject */\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n return proxied\n }\n\n\n return propertyValue\n },\n\n\n\n set (t, prop, newValue) {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n\n /** Reset store */\n if (prop === '$value' && isRootTarget) {\n const newClonedValue = deepClone(newValue)\n\n proxiedNestedObjectMap = {}\n Reflect.set(t, INTERNAL_VALUE_PROP, newClonedValue)\n store$$.next({ [INTERNAL_VALUE_PROP]: newClonedValue } as unknown as T)\n\n return true\n }\n\n\n const target = okTarget(t)\n const propValue = target[prop]\n\n /** If new value is the same as the current one */\n if (propValue === newValue) return true\n\n /**\n * Override nested proxied object in\n * cache proxying the new value passed in\n */\n if (shouldProxifyNestedObj && isObject(newValue) && typeof prop !== 'symbol') {\n // TODO: check if --- proxiedNestedObjectMap[prop].$value = value --- is possible instead of recreating new nested\n // proxiedNestedObjectMap[prop].$value = value\n\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n const newClonedValue = deepClone(newValue)\n\n newClonedValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, newClonedValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n // Remove nested object props from cache\n // nestedPath: 'a.b.c': remove -> 'a.b.c.d' -> 'a.b.c.d.e'\n Object.keys(proxiedNestedObjectMap).forEach((cachedProxyKey: string) => {\n if (cachedProxyKey.startsWith(propNestedPath)) delete proxiedNestedObjectMap[cachedProxyKey]\n })\n\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n target[prop] = newClonedValue\n } else {\n target[prop] = newValue\n }\n\n /** Push new store object (new reference for immutability) */\n store$$.next(Object.assign({}, store$$.getValue()))\n\n return true\n },\n\n\n\n\n has (t, p) {\n return Reflect.has(okTarget(t), p)\n },\n\n ownKeys (t) {\n return Reflect.ownKeys(okTarget(t))\n },\n\n getOwnPropertyDescriptor (t, p) {\n return Reflect.getOwnPropertyDescriptor(okTarget(t), p)\n },\n\n\n\n }) as any\n}\n","export const INTERNAL_VALUE_PROP = '$$value$$'\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { useRef, useSyncExternalStore } from 'react'\n\nimport { INTERNAL_VALUE_PROP } from './constants'\nimport { contentComparer, deepClone, getPartialObjectFromProperties } from '../utils'\n\nimport type { ObjectFromAttributes, UseSelector, WithoutType } from '../../types/shared'\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst isServerSymbol = Symbol.for('_isServer')\n\n\nexport function createStoreSelectorHook<T> (store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T> {\n const serverInitialValue = Object.assign({}, (store$$.getValue() as any)[INTERNAL_VALUE_PROP], { [isServerSymbol]: true })\n\n\n // SELECTOR HOOK -------------------------------------------------\n const useStoreWithSelectors = <Attributes extends Array<keyof WithoutType<T, Function>>>(\n selectors: Attributes,\n isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean,\n ): T => {\n const prevSlice = useRef< ObjectFromAttributes<T, Attributes> | null >(\n type === 'full'\n ? null\n // To avoid the first render to be always different even\n // if store changed other than selected attributes\n : getPartialObjectFromProperties(selectors, (store$$.getValue() as any)?.[INTERNAL_VALUE_PROP]),\n )\n\n const store = useSyncExternalStore(\n (onStoreChange) => {\n const subscription = store$$.subscribe({\n next: (v) => {\n if (type === 'full') {\n onStoreChange()\n\n return\n }\n\n // Partial store section ------------------------------------------------------------------\n const currentSlice = getPartialObjectFromProperties(selectors, (v as any)?.[INTERNAL_VALUE_PROP])\n\n const compareWithPreviousStore = (isEqualFunction ?? contentComparer as any)\n\n const isEquals = compareWithPreviousStore(prevSlice.current, currentSlice)\n\n if (!isEquals) {\n /**\n * Update previous slice with deepClone to avoid storing direct store$$ value reference.\n * This solve reference/value problems when changing the store object\n * (proxy.set is changing the target object which is 'v' here)\n */\n prevSlice.current = deepClone(currentSlice)\n onStoreChange()\n }\n // ----------------------------------------------------------------------------------------\n },\n })\n\n return () => { subscription.unsubscribe() }\n },\n () => store$$.getValue(),\n () => serverInitialValue,\n )\n\n\n const isServerStore = Object.getOwnPropertySymbols(store).includes(isServerSymbol)\n\n return isServerStore ? store : proxiedStore\n }\n\n return useStoreWithSelectors\n}\n","\n/* eslint-disable @typescript-eslint/ban-types */\nimport { createStoreSelectorHook, proxifyStore } from '../helpers'\nimport { INTERNAL_VALUE_PROP } from '../helpers/constants'\nimport {\n contentComparer,\n deepClone,\n getPartialObjectFromProperties,\n createCacheableSubject,\n okTarget,\n capitalize,\n isCamelCase,\n} from '../utils'\n\nimport type { Maybe, UseSelector, WithoutType } from '../../types/shared'\n\n\n\ntype CervelloStoreUseParam<StoreType> = {\n onChange: (cb: (store: StoreType) => void) => void\n onPartialChange: <\n Attributes extends keyof WithoutType<StoreType, Function>,\n R extends Exclude<Attributes, '$value'>,\n ArrayOfAttributes extends Array <R>,\n > (selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void\n}\n\n/**\n * Use function which allows you to listen for changes in the store.\n */\nexport type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void\n\ndeclare type Prettify<T> = {} & {\n [K in keyof T]: T[K];\n}\n\n\n\n\ntype CervelloStore<StoreType, StoreName extends string | undefined = undefined> =\n StoreName extends string\n ? Prettify<{\n // /** Reactive store */\n [K in `${StoreName}Store`]: StoreType\n }\n & {\n /** Hook to react to all changes done to 'store' */\n [K in `use${Capitalize<StoreName>}Store`]: () => StoreType\n }\n & {\n /** Hook to react to all changes done to 'store' */\n [K in `use${Capitalize<StoreName>}Selector`]: UseSelector<StoreType>\n }\n & {\n /** Resets the value to the initial value passed in */\n [K in `reset${Capitalize<StoreName>}Store`]: () => void\n }\n & {\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>\n }>\n :\n {\n /** Reactive store */\n store: StoreType\n /** Hook to react to all changes done to 'store' */\n useStore: () => StoreType\n /** Hook to react to changes done to 'store' in all the attributes provided */\n useSelector: UseSelector<StoreType>\n /** Resets the value to the initial value passed in */\n reset: () => void\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>\n }\n\n\nexport type CervelloOptions<StoreName extends string | undefined = undefined> = {\n name?: StoreName\n reactiveNestedObjects?: boolean\n}\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Function which returns the default values for the store\n *\n * @returns \\{ store, useStore, useSelector \\} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value\n */\nexport function cervello <\nT,\nStoreName extends string | undefined = undefined,\n> (initialValue: () => T, options?: CervelloOptions<StoreName>): Prettify< CervelloStore<Prettify<T & { $value: Maybe<T> }>, StoreName> >\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Object with the default values for the store\n *\n * @returns - \\{ store, useStore, useSelector \\} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value\n */\nexport function cervello <\nT extends Record<string, unknown>,\nStoreName extends string | undefined = undefined,\n> (initialValue: T, options?: CervelloOptions<StoreName>): Prettify< CervelloStore<Prettify<T & { $value: Maybe<T> }>, StoreName> >\n\n\n\n/** @internal */\nexport function cervello <T, StoreName extends string | undefined = undefined> (\n initialValue: T | (() => T),\n options: CervelloOptions<StoreName> = { reactiveNestedObjects: true },\n): CervelloStore<T & { $value: Maybe<T> }, StoreName> {\n const { name: optionsName, reactiveNestedObjects = true } = options\n\n if (optionsName && !isCamelCase(optionsName))\n throw new Error(`[cervello]: options.name must be in camelCase and \"${optionsName}\" doesn't respect this rule.`)\n\n\n const defaultValue: T = typeof initialValue === 'function' ? (initialValue as any)() : initialValue\n\n\n const clonedInitialValue = { [INTERNAL_VALUE_PROP]: deepClone(defaultValue) } as any as T\n\n /**\n * Object map to keep reference of nested object proxies created\n * to avoid problems with reference equality (i.e.: useEffect dependencies array)\n */\n const proxiedNestedObjectMap: any = {}\n\n const store$$ = createCacheableSubject(clonedInitialValue, false) as any\n const proxiedStore = proxifyStore<T>(store$$, clonedInitialValue, proxiedNestedObjectMap, reactiveNestedObjects)\n\n const cervelloStore = {\n [optionsName\n ? `${optionsName}Store`\n : 'store'\n ]: proxiedStore,\n\n [optionsName\n ? `reset${capitalize(optionsName)}Store`\n : 'reset'\n ] () {\n (proxiedStore as any).$value = defaultValue\n },\n\n // TO BE SEPARATED IN cervello/react package --------------------------------\n [optionsName\n ? `use${capitalize(optionsName)}Store`\n : 'useStore'\n ]: createStoreSelectorHook<T>(store$$, proxiedStore, 'full'),\n\n [optionsName\n ? `use${capitalize(optionsName)}Selector`\n : 'useSelector'\n ]: createStoreSelectorHook<T>(store$$, proxiedStore, 'slice'),\n // --------------------------------------------------------------------------\n\n use (...functionList: any) {\n functionList.forEach((func: any) => {\n func({\n onChange (cb: any) {\n store$$.subscribe({ next: (_: any) => { cb(proxiedStore) } })\n },\n onPartialChange (attrs: Array<keyof T>, cb: any) {\n let prevSlice: any = getPartialObjectFromProperties(attrs, okTarget(clonedInitialValue))\n\n store$$.subscribe({\n next: (v: T) => {\n const target = okTarget(v)\n const currentSlice = getPartialObjectFromProperties(attrs, target)\n\n const isEquals = contentComparer(prevSlice, currentSlice)\n\n if (!isEquals) {\n cb(proxiedStore)\n prevSlice = deepClone(currentSlice)\n }\n },\n })\n },\n })\n })\n\n return this\n },\n } as any\n\n\n\n return cervelloStore\n}\n","\nexport type Noop = () => void\n\n\nexport type Observer<T> = {\n next: (value: T) => void\n}\n\nexport type Subscription = {\n unsubscribe: Noop\n}\n\nexport type CacheableSubject<T> = {\n getValue: () => T\n subscribe: (value: Observer<T>) => Subscription\n unSubscribeAll: Noop\n} & Observer<T>\n\n\n\nexport function createCacheableSubject<T> (defaultValue: T, getValueOnSubscribe = true): CacheableSubject<T> {\n let observerList: Array<Observer<T>> = []\n let value = defaultValue\n\n const getValue = (): T => value\n\n const next = (newValue: T): void => {\n value = newValue\n observerList.forEach((o) => { o.next(newValue) })\n }\n\n const subscribe = (observer: Observer<T>): Subscription => {\n // Push cached value on subscribe\n if (getValueOnSubscribe) observer.next(value)\n // Store the observer to be notified\n observerList.push(observer)\n\n return {\n unsubscribe: () => {\n observerList = observerList.filter(o => o !== observer)\n },\n }\n }\n\n const unSubscribeAll = (): void => {\n observerList = []\n }\n\n return {\n getValue,\n next,\n subscribe,\n unSubscribeAll,\n }\n}\n"],"names":["deepClone","obj","newObj","Array","isArray","map","item","Object","entries","forEach","_ref","okTarget","target","_target$INTERNAL_VALU","getPartialObjectFromProperties","properties","reduce","acc","curr","stringify","JSON","contentComparer","a","b","str","charAt","toUpperCase","slice","toLocaleLowerCase","hasOwn","call","bind","hasOwnProperty","nestedPath","Symbol","proxifyStore","store$$","objectToProxify","proxiedNestedObjectMap","shouldProxifyNestedObj","Error","Proxy","get","t","prop","propertyValue","isObject","propNestedPath","cachedValue","proxied","set","newValue","isRootTarget","_store$$$next","newClonedValue","Reflect","next","keys","cachedProxyKey","startsWith","assign","getValue","has","p","ownKeys","getOwnPropertyDescriptor","isServerSymbol","createStoreSelectorHook","proxiedStore","type","_Object$assign","serverInitialValue","selectors","isEqualFunction","_store$$$getValue","prevSlice","useRef","store","useSyncExternalStore","onStoreChange","subscription","subscribe","v","currentSlice","current","unsubscribe","getOwnPropertySymbols","includes","initialValue","options","reactiveNestedObjects","optionsName","name","_options$reactiveNest","test","defaultValue","clonedInitialValue","_clonedInitialValue","createCacheableSubject","getValueOnSubscribe","observerList","value","o","observer","push","filter","unSubscribeAll","cervelloStore","capitalize","$value","_cervelloStore","use","func","onChange","cb","_","onPartialChange","attrs","this"],"mappings":"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"}
|
package/dist/cervello.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e||self).react={},e.react)}(this,function(e,t){var n="$$value$$";function r(e){if(!e||"object"!=typeof e)return e;var t={};return Array.isArray(e)?t=e.map(function(e){return r(e)}):Object.entries(e).forEach(function(e){t[e[0]]=r(e[1])}),t}var u=function(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)},o=function(e){var t;return null!=(t=e.$$value$$)?t:e};function c(e,t){return e.reduce(function(e,n){return e[n]=t[n],e},{})}var i=function(e){return JSON.stringify(e)},a=function(e,t){return i(e)===i(t)};function f(e){return e.charAt(0).toUpperCase()+e.slice(1).toLocaleLowerCase()}"hasOwn"in Object||(Object.hasOwn=Object.call.bind(Object.hasOwnProperty));var s=Symbol.for("nestedPath");function l(e,t,c,i){if("object"!=typeof t)throw new Error("Store must be an object");return new Proxy(t,{get:function(t,r){var a=o(t);if("toJSON"===r)return function(){return a};var f=a[r];if(i&&u(f)&&"symbol"!=typeof r){var v=Object.hasOwn(t,n)?r:a[s]+"."+r,b=c[v];if(b)return b;f[s]=v;var $=l(e,f,c,i);return c[v]=$,$}return f},set:function(t,a,f){var v=Object.hasOwn(t,n);if("$value"===a&&v){var b,$=r(f);return c={},Reflect.set(t,n,$),e.next(((b={}).$$value$$=$,b)),!0}var y=o(t);if(y[a]===f)return!0;if(i&&u(f)&&"symbol"!=typeof a){var p=v?a:y[s]+"."+a,h=r(f);h[s]=p;var d=l(e,h,c,i);Object.keys(c).forEach(function(e){e.startsWith(p)&&delete c[e]}),c[p]=d,y[a]=h}else y[a]=f;return e.next(Object.assign({},e.getValue())),!0},has:function(e,t){return Reflect.has(o(e),t)},ownKeys:function(e){return Reflect.ownKeys(o(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(o(e),t)}})}var v=Symbol.for("_isServer");function b(e,n,u){var o,i=Object.assign({},e.getValue().$$value$$,((o={})[v]=!0,o));return function(o,f){var s,l=t.useRef("full"===u?null:c(o,null==(s=e.getValue())?void 0:s.$$value$$)),b=t.useSyncExternalStore(function(t){var n=e.subscribe({next:function(e){if("full"!==u){var n=c(o,null==e?void 0:e.$$value$$);(null!=f?f:a)(l.current,n)||(l.current=r(n),t())}else t()}});return function(){n.unsubscribe()}},function(){return e.getValue()},function(){return i});return Object.getOwnPropertySymbols(b).includes(v)?b:n}}e.cervello=function(e,t){var n,u;void 0===t&&(t={reactiveNestedObjects:!0});var i=t.name,s=t.reactiveNestedObjects,v=void 0===s||s;if(i&&!/^[a-z]+([A-Z][a-z]*)*$/.test(i))throw new Error('[cervello]: options.name must be in camelCase and "'+i+"\" doesn't respect this rule.");var $="function"==typeof e?e():e,y=((n={}).$$value$$=r($),n),p=function(e,t){void 0===t&&(t=!0);var n=[],r=e;return{getValue:function(){return r},next:function(e){r=e,n.forEach(function(t){t.next(e)})},subscribe:function(e){return t&&e.next(r),n.push(e),{unsubscribe:function(){n=n.filter(function(t){return t!==e})}}},unSubscribeAll:function(){n=[]}}}(y,!1),h=l(p,y,{},v),d=((u={})[i?i+"Store":"store"]=h,u[i?"reset"+f(i)+"Store":"reset"]=function(){h.$value=$},u[i?"use"+f(i)+"Store":"useStore"]=b(p,h,"full"),u[i?"use"+f(i)+"Selector":"useSelector"]=b(p,h,"slice"),u.use=function(){return[].slice.call(arguments).forEach(function(e){e({onChange:function(e){p.subscribe({next:function(t){e(h)}})},onPartialChange:function(e,t){var n=c(e,o(y));p.subscribe({next:function(u){var i=o(u),f=c(e,i);a(n,f)||(t(h),n=r(f))}})}})}),this},u);return d}});
|
|
2
2
|
//# sourceMappingURL=cervello.umd.js.map
|
package/dist/cervello.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cervello.umd.js","sources":["../src/lib/helpers/constants.ts","../src/lib/utils/object.ts","../src/lib/helpers/proxify-store.ts","../src/lib/helpers/create-store-selector-hook.ts","../src/lib/store/index.ts","../src/lib/utils/subject.ts"],"sourcesContent":["export const INTERNAL_VALUE_PROP = '$$value$$'\n","import { INTERNAL_VALUE_PROP } from '../helpers/constants'\n\n\n/**\n * Clones all the provided object and nested properties and it also\n * iterates nested arrays to deepClone them\n *\n * @param obj - base object to be cloned\n * @returns new cloned object with new reference\n */\nexport function deepClone <T> (obj: T): T {\n if (!obj || typeof obj !== 'object')\n return obj\n\n let newObj = {} as any\n if (Array.isArray(obj)) {\n newObj = obj.map(item => deepClone(item))\n } else {\n Object.entries(obj).forEach(([key, value]) => {\n newObj[key] = deepClone(value)\n })\n }\n return newObj as T\n}\n\n\n/**\n * Guard to check is variable is an object\n * @param obj - variable to be checked\n * @returns boolean\n */\nexport const isObject = (obj: unknown): obj is Record<string | symbol, unknown> => (\n obj !== null\n && typeof obj === 'object'\n && !Array.isArray(obj)\n)\n\n\n\n\n/**\n * Returns the actual target value instead of root\n * object with internals props like $$value$$\n *\n * @param target - object from subscription or proxy\n * @returns actual value\n */\nexport const okTarget = (target: any): any => target[INTERNAL_VALUE_PROP] ?? target\n\n\n\n\n/**\n * Gets an object with just the properties requested\n * instead of the full object\n *\n * @param properties - object properties\n * @param obj - target to get the properties\n * @returns New object with just the requested properties\n */\nexport function getPartialObjectFromProperties<T> (properties: Array<keyof T>, obj: T): any {\n return properties.reduce<any>((acc, curr) => {\n acc[curr] = obj[curr]\n return acc\n }, {})\n}\n\n\n\nconst stringify = (obj: any): string => JSON.stringify(obj)\n/**\n * Compare 2 provided objects by stringing them\n * @param a - first object\n * @param b - second object\n * @returns boolean\n */\nexport const contentComparer = (a: any, b: any): boolean => stringify(a) === stringify(b)\n","/* eslint-disable @typescript-eslint/restrict-template-expressions */\n/* eslint-disable @typescript-eslint/no-dynamic-delete */\nimport { deepClone, isObject, okTarget } from '../utils'\nimport { INTERNAL_VALUE_PROP } from './constants'\n\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst nestedPath = Symbol.for('nestedPath')\n\n\n\n\nexport function proxifyStore <T> (store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T\n\nexport function proxifyStore <T extends Record<string | symbol, any>> (\n store$$: CacheableSubject<T>,\n objectToProxify: T,\n proxiedNestedObjectMap: any,\n shouldProxifyNestedObj: boolean,\n): T {\n if (typeof objectToProxify !== 'object') throw new Error('Store must be an object')\n\n /** Proxify object and push value to subject */\n return new Proxy(objectToProxify, {\n get (t, prop) {\n const target = okTarget(t)\n\n /**\n * Return actual object when toJSON\n * is called instead of $$value$$\n */\n if (prop === 'toJSON') return () => target\n\n const propertyValue = target[prop]\n\n if (shouldProxifyNestedObj && isObject(propertyValue) && typeof prop !== 'symbol') {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n\n /** Check if cached to return instead of re-create new proxy */\n const cachedValue = proxiedNestedObjectMap[propNestedPath]\n if (cachedValue) return cachedValue\n\n /** Set the nestedPath symbol to keep track of proxied */\n propertyValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, propertyValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n\n /** Store in cache the proxiedObject */\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n return proxied\n }\n\n\n return propertyValue\n },\n\n\n\n set (t, prop, newValue) {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n\n /** Reset store */\n if (prop === '$value' && isRootTarget) {\n const newClonedValue = deepClone(newValue)\n proxiedNestedObjectMap = {}\n Reflect.set(t, INTERNAL_VALUE_PROP, newClonedValue)\n store$$.next({ [INTERNAL_VALUE_PROP]: newClonedValue } as unknown as T)\n return true\n }\n\n\n const target = okTarget(t)\n const propValue = target[prop]\n\n /** If new value is the same as the current one */\n if (propValue === newValue) return true\n\n /**\n * Override nested proxied object in\n * cache proxying the new value passed in\n */\n if (shouldProxifyNestedObj && isObject(newValue) && typeof prop !== 'symbol') {\n // TODO: check if --- proxiedNestedObjectMap[prop].$value = value --- is possible instead of recreating new nested\n // proxiedNestedObjectMap[prop].$value = value\n\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n const newClonedValue = deepClone(newValue)\n\n newClonedValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, newClonedValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n // Remove nested object props from cache\n // nestedPath: 'a.b.c': remove -> 'a.b.c.d' -> 'a.b.c.d.e'\n Object.keys(proxiedNestedObjectMap).forEach((cachedProxyKey: string) => {\n if (cachedProxyKey.startsWith(propNestedPath)) delete proxiedNestedObjectMap[cachedProxyKey]\n })\n\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n target[prop] = newClonedValue\n } else {\n target[prop] = newValue\n }\n\n /** Push new store object (new reference for immutability) */\n store$$.next(Object.assign({}, store$$.getValue()))\n\n return true\n },\n\n\n\n\n has (t, p) {\n return Reflect.has(okTarget(t), p)\n },\n\n ownKeys (t) {\n return Reflect.ownKeys(okTarget(t))\n },\n\n getOwnPropertyDescriptor (t, p) {\n return Reflect.getOwnPropertyDescriptor(okTarget(t), p)\n },\n\n\n\n }) as any\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { useRef } from 'react'\nimport { useSyncExternalStore } from 'use-sync-external-store/shim/index.js'\n\nimport { contentComparer, getPartialObjectFromProperties } from '../utils'\nimport { INTERNAL_VALUE_PROP } from './constants'\n\nimport type { ObjectFromAttributes, UseSelector, WithoutType } from '../../types/shared'\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst isServerSymbol = Symbol.for('_isServer')\n\n\nexport function createStoreSelectorHook<T> (store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T> {\n const serverInitialValue = Object.assign({}, (store$$.getValue() as any)[INTERNAL_VALUE_PROP], { [isServerSymbol]: true })\n\n\n // SELECTOR HOOK -------------------------------------------------\n const useStoreWithSelectors = <Attributes extends Array<keyof WithoutType<T, Function>>>(\n selectors: Attributes,\n isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean,\n ): T => {\n const prevSlice = useRef< ObjectFromAttributes<T, Attributes> | null >(null)\n\n const store = useSyncExternalStore(\n (onStoreChange) => {\n const subscription = store$$.subscribe({\n next: (v) => {\n if (type === 'full') return onStoreChange()\n\n const currentSlice = getPartialObjectFromProperties(selectors, (v as any)?.[INTERNAL_VALUE_PROP])\n\n const compareWithPreviousStore = (isEqualFunction ?? contentComparer as any)\n\n const isEquals = compareWithPreviousStore(prevSlice.current, currentSlice)\n if (!isEquals) {\n prevSlice.current = currentSlice\n onStoreChange()\n }\n },\n })\n\n return () => { subscription.unsubscribe() }\n },\n () => store$$.getValue(),\n () => serverInitialValue,\n )\n\n\n const isServerStore = Object.getOwnPropertySymbols(store).includes(isServerSymbol)\n\n return isServerStore ? store : proxiedStore\n }\n\n return useStoreWithSelectors\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { createStoreSelectorHook, proxifyStore } from '../helpers'\nimport { INTERNAL_VALUE_PROP } from '../helpers/constants'\nimport {\n contentComparer,\n deepClone,\n getPartialObjectFromProperties,\n createCacheableSubject,\n okTarget,\n} from '../utils'\n\nimport type { Maybe, UseSelector, WithoutType } from '../../types/shared'\n\n\n\ninterface CervelloStoreUseParam<StoreType> {\n onChange: (cb: (store: StoreType) => void) => void\n onPartialChange: <\n Attributes extends keyof WithoutType<StoreType, Function>,\n R extends Exclude<Attributes, '$value'>,\n ArrayOfAttributes extends Array <R>,\n > (selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void\n}\n\n/**\n * Use function which allows you to listen for changes in the store.\n */\nexport type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void\n\n\ninterface CervelloStore<StoreType> {\n /** Reactive store */\n store: StoreType\n /** Hook to react to all changes done to 'store' */\n useStore: () => StoreType\n /** Hook to react to changes done to 'store' in all the attributes provided */\n useSelector: UseSelector<StoreType>\n /** Resets the value to the initial value passed in */\n reset: () => void\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType>\n}\n\n\nexport interface CervelloOptions {\n reactiveNestedObjects: boolean\n}\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Object with the default values for the store\n *\n * @returns - \\{ store, useStore, useSelector \\}\n */\nexport function cervello <T> (initialValue: T, options?: CervelloOptions): CervelloStore<T & { $value: Maybe<T> }>\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Function which returns the default values for the store\n *\n * @returns \\{ store, useStore, useSelector \\}\n */\nexport function cervello <T> (initialValue: () => T, options?: CervelloOptions): CervelloStore<T & { $value: Maybe<T> }>\n\n\n/** @internal */\nexport function cervello <T> (\n initialValue: T | (() => T),\n options: CervelloOptions = { reactiveNestedObjects: true },\n): CervelloStore<T & { $value: Maybe<T> }> {\n const defaultValue: T = typeof initialValue === 'function' ? (initialValue as any)() : initialValue\n\n\n const clonedInitialValue = { [INTERNAL_VALUE_PROP]: deepClone(defaultValue) } as any as T\n\n /**\n * Object map to keep reference of nested object proxies created\n * to avoid problems with reference equality (i.e.: useEffect dependencies array)\n */\n const proxiedNestedObjectMap: any = {}\n\n const store$$ = createCacheableSubject(clonedInitialValue, false) as any\n const proxiedStore = proxifyStore<T>(store$$, clonedInitialValue, proxiedNestedObjectMap, options.reactiveNestedObjects)\n\n const cervelloStore = {\n store: proxiedStore,\n reset () {\n (proxiedStore as any).$value = defaultValue\n },\n\n // TO BE SEPARATED IN cervello/react package --------------------------------\n useStore: createStoreSelectorHook<T>(store$$, proxiedStore, 'full'),\n useSelector: createStoreSelectorHook<T>(store$$, proxiedStore, 'slice'),\n // --------------------------------------------------------------------------\n\n use (...functionList: any) {\n functionList.forEach((func: any) => {\n func({\n onChange (cb: any) {\n store$$.subscribe({ next: (_: any) => { cb(proxiedStore) } })\n },\n onPartialChange (attrs: Array<keyof T>, cb: any) {\n let prevSlice: any = getPartialObjectFromProperties(attrs, okTarget(clonedInitialValue))\n store$$.subscribe({\n next: (v: T) => {\n const target = okTarget(v)\n const currentSlice = getPartialObjectFromProperties(attrs, target)\n\n const isEquals = contentComparer(prevSlice, currentSlice)\n if (!isEquals) {\n cb(proxiedStore)\n prevSlice = deepClone(currentSlice)\n }\n },\n })\n },\n })\n })\n return this\n },\n } as any\n\n\n\n return cervelloStore as CervelloStore<T & { $value: Maybe<T> }>\n}\n","\nexport type Noop = () => void\n\n\nexport interface Observer<T> {\n next: (value: T) => void\n}\n\nexport interface Subscription {\n unsubscribe: Noop\n}\n\nexport type CacheableSubject<T> = {\n getValue: () => T\n subscribe: (value: Observer<T>) => Subscription\n unSubscribeAll: Noop\n} & Observer<T>\n\n\n\nexport function createCacheableSubject<T> (defaultValue: T, getValueOnSubscribe = true): CacheableSubject<T> {\n let observerList: Array<Observer<T>> = []\n let value = defaultValue\n\n const getValue = (): T => value\n\n const next = (newValue: T): void => {\n value = newValue\n observerList.forEach((o) => { o.next(newValue) })\n }\n\n const subscribe = (observer: Observer<T>): Subscription => {\n // Push cached value on subscribe\n if (getValueOnSubscribe) observer.next(value)\n // Store the observer to be notified\n observerList.push(observer)\n return {\n unsubscribe: () => {\n observerList = observerList.filter(o => o !== observer)\n },\n }\n }\n\n const unSubscribeAll = (): void => {\n observerList = []\n }\n\n return {\n getValue,\n next,\n subscribe,\n unSubscribeAll,\n }\n}\n"],"names":["INTERNAL_VALUE_PROP","deepClone","obj","newObj","Array","isArray","map","item","Object","entries","forEach","_ref","okTarget","target","_target$INTERNAL_VALU","getPartialObjectFromProperties","properties","reduce","acc","curr","stringify","JSON","contentComparer","a","b","nestedPath","Symbol","proxifyStore","store$$","objectToProxify","proxiedNestedObjectMap","shouldProxifyNestedObj","Error","Proxy","get","t","prop","propertyValue","isObject","propNestedPath","hasOwn","cachedValue","proxied","set","newValue","isRootTarget","_store$$$next","newClonedValue","Reflect","next","_newClonedValue","keys","cachedProxyKey","startsWith","assign","getValue","has","p","ownKeys","getOwnPropertyDescriptor","isServerSymbol","createStoreSelectorHook","proxiedStore","type","_Object$assign","serverInitialValue","selectors","isEqualFunction","prevSlice","useRef","store","useSyncExternalStore","onStoreChange","subscription","subscribe","v","currentSlice","current","unsubscribe","getOwnPropertySymbols","includes","cervello","initialValue","options","reactiveNestedObjects","defaultValue","clonedInitialValue","_clonedInitialValue","createCacheableSubject","getValueOnSubscribe","observerList","value","o","observer","push","filter","unSubscribeAll","cervelloStore","reset","$value","useStore","useSelector","use","func","onChange","cb","_","onPartialChange","attrs","this"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cervello.umd.js","sources":["../src/lib/helpers/constants.ts","../src/lib/utils/object.ts","../src/lib/utils/string.ts","../src/lib/helpers/proxify-store.ts","../src/lib/helpers/create-store-selector-hook.ts","../src/lib/store/index.ts","../src/lib/utils/subject.ts"],"sourcesContent":["export const INTERNAL_VALUE_PROP = '$$value$$'\n","import { INTERNAL_VALUE_PROP } from '../helpers/constants'\n\n\n/**\n * Clones all the provided object and nested properties and it also\n * iterates nested arrays to deepClone them\n *\n * @param obj - base object to be cloned\n * @returns new cloned object with new reference\n */\nexport function deepClone <T> (obj: T): T {\n if (!obj || typeof obj !== 'object')\n return obj\n\n let newObj = {} as any\n if (Array.isArray(obj)) {\n newObj = obj.map(item => deepClone(item))\n } else {\n Object.entries(obj).forEach(([key, value]) => {\n newObj[key] = deepClone(value)\n })\n }\n return newObj as T\n}\n\n\n/**\n * Guard to check is variable is an object\n * @param obj - variable to be checked\n * @returns boolean\n */\nexport const isObject = (obj: unknown): obj is Record<string | symbol, unknown> => (\n obj !== null\n && typeof obj === 'object'\n && !Array.isArray(obj)\n)\n\n\n\n\n/**\n * Returns the actual target value instead of root\n * object with internals props like $$value$$\n *\n * @param target - object from subscription or proxy\n * @returns actual value\n */\nexport const okTarget = (target: any): any => target[INTERNAL_VALUE_PROP] ?? target\n\n\n\n\n/**\n * Gets an object with just the properties requested\n * instead of the full object\n *\n * @param properties - object properties\n * @param obj - target to get the properties\n * @returns New object with just the requested properties\n */\nexport function getPartialObjectFromProperties<T> (properties: Array<keyof T>, obj: T): any {\n return properties.reduce<any>((acc, curr) => {\n acc[curr] = obj[curr]\n return acc\n }, {})\n}\n\n\n\nconst stringify = (obj: any): string => JSON.stringify(obj)\n/**\n * Compare 2 provided objects by stringing them\n * @param a - first object\n * @param b - second object\n * @returns boolean\n */\nexport const contentComparer = (a: any, b: any): boolean => stringify(a) === stringify(b)\n","\nexport function capitalize (str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1).toLocaleLowerCase()\n}\n\nexport function isCamelCase (str: string): boolean {\n const regex = /^[a-z]+([A-Z][a-z]*)*$/\n\n return regex.test(str)\n}\n","\n/* eslint-disable @typescript-eslint/no-dynamic-delete */\nimport { INTERNAL_VALUE_PROP } from './constants'\nimport { deepClone, isObject, okTarget } from '../utils'\n\nimport type { CacheableSubject } from '../utils/subject'\n\n\n// @ts-expect-error - Object.hasOwn is not defined in older Safari's browsers\n('hasOwn' in Object) || (Object.hasOwn = Object.call.bind(Object.hasOwnProperty))\n\nconst nestedPath = Symbol.for('nestedPath')\n\n\n\n\nexport function proxifyStore <T> (store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T\n\nexport function proxifyStore <T extends Record<string | symbol, any>> (\n store$$: CacheableSubject<T>,\n objectToProxify: T,\n proxiedNestedObjectMap: any,\n shouldProxifyNestedObj: boolean,\n): T {\n if (typeof objectToProxify !== 'object') throw new Error('Store must be an object')\n\n /** Proxify object and push value to subject */\n return new Proxy(objectToProxify, {\n get (t, prop) {\n const target = okTarget(t)\n\n /**\n * Return actual object when toJSON\n * is called instead of $$value$$\n */\n if (prop === 'toJSON') return () => target\n\n const propertyValue = target[prop]\n\n if (shouldProxifyNestedObj && isObject(propertyValue) && typeof prop !== 'symbol') {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n\n /** Check if cached to return instead of re-create new proxy */\n const cachedValue = proxiedNestedObjectMap[propNestedPath]\n\n if (cachedValue) return cachedValue\n\n /** Set the nestedPath symbol to keep track of proxied */\n propertyValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, propertyValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n\n /** Store in cache the proxiedObject */\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n return proxied\n }\n\n\n return propertyValue\n },\n\n\n\n set (t, prop, newValue) {\n const isRootTarget = Object.hasOwn(t, INTERNAL_VALUE_PROP)\n\n /** Reset store */\n if (prop === '$value' && isRootTarget) {\n const newClonedValue = deepClone(newValue)\n\n proxiedNestedObjectMap = {}\n Reflect.set(t, INTERNAL_VALUE_PROP, newClonedValue)\n store$$.next({ [INTERNAL_VALUE_PROP]: newClonedValue } as unknown as T)\n\n return true\n }\n\n\n const target = okTarget(t)\n const propValue = target[prop]\n\n /** If new value is the same as the current one */\n if (propValue === newValue) return true\n\n /**\n * Override nested proxied object in\n * cache proxying the new value passed in\n */\n if (shouldProxifyNestedObj && isObject(newValue) && typeof prop !== 'symbol') {\n // TODO: check if --- proxiedNestedObjectMap[prop].$value = value --- is possible instead of recreating new nested\n // proxiedNestedObjectMap[prop].$value = value\n\n const propNestedPath = isRootTarget ? prop : `${target[nestedPath]}.${prop}`\n const newClonedValue = deepClone(newValue)\n\n newClonedValue[nestedPath] = propNestedPath\n\n const proxied = proxifyStore(store$$ as any, newClonedValue, proxiedNestedObjectMap, shouldProxifyNestedObj)\n\n // Remove nested object props from cache\n // nestedPath: 'a.b.c': remove -> 'a.b.c.d' -> 'a.b.c.d.e'\n Object.keys(proxiedNestedObjectMap).forEach((cachedProxyKey: string) => {\n if (cachedProxyKey.startsWith(propNestedPath)) delete proxiedNestedObjectMap[cachedProxyKey]\n })\n\n proxiedNestedObjectMap[propNestedPath] = proxied\n\n target[prop] = newClonedValue\n } else {\n target[prop] = newValue\n }\n\n /** Push new store object (new reference for immutability) */\n store$$.next(Object.assign({}, store$$.getValue()))\n\n return true\n },\n\n\n\n\n has (t, p) {\n return Reflect.has(okTarget(t), p)\n },\n\n ownKeys (t) {\n return Reflect.ownKeys(okTarget(t))\n },\n\n getOwnPropertyDescriptor (t, p) {\n return Reflect.getOwnPropertyDescriptor(okTarget(t), p)\n },\n\n\n\n }) as any\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { useRef, useSyncExternalStore } from 'react'\n\nimport { INTERNAL_VALUE_PROP } from './constants'\nimport { contentComparer, deepClone, getPartialObjectFromProperties } from '../utils'\n\nimport type { ObjectFromAttributes, UseSelector, WithoutType } from '../../types/shared'\nimport type { CacheableSubject } from '../utils/subject'\n\n\n\nconst isServerSymbol = Symbol.for('_isServer')\n\n\nexport function createStoreSelectorHook<T> (store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T> {\n const serverInitialValue = Object.assign({}, (store$$.getValue() as any)[INTERNAL_VALUE_PROP], { [isServerSymbol]: true })\n\n\n // SELECTOR HOOK -------------------------------------------------\n const useStoreWithSelectors = <Attributes extends Array<keyof WithoutType<T, Function>>>(\n selectors: Attributes,\n isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean,\n ): T => {\n const prevSlice = useRef< ObjectFromAttributes<T, Attributes> | null >(\n type === 'full'\n ? null\n // To avoid the first render to be always different even\n // if store changed other than selected attributes\n : getPartialObjectFromProperties(selectors, (store$$.getValue() as any)?.[INTERNAL_VALUE_PROP]),\n )\n\n const store = useSyncExternalStore(\n (onStoreChange) => {\n const subscription = store$$.subscribe({\n next: (v) => {\n if (type === 'full') {\n onStoreChange()\n\n return\n }\n\n // Partial store section ------------------------------------------------------------------\n const currentSlice = getPartialObjectFromProperties(selectors, (v as any)?.[INTERNAL_VALUE_PROP])\n\n const compareWithPreviousStore = (isEqualFunction ?? contentComparer as any)\n\n const isEquals = compareWithPreviousStore(prevSlice.current, currentSlice)\n\n if (!isEquals) {\n /**\n * Update previous slice with deepClone to avoid storing direct store$$ value reference.\n * This solve reference/value problems when changing the store object\n * (proxy.set is changing the target object which is 'v' here)\n */\n prevSlice.current = deepClone(currentSlice)\n onStoreChange()\n }\n // ----------------------------------------------------------------------------------------\n },\n })\n\n return () => { subscription.unsubscribe() }\n },\n () => store$$.getValue(),\n () => serverInitialValue,\n )\n\n\n const isServerStore = Object.getOwnPropertySymbols(store).includes(isServerSymbol)\n\n return isServerStore ? store : proxiedStore\n }\n\n return useStoreWithSelectors\n}\n","\n/* eslint-disable @typescript-eslint/ban-types */\nimport { createStoreSelectorHook, proxifyStore } from '../helpers'\nimport { INTERNAL_VALUE_PROP } from '../helpers/constants'\nimport {\n contentComparer,\n deepClone,\n getPartialObjectFromProperties,\n createCacheableSubject,\n okTarget,\n capitalize,\n isCamelCase,\n} from '../utils'\n\nimport type { Maybe, UseSelector, WithoutType } from '../../types/shared'\n\n\n\ntype CervelloStoreUseParam<StoreType> = {\n onChange: (cb: (store: StoreType) => void) => void\n onPartialChange: <\n Attributes extends keyof WithoutType<StoreType, Function>,\n R extends Exclude<Attributes, '$value'>,\n ArrayOfAttributes extends Array <R>,\n > (selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void\n}\n\n/**\n * Use function which allows you to listen for changes in the store.\n */\nexport type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void\n\ndeclare type Prettify<T> = {} & {\n [K in keyof T]: T[K];\n}\n\n\n\n\ntype CervelloStore<StoreType, StoreName extends string | undefined = undefined> =\n StoreName extends string\n ? Prettify<{\n // /** Reactive store */\n [K in `${StoreName}Store`]: StoreType\n }\n & {\n /** Hook to react to all changes done to 'store' */\n [K in `use${Capitalize<StoreName>}Store`]: () => StoreType\n }\n & {\n /** Hook to react to all changes done to 'store' */\n [K in `use${Capitalize<StoreName>}Selector`]: UseSelector<StoreType>\n }\n & {\n /** Resets the value to the initial value passed in */\n [K in `reset${Capitalize<StoreName>}Store`]: () => void\n }\n & {\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>\n }>\n :\n {\n /** Reactive store */\n store: StoreType\n /** Hook to react to all changes done to 'store' */\n useStore: () => StoreType\n /** Hook to react to changes done to 'store' in all the attributes provided */\n useSelector: UseSelector<StoreType>\n /** Resets the value to the initial value passed in */\n reset: () => void\n /** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */\n use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>\n }\n\n\nexport type CervelloOptions<StoreName extends string | undefined = undefined> = {\n name?: StoreName\n reactiveNestedObjects?: boolean\n}\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Function which returns the default values for the store\n *\n * @returns \\{ store, useStore, useSelector \\} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value\n */\nexport function cervello <\nT,\nStoreName extends string | undefined = undefined,\n> (initialValue: () => T, options?: CervelloOptions<StoreName>): Prettify< CervelloStore<Prettify<T & { $value: Maybe<T> }>, StoreName> >\n\n\n/**\n * Creates a store that is reactive and can be used inside and outside of React components.\n * @param initialValue - Object with the default values for the store\n *\n * @returns - \\{ store, useStore, useSelector \\} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value\n */\nexport function cervello <\nT extends Record<string, unknown>,\nStoreName extends string | undefined = undefined,\n> (initialValue: T, options?: CervelloOptions<StoreName>): Prettify< CervelloStore<Prettify<T & { $value: Maybe<T> }>, StoreName> >\n\n\n\n/** @internal */\nexport function cervello <T, StoreName extends string | undefined = undefined> (\n initialValue: T | (() => T),\n options: CervelloOptions<StoreName> = { reactiveNestedObjects: true },\n): CervelloStore<T & { $value: Maybe<T> }, StoreName> {\n const { name: optionsName, reactiveNestedObjects = true } = options\n\n if (optionsName && !isCamelCase(optionsName))\n throw new Error(`[cervello]: options.name must be in camelCase and \"${optionsName}\" doesn't respect this rule.`)\n\n\n const defaultValue: T = typeof initialValue === 'function' ? (initialValue as any)() : initialValue\n\n\n const clonedInitialValue = { [INTERNAL_VALUE_PROP]: deepClone(defaultValue) } as any as T\n\n /**\n * Object map to keep reference of nested object proxies created\n * to avoid problems with reference equality (i.e.: useEffect dependencies array)\n */\n const proxiedNestedObjectMap: any = {}\n\n const store$$ = createCacheableSubject(clonedInitialValue, false) as any\n const proxiedStore = proxifyStore<T>(store$$, clonedInitialValue, proxiedNestedObjectMap, reactiveNestedObjects)\n\n const cervelloStore = {\n [optionsName\n ? `${optionsName}Store`\n : 'store'\n ]: proxiedStore,\n\n [optionsName\n ? `reset${capitalize(optionsName)}Store`\n : 'reset'\n ] () {\n (proxiedStore as any).$value = defaultValue\n },\n\n // TO BE SEPARATED IN cervello/react package --------------------------------\n [optionsName\n ? `use${capitalize(optionsName)}Store`\n : 'useStore'\n ]: createStoreSelectorHook<T>(store$$, proxiedStore, 'full'),\n\n [optionsName\n ? `use${capitalize(optionsName)}Selector`\n : 'useSelector'\n ]: createStoreSelectorHook<T>(store$$, proxiedStore, 'slice'),\n // --------------------------------------------------------------------------\n\n use (...functionList: any) {\n functionList.forEach((func: any) => {\n func({\n onChange (cb: any) {\n store$$.subscribe({ next: (_: any) => { cb(proxiedStore) } })\n },\n onPartialChange (attrs: Array<keyof T>, cb: any) {\n let prevSlice: any = getPartialObjectFromProperties(attrs, okTarget(clonedInitialValue))\n\n store$$.subscribe({\n next: (v: T) => {\n const target = okTarget(v)\n const currentSlice = getPartialObjectFromProperties(attrs, target)\n\n const isEquals = contentComparer(prevSlice, currentSlice)\n\n if (!isEquals) {\n cb(proxiedStore)\n prevSlice = deepClone(currentSlice)\n }\n },\n })\n },\n })\n })\n\n return this\n },\n } as any\n\n\n\n return cervelloStore\n}\n","\nexport type Noop = () => void\n\n\nexport type Observer<T> = {\n next: (value: T) => void\n}\n\nexport type Subscription = {\n unsubscribe: Noop\n}\n\nexport type CacheableSubject<T> = {\n getValue: () => T\n subscribe: (value: Observer<T>) => Subscription\n unSubscribeAll: Noop\n} & Observer<T>\n\n\n\nexport function createCacheableSubject<T> (defaultValue: T, getValueOnSubscribe = true): CacheableSubject<T> {\n let observerList: Array<Observer<T>> = []\n let value = defaultValue\n\n const getValue = (): T => value\n\n const next = (newValue: T): void => {\n value = newValue\n observerList.forEach((o) => { o.next(newValue) })\n }\n\n const subscribe = (observer: Observer<T>): Subscription => {\n // Push cached value on subscribe\n if (getValueOnSubscribe) observer.next(value)\n // Store the observer to be notified\n observerList.push(observer)\n\n return {\n unsubscribe: () => {\n observerList = observerList.filter(o => o !== observer)\n },\n }\n }\n\n const unSubscribeAll = (): void => {\n observerList = []\n }\n\n return {\n getValue,\n next,\n subscribe,\n unSubscribeAll,\n }\n}\n"],"names":["INTERNAL_VALUE_PROP","deepClone","obj","newObj","Array","isArray","map","item","Object","entries","forEach","_ref","okTarget","target","_target$INTERNAL_VALU","getPartialObjectFromProperties","properties","reduce","acc","curr","stringify","JSON","contentComparer","a","b","str","charAt","toUpperCase","slice","toLocaleLowerCase","hasOwn","call","bind","hasOwnProperty","nestedPath","Symbol","proxifyStore","store$$","objectToProxify","proxiedNestedObjectMap","shouldProxifyNestedObj","Error","Proxy","get","t","prop","propertyValue","isObject","propNestedPath","cachedValue","proxied","set","newValue","isRootTarget","_store$$$next","newClonedValue","Reflect","next","keys","cachedProxyKey","startsWith","assign","getValue","has","p","ownKeys","getOwnPropertyDescriptor","isServerSymbol","createStoreSelectorHook","proxiedStore","type","_Object$assign","serverInitialValue","selectors","isEqualFunction","_store$$$getValue","prevSlice","useRef","store","useSyncExternalStore","onStoreChange","subscription","subscribe","v","currentSlice","current","unsubscribe","getOwnPropertySymbols","includes","initialValue","options","reactiveNestedObjects","optionsName","name","_options$reactiveNest","test","defaultValue","clonedInitialValue","_clonedInitialValue","createCacheableSubject","getValueOnSubscribe","observerList","value","o","observer","push","filter","unSubscribeAll","cervelloStore","capitalize","$value","_cervelloStore","use","func","onChange","cb","_","onPartialChange","attrs","this"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cervello/react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"description": "π€― Simplest and truly reactive state manager for React",
|
|
6
6
|
"author": "Chempo",
|
|
7
7
|
"license": "MIT",
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"zustand",
|
|
23
23
|
"reactive",
|
|
24
24
|
"observable",
|
|
25
|
-
"performance"
|
|
25
|
+
"performance",
|
|
26
|
+
"easy",
|
|
27
|
+
"simple",
|
|
28
|
+
"fast"
|
|
26
29
|
],
|
|
27
30
|
"sideEffects": false,
|
|
28
31
|
"exports": {
|
|
@@ -50,22 +53,18 @@
|
|
|
50
53
|
"types"
|
|
51
54
|
],
|
|
52
55
|
"peerDependencies": {
|
|
53
|
-
"react": "
|
|
54
|
-
},
|
|
55
|
-
"dependencies": {
|
|
56
|
-
"use-sync-external-store": "^1.2.0"
|
|
56
|
+
"react": ">= 18.2.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@testing-library/dom": "
|
|
60
|
-
"@testing-library/react": "
|
|
61
|
-
"@types/react": "18.
|
|
62
|
-
"@types/use-sync-external-store": "^0.0.3",
|
|
59
|
+
"@testing-library/dom": "9.3.3",
|
|
60
|
+
"@testing-library/react": "14.0.0",
|
|
61
|
+
"@types/react": "18.2.33",
|
|
63
62
|
"microbundle": "0.15.1",
|
|
64
63
|
"react": "18.2.0",
|
|
65
|
-
"typescript": "
|
|
66
|
-
"vite": "
|
|
67
|
-
"vitest": "
|
|
68
|
-
"vitest-react": "
|
|
64
|
+
"typescript": "5.2.2",
|
|
65
|
+
"vite": "4.5.0",
|
|
66
|
+
"vitest": "0.34.6",
|
|
67
|
+
"vitest-react": "0.9.5"
|
|
69
68
|
},
|
|
70
69
|
"scripts": {
|
|
71
70
|
"release": "pnpx bumpp --push=false && npm publish --access public",
|
package/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cervello } from './lib/store';
|
|
2
|
-
export type { CervelloUseFunction as UseFunction, CervelloOptions } from './lib/store';
|
|
1
|
+
export { cervello } from './lib/store';
|
|
2
|
+
export type { CervelloUseFunction as UseFunction, CervelloOptions } from './lib/store';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const INTERNAL_VALUE_PROP = "$$value$$";
|
|
1
|
+
export declare const INTERNAL_VALUE_PROP = "$$value$$";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { UseSelector } from '../../types/shared';
|
|
2
|
-
import type { CacheableSubject } from '../utils/subject';
|
|
3
|
-
export declare function createStoreSelectorHook<T>(store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T>;
|
|
1
|
+
import type { UseSelector } from '../../types/shared';
|
|
2
|
+
import type { CacheableSubject } from '../utils/subject';
|
|
3
|
+
export declare function createStoreSelectorHook<T>(store$$: CacheableSubject<T>, proxiedStore: any, type: 'full' | 'slice'): UseSelector<T>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './proxify-store';
|
|
2
|
-
export * from './create-store-selector-hook';
|
|
1
|
+
export * from './proxify-store';
|
|
2
|
+
export * from './create-store-selector-hook';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { CacheableSubject } from '../utils/subject';
|
|
2
|
-
export declare function proxifyStore<T>(store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T;
|
|
1
|
+
import type { CacheableSubject } from '../utils/subject';
|
|
2
|
+
export declare function proxifyStore<T>(store$$: CacheableSubject<T>, objectToProxify: T, proxiedNestedObjectMap: any, shouldProxifyNestedObj: boolean): T;
|
|
@@ -1,43 +1,58 @@
|
|
|
1
|
-
import type { Maybe, UseSelector, WithoutType } from '../../types/shared';
|
|
2
|
-
|
|
3
|
-
onChange: (cb: (store: StoreType) => void) => void;
|
|
4
|
-
onPartialChange: <Attributes extends keyof WithoutType<StoreType, Function>, R extends Exclude<Attributes, '$value'>, ArrayOfAttributes extends Array<R>>(selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Use function which allows you to listen for changes in the store.
|
|
8
|
-
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
import type { Maybe, UseSelector, WithoutType } from '../../types/shared';
|
|
2
|
+
type CervelloStoreUseParam<StoreType> = {
|
|
3
|
+
onChange: (cb: (store: StoreType) => void) => void;
|
|
4
|
+
onPartialChange: <Attributes extends keyof WithoutType<StoreType, Function>, R extends Exclude<Attributes, '$value'>, ArrayOfAttributes extends Array<R>>(selectors: ArrayOfAttributes, cb: (store: StoreType) => void) => void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Use function which allows you to listen for changes in the store.
|
|
8
|
+
*/
|
|
9
|
+
export type CervelloUseFunction<StoreType> = (param: CervelloStoreUseParam<StoreType>) => void;
|
|
10
|
+
declare type Prettify<T> = {} & {
|
|
11
|
+
[K in keyof T]: T[K];
|
|
12
|
+
};
|
|
13
|
+
type CervelloStore<StoreType, StoreName extends string | undefined = undefined> = StoreName extends string ? Prettify<{
|
|
14
|
+
[K in `${StoreName}Store`]: StoreType;
|
|
15
|
+
} & {
|
|
16
|
+
[K in `use${Capitalize<StoreName>}Store`]: () => StoreType;
|
|
17
|
+
} & {
|
|
18
|
+
[K in `use${Capitalize<StoreName>}Selector`]: UseSelector<StoreType>;
|
|
19
|
+
} & {
|
|
20
|
+
[K in `reset${Capitalize<StoreName>}Store`]: () => void;
|
|
21
|
+
} & {
|
|
22
|
+
/** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */
|
|
23
|
+
use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>;
|
|
24
|
+
}> : {
|
|
25
|
+
/** Reactive store */
|
|
26
|
+
store: StoreType;
|
|
27
|
+
/** Hook to react to all changes done to 'store' */
|
|
28
|
+
useStore: () => StoreType;
|
|
29
|
+
/** Hook to react to changes done to 'store' in all the attributes provided */
|
|
30
|
+
useSelector: UseSelector<StoreType>;
|
|
31
|
+
/** Resets the value to the initial value passed in */
|
|
32
|
+
reset: () => void;
|
|
33
|
+
/** Functions to be attached to changes in a general way (tracking, loggers, ... etc) */
|
|
34
|
+
use: (...functions: Array<(useObj: CervelloStoreUseParam<StoreType>) => void>) => CervelloStore<StoreType, StoreName>;
|
|
35
|
+
};
|
|
36
|
+
export type CervelloOptions<StoreName extends string | undefined = undefined> = {
|
|
37
|
+
name?: StoreName;
|
|
38
|
+
reactiveNestedObjects?: boolean;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Creates a store that is reactive and can be used inside and outside of React components.
|
|
42
|
+
* @param initialValue - Function which returns the default values for the store
|
|
43
|
+
*
|
|
44
|
+
* @returns \{ store, useStore, useSelector \} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value
|
|
45
|
+
*/
|
|
46
|
+
export declare function cervello<T, StoreName extends string | undefined = undefined>(initialValue: () => T, options?: CervelloOptions<StoreName>): Prettify<CervelloStore<Prettify<T & {
|
|
47
|
+
$value: Maybe<T>;
|
|
48
|
+
}>, StoreName>>;
|
|
49
|
+
/**
|
|
50
|
+
* Creates a store that is reactive and can be used inside and outside of React components.
|
|
51
|
+
* @param initialValue - Object with the default values for the store
|
|
52
|
+
*
|
|
53
|
+
* @returns - \{ store, useStore, useSelector \} or `{ exampleStore, useExampleStore, useExampleSelector }` if name is provided in options with 'example' value
|
|
54
|
+
*/
|
|
55
|
+
export declare function cervello<T extends Record<string, unknown>, StoreName extends string | undefined = undefined>(initialValue: T, options?: CervelloOptions<StoreName>): Prettify<CervelloStore<Prettify<T & {
|
|
56
|
+
$value: Maybe<T>;
|
|
57
|
+
}>, StoreName>>;
|
|
58
|
+
export {};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export * from './object';
|
|
2
|
-
export * from './subject';
|
|
1
|
+
export * from './object';
|
|
2
|
+
export * from './subject';
|
|
3
|
+
export * from './string';
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Clones all the provided object and nested properties and it also
|
|
3
|
-
* iterates nested arrays to deepClone them
|
|
4
|
-
*
|
|
5
|
-
* @param obj - base object to be cloned
|
|
6
|
-
* @returns new cloned object with new reference
|
|
7
|
-
*/
|
|
8
|
-
export declare function deepClone<T>(obj: T): T;
|
|
9
|
-
/**
|
|
10
|
-
* Guard to check is variable is an object
|
|
11
|
-
* @param obj - variable to be checked
|
|
12
|
-
* @returns boolean
|
|
13
|
-
*/
|
|
14
|
-
export declare const isObject: (obj: unknown) => obj is Record<string | symbol, unknown>;
|
|
15
|
-
/**
|
|
16
|
-
* Returns the actual target value instead of root
|
|
17
|
-
* object with internals props like $$value$$
|
|
18
|
-
*
|
|
19
|
-
* @param target - object from subscription or proxy
|
|
20
|
-
* @returns actual value
|
|
21
|
-
*/
|
|
22
|
-
export declare const okTarget: (target: any) => any;
|
|
23
|
-
/**
|
|
24
|
-
* Gets an object with just the properties requested
|
|
25
|
-
* instead of the full object
|
|
26
|
-
*
|
|
27
|
-
* @param properties - object properties
|
|
28
|
-
* @param obj - target to get the properties
|
|
29
|
-
* @returns New object with just the requested properties
|
|
30
|
-
*/
|
|
31
|
-
export declare function getPartialObjectFromProperties<T>(properties: Array<keyof T>, obj: T): any;
|
|
32
|
-
/**
|
|
33
|
-
* Compare 2 provided objects by stringing them
|
|
34
|
-
* @param a - first object
|
|
35
|
-
* @param b - second object
|
|
36
|
-
* @returns boolean
|
|
37
|
-
*/
|
|
38
|
-
export declare const contentComparer: (a: any, b: any) => boolean;
|
|
1
|
+
/**
|
|
2
|
+
* Clones all the provided object and nested properties and it also
|
|
3
|
+
* iterates nested arrays to deepClone them
|
|
4
|
+
*
|
|
5
|
+
* @param obj - base object to be cloned
|
|
6
|
+
* @returns new cloned object with new reference
|
|
7
|
+
*/
|
|
8
|
+
export declare function deepClone<T>(obj: T): T;
|
|
9
|
+
/**
|
|
10
|
+
* Guard to check is variable is an object
|
|
11
|
+
* @param obj - variable to be checked
|
|
12
|
+
* @returns boolean
|
|
13
|
+
*/
|
|
14
|
+
export declare const isObject: (obj: unknown) => obj is Record<string | symbol, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the actual target value instead of root
|
|
17
|
+
* object with internals props like $$value$$
|
|
18
|
+
*
|
|
19
|
+
* @param target - object from subscription or proxy
|
|
20
|
+
* @returns actual value
|
|
21
|
+
*/
|
|
22
|
+
export declare const okTarget: (target: any) => any;
|
|
23
|
+
/**
|
|
24
|
+
* Gets an object with just the properties requested
|
|
25
|
+
* instead of the full object
|
|
26
|
+
*
|
|
27
|
+
* @param properties - object properties
|
|
28
|
+
* @param obj - target to get the properties
|
|
29
|
+
* @returns New object with just the requested properties
|
|
30
|
+
*/
|
|
31
|
+
export declare function getPartialObjectFromProperties<T>(properties: Array<keyof T>, obj: T): any;
|
|
32
|
+
/**
|
|
33
|
+
* Compare 2 provided objects by stringing them
|
|
34
|
+
* @param a - first object
|
|
35
|
+
* @param b - second object
|
|
36
|
+
* @returns boolean
|
|
37
|
+
*/
|
|
38
|
+
export declare const contentComparer: (a: any, b: any) => boolean;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
next: (value: T) => void;
|
|
4
|
-
}
|
|
5
|
-
export
|
|
6
|
-
unsubscribe: Noop;
|
|
7
|
-
}
|
|
8
|
-
export
|
|
9
|
-
getValue: () => T;
|
|
10
|
-
subscribe: (value: Observer<T>) => Subscription;
|
|
11
|
-
unSubscribeAll: Noop;
|
|
12
|
-
} & Observer<T>;
|
|
13
|
-
export declare function createCacheableSubject<T>(defaultValue: T, getValueOnSubscribe?: boolean): CacheableSubject<T>;
|
|
1
|
+
export type Noop = () => void;
|
|
2
|
+
export type Observer<T> = {
|
|
3
|
+
next: (value: T) => void;
|
|
4
|
+
};
|
|
5
|
+
export type Subscription = {
|
|
6
|
+
unsubscribe: Noop;
|
|
7
|
+
};
|
|
8
|
+
export type CacheableSubject<T> = {
|
|
9
|
+
getValue: () => T;
|
|
10
|
+
subscribe: (value: Observer<T>) => Subscription;
|
|
11
|
+
unSubscribeAll: Noop;
|
|
12
|
+
} & Observer<T>;
|
|
13
|
+
export declare function createCacheableSubject<T>(defaultValue: T, getValueOnSubscribe?: boolean): CacheableSubject<T>;
|
package/types/types/shared.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export
|
|
2
|
-
[K in keyof T]: Exclude<T[K], undefined> extends V ? never : (T[K] extends Record<string, unknown> ? WithoutType<T[K], V> : T[K]);
|
|
3
|
-
}> = Pick<WithNever, {
|
|
4
|
-
[K in keyof WithNever]: WithNever[K] extends never ? never : K;
|
|
5
|
-
}[keyof WithNever]>;
|
|
6
|
-
export
|
|
7
|
-
[k in Attributes[number]]: T[k];
|
|
8
|
-
};
|
|
9
|
-
export
|
|
10
|
-
export
|
|
1
|
+
export type WithoutType<T, V, WithNever = {
|
|
2
|
+
[K in keyof T]: Exclude<T[K], undefined> extends V ? never : (T[K] extends Record<string, unknown> ? WithoutType<T[K], V> : T[K]);
|
|
3
|
+
}> = Pick<WithNever, {
|
|
4
|
+
[K in keyof WithNever]: WithNever[K] extends never ? never : K;
|
|
5
|
+
}[keyof WithNever]>;
|
|
6
|
+
export type ObjectFromAttributes<T, Attributes extends Array<keyof WithoutType<T, Function>>> = {
|
|
7
|
+
[k in Attributes[number]]: T[k];
|
|
8
|
+
};
|
|
9
|
+
export type Maybe<T> = T | undefined;
|
|
10
|
+
export type UseSelector<T> = <Attributes extends Array<Exclude<keyof WithoutType<T, Function>, '$value'>>>(selectors: Attributes, isEqualFunction?: (previousState: ObjectFromAttributes<T, Attributes>, currentState: ObjectFromAttributes<T, Attributes>) => boolean) => T;
|