@codeleap/store 4.3.3 → 4.3.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/store",
3
- "version": "4.3.3",
3
+ "version": "4.3.5",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  "directory": "packages/store"
10
10
  },
11
11
  "devDependencies": {
12
- "@codeleap/config": "4.3.3",
12
+ "@codeleap/config": "4.3.5",
13
13
  "ts-node-dev": "1.1.8"
14
14
  },
15
15
  "scripts": {
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/store",
3
- "version": "4.3.3",
3
+ "version": "4.3.5",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
package/src/array.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { WritableAtom } from "nanostores"
2
+
3
+ export function arrayHandler<T extends any[]>(store: WritableAtom<T>) {
4
+ return new Proxy([], {
5
+ get(target, p, receiver){
6
+ const val = store.get()
7
+
8
+ const property = val[p]
9
+
10
+ if(typeof property == 'function') {
11
+ return (...args) => {
12
+ const r = val[p](...args)
13
+
14
+ store.set(val)
15
+
16
+ return r
17
+ }
18
+ }
19
+
20
+ return property
21
+ }
22
+ })
23
+ }
24
+
25
+
26
+ export const arrayOps = Object.getOwnPropertyNames(Array.prototype)
@@ -1,8 +1,9 @@
1
1
  import { useStore } from '@nanostores/react'
2
2
  import { setPersistentEngine, persistentAtom } from '@nanostores/persistent'
3
- import { atom } from 'nanostores'
3
+ import { atom, WritableAtom } from 'nanostores'
4
4
  import { GlobalState, GlobalStateConfig, StateSelector } from './types'
5
5
  import { stateAssign, useStateSelector } from './utils'
6
+ import { arrayHandler, arrayOps } from './array'
6
7
 
7
8
  const defaultConfig: GlobalStateConfig = {
8
9
  persistKey: null,
@@ -36,6 +37,22 @@ export function globalState<T>(value: T, config: GlobalStateConfig = defaultConf
36
37
  }
37
38
  }
38
39
 
40
+ if(prop == 'reset'){
41
+ return Reflect.get(target, 'set', receiver)
42
+ }
43
+
44
+ if(arrayOps.includes(prop as string)){
45
+ const currentValue = target.get()
46
+
47
+ if(!Array.isArray(currentValue)) {
48
+ throw new Error('Cannot call array methods on a non array store')
49
+ }
50
+
51
+ const handle = arrayHandler(target as WritableAtom<any[]>)
52
+
53
+ return Reflect.get(handle, prop, receiver)
54
+ }
55
+
39
56
  return Reflect.get(target, prop, receiver)
40
57
  }
41
58
  }) as unknown as GlobalState<T>
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from './globalState'
2
2
  export * from './types'
3
- export * from './utils'
3
+ export * from './utils'
@@ -0,0 +1,69 @@
1
+ import { expect, test } from "bun:test"
2
+ import {globalState} from '../globalState'
3
+
4
+ test("store.set()", () => {
5
+ const store = globalState(1)
6
+
7
+ store.set(4)
8
+
9
+ expect(store.get()).toBe(4)
10
+ })
11
+
12
+ test("store.set() with object", () => {
13
+ const store = globalState({
14
+ a: 1,
15
+ b: "Test"
16
+ })
17
+
18
+ store.set({
19
+ a: 4
20
+ })
21
+
22
+ expect(store.get().a).toBe(4)
23
+ })
24
+
25
+ test("store.reset() with object", () => {
26
+ const store = globalState({
27
+ a: 1,
28
+ b: "Test"
29
+ })
30
+
31
+ store.reset({
32
+ a: 4,
33
+ b: "Changed"
34
+ })
35
+
36
+ const newVal = store.get()
37
+ expect(newVal.a).toBe(4)
38
+ expect(newVal.b).toBe('Changed')
39
+ })
40
+
41
+ test('store array methods', () => {
42
+ const store = globalState([] as number[])
43
+
44
+ store.push(10)
45
+ store.unshift(100)
46
+
47
+ const doubled = store.map(v => v * 2)
48
+
49
+ const val = store.get()
50
+
51
+ expect(val[0]).toBe(100)
52
+ expect(val[1]).toBe(10)
53
+
54
+ expect(doubled[0]).toBe(200)
55
+ expect(doubled[1]).toBe(20)
56
+ })
57
+
58
+ test("store.listen()", () => {
59
+ const store = globalState(1)
60
+
61
+ store.listen((current, prev) => {
62
+ expect(current).toBe(4)
63
+ expect(prev).toBe(1)
64
+ })
65
+
66
+ store.set(4)
67
+
68
+ expect(store.get()).toBe(4)
69
+ })
@@ -0,0 +1,48 @@
1
+ import { expect, test } from "bun:test"
2
+ import {globalState} from '../globalState'
3
+ import { createStateSlice } from "../utils"
4
+
5
+ test("slice selects consistently", () => {
6
+ const store = globalState({
7
+ a: 1,
8
+ b: "Test"
9
+ })
10
+
11
+ const slice = createStateSlice(store,
12
+ v => v.a
13
+ )
14
+
15
+ store.set({
16
+ a: 4
17
+ })
18
+
19
+ expect(slice.get()).toBe(4)
20
+
21
+ store.set({
22
+ a: 10
23
+ })
24
+
25
+ expect(slice.get()).toBe(10)
26
+ })
27
+
28
+ test("slice sets consistently", () => {
29
+ const store = globalState({
30
+ a: 1,
31
+ b: "Test"
32
+ })
33
+
34
+ const slice = createStateSlice(store,
35
+ v => v.a,
36
+ v => ({ a: v })
37
+ )
38
+
39
+ slice.set(4)
40
+
41
+ expect(slice.get()).toBe(4)
42
+ expect(store.get().a).toBe(4)
43
+
44
+ slice.set(10)
45
+
46
+ expect(slice.get()).toBe(10)
47
+ expect(store.get().a).toBe(10)
48
+ })
package/src/types.ts CHANGED
@@ -3,10 +3,15 @@ import { PersistentStore, PersistentEvents, PersistentEvent } from '@nanostores/
3
3
 
4
4
  export type StateSelector<S, R> = (state: S) => R
5
5
 
6
- export type GlobalState<T> = Omit<WritableAtom<T>, 'set'> & {
6
+ export type GlobalState<T> = Omit<WritableAtom<T>,'set'> & {
7
7
  use: <Selected = T>(selector?: StateSelector<T, Selected>) => Selected
8
- set: (newValue: Partial<T>) => T
9
- }
8
+
9
+ set: T extends Record<string, any> ? (newValue: Partial<T>) => T : WritableAtom<T>['set']
10
+
11
+ reset: WritableAtom<T>['set']
12
+ } & (
13
+ T extends any[] ? Array<T[number]> : {}
14
+ )
10
15
 
11
16
  export type GlobalStateConfig = {
12
17
  persistKey?: string
package/src/utils.ts CHANGED
@@ -1,37 +1,51 @@
1
1
  import { useStore } from '@nanostores/react'
2
- import { Store } from 'nanostores'
2
+ import { WritableStore } from 'nanostores'
3
+ import { useMemo } from 'react'
3
4
 
4
- export const createStateSelector = <T, R>(
5
- store: Store<T>,
6
- selector: (state: T) => R
5
+ export function stateAssign<T>(newValue: Partial<T>, stateValue: T): T {
6
+ if (
7
+ typeof stateValue === "object" && stateValue !== null
8
+ ) {
9
+ return {
10
+ ...stateValue,
11
+ ...newValue,
12
+ } as T
13
+ }
14
+
15
+ return newValue as T
16
+
17
+ }
18
+
19
+ export const createStateSlice = <T, R>(
20
+ store: WritableStore<T>,
21
+ selector: (state: T) => R,
22
+ deselector?: (result: R) => T extends Record<string, any> ? Partial<T> : T
7
23
  ) => ({
8
24
  get: () => selector(store.get()),
9
25
  listen: (listener: (value: R) => void) => {
10
26
  return store.listen((state) => {
11
27
  listener(selector(state))
12
28
  })
29
+ },
30
+ set(v: R) {
31
+
32
+ if(!deselector) {
33
+ throw new Error('[createStateSelector] deselector must be implemented to call set on state slices')
34
+ }
35
+
36
+ const parsed = deselector(v)
37
+
38
+ const newValue = stateAssign(parsed, store.get())
39
+
40
+ store.set(newValue)
13
41
  }
14
- } as Store)
42
+ } as WritableStore<R>)
15
43
 
16
44
  export function useStateSelector<T, R>(
17
- store: Store<T>,
45
+ store: WritableStore<T>,
18
46
  selector: (state: T) => R
19
47
  ): R {
20
- return useStore(createStateSelector(store, selector))
21
- }
48
+ const slice = useMemo(() => createStateSlice(store, selector), [selector])
22
49
 
23
- export function stateAssign<T>(newValue: Partial<T>, stateValue: T): T {
24
- if (Array.isArray(stateValue)) {
25
- return [
26
- ...stateValue,
27
- ...(Array.isArray(newValue) ? newValue : [newValue]),
28
- ] as T
29
- } else if (typeof stateValue === "object" && stateValue !== null) {
30
- return {
31
- ...stateValue,
32
- ...newValue,
33
- } as T
34
- } else {
35
- return newValue as T
36
- }
50
+ return useStore(slice)
37
51
  }