synstate 3.0.5 → 3.0.6
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 +1 -1
- package/dist/core/base/create-observable-base.d.mts +51 -6
- package/dist/core/base/create-observable-base.d.mts.map +1 -1
- package/dist/core/base/create-observable-base.mjs +48 -17
- package/dist/core/base/create-observable-base.mjs.map +1 -1
- package/package.json +4 -4
- package/src/core/base/create-observable-base.mts +117 -26
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- 🎯 **Simple State Management**: Easy-to-use `createState` and `createReducer` similar to React useState/useReducer for global state
|
|
25
25
|
- ⚡ **High Performance**: Optimized for fast state updates and minimal re-renders
|
|
26
26
|
- 🎨 **Type-Safe**: Full TypeScript support with precise type inference
|
|
27
|
-
- 🚀 **Lightweight**: <!-- bundle-size:synstate -->~4.
|
|
27
|
+
- 🚀 **Lightweight**: <!-- bundle-size:synstate -->~4.7 kB min+gzip<!-- /bundle-size:synstate --> with two external runtime dependencies ([ts-data-forge](https://www.npmjs.com/package/ts-data-forge) and [ts-std-forge](https://www.npmjs.com/package/ts-std-forge), and the first depends on the second)
|
|
28
28
|
- 🌐 **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JavaScript
|
|
29
29
|
- 🔄 **Reactive Updates**: Automatic propagation of state changes to all subscribers
|
|
30
30
|
- 📡 **Event System**: Built-in `createValueEmitter`, `createEventEmitter` for event-driven architecture
|
|
@@ -15,12 +15,42 @@ import { type ChildObservable, type ObservableBase, type ObservableId, type Obse
|
|
|
15
15
|
*/
|
|
16
16
|
export type ObservableBaseHandle<A> = Readonly<{
|
|
17
17
|
id: ObservableId;
|
|
18
|
+
/**
|
|
19
|
+
* Registers the public object {@link assembleObservable} built from this
|
|
20
|
+
* handle, so that its four state flags (`isCompleted`, `updateToken`,
|
|
21
|
+
* `hasSubscriber`, `hasChild`) can be mirrored onto it as plain data
|
|
22
|
+
* properties.
|
|
23
|
+
*
|
|
24
|
+
* They used to be getters delegating to this handle's closures, and that
|
|
25
|
+
* costs more than it looks. Reading one was an accessor call plus a closure
|
|
26
|
+
* call where a field read would do — `updateToken` is read once per child
|
|
27
|
+
* per update, so the propagation path paid it on every step — and an object
|
|
28
|
+
* literal carrying accessors is far more expensive to create than one
|
|
29
|
+
* carrying data, which is most of what building a graph costs. Measured on
|
|
30
|
+
* one machine with `pnpm run benchmark`: the derived chain's 100,000 updates
|
|
31
|
+
* 16.7 ms -> 13.6 ms, and the cascaded diamond at N=20, whose measurement is
|
|
32
|
+
* dominated by graph construction, 587 ms -> 362 ms.
|
|
33
|
+
*
|
|
34
|
+
* Every write below therefore has to keep the mirror in step; that is the
|
|
35
|
+
* price of not reading through a closure on the hot path. It is paid in
|
|
36
|
+
* clarity too, and on purpose: `hasSubscriber` and `hasChild` are derived
|
|
37
|
+
* from `mut_subscribers` and `mut_children`, which a getter stated once and
|
|
38
|
+
* declaratively, whereas the mirror restates it at every write.
|
|
39
|
+
*
|
|
40
|
+
* Nor can the rarely read flags alone go back to getters while `updateToken`
|
|
41
|
+
* stays data. A single accessor in the literal is enough to lose nearly all
|
|
42
|
+
* of the gain: measured on Node 26 with the `...extra` spread followed by
|
|
43
|
+
* these properties, one getter among data properties made the literal about
|
|
44
|
+
* 10x slower to create than all data (four getters: about 15x) and gave
|
|
45
|
+
* every instance its own hidden class (`%HaveSameMap` false). Keep all four
|
|
46
|
+
* as data, or measure again before changing that.
|
|
47
|
+
*/
|
|
48
|
+
attach: (writers: ObservableStateWriters) => void;
|
|
18
49
|
addChild: <B>(child: ChildObservable<B>) => void;
|
|
19
50
|
getSnapshot: () => Optional<A>;
|
|
20
51
|
isCompleted: () => boolean;
|
|
21
52
|
updateToken: () => UpdateToken;
|
|
22
53
|
hasSubscriber: () => boolean;
|
|
23
|
-
hasChild: () => boolean;
|
|
24
54
|
hasActiveChild: () => boolean;
|
|
25
55
|
subscribe: (onNext: (v: A) => void, onComplete?: () => void) => Subscription;
|
|
26
56
|
/** Stores `nextValue` as the current value and notifies subscribers. */
|
|
@@ -33,6 +63,19 @@ export type ObservableBaseHandle<A> = Readonly<{
|
|
|
33
63
|
*/
|
|
34
64
|
completeBase: () => void;
|
|
35
65
|
}>;
|
|
66
|
+
/**
|
|
67
|
+
* How the handle writes the four state flags onto the public object. The
|
|
68
|
+
* writers are closures over that object rather than the object itself, because
|
|
69
|
+
* a parameter this side could mutate has to be a mutable type, which
|
|
70
|
+
* `@typescript-eslint/prefer-readonly-parameter-types` rejects — and the object
|
|
71
|
+
* is `Readonly` to everyone else.
|
|
72
|
+
*/
|
|
73
|
+
type ObservableStateWriters = Readonly<{
|
|
74
|
+
setIsCompleted: (value: boolean) => void;
|
|
75
|
+
setUpdateToken: (value: UpdateToken) => void;
|
|
76
|
+
setHasSubscriber: (value: boolean) => void;
|
|
77
|
+
setHasChild: (value: boolean) => void;
|
|
78
|
+
}>;
|
|
36
79
|
export declare const createObservableBaseHandle: <A>(initialValue: Optional<A>) => ObservableBaseHandle<A>;
|
|
37
80
|
/**
|
|
38
81
|
* The extra members every manager observable — a root or an async child — owns:
|
|
@@ -63,17 +106,19 @@ type AssembleObservableArgs<A, Kind extends ObservableKind, Extra extends Readon
|
|
|
63
106
|
/**
|
|
64
107
|
* Kind-specific public members (`parents`, `addDescendant`) and leaf
|
|
65
108
|
* extensions (`next`, `start`). They are merged here, in the one place the
|
|
66
|
-
* final object literal is created, because the
|
|
67
|
-
*
|
|
109
|
+
* final object literal is created, because the handle keeps the state flags
|
|
110
|
+
* up to date on that object alone: a copy made by spreading it afterwards
|
|
111
|
+
* would freeze their current values.
|
|
68
112
|
*/
|
|
69
113
|
extra: Extra;
|
|
70
114
|
}>;
|
|
71
115
|
/**
|
|
72
116
|
* Builds the public observable object from a base handle. This replaces the
|
|
73
117
|
* implicit assembly a `class` performs: state stays in the handle's closures,
|
|
74
|
-
* `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become
|
|
75
|
-
*
|
|
76
|
-
* `tryComplete`, `complete`) is passed in already
|
|
118
|
+
* `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become data
|
|
119
|
+
* properties the handle keeps in step through `attach`, and the kind-specific
|
|
120
|
+
* behavior (`tryUpdate`, `tryComplete`, `complete`) is passed in already
|
|
121
|
+
* composed.
|
|
77
122
|
*/
|
|
78
123
|
export declare const assembleObservable: <A, Kind extends ObservableKind, Extra extends ReadonlyRecord<string, unknown>>({ kind, depth, handle, tryUpdate, tryComplete, complete, extra, }: AssembleObservableArgs<A, Kind, Extra>) => Extra & ObservableBase<A> & Readonly<{
|
|
79
124
|
kind: Kind;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-observable-base.d.mts","sourceRoot":"","sources":["../../../src/core/base/create-observable-base.mts"],"names":[],"mappings":"AAAA,OAAO,EAAO,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,eAAe,EAGpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EAInB,KAAK,YAAY,EACjB,KAAK,WAAW,EAEjB,MAAM,oBAAoB,CAAC;AAS5B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC7C,EAAE,EAAE,YAAY,CAAC;IAEjB,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAEjD,WAAW,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE/B,WAAW,EAAE,MAAM,OAAO,CAAC;IAE3B,WAAW,EAAE,MAAM,WAAW,CAAC;IAE/B,aAAa,EAAE,MAAM,OAAO,CAAC;IAE7B,
|
|
1
|
+
{"version":3,"file":"create-observable-base.d.mts","sourceRoot":"","sources":["../../../src/core/base/create-observable-base.mts"],"names":[],"mappings":"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"}
|
|
@@ -8,26 +8,43 @@ export const createObservableBaseHandle = (initialValue) => {
|
|
|
8
8
|
let mut_currentValue = initialValue;
|
|
9
9
|
let mut_isCompleted = false;
|
|
10
10
|
let mut_updateToken = issueUpdateToken();
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
let mut_writers;
|
|
18
|
+
const attach = (writers) => {
|
|
19
|
+
mut_writers = writers;
|
|
20
|
+
writers.setIsCompleted(mut_isCompleted);
|
|
21
|
+
writers.setUpdateToken(mut_updateToken);
|
|
22
|
+
writers.setHasSubscriber(mut_subscribers.size > 0);
|
|
23
|
+
writers.setHasChild(Arr.isNonEmpty(mut_children));
|
|
24
|
+
};
|
|
11
25
|
const addSubscriber = (s) => {
|
|
12
26
|
|
|
13
27
|
const subscriberId = issueSubscriberId();
|
|
14
28
|
mut_subscribers.set(subscriberId, s);
|
|
29
|
+
mut_writers?.setHasSubscriber(true);
|
|
15
30
|
return subscriberId;
|
|
16
31
|
};
|
|
17
32
|
const removeSubscriber = (subscriberId) => {
|
|
18
33
|
mut_subscribers.delete(subscriberId);
|
|
34
|
+
mut_writers?.setHasSubscriber(mut_subscribers.size > 0);
|
|
19
35
|
};
|
|
20
36
|
const addChild = (child) => {
|
|
21
37
|
mut_children = Arr.toPushed(mut_children, child);
|
|
38
|
+
mut_writers?.setHasChild(true);
|
|
22
39
|
};
|
|
23
40
|
const getSnapshot = () => mut_currentValue;
|
|
24
41
|
const isCompleted = () => mut_isCompleted;
|
|
25
42
|
const updateToken = () => mut_updateToken;
|
|
26
43
|
const hasSubscriber = () => mut_subscribers.size > 0;
|
|
27
|
-
const hasChild = () => Arr.isNonEmpty(mut_children);
|
|
28
44
|
const hasActiveChild = () => mut_children.some((c) => !c.isCompleted);
|
|
29
45
|
const setNext = (nextValue, nextUpdateToken) => {
|
|
30
46
|
mut_updateToken = nextUpdateToken;
|
|
47
|
+
mut_writers?.setUpdateToken(nextUpdateToken);
|
|
31
48
|
mut_currentValue = Optional.some(nextValue);
|
|
32
49
|
for (const s of mut_subscribers.values()) {
|
|
33
50
|
s.onNext(nextValue);
|
|
@@ -39,12 +56,14 @@ export const createObservableBaseHandle = (initialValue) => {
|
|
|
39
56
|
}
|
|
40
57
|
|
|
41
58
|
mut_isCompleted = true;
|
|
59
|
+
mut_writers?.setIsCompleted(true);
|
|
42
60
|
|
|
43
61
|
for (const s of mut_subscribers.values()) {
|
|
44
62
|
s.onComplete();
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
mut_subscribers.clear();
|
|
66
|
+
mut_writers?.setHasSubscriber(false);
|
|
48
67
|
|
|
49
68
|
for (const o of mut_children) {
|
|
50
69
|
o.tryComplete();
|
|
@@ -71,12 +90,12 @@ export const createObservableBaseHandle = (initialValue) => {
|
|
|
71
90
|
};
|
|
72
91
|
return {
|
|
73
92
|
id,
|
|
93
|
+
attach,
|
|
74
94
|
addChild,
|
|
75
95
|
getSnapshot,
|
|
76
96
|
isCompleted,
|
|
77
97
|
updateToken,
|
|
78
98
|
hasSubscriber,
|
|
79
|
-
hasChild,
|
|
80
99
|
hasActiveChild,
|
|
81
100
|
subscribe,
|
|
82
101
|
setNext,
|
|
@@ -128,32 +147,30 @@ const tryUpdateNotImplemented = (_updateToken) => {
|
|
|
128
147
|
|
|
129
148
|
|
|
130
149
|
|
|
150
|
+
|
|
131
151
|
export const assembleObservable = ({ kind, depth, handle, tryUpdate, tryComplete, complete, extra, }) => {
|
|
132
152
|
function pipe(operator) {
|
|
133
153
|
return operator(
|
|
134
154
|
|
|
135
155
|
|
|
136
|
-
|
|
156
|
+
mut_observable);
|
|
137
157
|
}
|
|
138
|
-
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
const mut_observable = {
|
|
139
164
|
...extra,
|
|
140
165
|
id: handle.id,
|
|
141
166
|
kind,
|
|
142
167
|
depth,
|
|
143
168
|
addChild: handle.addChild,
|
|
144
169
|
getSnapshot: handle.getSnapshot,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return handle.updateToken();
|
|
150
|
-
},
|
|
151
|
-
get hasSubscriber() {
|
|
152
|
-
return handle.hasSubscriber();
|
|
153
|
-
},
|
|
154
|
-
get hasChild() {
|
|
155
|
-
return handle.hasChild();
|
|
156
|
-
},
|
|
170
|
+
isCompleted: false,
|
|
171
|
+
updateToken: handle.updateToken(),
|
|
172
|
+
hasSubscriber: false,
|
|
173
|
+
hasChild: false,
|
|
157
174
|
hasActiveChild: handle.hasActiveChild,
|
|
158
175
|
tryUpdate,
|
|
159
176
|
tryComplete,
|
|
@@ -161,6 +178,20 @@ export const assembleObservable = ({ kind, depth, handle, tryUpdate, tryComplete
|
|
|
161
178
|
subscribe: handle.subscribe,
|
|
162
179
|
pipe,
|
|
163
180
|
};
|
|
164
|
-
|
|
181
|
+
handle.attach({
|
|
182
|
+
setIsCompleted: (value) => {
|
|
183
|
+
mut_observable.isCompleted = value;
|
|
184
|
+
},
|
|
185
|
+
setUpdateToken: (value) => {
|
|
186
|
+
mut_observable.updateToken = value;
|
|
187
|
+
},
|
|
188
|
+
setHasSubscriber: (value) => {
|
|
189
|
+
mut_observable.hasSubscriber = value;
|
|
190
|
+
},
|
|
191
|
+
setHasChild: (value) => {
|
|
192
|
+
mut_observable.hasChild = value;
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
return mut_observable;
|
|
165
196
|
};
|
|
166
197
|
//# sourceMappingURL=create-observable-base.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-observable-base.mjs","sourceRoot":"","sources":["../../../src/core/base/create-observable-base.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAoB3C,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,GACb,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-observable-base.mjs","sourceRoot":"","sources":["../../../src/core/base/create-observable-base.mts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synstate",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6",
|
|
4
4
|
"description": "Type-safe State Management Library for TypeScript/JavaScript",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@types/react": "19.3.0",
|
|
40
40
|
"@vitest/browser-playwright": "5.0.1",
|
|
41
41
|
"argparse": "3.0.2",
|
|
42
|
-
"eslint-config-typed": "5.15.
|
|
42
|
+
"eslint-config-typed": "5.15.1",
|
|
43
43
|
"eslint-plugin-ts-data-forge": "0.8.1",
|
|
44
44
|
"eslint-plugin-ts-fortress": "1.2.3",
|
|
45
45
|
"eslint-plugin-ts-type-forge": "0.8.4",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"prettier-plugin-packagejson": "3.0.2",
|
|
52
52
|
"react": "19.3.0",
|
|
53
53
|
"rxjs": "7.8.2",
|
|
54
|
-
"synstate-react-hooks": "3.0.
|
|
55
|
-
"synstate-react-hooks-compat": "3.0.
|
|
54
|
+
"synstate-react-hooks": "3.0.6",
|
|
55
|
+
"synstate-react-hooks-compat": "3.0.6",
|
|
56
56
|
"ts-repo-utils": "10.7.1",
|
|
57
57
|
"valtio": "2.3.2",
|
|
58
58
|
"zustand": "5.0.15"
|
|
@@ -42,6 +42,38 @@ import {
|
|
|
42
42
|
export type ObservableBaseHandle<A> = Readonly<{
|
|
43
43
|
id: ObservableId;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Registers the public object {@link assembleObservable} built from this
|
|
47
|
+
* handle, so that its four state flags (`isCompleted`, `updateToken`,
|
|
48
|
+
* `hasSubscriber`, `hasChild`) can be mirrored onto it as plain data
|
|
49
|
+
* properties.
|
|
50
|
+
*
|
|
51
|
+
* They used to be getters delegating to this handle's closures, and that
|
|
52
|
+
* costs more than it looks. Reading one was an accessor call plus a closure
|
|
53
|
+
* call where a field read would do — `updateToken` is read once per child
|
|
54
|
+
* per update, so the propagation path paid it on every step — and an object
|
|
55
|
+
* literal carrying accessors is far more expensive to create than one
|
|
56
|
+
* carrying data, which is most of what building a graph costs. Measured on
|
|
57
|
+
* one machine with `pnpm run benchmark`: the derived chain's 100,000 updates
|
|
58
|
+
* 16.7 ms -> 13.6 ms, and the cascaded diamond at N=20, whose measurement is
|
|
59
|
+
* dominated by graph construction, 587 ms -> 362 ms.
|
|
60
|
+
*
|
|
61
|
+
* Every write below therefore has to keep the mirror in step; that is the
|
|
62
|
+
* price of not reading through a closure on the hot path. It is paid in
|
|
63
|
+
* clarity too, and on purpose: `hasSubscriber` and `hasChild` are derived
|
|
64
|
+
* from `mut_subscribers` and `mut_children`, which a getter stated once and
|
|
65
|
+
* declaratively, whereas the mirror restates it at every write.
|
|
66
|
+
*
|
|
67
|
+
* Nor can the rarely read flags alone go back to getters while `updateToken`
|
|
68
|
+
* stays data. A single accessor in the literal is enough to lose nearly all
|
|
69
|
+
* of the gain: measured on Node 26 with the `...extra` spread followed by
|
|
70
|
+
* these properties, one getter among data properties made the literal about
|
|
71
|
+
* 10x slower to create than all data (four getters: about 15x) and gave
|
|
72
|
+
* every instance its own hidden class (`%HaveSameMap` false). Keep all four
|
|
73
|
+
* as data, or measure again before changing that.
|
|
74
|
+
*/
|
|
75
|
+
attach: (writers: ObservableStateWriters) => void;
|
|
76
|
+
|
|
45
77
|
addChild: <B>(child: ChildObservable<B>) => void;
|
|
46
78
|
|
|
47
79
|
getSnapshot: () => Optional<A>;
|
|
@@ -52,8 +84,6 @@ export type ObservableBaseHandle<A> = Readonly<{
|
|
|
52
84
|
|
|
53
85
|
hasSubscriber: () => boolean;
|
|
54
86
|
|
|
55
|
-
hasChild: () => boolean;
|
|
56
|
-
|
|
57
87
|
hasActiveChild: () => boolean;
|
|
58
88
|
|
|
59
89
|
subscribe: (onNext: (v: A) => void, onComplete?: () => void) => Subscription;
|
|
@@ -70,6 +100,20 @@ export type ObservableBaseHandle<A> = Readonly<{
|
|
|
70
100
|
completeBase: () => void;
|
|
71
101
|
}>;
|
|
72
102
|
|
|
103
|
+
/**
|
|
104
|
+
* How the handle writes the four state flags onto the public object. The
|
|
105
|
+
* writers are closures over that object rather than the object itself, because
|
|
106
|
+
* a parameter this side could mutate has to be a mutable type, which
|
|
107
|
+
* `@typescript-eslint/prefer-readonly-parameter-types` rejects — and the object
|
|
108
|
+
* is `Readonly` to everyone else.
|
|
109
|
+
*/
|
|
110
|
+
type ObservableStateWriters = Readonly<{
|
|
111
|
+
setIsCompleted: (value: boolean) => void;
|
|
112
|
+
setUpdateToken: (value: UpdateToken) => void;
|
|
113
|
+
setHasSubscriber: (value: boolean) => void;
|
|
114
|
+
setHasChild: (value: boolean) => void;
|
|
115
|
+
}>;
|
|
116
|
+
|
|
73
117
|
export const createObservableBaseHandle = <A,>(
|
|
74
118
|
initialValue: Optional<A>,
|
|
75
119
|
): ObservableBaseHandle<A> => {
|
|
@@ -85,17 +129,41 @@ export const createObservableBaseHandle = <A,>(
|
|
|
85
129
|
|
|
86
130
|
let mut_updateToken: UpdateToken = issueUpdateToken();
|
|
87
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Set once {@link assembleObservable} has built the public object. It does
|
|
134
|
+
* not exist while a leaf factory's `init` callback runs — that callback may
|
|
135
|
+
* already subscribe or complete — so every write below is optional, and
|
|
136
|
+
* `attach` copies the state as it stands rather than assuming defaults.
|
|
137
|
+
*/
|
|
138
|
+
let mut_writers: ObservableStateWriters | undefined;
|
|
139
|
+
|
|
140
|
+
const attach = (writers: ObservableStateWriters): void => {
|
|
141
|
+
mut_writers = writers;
|
|
142
|
+
|
|
143
|
+
writers.setIsCompleted(mut_isCompleted);
|
|
144
|
+
|
|
145
|
+
writers.setUpdateToken(mut_updateToken);
|
|
146
|
+
|
|
147
|
+
writers.setHasSubscriber(mut_subscribers.size > 0);
|
|
148
|
+
|
|
149
|
+
writers.setHasChild(Arr.isNonEmpty(mut_children));
|
|
150
|
+
};
|
|
151
|
+
|
|
88
152
|
const addSubscriber = (s: Subscriber<A>): SubscriberId => {
|
|
89
153
|
// return the id of added subscriber
|
|
90
154
|
const subscriberId = issueSubscriberId();
|
|
91
155
|
|
|
92
156
|
mut_subscribers.set(subscriberId, s);
|
|
93
157
|
|
|
158
|
+
mut_writers?.setHasSubscriber(true);
|
|
159
|
+
|
|
94
160
|
return subscriberId;
|
|
95
161
|
};
|
|
96
162
|
|
|
97
163
|
const removeSubscriber = (subscriberId: SubscriberId): void => {
|
|
98
164
|
mut_subscribers.delete(subscriberId);
|
|
165
|
+
|
|
166
|
+
mut_writers?.setHasSubscriber(mut_subscribers.size > 0);
|
|
99
167
|
};
|
|
100
168
|
|
|
101
169
|
const addChild = <B,>(child: ChildObservable<B>): void => {
|
|
@@ -104,6 +172,8 @@ export const createObservableBaseHandle = <A,>(
|
|
|
104
172
|
|
|
105
173
|
child as ChildObservable<unknown>,
|
|
106
174
|
);
|
|
175
|
+
|
|
176
|
+
mut_writers?.setHasChild(true);
|
|
107
177
|
};
|
|
108
178
|
|
|
109
179
|
const getSnapshot = (): Optional<A> => mut_currentValue;
|
|
@@ -114,14 +184,14 @@ export const createObservableBaseHandle = <A,>(
|
|
|
114
184
|
|
|
115
185
|
const hasSubscriber = (): boolean => mut_subscribers.size > 0;
|
|
116
186
|
|
|
117
|
-
const hasChild = (): boolean => Arr.isNonEmpty(mut_children);
|
|
118
|
-
|
|
119
187
|
const hasActiveChild = (): boolean =>
|
|
120
188
|
mut_children.some((c) => !c.isCompleted);
|
|
121
189
|
|
|
122
190
|
const setNext = (nextValue: A, nextUpdateToken: UpdateToken): void => {
|
|
123
191
|
mut_updateToken = nextUpdateToken;
|
|
124
192
|
|
|
193
|
+
mut_writers?.setUpdateToken(nextUpdateToken);
|
|
194
|
+
|
|
125
195
|
mut_currentValue = Optional.some(nextValue);
|
|
126
196
|
|
|
127
197
|
for (const s of mut_subscribers.values()) {
|
|
@@ -137,6 +207,8 @@ export const createObservableBaseHandle = <A,>(
|
|
|
137
207
|
// change state
|
|
138
208
|
mut_isCompleted = true;
|
|
139
209
|
|
|
210
|
+
mut_writers?.setIsCompleted(true);
|
|
211
|
+
|
|
140
212
|
// run subscribers for the current value
|
|
141
213
|
for (const s of mut_subscribers.values()) {
|
|
142
214
|
s.onComplete();
|
|
@@ -145,6 +217,8 @@ export const createObservableBaseHandle = <A,>(
|
|
|
145
217
|
// remove all subscribers
|
|
146
218
|
mut_subscribers.clear();
|
|
147
219
|
|
|
220
|
+
mut_writers?.setHasSubscriber(false);
|
|
221
|
+
|
|
148
222
|
// propagate to children
|
|
149
223
|
for (const o of mut_children) {
|
|
150
224
|
o.tryComplete();
|
|
@@ -183,12 +257,12 @@ export const createObservableBaseHandle = <A,>(
|
|
|
183
257
|
|
|
184
258
|
return {
|
|
185
259
|
id,
|
|
260
|
+
attach,
|
|
186
261
|
addChild,
|
|
187
262
|
getSnapshot,
|
|
188
263
|
isCompleted,
|
|
189
264
|
updateToken,
|
|
190
265
|
hasSubscriber,
|
|
191
|
-
hasChild,
|
|
192
266
|
hasActiveChild,
|
|
193
267
|
subscribe,
|
|
194
268
|
setNext,
|
|
@@ -282,8 +356,9 @@ type AssembleObservableArgs<
|
|
|
282
356
|
/**
|
|
283
357
|
* Kind-specific public members (`parents`, `addDescendant`) and leaf
|
|
284
358
|
* extensions (`next`, `start`). They are merged here, in the one place the
|
|
285
|
-
* final object literal is created, because the
|
|
286
|
-
*
|
|
359
|
+
* final object literal is created, because the handle keeps the state flags
|
|
360
|
+
* up to date on that object alone: a copy made by spreading it afterwards
|
|
361
|
+
* would freeze their current values.
|
|
287
362
|
*/
|
|
288
363
|
extra: Extra;
|
|
289
364
|
}>;
|
|
@@ -291,9 +366,10 @@ type AssembleObservableArgs<
|
|
|
291
366
|
/**
|
|
292
367
|
* Builds the public observable object from a base handle. This replaces the
|
|
293
368
|
* implicit assembly a `class` performs: state stays in the handle's closures,
|
|
294
|
-
* `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become
|
|
295
|
-
*
|
|
296
|
-
* `tryComplete`, `complete`) is passed in already
|
|
369
|
+
* `isCompleted` / `updateToken` / `hasSubscriber` / `hasChild` become data
|
|
370
|
+
* properties the handle keeps in step through `attach`, and the kind-specific
|
|
371
|
+
* behavior (`tryUpdate`, `tryComplete`, `complete`) is passed in already
|
|
372
|
+
* composed.
|
|
297
373
|
*/
|
|
298
374
|
export const assembleObservable = <
|
|
299
375
|
A,
|
|
@@ -323,11 +399,16 @@ export const assembleObservable = <
|
|
|
323
399
|
return operator(
|
|
324
400
|
// @sumi-expect-error banned-syntax/no-unsafe-type-assertion
|
|
325
401
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
326
|
-
|
|
402
|
+
mut_observable as unknown as InitializedObservable<A>,
|
|
327
403
|
);
|
|
328
404
|
}
|
|
329
405
|
|
|
330
|
-
|
|
406
|
+
// The four state flags are plain data properties kept in step by the handle,
|
|
407
|
+
// not getters delegating to it — see `ObservableBaseHandle.attach` for the
|
|
408
|
+
// measurements that decided this. `updateToken` in particular is read once
|
|
409
|
+
// per child per update, and `tryUpdate` reading a field rather than calling
|
|
410
|
+
// through an accessor is most of what the change buys.
|
|
411
|
+
const mut_observable = {
|
|
331
412
|
...extra,
|
|
332
413
|
|
|
333
414
|
id: handle.id,
|
|
@@ -340,21 +421,13 @@ export const assembleObservable = <
|
|
|
340
421
|
|
|
341
422
|
getSnapshot: handle.getSnapshot,
|
|
342
423
|
|
|
343
|
-
|
|
344
|
-
return handle.isCompleted();
|
|
345
|
-
},
|
|
424
|
+
isCompleted: false,
|
|
346
425
|
|
|
347
|
-
|
|
348
|
-
return handle.updateToken();
|
|
349
|
-
},
|
|
426
|
+
updateToken: handle.updateToken(),
|
|
350
427
|
|
|
351
|
-
|
|
352
|
-
return handle.hasSubscriber();
|
|
353
|
-
},
|
|
428
|
+
hasSubscriber: false,
|
|
354
429
|
|
|
355
|
-
|
|
356
|
-
return handle.hasChild();
|
|
357
|
-
},
|
|
430
|
+
hasChild: false,
|
|
358
431
|
|
|
359
432
|
hasActiveChild: handle.hasActiveChild,
|
|
360
433
|
|
|
@@ -367,7 +440,25 @@ export const assembleObservable = <
|
|
|
367
440
|
subscribe: handle.subscribe,
|
|
368
441
|
|
|
369
442
|
pipe,
|
|
370
|
-
}
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
handle.attach({
|
|
446
|
+
setIsCompleted: (value) => {
|
|
447
|
+
mut_observable.isCompleted = value;
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
setUpdateToken: (value) => {
|
|
451
|
+
mut_observable.updateToken = value;
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
setHasSubscriber: (value) => {
|
|
455
|
+
mut_observable.hasSubscriber = value;
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
setHasChild: (value) => {
|
|
459
|
+
mut_observable.hasChild = value;
|
|
460
|
+
},
|
|
461
|
+
});
|
|
371
462
|
|
|
372
|
-
return
|
|
463
|
+
return mut_observable;
|
|
373
464
|
};
|