@guanghechen/observable 7.0.0 → 7.1.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/CHANGELOG.md CHANGED
@@ -1,8 +1,60 @@
1
1
  # Change Log
2
2
 
3
+ ## 7.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ### @guanghechen/reporter
8
+ - feat: add `setLevel` method for dynamic log level change
9
+ - refactor: split source into modular files (`level.ts`, `chalk.ts`, `types.ts`, `reporter.ts`)
10
+ - refactor: move `IReporter` types from `@guanghechen/types` to `@guanghechen/reporter`
11
+ - export: add level utilities (`LogLevelEnum`, `ILogLevel`, `LOG_LEVELS`, `LOG_LEVEL_VALUES`,
12
+ `isLogLevel`, `getLogLevelValue`, `resolveLogLevel`)
13
+ - export: add chalk utilities (`ANSI`, `formatTag`)
14
+
15
+ ### @guanghechen/types
16
+ - refactor: remove `IReporter` and `IReporterLevel` exports (moved to `@guanghechen/reporter`)
17
+
18
+ ### @guanghechen/commander
19
+ - feat: add predefined options `logLevelOption` and `silentOption` with `apply` callback support
20
+ - refactor: use `ILogLevel` from `@guanghechen/reporter` instead of `IReporterLevel`
21
+
22
+ ### Dependent packages
23
+ - chore: bump version for packages depending on `@guanghechen/types`, `@guanghechen/reporter`, or
24
+ `@guanghechen/commander`
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies:
29
+ - @guanghechen/types@2.2.0
30
+ - @guanghechen/disposable@2.1.0
31
+ - @guanghechen/subscriber@2.1.0
32
+
3
33
  All notable changes to this project will be documented in this file. See
4
34
  [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
35
 
36
+ ## 7.0.1 (2025-02-07)
37
+
38
+ ### Improvements
39
+
40
+ - Clean up build configs and standardize package exports
41
+ - Enhance type safety and resource cleanup
42
+
43
+ ### Documentation
44
+
45
+ - Update README.md
46
+
47
+ ### Miscellaneous
48
+
49
+ - Add LICENSE file
50
+ - Migrate from lerna to changesets
51
+
52
+ ## 7.0.0 (2025-01-15)
53
+
54
+ ### Improvements
55
+
56
+ - Upgrade to stable release
57
+
6
58
  ## <small>6.1.3 (2024-09-19)</small>
7
59
 
8
60
  - :art: improve(viewmodel): support to create Computed from another Computed
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-present guanghechen (https://github.com/guanghechen)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -49,7 +49,8 @@
49
49
  </header>
50
50
  <br/>
51
51
 
52
- Observable pattern implementation with ticker functionality for reactive programming.
52
+ Observable pattern implementation with ticker functionality for reactive programming. Provides value
53
+ change notification with optional delay debouncing and custom equality comparison.
53
54
 
54
55
  ## Install
55
56
 
@@ -67,88 +68,120 @@ Observable pattern implementation with ticker functionality for reactive program
67
68
 
68
69
  ## Usage
69
70
 
70
- | Name | Description |
71
- | :--------------: | :-------------------------------------------------------: |
72
- | `Observable` | Reactive observable value with change notification |
73
- | `Ticker` | Timer-based ticker for scheduled notifications |
71
+ ### Basic Observable
74
72
 
75
- ## Example
73
+ ```typescript
74
+ import { Observable } from '@guanghechen/observable'
75
+ import { Subscriber } from '@guanghechen/subscriber'
76
76
 
77
- - Basic observable:
77
+ const count = new Observable<number>(0)
78
78
 
79
- ```typescript
80
- import { Observable } from '@guanghechen/observable'
81
- import { Subscriber } from '@guanghechen/subscriber'
79
+ const subscriber = new Subscriber({
80
+ onNext: (newValue, oldValue) => {
81
+ console.log(`Value changed from ${oldValue} to ${newValue}`)
82
+ }
83
+ })
82
84
 
83
- const observable = new Observable<number>(0)
85
+ const unsubscribable = count.subscribe(subscriber)
84
86
 
85
- // Create a subscriber
86
- const subscriber = new Subscriber({
87
- onNext: (newValue, oldValue) => {
88
- console.log(`Value changed from ${oldValue} to ${newValue}`)
89
- },
90
- onDispose: () => {
91
- console.log('Subscriber disposed')
92
- }
93
- })
87
+ count.next(1) // "Value changed from 0 to 1"
88
+ count.next(2) // "Value changed from 1 to 2"
89
+ count.next(2) // No notification (same value)
94
90
 
95
- // Subscribe to changes
96
- const unsubscribe = observable.subscribe(subscriber)
91
+ console.log(count.getSnapshot()) // 2
97
92
 
98
- observable.next(1) // Output: "Value changed from undefined to 1"
99
- observable.next(2) // Output: "Value changed from 1 to 2"
93
+ unsubscribable.unsubscribe()
94
+ count.dispose()
95
+ ```
100
96
 
101
- unsubscribe.unsubscribe()
102
- ```
97
+ ### Observable with Custom Equality
103
98
 
104
- - Observable with custom equality:
99
+ ```typescript
100
+ import { Observable } from '@guanghechen/observable'
101
+ import { Subscriber } from '@guanghechen/subscriber'
105
102
 
106
- ```typescript
107
- import { Observable } from '@guanghechen/observable'
108
- import { Subscriber } from '@guanghechen/subscriber'
103
+ interface IUser {
104
+ id: number
105
+ name: string
106
+ }
109
107
 
110
- const observable = new Observable<{id: number, name: string}>(
111
- { id: 1, name: 'John' },
112
- { equals: (a, b) => a.id === b.id }
113
- )
108
+ const user = new Observable<IUser>(
109
+ { id: 1, name: 'John' },
110
+ { equals: (a, b) => a.id === b.id }
111
+ )
114
112
 
115
- const subscriber = new Subscriber({
116
- onNext: (newVal) => {
117
- console.log('Object changed:', newVal)
118
- }
119
- })
113
+ const subscriber = new Subscriber<IUser>({
114
+ onNext: (value) => console.log('User changed:', value)
115
+ })
120
116
 
121
- observable.subscribe(subscriber)
117
+ user.subscribe(subscriber)
122
118
 
123
- // This won't trigger notification (same id)
124
- observable.next({ id: 1, name: 'Jane' })
119
+ // This won't trigger notification (same id)
120
+ user.next({ id: 1, name: 'Jane' })
125
121
 
126
- // This will trigger notification (different id)
127
- observable.next({ id: 2, name: 'Bob' })
128
- ```
122
+ // This will trigger notification (different id)
123
+ user.next({ id: 2, name: 'Bob' })
129
124
 
130
- - Using ticker:
125
+ // Force notification even if equal
126
+ user.next({ id: 2, name: 'Bob Updated' }, { force: true })
127
+ ```
131
128
 
132
- ```typescript
133
- import { Ticker } from '@guanghechen/observable'
134
- import { Subscriber } from '@guanghechen/subscriber'
129
+ ### Observable with Delay (Debouncing)
135
130
 
136
- const ticker = new Ticker<string>('initial', { interval: 1000 })
131
+ ```typescript
132
+ import { Observable } from '@guanghechen/observable'
133
+ import { Subscriber } from '@guanghechen/subscriber'
137
134
 
138
- const subscriber = new Subscriber({
139
- onNext: (value) => {
140
- console.log('Ticker value:', value)
141
- }
142
- })
135
+ // Debounce notifications by 100ms
136
+ const search = new Observable<string>('', { delay: 100 })
143
137
 
144
- ticker.subscribe(subscriber)
145
- ticker.start()
146
-
147
- // Update ticker value
148
- setTimeout(() => {
149
- ticker.next('updated value')
150
- }, 2000)
151
- ```
138
+ const subscriber = new Subscriber<string>({
139
+ onNext: (value) => console.log('Search:', value)
140
+ })
141
+
142
+ search.subscribe(subscriber)
143
+
144
+ // Rapid updates - only the last value will be notified after 100ms
145
+ search.next('h')
146
+ search.next('he')
147
+ search.next('hel')
148
+ search.next('hell')
149
+ search.next('hello')
150
+ // Output after 100ms: "Search: hello"
151
+ ```
152
+
153
+ ### Ticker
154
+
155
+ Ticker is a specialized observable that increments a counter value:
156
+
157
+ ```typescript
158
+ import { Observable, Ticker } from '@guanghechen/observable'
159
+ import { Subscriber } from '@guanghechen/subscriber'
160
+
161
+ const ticker = new Ticker({ start: 0, delay: 100 })
162
+
163
+ const subscriber = new Subscriber<number>({
164
+ onNext: (tick) => console.log('Tick:', tick)
165
+ })
166
+
167
+ ticker.subscribe(subscriber)
168
+
169
+ ticker.tick() // Tick: 1
170
+ ticker.tick() // Tick: 2
171
+
172
+ // Observe other observables - ticker increments when they change
173
+ const name = new Observable<string>('John')
174
+ const unobservable = ticker.observe(name)
175
+
176
+ name.next('Jane') // Also triggers: Tick: 3
177
+
178
+ unobservable.unobserve()
179
+ ticker.dispose()
180
+ ```
181
+
182
+ ## Reference
183
+
184
+ - [homepage][homepage]
152
185
 
153
186
  [homepage]:
154
- https://github.com/guanghechen/sora/tree/@guanghechen/observable@7.0.0/packages/observable#readme
187
+ https://github.com/guanghechen/sora/tree/@guanghechen/observable@7.0.0/packages/observable#readme
package/lib/cjs/index.cjs CHANGED
@@ -22,6 +22,7 @@ class Observable extends disposable.BatchDisposable {
22
22
  equals;
23
23
  _delay;
24
24
  _subscribers;
25
+ _onError;
25
26
  _value;
26
27
  _updateTick;
27
28
  _notifyTick;
@@ -29,7 +30,7 @@ class Observable extends disposable.BatchDisposable {
29
30
  _timer;
30
31
  constructor(defaultValue, options = {}) {
31
32
  super();
32
- const { equals = defaultEquals } = options;
33
+ const { equals = defaultEquals, onError } = options;
33
34
  this._delay = Math.max(0, Number(options.delay) || 0);
34
35
  this._subscribers = new subscriber.Subscribers();
35
36
  this._value = defaultValue;
@@ -38,6 +39,11 @@ class Observable extends disposable.BatchDisposable {
38
39
  this._lastNotifiedValue = undefined;
39
40
  this._timer = undefined;
40
41
  this.equals = equals;
42
+ this._onError =
43
+ onError ??
44
+ (error => {
45
+ console.error('Error in observable notification:', error);
46
+ });
41
47
  }
42
48
  dispose() {
43
49
  if (this.disposed)
@@ -105,7 +111,7 @@ class Observable extends disposable.BatchDisposable {
105
111
  }
106
112
  }
107
113
  catch (error) {
108
- console.error('Error in observable notification:', error);
114
+ this._onError(error);
109
115
  }
110
116
  finally {
111
117
  this._timer = undefined;
package/lib/esm/index.mjs CHANGED
@@ -20,6 +20,7 @@ class Observable extends BatchDisposable {
20
20
  equals;
21
21
  _delay;
22
22
  _subscribers;
23
+ _onError;
23
24
  _value;
24
25
  _updateTick;
25
26
  _notifyTick;
@@ -27,7 +28,7 @@ class Observable extends BatchDisposable {
27
28
  _timer;
28
29
  constructor(defaultValue, options = {}) {
29
30
  super();
30
- const { equals = defaultEquals } = options;
31
+ const { equals = defaultEquals, onError } = options;
31
32
  this._delay = Math.max(0, Number(options.delay) || 0);
32
33
  this._subscribers = new Subscribers();
33
34
  this._value = defaultValue;
@@ -36,6 +37,11 @@ class Observable extends BatchDisposable {
36
37
  this._lastNotifiedValue = undefined;
37
38
  this._timer = undefined;
38
39
  this.equals = equals;
40
+ this._onError =
41
+ onError ??
42
+ (error => {
43
+ console.error('Error in observable notification:', error);
44
+ });
39
45
  }
40
46
  dispose() {
41
47
  if (this.disposed)
@@ -103,7 +109,7 @@ class Observable extends BatchDisposable {
103
109
  }
104
110
  }
105
111
  catch (error) {
106
- console.error('Error in observable notification:', error);
112
+ this._onError(error);
107
113
  }
108
114
  finally {
109
115
  this._timer = undefined;
@@ -1,5 +1,6 @@
1
- import { IBatchDisposable, BatchDisposable } from '@guanghechen/disposable';
1
+ import { BatchDisposable } from '@guanghechen/disposable';
2
2
  import { ISubscribable, ISubscribers, ISubscriber, IUnsubscribable } from '@guanghechen/subscriber';
3
+ import { IBatchDisposable } from '@guanghechen/types';
3
4
 
4
5
  type IEquals<T> = (x: T, y: T) => boolean;
5
6
  interface IObservableOptions<T> {
@@ -11,6 +12,10 @@ interface IObservableOptions<T> {
11
12
  * Determine whether the two values are equal.
12
13
  */
13
14
  readonly equals?: IEquals<T>;
15
+ /**
16
+ * Error handler for async notifications.
17
+ */
18
+ readonly onError?: (error: unknown) => void;
14
19
  }
15
20
  interface IObservableNextOptions {
16
21
  /**
@@ -48,6 +53,7 @@ declare class Observable<T> extends BatchDisposable implements IObservable<T> {
48
53
  readonly equals: IEquals<T>;
49
54
  protected readonly _delay: number;
50
55
  protected readonly _subscribers: ISubscribers<T>;
56
+ protected readonly _onError: (error: unknown) => void;
51
57
  protected _value: T;
52
58
  protected _updateTick: number;
53
59
  protected _notifyTick: number;
@@ -108,7 +114,7 @@ declare class Ticker extends Observable<number> implements ITicker {
108
114
  observe<T>(observable: IBaseObservable<T>, options?: ITickerObserveOptions): IUnobservable;
109
115
  }
110
116
 
111
- declare const noop: (..._args: any[]) => void;
117
+ declare const noop: (..._args: unknown[]) => void;
112
118
  declare const noopUnsubscribable: IUnsubscribable;
113
119
  declare const noopUnobservable: IUnobservable;
114
120
  declare const isObservable: (obj: unknown) => obj is IObservable<unknown>;
package/package.json CHANGED
@@ -1,37 +1,33 @@
1
1
  {
2
2
  "name": "@guanghechen/observable",
3
- "version": "7.0.0",
3
+ "version": "7.1.0",
4
4
  "author": {
5
5
  "name": "guanghechen",
6
6
  "url": "https://github.com/guanghechen/"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.1.8",
10
+ "url": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@7.0.0",
11
11
  "directory": "packages/observable"
12
12
  },
13
- "homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@6.1.8/packages/observable#readme",
13
+ "homepage": "https://github.com/guanghechen/sora/tree/@guanghechen/observable@7.0.0/packages/observable#readme",
14
14
  "keywords": [
15
15
  "observable"
16
16
  ],
17
17
  "type": "module",
18
18
  "exports": {
19
- "types": "./lib/types/index.d.ts",
20
- "require": "./lib/cjs/index.cjs",
21
- "import": "./lib/esm/index.mjs"
19
+ ".": {
20
+ "types": "./lib/types/index.d.ts",
21
+ "source": "./src/index.ts",
22
+ "import": "./lib/esm/index.mjs",
23
+ "require": "./lib/cjs/index.cjs"
24
+ }
22
25
  },
23
26
  "types": "./lib/types/index.d.ts",
24
27
  "main": "./lib/cjs/index.cjs",
25
28
  "module": "./lib/esm/index.mjs",
26
29
  "source": "./src/index.ts",
27
30
  "license": "MIT",
28
- "scripts": {
29
- "build": "rollup -c ../../rollup.config.mjs",
30
- "clean": "rimraf lib",
31
- "test": "vitest run --config ../../vitest.config.ts",
32
- "test:coverage": "vitest run --config ../../vitest.config.ts --coverage",
33
- "test:update": "vitest run --config ../../vitest.config.ts -u"
34
- },
35
31
  "files": [
36
32
  "lib/",
37
33
  "!lib/**/*.map",
@@ -41,8 +37,15 @@
41
37
  "README.md"
42
38
  ],
43
39
  "dependencies": {
44
- "@guanghechen/disposable": "^2.0.0",
45
- "@guanghechen/subscriber": "^2.0.0"
40
+ "@guanghechen/disposable": "^2.1.0",
41
+ "@guanghechen/subscriber": "^2.1.0",
42
+ "@guanghechen/types": "^2.2.0"
46
43
  },
47
- "gitHead": "12990a720b31d50d217e2e17a6191256dc94eda6"
48
- }
44
+ "scripts": {
45
+ "build": "rollup -c ../../rollup.config.mjs",
46
+ "clean": "rimraf lib",
47
+ "test": "vitest run --config ../../vitest.config.ts",
48
+ "test:coverage": "vitest run --config ../../vitest.config.ts --coverage",
49
+ "test:update": "vitest run --config ../../vitest.config.ts -u"
50
+ }
51
+ }