evg_observable 3.0.1 → 3.1.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/.claudeignore +9 -0
- package/README.md +298 -21
- package/package.json +42 -18
- package/repo/evg_observable.js +1 -1
- package/repo/evg_observable.old.js +1 -0
- package/scripts/README.md +69 -0
- package/scripts/claude-full-check.sh +33 -0
- package/scripts/claude-pr-prep.sh +73 -0
- package/scripts/claude-review-files.sh +61 -0
- package/src/outLib/CoreTypes.d.ts +42 -0
- package/src/outLib/CoreTypes.js +2 -0
- package/src/outLib/FilterCollection.js +2 -1
- package/src/outLib/FilterTypes.d.ts +27 -0
- package/src/outLib/FilterTypes.js +2 -0
- package/src/outLib/FunctionLibs.js +5 -4
- package/src/outLib/Observable.d.ts +2 -1
- package/src/outLib/Observable.js +14 -5
- package/src/outLib/OrderedSubscribeObject.d.ts +3 -0
- package/src/outLib/OrderedSubscribeObject.js +9 -0
- package/src/outLib/Pipe.d.ts +8 -1
- package/src/outLib/Pipe.js +123 -3
- package/src/outLib/PipeTypes.d.ts +77 -0
- package/src/outLib/PipeTypes.js +2 -0
- package/src/outLib/SubscribeObject.js +19 -42
- package/src/outLib/SubscriptionTypes.d.ts +40 -0
- package/src/outLib/SubscriptionTypes.js +2 -0
- package/src/outLib/Types.d.ts +4 -178
- package/src/outLib/Types.js +18 -0
- package/src/outLib-esm/index.mjs +1 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# scripts/claude-pr-prep.sh
|
|
3
|
+
# Run tests and generate a PR description for the current branch.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./scripts/claude-pr-prep.sh # auto-detect base branch
|
|
7
|
+
# ./scripts/claude-pr-prep.sh dev # explicit base branch
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# Determine base branch
|
|
12
|
+
if [ -n "$1" ]; then
|
|
13
|
+
BASE="$1"
|
|
14
|
+
else
|
|
15
|
+
for branch in dev stage main master; do
|
|
16
|
+
if git rev-parse --verify "$branch" >/dev/null 2>&1; then
|
|
17
|
+
BASE="$branch"
|
|
18
|
+
break
|
|
19
|
+
fi
|
|
20
|
+
done
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ -z "$BASE" ]; then
|
|
24
|
+
echo "Error: no base branch found (tried: dev, stage, main, master)"
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
CURRENT=$(git branch --show-current)
|
|
29
|
+
|
|
30
|
+
echo "Preparing branch '$CURRENT' for PR into '$BASE'..."
|
|
31
|
+
echo ""
|
|
32
|
+
|
|
33
|
+
# Run tests
|
|
34
|
+
echo "=== Running tests ==="
|
|
35
|
+
TEST_OUTPUT=$(npm test 2>&1) || true
|
|
36
|
+
echo "$TEST_OUTPUT" | tail -5
|
|
37
|
+
echo ""
|
|
38
|
+
|
|
39
|
+
# Get commit log
|
|
40
|
+
COMMITS=$(git log "$BASE".."$CURRENT" --oneline 2>/dev/null)
|
|
41
|
+
|
|
42
|
+
if [ -z "$COMMITS" ]; then
|
|
43
|
+
echo "No commits between '$CURRENT' and '$BASE'. Nothing to prepare."
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Generate PR description
|
|
48
|
+
echo "=== Generating PR description ==="
|
|
49
|
+
echo ""
|
|
50
|
+
|
|
51
|
+
claude -p "Generate a pull request description for the EVG Observable library.
|
|
52
|
+
|
|
53
|
+
Branch: $CURRENT -> $BASE
|
|
54
|
+
|
|
55
|
+
Commits:
|
|
56
|
+
$COMMITS
|
|
57
|
+
|
|
58
|
+
Test results (last 5 lines):
|
|
59
|
+
$(echo "$TEST_OUTPUT" | tail -5)
|
|
60
|
+
|
|
61
|
+
Write a PR description in this format:
|
|
62
|
+
## Summary
|
|
63
|
+
<2-4 bullet points describing what changed and why>
|
|
64
|
+
|
|
65
|
+
## Changes
|
|
66
|
+
<list of key changes grouped by category>
|
|
67
|
+
|
|
68
|
+
## Test plan
|
|
69
|
+
<what was tested, test results summary>
|
|
70
|
+
|
|
71
|
+
Be concise and specific to this library." \
|
|
72
|
+
--print \
|
|
73
|
+
--max-turns 3
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# scripts/claude-review-files.sh
|
|
3
|
+
# Review current branch changes against base branch using Claude headless mode.
|
|
4
|
+
# Checks: correctness, types, naming, resource cleanup, regression risk.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./scripts/claude-review-files.sh # auto-detect base branch
|
|
8
|
+
# ./scripts/claude-review-files.sh dev # explicit base branch
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
# Determine base branch
|
|
13
|
+
if [ -n "$1" ]; then
|
|
14
|
+
BASE="$1"
|
|
15
|
+
else
|
|
16
|
+
for branch in dev stage main master; do
|
|
17
|
+
if git rev-parse --verify "$branch" >/dev/null 2>&1; then
|
|
18
|
+
BASE="$branch"
|
|
19
|
+
break
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if [ -z "$BASE" ]; then
|
|
25
|
+
echo "Error: no base branch found (tried: dev, stage, main, master)"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
CURRENT=$(git branch --show-current)
|
|
30
|
+
DIFF=$(git diff "$BASE"..."$CURRENT" 2>/dev/null)
|
|
31
|
+
|
|
32
|
+
if [ -z "$DIFF" ]; then
|
|
33
|
+
echo "No changes between '$CURRENT' and '$BASE'. Nothing to review."
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
echo "Reviewing branch '$CURRENT' against '$BASE'..."
|
|
38
|
+
echo ""
|
|
39
|
+
|
|
40
|
+
claude -p "You are reviewing a code diff for the EVG Observable library.
|
|
41
|
+
|
|
42
|
+
Base branch: $BASE
|
|
43
|
+
Current branch: $CURRENT
|
|
44
|
+
|
|
45
|
+
Here is the diff:
|
|
46
|
+
|
|
47
|
+
$DIFF
|
|
48
|
+
|
|
49
|
+
Review this diff. For each changed file evaluate:
|
|
50
|
+
- **Correctness**: Does the code do what it should?
|
|
51
|
+
- **Edge cases**: Boundary conditions handled? (null, undefined, empty arrays)
|
|
52
|
+
- **Type safety**: Proper TypeScript types, no unnecessary \`any\`
|
|
53
|
+
- **Naming**: PascalCase classes, camelCase methods, \`I\` prefix for interfaces, \`\$\` suffix for observables
|
|
54
|
+
- **Style**: 2-space indent, JSDoc on public methods
|
|
55
|
+
- **Resource cleanup**: Are subscriptions cleaned up? destroy()/unsubscribe() where needed?
|
|
56
|
+
- **Regression risk**: Could this break existing behavior?
|
|
57
|
+
|
|
58
|
+
Output a table of issues found with severity (critical/major/minor), file, line, and description.
|
|
59
|
+
If no issues found, say so. Be specific and concise." \
|
|
60
|
+
--print \
|
|
61
|
+
--max-turns 5
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type ICallback<T> = (value?: T) => any;
|
|
2
|
+
export type IErrorCallback = (errorData: any, errorMessage: any) => void;
|
|
3
|
+
export type IListener<T> = ICallback<T>;
|
|
4
|
+
export type IDestroy = {
|
|
5
|
+
destroy(): void;
|
|
6
|
+
isDestroyed: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type IOrder = {
|
|
9
|
+
order: number;
|
|
10
|
+
};
|
|
11
|
+
export type ISetObservableValue = {
|
|
12
|
+
next(value: any): void;
|
|
13
|
+
};
|
|
14
|
+
export type IPause = {
|
|
15
|
+
pause(): void;
|
|
16
|
+
resume(): void;
|
|
17
|
+
};
|
|
18
|
+
export type ISubscribeCounter = {
|
|
19
|
+
size(): number;
|
|
20
|
+
};
|
|
21
|
+
export type IStream<T> = {
|
|
22
|
+
of(value: T[]): void;
|
|
23
|
+
};
|
|
24
|
+
export type ISend<T> = {
|
|
25
|
+
send(value: T): void;
|
|
26
|
+
};
|
|
27
|
+
export type IChainContainer = {
|
|
28
|
+
chain: any[];
|
|
29
|
+
};
|
|
30
|
+
export type IPipePayload = {
|
|
31
|
+
isBreak: boolean;
|
|
32
|
+
isUnsubscribe: boolean;
|
|
33
|
+
isAvailable: boolean;
|
|
34
|
+
debounceMs: number;
|
|
35
|
+
debounceTimer: any;
|
|
36
|
+
debounceValue: any;
|
|
37
|
+
debounceIndex: number;
|
|
38
|
+
payload: any;
|
|
39
|
+
};
|
|
40
|
+
export type IChainCallback = (data: IPipePayload) => void;
|
|
41
|
+
export type ICombinedSubscriber<T> = IListener<T> | ISetObservableValue;
|
|
42
|
+
export type ISubscribeGroup<T> = ICombinedSubscriber<T> | ICombinedSubscriber<T>[];
|
|
@@ -15,7 +15,8 @@ class FilterCollection {
|
|
|
15
15
|
return this;
|
|
16
16
|
}
|
|
17
17
|
and(condition) {
|
|
18
|
-
return this.push((data) => condition(data.payload)
|
|
18
|
+
return this.push((data) => { if (condition(data.payload))
|
|
19
|
+
data.isAvailable = true; });
|
|
19
20
|
}
|
|
20
21
|
allOf(conditions) {
|
|
21
22
|
if (!Array.isArray(conditions))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FilterSwitchCase } from "./FilterCollection";
|
|
2
|
+
import { ICallback } from "./CoreTypes";
|
|
3
|
+
export type IFilterPayload = {
|
|
4
|
+
isBreak: boolean;
|
|
5
|
+
isAvailable: boolean;
|
|
6
|
+
payload: any;
|
|
7
|
+
};
|
|
8
|
+
export type IFilterChainCallback = (data: IFilterPayload) => void;
|
|
9
|
+
export type IFilterResponse = {
|
|
10
|
+
isOK: boolean;
|
|
11
|
+
payload: any;
|
|
12
|
+
};
|
|
13
|
+
export type IFilter<T> = {
|
|
14
|
+
and(condition: ICallback<any>): IFilterSetup<T>;
|
|
15
|
+
allOf(conditions: ICallback<any>[]): IFilterSetup<T>;
|
|
16
|
+
};
|
|
17
|
+
export type IFilterSetup<T> = IFilter<T> & IFilterSwitch<T>;
|
|
18
|
+
export type IFilterSwitch<T> = {
|
|
19
|
+
choice(): FilterSwitchCase<T>;
|
|
20
|
+
};
|
|
21
|
+
export type IFilterCase<T> = {
|
|
22
|
+
or(condition: ICallback<any>): IFilterCase<T>;
|
|
23
|
+
anyOf(conditions: ICallback<any>[]): IFilterCase<T>;
|
|
24
|
+
};
|
|
25
|
+
export type IAddFilter<T> = {
|
|
26
|
+
addFilter(): IFilterSetup<T>;
|
|
27
|
+
};
|
|
@@ -36,11 +36,12 @@ function quickDeleteFromArray(arr, component) {
|
|
|
36
36
|
}
|
|
37
37
|
function getListener(listenerGroup) {
|
|
38
38
|
if (Array.isArray(listenerGroup)) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const len = listenerGroup.length;
|
|
40
|
+
const group = new Array(len);
|
|
41
|
+
for (let i = 0; i < len; i++)
|
|
42
|
+
group[i] = wrapListener(listenerGroup[i]);
|
|
42
43
|
return (data) => {
|
|
43
|
-
for (let i = 0; i <
|
|
44
|
+
for (let i = 0; i < len; i++)
|
|
44
45
|
group[i](data);
|
|
45
46
|
};
|
|
46
47
|
}
|
|
@@ -8,7 +8,7 @@ export declare class Observable<T> implements IObserver<T>, IStream<T>, IAddFilt
|
|
|
8
8
|
protected process: boolean;
|
|
9
9
|
protected trash: ISubscriptionLike[];
|
|
10
10
|
protected filters: FilterCollection<T>;
|
|
11
|
-
protected _value: T;
|
|
11
|
+
protected _value: T | null;
|
|
12
12
|
constructor(value: T);
|
|
13
13
|
addFilter(errorHandler?: IErrorCallback): IFilterSetup<T>;
|
|
14
14
|
disable(): void;
|
|
@@ -21,6 +21,7 @@ export declare class Observable<T> implements IObserver<T>, IStream<T>, IAddFilt
|
|
|
21
21
|
unSubscribe(listener: ISubscriptionLike): void;
|
|
22
22
|
destroy(): void;
|
|
23
23
|
unsubscribeAll(): void;
|
|
24
|
+
protected clearDebounceTimers(): void;
|
|
24
25
|
getValue(): T | undefined;
|
|
25
26
|
size(): number;
|
|
26
27
|
subscribe(observer: ISubscribeGroup<T>, errorHandler?: IErrorCallback): ISubscriptionLike | undefined;
|
package/src/outLib/Observable.js
CHANGED
|
@@ -45,6 +45,12 @@ class Observable {
|
|
|
45
45
|
for (let i = 0; i < len; i++)
|
|
46
46
|
subs[i].send(value);
|
|
47
47
|
this.process = false;
|
|
48
|
+
if (this.killed) {
|
|
49
|
+
this.clearDebounceTimers();
|
|
50
|
+
this._value = null;
|
|
51
|
+
this.subs.length = 0;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
48
54
|
this.trash.length && this.clearTrash();
|
|
49
55
|
}
|
|
50
56
|
of(values) {
|
|
@@ -86,26 +92,29 @@ class Observable {
|
|
|
86
92
|
return;
|
|
87
93
|
this.killed = true;
|
|
88
94
|
if (!this.process) {
|
|
95
|
+
this.clearDebounceTimers();
|
|
89
96
|
this._value = null;
|
|
90
97
|
this.subs.length = 0;
|
|
91
|
-
return;
|
|
92
98
|
}
|
|
93
|
-
Promise.resolve().then(() => {
|
|
94
|
-
this._value = null;
|
|
95
|
-
this.subs.length = 0;
|
|
96
|
-
});
|
|
97
99
|
}
|
|
98
100
|
unsubscribeAll() {
|
|
99
101
|
if (this.killed)
|
|
100
102
|
return;
|
|
101
103
|
if (this.process) {
|
|
104
|
+
this.clearDebounceTimers();
|
|
102
105
|
const subs = this.subs;
|
|
103
106
|
for (let i = 0; i < subs.length; i++)
|
|
104
107
|
this.trash.push(subs[i]);
|
|
105
108
|
return;
|
|
106
109
|
}
|
|
110
|
+
this.clearDebounceTimers();
|
|
107
111
|
this.subs.length = 0;
|
|
108
112
|
}
|
|
113
|
+
clearDebounceTimers() {
|
|
114
|
+
const subs = this.subs;
|
|
115
|
+
for (let i = 0; i < subs.length; i++)
|
|
116
|
+
clearTimeout(subs[i].flow.debounceTimer);
|
|
117
|
+
}
|
|
109
118
|
getValue() {
|
|
110
119
|
if (this.killed)
|
|
111
120
|
return undefined;
|
|
@@ -7,4 +7,7 @@ export declare class OrderedSubscribeObject<T> extends SubscribeObject<T> implem
|
|
|
7
7
|
set order(value: number);
|
|
8
8
|
subscribe(observer: IListener<T> | ISetObservableValue, errorHandler?: IErrorCallback): IOrderedSubscriptionLike;
|
|
9
9
|
once(): IOrderedSubscribe<T>;
|
|
10
|
+
take(n: number): IOrderedSubscribe<T>;
|
|
11
|
+
skip(n: number): IOrderedSetup<T>;
|
|
12
|
+
scan<K>(fn: (accumulator: K, value: T) => K, seed: K): IOrderedSetup<K>;
|
|
10
13
|
}
|
|
@@ -25,5 +25,14 @@ class OrderedSubscribeObject extends SubscribeObject_1.SubscribeObject {
|
|
|
25
25
|
once() {
|
|
26
26
|
return super.once();
|
|
27
27
|
}
|
|
28
|
+
take(n) {
|
|
29
|
+
return super.take(n);
|
|
30
|
+
}
|
|
31
|
+
skip(n) {
|
|
32
|
+
return super.skip(n);
|
|
33
|
+
}
|
|
34
|
+
scan(fn, seed) {
|
|
35
|
+
return super.scan(fn, seed);
|
|
36
|
+
}
|
|
28
37
|
}
|
|
29
38
|
exports.OrderedSubscribeObject = OrderedSubscribeObject;
|
package/src/outLib/Pipe.d.ts
CHANGED
|
@@ -6,15 +6,22 @@ export declare abstract class Pipe<T> implements ISubscribe<T> {
|
|
|
6
6
|
abstract subscribe(listener: IListener<T> | ISetObservableValue, errorHandler?: IErrorCallback): ISubscriptionLike | undefined;
|
|
7
7
|
private push;
|
|
8
8
|
once(): ISubscribe<T>;
|
|
9
|
+
take(n: number): ISubscribe<T>;
|
|
10
|
+
skip(n: number): ISetup<T>;
|
|
9
11
|
unsubscribeBy(condition: ICallback<T>): ISetup<T>;
|
|
10
12
|
and(condition: ICallback<T>): ISetup<T>;
|
|
11
13
|
allOf(conditions: ICallback<any>[]): ISetup<T>;
|
|
12
14
|
choice(): PipeSwitchCase<T>;
|
|
13
15
|
map<K>(condition: ICallback<T>): ISetup<K>;
|
|
16
|
+
scan<K>(fn: (accumulator: K, value: T) => K, seed: K): ISetup<K>;
|
|
17
|
+
tap(fn: ICallback<T>): ISetup<T>;
|
|
18
|
+
throttle(ms: number): ISetup<T>;
|
|
19
|
+
debounce(ms: number): ISetup<T>;
|
|
20
|
+
distinctUntilChanged(comparator?: (previous: T, current: T) => boolean): ISetup<T>;
|
|
14
21
|
toJson(): ISetup<string>;
|
|
15
22
|
fromJson<K>(): ISetup<K>;
|
|
16
23
|
group(): IGroupSubscription<T>;
|
|
17
|
-
processChain(listener
|
|
24
|
+
processChain(listener?: IListener<T>): void;
|
|
18
25
|
}
|
|
19
26
|
export declare class PipeSwitchCase<T> extends SwitchCase<T, Pipe<T>, IPipeCase<T>> implements ISubscribe<T> {
|
|
20
27
|
subscribe(listener: IListener<T> | ISetObservableValue, errorHandler?: IErrorCallback): ISubscriptionLike | undefined;
|
package/src/outLib/Pipe.js
CHANGED
|
@@ -4,7 +4,16 @@ exports.PipeSwitchCase = exports.Pipe = void 0;
|
|
|
4
4
|
const AbstractSwitchCase_1 = require("./AbstractSwitchCase");
|
|
5
5
|
class Pipe {
|
|
6
6
|
chain = [];
|
|
7
|
-
flow = {
|
|
7
|
+
flow = {
|
|
8
|
+
isBreak: false,
|
|
9
|
+
isUnsubscribe: false,
|
|
10
|
+
isAvailable: false,
|
|
11
|
+
debounceMs: 0,
|
|
12
|
+
debounceTimer: 0,
|
|
13
|
+
debounceValue: undefined,
|
|
14
|
+
debounceIndex: 0,
|
|
15
|
+
payload: null
|
|
16
|
+
};
|
|
8
17
|
push(callback) {
|
|
9
18
|
this.chain.push(callback);
|
|
10
19
|
return this;
|
|
@@ -15,6 +24,33 @@ class Pipe {
|
|
|
15
24
|
data.isUnsubscribe = true;
|
|
16
25
|
});
|
|
17
26
|
}
|
|
27
|
+
take(n) {
|
|
28
|
+
if (n < 0)
|
|
29
|
+
n = 0;
|
|
30
|
+
let count = 0;
|
|
31
|
+
return this.push((data) => {
|
|
32
|
+
if (count >= n) {
|
|
33
|
+
data.isUnsubscribe = true;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
count++;
|
|
37
|
+
this.listener(data.payload);
|
|
38
|
+
if (count >= n)
|
|
39
|
+
data.isUnsubscribe = true;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
skip(n) {
|
|
43
|
+
if (n < 0)
|
|
44
|
+
n = 0;
|
|
45
|
+
let count = 0;
|
|
46
|
+
return this.push((data) => {
|
|
47
|
+
if (count < n) {
|
|
48
|
+
count++;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
data.isAvailable = true;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
18
54
|
unsubscribeBy(condition) {
|
|
19
55
|
return this.push((data) => {
|
|
20
56
|
data.isAvailable = true;
|
|
@@ -23,7 +59,8 @@ class Pipe {
|
|
|
23
59
|
});
|
|
24
60
|
}
|
|
25
61
|
and(condition) {
|
|
26
|
-
return this.push((data) => condition(data.payload)
|
|
62
|
+
return this.push((data) => { if (condition(data.payload))
|
|
63
|
+
data.isAvailable = true; });
|
|
27
64
|
}
|
|
28
65
|
allOf(conditions) {
|
|
29
66
|
if (!Array.isArray(conditions))
|
|
@@ -41,6 +78,53 @@ class Pipe {
|
|
|
41
78
|
data.isAvailable = true;
|
|
42
79
|
});
|
|
43
80
|
}
|
|
81
|
+
scan(fn, seed) {
|
|
82
|
+
let accumulator = seed;
|
|
83
|
+
return this.push((data) => {
|
|
84
|
+
accumulator = fn(accumulator, data.payload);
|
|
85
|
+
data.payload = accumulator;
|
|
86
|
+
data.isAvailable = true;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
tap(fn) {
|
|
90
|
+
return this.push((data) => {
|
|
91
|
+
fn(data.payload);
|
|
92
|
+
data.isAvailable = true;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
throttle(ms) {
|
|
96
|
+
let lastEmitTime = 0;
|
|
97
|
+
return this.push((data) => {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
if (now - lastEmitTime >= ms) {
|
|
100
|
+
lastEmitTime = now;
|
|
101
|
+
data.isAvailable = true;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
debounce(ms) {
|
|
106
|
+
return this.push((data) => {
|
|
107
|
+
data.isAvailable = true;
|
|
108
|
+
data.debounceMs = ms;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
distinctUntilChanged(comparator) {
|
|
112
|
+
let hasPrevious = false;
|
|
113
|
+
let previousValue;
|
|
114
|
+
return this.push((data) => {
|
|
115
|
+
const current = data.payload;
|
|
116
|
+
if (hasPrevious) {
|
|
117
|
+
const isSame = comparator
|
|
118
|
+
? comparator(previousValue, current)
|
|
119
|
+
: previousValue === current;
|
|
120
|
+
if (isSame)
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
hasPrevious = true;
|
|
124
|
+
previousValue = current;
|
|
125
|
+
data.isAvailable = true;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
44
128
|
toJson() {
|
|
45
129
|
return this.push((data) => {
|
|
46
130
|
data.payload = JSON.stringify(data.payload);
|
|
@@ -63,15 +147,51 @@ class Pipe {
|
|
|
63
147
|
for (let i = 0; i < len; i++) {
|
|
64
148
|
data.isUnsubscribe = false;
|
|
65
149
|
data.isAvailable = false;
|
|
150
|
+
data.debounceMs = 0;
|
|
66
151
|
chain[i](data);
|
|
67
152
|
if (data.isUnsubscribe)
|
|
68
153
|
return this.unsubscribe();
|
|
154
|
+
if (data.debounceMs > 0) {
|
|
155
|
+
data.debounceValue = data.payload;
|
|
156
|
+
data.debounceIndex = i + 1;
|
|
157
|
+
const continueChain = () => {
|
|
158
|
+
data.debounceTimer = 0;
|
|
159
|
+
data.payload = data.debounceValue;
|
|
160
|
+
data.isBreak = false;
|
|
161
|
+
for (let j = data.debounceIndex; j < len; j++) {
|
|
162
|
+
data.isUnsubscribe = false;
|
|
163
|
+
data.isAvailable = false;
|
|
164
|
+
data.debounceMs = 0;
|
|
165
|
+
chain[j](data);
|
|
166
|
+
if (data.isUnsubscribe)
|
|
167
|
+
return this.unsubscribe();
|
|
168
|
+
if (data.debounceMs > 0) {
|
|
169
|
+
data.debounceValue = data.payload;
|
|
170
|
+
data.debounceIndex = j + 1;
|
|
171
|
+
clearTimeout(data.debounceTimer);
|
|
172
|
+
data.debounceTimer = setTimeout(continueChain, data.debounceMs);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (!data.isAvailable)
|
|
176
|
+
return;
|
|
177
|
+
if (data.isBreak)
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
if (listener)
|
|
181
|
+
listener(data.payload);
|
|
182
|
+
};
|
|
183
|
+
clearTimeout(data.debounceTimer);
|
|
184
|
+
data.debounceTimer = setTimeout(continueChain, data.debounceMs);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
69
187
|
if (!data.isAvailable)
|
|
70
188
|
return;
|
|
71
189
|
if (data.isBreak)
|
|
72
190
|
break;
|
|
73
191
|
}
|
|
74
|
-
|
|
192
|
+
data.isAvailable = true;
|
|
193
|
+
if (listener)
|
|
194
|
+
listener(data.payload);
|
|
75
195
|
}
|
|
76
196
|
}
|
|
77
197
|
exports.Pipe = Pipe;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { PipeSwitchCase } from "./Pipe";
|
|
2
|
+
import { ICallback, IPause, IOrder, ISend } from "./CoreTypes";
|
|
3
|
+
import { ISubscribe, IOrderedSubscribe, ISubscriptionLike, IGroupSubscription } from "./SubscriptionTypes";
|
|
4
|
+
export type ISwitch<T> = {
|
|
5
|
+
choice(): PipeSwitchCase<T>;
|
|
6
|
+
};
|
|
7
|
+
export type IGroup<T> = {
|
|
8
|
+
group(): IGroupSubscription<T>;
|
|
9
|
+
};
|
|
10
|
+
export type IOrderedGroup<T> = {
|
|
11
|
+
group(): IGroupSubscription<T>;
|
|
12
|
+
};
|
|
13
|
+
export type IOnce<T> = {
|
|
14
|
+
once(): ISubscribe<T>;
|
|
15
|
+
};
|
|
16
|
+
export type IOrderedOnce<T> = {
|
|
17
|
+
once(): IOrderedSubscribe<T>;
|
|
18
|
+
};
|
|
19
|
+
export type ITake<T> = {
|
|
20
|
+
take(n: number): ISubscribe<T>;
|
|
21
|
+
};
|
|
22
|
+
export type IOrderedTake<T> = {
|
|
23
|
+
take(n: number): IOrderedSubscribe<T>;
|
|
24
|
+
};
|
|
25
|
+
export type ISkip<T> = {
|
|
26
|
+
skip(n: number): ISetup<T>;
|
|
27
|
+
};
|
|
28
|
+
export type IOrderedSkip<T> = {
|
|
29
|
+
skip(n: number): IOrderedSetup<T>;
|
|
30
|
+
};
|
|
31
|
+
export type IScan<T> = {
|
|
32
|
+
scan<K>(fn: (accumulator: K, value: T) => K, seed: K): ISetup<K>;
|
|
33
|
+
};
|
|
34
|
+
export type IOrderedScan<T> = {
|
|
35
|
+
scan<K>(fn: (accumulator: K, value: T) => K, seed: K): IOrderedSetup<K>;
|
|
36
|
+
};
|
|
37
|
+
export type IUnsubscribeByPositive<T> = {
|
|
38
|
+
unsubscribeBy(condition: ICallback<T>): ISetup<T>;
|
|
39
|
+
};
|
|
40
|
+
export type IOrderedUnsubscribeByPositive<T> = {
|
|
41
|
+
unsubscribeBy(condition: ICallback<T>): ISetup<T>;
|
|
42
|
+
};
|
|
43
|
+
export type IEmitByPositive<T> = {
|
|
44
|
+
and(condition: ICallback<T>): ISetup<T>;
|
|
45
|
+
allOf(conditions: ICallback<T>[]): ISetup<T>;
|
|
46
|
+
};
|
|
47
|
+
export type IOrderedEmitByPositive<T> = {
|
|
48
|
+
and(condition: ICallback<any>): ISetup<T>;
|
|
49
|
+
allOf(conditions: ICallback<any>[]): ISetup<T>;
|
|
50
|
+
};
|
|
51
|
+
export type ITransform<T> = {
|
|
52
|
+
map<K>(condition: ICallback<T>): ISetup<K>;
|
|
53
|
+
};
|
|
54
|
+
export type IThrottle<T> = {
|
|
55
|
+
throttle(ms: number): ISetup<T>;
|
|
56
|
+
};
|
|
57
|
+
export type IDebounce<T> = {
|
|
58
|
+
debounce(ms: number): ISetup<T>;
|
|
59
|
+
};
|
|
60
|
+
export type IDistinctUntilChanged<T> = {
|
|
61
|
+
distinctUntilChanged(comparator?: (previous: T, current: T) => boolean): ISetup<T>;
|
|
62
|
+
};
|
|
63
|
+
export type ITap<T> = {
|
|
64
|
+
tap(fn: ICallback<T>): ISetup<T>;
|
|
65
|
+
};
|
|
66
|
+
export type ISerialisation = {
|
|
67
|
+
toJson(): ISetup<string>;
|
|
68
|
+
fromJson<K>(): ISetup<K>;
|
|
69
|
+
};
|
|
70
|
+
export type ISetup<T> = IUnsubscribeByPositive<T> & IEmitByPositive<T> & IOnce<T> & ITake<T> & ISkip<T> & IScan<T> & ISwitch<T> & ITransform<T> & IThrottle<T> & IDebounce<T> & IDistinctUntilChanged<T> & ITap<T> & ISerialisation & IGroup<T> & ISubscribe<T>;
|
|
71
|
+
export type IOrderedSetup<T> = IOrderedUnsubscribeByPositive<T> & IOrderedEmitByPositive<T> & IOrderedOnce<T> & IOrderedTake<T> & IOrderedSkip<T> & IOrderedScan<T> & ISwitch<T> & ITransform<T> & IThrottle<T> & IDebounce<T> & IDistinctUntilChanged<T> & ITap<T> & ISerialisation & IOrderedGroup<T> & IOrderedSubscribe<T>;
|
|
72
|
+
export type ISubscribeObject<T> = ISubscriptionLike & IPause & IOrder & ISend<T> & ISetup<T>;
|
|
73
|
+
export type IPipeCase<T> = ISubscribe<T> & {
|
|
74
|
+
or(condition: ICallback<any>): IPipeCase<T> & ISubscribe<T>;
|
|
75
|
+
anyOf(conditions: ICallback<any>[]): IPipeCase<T> & ISubscribe<T>;
|
|
76
|
+
group(): IGroupSubscription<T>;
|
|
77
|
+
};
|