@dvashim/store 1.3.0 → 1.4.1

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 CHANGED
@@ -103,6 +103,49 @@ const unsubscribe = count$.subscribe((state, prevState) => {
103
103
  unsubscribe()
104
104
  ```
105
105
 
106
+ ### `ComputedStore`
107
+
108
+ A read-only reactive store that derives its value from a source store using a selector. Automatically updates when the source changes. Accepts any `SourceStore<T>` (including `Store` or another `ComputedStore`) as its source.
109
+
110
+ ```ts
111
+ import { createStore, ComputedStore } from '@dvashim/store'
112
+
113
+
114
+ const todos$ = createStore([
115
+ { text: 'Buy milk', done: true },
116
+ { text: 'Walk dog', done: false },
117
+ ])
118
+
119
+ const remaining$ = new ComputedStore(todos$, (todos) =>
120
+ todos.filter((t) => !t.done).length
121
+ )
122
+
123
+ remaining$.get() // 1
124
+ remaining$.subscribe((count, prev) => console.log(`${prev} → ${count}`))
125
+ ```
126
+
127
+ #### Chaining
128
+
129
+ `ComputedStore` implements `SourceStore<U>`, so it can be used as the source for another `ComputedStore`.
130
+
131
+ ```ts
132
+ const count$ = new ComputedStore(todos$, (todos) => todos.length)
133
+ const label$ = new ComputedStore(count$, (n) => `${n} items`)
134
+ label$.get() // "2 items"
135
+ ```
136
+
137
+ #### `computed.connect()` / `computed.disconnect()`
138
+
139
+ Control the subscription to the source store. After `disconnect()`, the derived value stops updating and `get()` returns the last known value. Call `connect()` to resume.
140
+
141
+ ```ts
142
+ remaining$.disconnect()
143
+ remaining$.isConnected // false
144
+
145
+ remaining$.connect()
146
+ remaining$.isConnected // true
147
+ ```
148
+
106
149
  ### `useStore(store, selector?)`
107
150
 
108
151
  React hook that subscribes a component to a store.
@@ -127,6 +170,21 @@ function UserName() {
127
170
  }
128
171
  ```
129
172
 
173
+ ### Types
174
+
175
+ The following types are exported from the package:
176
+
177
+ ```ts
178
+ import type { Selector, Subscriber, UpdateOptions, SourceStore } from '@dvashim/store'
179
+ ```
180
+
181
+ | Type | Definition |
182
+ | ---- | ---------- |
183
+ | `Selector<T, U>` | `(state: T) => U` |
184
+ | `Subscriber<T>` | `(state: T, prevState: T) => void` |
185
+ | `UpdateOptions` | `{ force?: boolean }` |
186
+ | `SourceStore<T>` | `{ get(): T; subscribe(fn: Subscriber<T>): () => void }` — shared interface implemented by both `Store` and `ComputedStore` |
187
+
130
188
  ## Patterns
131
189
 
132
190
  ### Shared stores across components
@@ -0,0 +1,13 @@
1
+ import type { Selector, SourceStore, Subscriber } from './types';
2
+ export declare class ComputedStore<T, U> implements SourceStore<U> {
3
+ #private;
4
+ constructor(source: SourceStore<T>, selector: Selector<T, U>);
5
+ connect(): void;
6
+ disconnect(): void;
7
+ get isConnected(): boolean;
8
+ protected get source(): SourceStore<T>;
9
+ protected get selector(): Selector<T, U>;
10
+ get(): U;
11
+ subscribe(fn: Subscriber<U>): () => void;
12
+ }
13
+ //# sourceMappingURL=ComputedStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComputedStore.d.ts","sourceRoot":"","sources":["../src/ComputedStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEhE,qBAAa,aAAa,CAAC,CAAC,EAAE,CAAC,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;;gBAM5C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;IAO5D,OAAO;IAOP,UAAU;IAKV,IAAI,WAAW,YAEd;IAED,SAAS,KAAK,MAAM,mBAEnB;IAED,SAAS,KAAK,QAAQ,mBAErB;IAED,GAAG;IAIH,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;CAG5B"}
@@ -0,0 +1,52 @@
1
+ import { Store } from './Store';
2
+ export class ComputedStore {
3
+ #source;
4
+ #derived;
5
+ #selector;
6
+ #unsubscribe;
7
+ constructor(source, selector) {
8
+ this.#source = source;
9
+ this.#derived = new Store(selector(source.get()));
10
+ this.#selector = selector;
11
+ this.connect();
12
+ }
13
+ connect() {
14
+ this.disconnect();
15
+ this.#unsubscribe = this.#source.subscribe((state) => {
16
+ this.#derived.set(this.#selector(state));
17
+ });
18
+ }
19
+ disconnect() {
20
+ this.#unsubscribe?.();
21
+ this.#unsubscribe = undefined;
22
+ }
23
+ get isConnected() {
24
+ return !!this.#unsubscribe;
25
+ }
26
+ get source() {
27
+ return this.#source;
28
+ }
29
+ get selector() {
30
+ return this.#selector;
31
+ }
32
+ get() {
33
+ return this.#derived.get();
34
+ }
35
+ subscribe(fn) {
36
+ return this.#derived.subscribe(fn);
37
+ }
38
+ }
39
+ // const s = new Store([1, 2])
40
+ // const len = new ComputedStore(s, (state) => state.length)
41
+ // const lenString = new ComputedStore(len, (state) => `length: ${state}`)
42
+ // const sum = new ComputedStore(s, (state) => state.reduce((a, b) => a + b, 0))
43
+ // console.log(len.get()) // 2
44
+ // console.log(sum.get()) // 3
45
+ // console.log(lenString.get()) // length: 2
46
+ // const len1 = new SubStore(s, (state) => state.length)
47
+ // const lenString1 = new SubStore(len1, (state) => `length: ${state}`)
48
+ // const sum1 = new SubStore(s, (state) => state.reduce((a, b) => a + b, 0))
49
+ // console.log(len1.get()) // 2
50
+ // console.log(sum1.get()) // 3
51
+ // console.log(lenString1.get()) // length: 2
52
+ //# sourceMappingURL=ComputedStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComputedStore.js","sourceRoot":"","sources":["../src/ComputedStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAG/B,MAAM,OAAO,aAAa;IACf,OAAO,CAAgB;IACvB,QAAQ,CAAU;IAClB,SAAS,CAAgB;IAClC,YAAY,CAA2B;IAEvC,YAAY,MAAsB,EAAE,QAAwB;QAC1D,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;QACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QACjD,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;QACzB,IAAI,CAAC,OAAO,EAAE,CAAA;IAChB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,UAAU,EAAE,CAAA;QACjB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACnD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,UAAU;QACR,IAAI,CAAC,YAAY,EAAE,EAAE,CAAA;QACrB,IAAI,CAAC,YAAY,GAAG,SAAS,CAAA;IAC/B,CAAC;IAED,IAAI,WAAW;QACb,OAAO,CAAC,CAAC,IAAI,CAAC,YAAY,CAAA;IAC5B,CAAC;IAED,IAAc,MAAM;QAClB,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;IAED,IAAc,QAAQ;QACpB,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAA;IAC5B,CAAC;IAED,SAAS,CAAC,EAAiB;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;IACpC,CAAC;CACF;AAED,8BAA8B;AAC9B,4DAA4D;AAC5D,0EAA0E;AAC1E,gFAAgF;AAChF,8BAA8B;AAC9B,8BAA8B;AAC9B,4CAA4C;AAE5C,wDAAwD;AACxD,uEAAuE;AACvE,4EAA4E;AAC5E,+BAA+B;AAC/B,+BAA+B;AAC/B,6CAA6C"}
package/dist/Store.d.ts CHANGED
@@ -1,13 +1,10 @@
1
- type Subscriber<T> = (state: T, prevState: T) => void;
1
+ import type { SourceStore, Subscriber, UpdateOptions } from './types';
2
2
  type Updater<T> = (prevValue: T) => T;
3
- type UpdateOptions = {
4
- force?: boolean;
5
- };
6
3
  /**
7
4
  * Reactive state container with subscription-based change notification.
8
5
  * @typeParam T - The type of the stored state.
9
6
  */
10
- export declare class Store<T> {
7
+ export declare class Store<T> implements SourceStore<T> {
11
8
  #private;
12
9
  constructor(initialState: T);
13
10
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Store.d.ts","sourceRoot":"","sources":["../src/Store.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,KAAK,IAAI,CAAA;AACrD,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAA;AACrC,KAAK,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AASxC;;;GAGG;AACH,qBAAa,KAAK,CAAC,CAAC;;gBAMN,YAAY,EAAE,CAAC;IAI3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAKxC,iCAAiC;IACjC,GAAG,IAAI,CAAC;IAIR;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;IAKrC;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;CA8DpD"}
1
+ {"version":3,"file":"Store.d.ts","sourceRoot":"","sources":["../src/Store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAErE,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAA;AASrC;;;GAGG;AACH,qBAAa,KAAK,CAAC,CAAC,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;;gBAMjC,YAAY,EAAE,CAAC;IAI3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAKxC,iCAAiC;IACjC,GAAG,IAAI,CAAC;IAIR;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;IAKrC;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;CA8DpD"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './ComputedStore';
1
2
  export * from './createStore';
2
3
  export * from './Store';
3
4
  export * from './types';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './ComputedStore';
1
2
  export * from './createStore';
2
3
  export * from './Store';
3
4
  export * from './types';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA"}
package/dist/types.d.ts CHANGED
@@ -1,2 +1,10 @@
1
1
  export type Selector<T, U> = (state: T) => U;
2
+ export type Subscriber<T> = (state: T, prevState: T) => void;
3
+ export type UpdateOptions = {
4
+ force?: boolean;
5
+ };
6
+ export type SourceStore<T> = {
7
+ get: () => T;
8
+ subscribe: (fn: Subscriber<T>) => () => void;
9
+ };
2
10
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA;AAC5C,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,KAAK,IAAI,CAAA;AAC5D,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAC/C,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,GAAG,EAAE,MAAM,CAAC,CAAA;IACZ,SAAS,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,CAAA;CAC7C,CAAA"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "@dvashim/store",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
- "version": "1.3.0",
7
+ "version": "1.4.1",
8
8
  "author": {
9
9
  "email": "aleksei@dvashim.dev",
10
10
  "name": "Aleksei Reznichenko"