flinker 2.0.3 → 2.0.4

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
@@ -1,7 +1,210 @@
1
- # Install
2
- ```cli
1
+ ## Intro
2
+ __Flinker__ is a functional reactive programming library. __Flinker__ is not limited to user interfaces. It also can be adapted to React.js (e.g. [flinker-react](https://github.com/Dittner/Flinker-React)) or your own frontend framework (e.g. [flinker-dom](https://github.com/Dittner/FlinkerDom)).
3
+
4
+ ## RXPublisher, RXPipeline, RXOperator and RXSubscriber
5
+ Four key concepts: publisher, pipeline, operator and subscriber. A publisher provides data when available and upon request. To subscribe to changes, the publisher provides the `pipe()` method, which creates a pipeline.
6
+
7
+ RXPipeline is a queue of operators (RXOperator) to control the flow of data. Operators are a convenient name for a number of pre-built functions such as: map, flatMap, filter, debounce, etc.
8
+
9
+ Subscribers are responsible for accepting the data (and possible errors) provided by a publisher. Subscribers are joined to the end of a pipeline.
10
+
11
+ A publisher is generating and sending data, operators are reacting to that data and potentially changing it, and subscribers accepting result. RXPublisher cannot be used directly, its subclasses must be used instead:
12
+
13
+ + RXObservableValue
14
+ + RXObservableEntity
15
+ + RXSubject
16
+ + RXEmitter
17
+ + RXOperation
18
+ + RXBuffer
19
+ + RXCombine
20
+ + RXJustComplete
21
+ + RXDelayedComplete
22
+ + RXJustError
23
+ + RXDelayedError
24
+ + RXFrom
25
+ + RXQueue
26
+
27
+ ## RXObservableValue
28
+ It does not dispatch errors and completion, has a default value.
29
+
30
+ ```ts
31
+ let buffer = ''
32
+ const rx = new RXObservableValue(1) // extends RXPublisher
33
+ rx.pipe() // creates RXPipeline
34
+ .onReceive(v => buffer += v) // creates RXSubscriber
35
+ .subscribe() // returns unsubscribe function: () => void
36
+
37
+ expect(buffer).toBe('1') //true
38
+
39
+ rx.value = 2
40
+ rx.value = 3
41
+
42
+ expect(buffer).toBe('123') //true
43
+ ```
44
+
45
+ ## RXOperation
46
+ It dispatches value or error and immediately after that completion.
47
+
48
+ ```ts
49
+ const op = new RXOperation<number, void>()
50
+ let buffer1 = ''
51
+ let buffer2 = ''
52
+
53
+ op.pipe()
54
+ .onReceive(v => (buffer1 += v + ''))
55
+ .onError(() => (buffer1 += 'e'))
56
+ .onComplete(() => (buffer1 += 'c'))
57
+ .subscribe()
58
+
59
+ op.fail()
60
+ op.fail() // no effect
61
+ op.success(10) // no effect
62
+ op.success(20) // no effect
63
+
64
+ op.pipe()
65
+ .onReceive(value => (buffer2 += value + ''))
66
+ .onError(() => (buffer2 += 'e'))
67
+ .onComplete(() => (buffer2 += 'c'))
68
+ .subscribe()
69
+
70
+ expect(buffer1).toBe('ec') // true
71
+ expect(buffer2).toBe('ec') // true
72
+ ```
73
+
74
+ ## RXSubject
75
+ It has a default value. If you do not want to have a default value use RXEmitter instead.
76
+
77
+ ```ts
78
+ const rx = new RXSubject(0)
79
+ let buffer = -1
80
+
81
+ rx.pipe()
82
+ .onReceive(v => buffer = v)
83
+ .subscribe()
84
+
85
+ expect(buffer).toBe(0) // true
86
+
87
+ rx.send(1)
88
+ expect(buffer).toBe(1) // true
89
+
90
+ rx.sendComplete()
91
+ expect(buffer).toBe(1) // true
92
+
93
+ rx.send(2) // no effect after complete
94
+ expect(buffer).toBe(1) // true
95
+ ```
96
+
97
+ ## Map Operator
98
+ ```ts
99
+ const rx = new RXEmitter<string, never>()
100
+ let buffer = ''
101
+
102
+ rx.pipe()
103
+ .map(v => v.toUpperCase())
104
+ .map(v => v ? v : '-')
105
+ .onReceive(v => buffer += v)
106
+ .subscribe()
107
+
108
+ expect(buffer).toBe('') // true
109
+
110
+ rx.send('a')
111
+ rx.send('b')
112
+ rx.send('')
113
+ rx.send('c')
114
+ expect(buffer).toBe('AB-C') // true
115
+ ```
116
+
117
+ ## Filter Operator
118
+ ```ts
119
+ const rx = new RXSubject(0)
120
+ let buffer = ''
121
+
122
+ rx.pipe()
123
+ .removeDuplicates()
124
+ .filter(v => v % 2 === 0)
125
+ .map(v => v + '')
126
+ .filter(v => v.length === 1)
127
+ .onReceive(v => buffer += v)
128
+ .subscribe()
129
+
130
+ rx.send(0)
131
+ rx.send(0)
132
+ rx.send(1)
133
+ rx.send(1)
134
+ rx.send(2)
135
+ rx.send(3)
136
+ rx.send(4)
137
+ rx.send(5)
138
+ rx.send(6)
139
+ rx.send(7)
140
+ rx.send(8)
141
+ rx.send(9)
142
+ rx.send(10)
143
+ rx.send(11)
144
+ rx.send(12)
145
+
146
+ expect(buffer).toBe('02468') // true
147
+ ```
148
+
149
+ ## SkipNullable Operator
150
+ ```ts
151
+ const rx = new RXSubject<number | undefined | null, never>(0)
152
+ let buffer = ''
153
+
154
+ rx.pipe()
155
+ .removeDuplicates()
156
+ .skipNullable()
157
+ .map(v => v + '')
158
+ .onReceive(v => buffer += v)
159
+ .subscribe()
160
+
161
+ rx.send(0)
162
+ rx.send(1)
163
+ rx.send(undefined)
164
+ rx.send(2)
165
+ rx.send(2)
166
+ rx.send(3)
167
+ rx.send(null)
168
+ rx.send(4)
169
+ rx.send(5)
170
+
171
+ expect(buffer).toBe('012345') // true
172
+ ```
173
+
174
+ ## FlatMap Operator
175
+ Transforms all elements from an upstream publisher into a new publisher.
176
+
177
+ ```ts
178
+ class Task {
179
+ readonly $isDone = new RXObservableValue(false)
180
+ }
181
+
182
+ const task = new Task()
183
+ const $selectedTask = new RXObservableValue<Task | undefined>(undefined)
184
+ let buffer = ''
185
+
186
+ $selectedTask.pipe()
187
+ .skipNullable()
188
+ .flatMap(t => t.$isDone)
189
+ .onReceive(isDone => buffer = isDone + '')
190
+ .subscribe()
191
+
192
+ expect(buffer).toBe('') // no submission: selectedTask is undefined
193
+
194
+ $selectedTask.value = task
195
+ expect(buffer).toBe('false') // buffer is updated with default value false
196
+
197
+ task.$isDone.value = true
198
+ expect(buffer).toBe('true') // buffer is updated with value true
199
+ ```
200
+
201
+ ## More examples
202
+ You can find more examples in the test-folder: [https://github.com/Dittner/Flinker/tree/main/test/rx](https://github.com/Dittner/Flinker/tree/main/test/rx)
203
+
204
+ ## Install
205
+ ```code
3
206
  npm i flinker
4
207
  ```
5
208
 
6
- # License
209
+ ## License
7
210
  MIT
package/dist/esm/RX.js CHANGED
@@ -1,8 +1,5 @@
1
1
  import { RXCombine, RXDelayedComplete, RXDelayedError, RXFrom, RXJustComplete, RXJustError, RXQueue, RXWaitUntilComplete } from './RXPublisher';
2
2
  export class RX {
3
- //--------------------------------------
4
- // STATIC METHODS
5
- //--------------------------------------
6
3
  static combine(...list) {
7
4
  return new RXCombine(list).asObservable;
8
5
  }
@@ -1,21 +1,4 @@
1
1
  import { RXPipeline } from './RXPipeline';
2
- //WeakRef is not supported in RN
3
- // export class RXPipelineGarbage {
4
- // static self = new RXPipelineGarbage()
5
- // private readonly list = Array<WeakRef<RXAnyPipeline>>()
6
- // private total = 0
7
- //
8
- // register(p: RXAnyPipeline) {
9
- // this.list.push(new WeakRef(p))
10
- // this.total++
11
- // this.log()
12
- // }
13
- //
14
- // log() {
15
- // const disposedCount = this.list.reduce((res, ref) => ref.deref() ? res + 1 : res, 0)
16
- // Logger.i('GC: disposed pipelines:', disposedCount + '/' + this.total)
17
- // }
18
- // }
19
2
  const generateSUID = (() => {
20
3
  let value = 0;
21
4
  return () => {
@@ -40,7 +23,6 @@ export class RXPublisher {
40
23
  pipe() {
41
24
  const pipe = new RXPipeline(this);
42
25
  this.pipelines.push(pipe);
43
- //RXPipelineGarbage.self.register(pipe)
44
26
  return pipe.asOperator;
45
27
  }
46
28
  didSubscribe(p) {
@@ -24,7 +24,7 @@ export class RXSubscriber {
24
24
  }
25
25
  onReceive(f) {
26
26
  if (this.isComplete)
27
- throw new Error('RXSubscriber is complete: It can update onReceiveCallback');
27
+ throw new Error('RXSubscriber is complete: It can not update onReceiveCallback');
28
28
  else if (this.isSubscribed)
29
29
  throw new Error('RXSubscriber can not update onReceiveCallback: subscribe() is already evoked');
30
30
  this.onReceiveCallback = f;
@@ -1,16 +1,16 @@
1
1
  import { type RXAnyOperatorProtocol, type RXOperatorProtocol } from './RXOperator';
2
2
  import { type RXAnyPipeline } from './RXPipeline';
3
3
  import { type RXObject, type RXObjectType } from './RX';
4
+ export type SessionUID = number;
4
5
  export type AnyRXObservable = RXObservable<any, any>;
5
6
  export interface RXObservable<V, E> extends RXObject {
6
- suid: RXPublisherUID;
7
+ suid: SessionUID;
7
8
  isComplete: boolean;
8
9
  pipe(): RXOperatorProtocol<V, E>;
9
10
  }
10
- export type RXPublisherUID = number;
11
11
  export type RXAnyPublisher = RXPublisher<any, any>;
12
12
  export declare class RXPublisher<V, E> implements RXObservable<V, E> {
13
- readonly suid: RXPublisherUID;
13
+ readonly suid: number;
14
14
  readonly type: RXObjectType;
15
15
  protected readonly pipelines: RXAnyPipeline[];
16
16
  constructor();
@@ -1,5 +1,5 @@
1
1
  export { RXAnySender, RXSender, RX, } from "./RX";
2
- export { AnyRXObservable, RXObservable, RXPublisherUID, RXPublisher, RXJustComplete, RXJustError, RXDelayedComplete, RXDelayedError, RXEmitter, RXSubject, RXBuffer, RXOperation, RXCombine, RXFrom, RXWaitUntilComplete, RXObservableEntity, RXObservableValue, RXAnyQueueOperator, RXQueueOperator, RXQueue, } from "./RXPublisher";
2
+ export { AnyRXObservable, RXObservable, SessionUID, RXPublisher, RXJustComplete, RXJustError, RXDelayedComplete, RXDelayedError, RXEmitter, RXSubject, RXBuffer, RXOperation, RXCombine, RXFrom, RXWaitUntilComplete, RXObservableEntity, RXObservableValue, RXAnyQueueOperator, RXQueueOperator, RXQueue, } from "./RXPublisher";
3
3
  export { RXOperatorProtocol, RXAnyOperator, RXOperator, RXMap, RXFlatMap, RXForEach, RXSequent, RXParallel, RXFilter, RXSpread, RXSkipFirst, RXSkipNullable, RXRemoveDuplicates, RXDebounce, RXReplaceError, } from "./RXOperator";
4
4
  export { RXAnyPipeline, RXPipeline } from "./RXPipeline";
5
5
  export { ErrorMethod, CompleteMethod, SubscribeMethod, RXAnySubscriber, RXSubscriber } from "./RXSubscriber";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "flinker",
3
3
  "description": "RX.ts lib for building frontend apps",
4
- "version": "2.0.3",
4
+ "version": "2.0.4",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Dittner/Flinker.git"
@@ -16,7 +16,6 @@
16
16
  },
17
17
  "homepage": "https://github.com/Dittner/Flinker",
18
18
  "private": false,
19
- "main": "./dist/cjs/index.js",
20
19
  "module": "./dist/esm/index.js",
21
20
  "types": "./dist/types/index.d.ts",
22
21
  "files": [
@@ -33,10 +32,6 @@
33
32
  "build:cjs": "tsc --module commonjs --target es5 --outDir dist/cjs"
34
33
  },
35
34
  "eslintConfig": {
36
- "extends": [
37
- "react-app",
38
- "react-app/jest"
39
- ]
40
35
  },
41
36
  "browserslist": {
42
37
  "production": [
@@ -52,7 +47,6 @@
52
47
  },
53
48
  "devDependencies": {
54
49
  "@testing-library/jest-dom": "^6.6.3",
55
- "@testing-library/react": "^16.2.0",
56
50
  "@testing-library/user-event": "^14.6.1",
57
51
  "@types/jest": "^29.5.14",
58
52
  "@types/node": "^22.13.13",
@@ -60,9 +54,7 @@
60
54
  "jest": "^29.7.0",
61
55
  "jest-environment-jsdom": "^29.7.0",
62
56
  "ts-jest": "^29.3.0",
63
- "typescript": "^5.8.2",
64
- "@types/react": "^19.0.12",
65
- "@types/react-dom": "^19.0.4"
57
+ "typescript": "^5.8.2"
66
58
  },
67
59
  "directories": {
68
60
  "test": "test"