subay 0.0.6 → 0.0.7

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/src/s.ts ADDED
@@ -0,0 +1,193 @@
1
+ interface IUpdater {
2
+ (): void;
3
+ _whoIOwn: Array<IUpdater>;
4
+ _whoINeed: Array<ITrigger>;
5
+ _dirty: boolean;
6
+ _onCleanupCallback: Array<() => void>;
7
+ }
8
+
9
+ interface ITrigger {
10
+ _whoNeedMe: Array<IUpdater>;
11
+ _data: IO<any>;
12
+ _pendingValue: any;
13
+ }
14
+
15
+ interface IS<T> {
16
+ (): T;
17
+ }
18
+
19
+ interface IO<T> {
20
+ (): T;
21
+ (nextValue: T): T;
22
+ }
23
+
24
+ let currentUpdate: IUpdater | undefined = undefined;
25
+ const NoValue: unique symbol = Symbol();
26
+ let triggerQueue: Array<ITrigger> | undefined = undefined;
27
+ const fun2s = new WeakMap<(...args: any[]) => any, IUpdater>();
28
+ class Reactive {}
29
+ class Observable extends Reactive {}
30
+ class Computation extends Reactive {}
31
+ const cProto = new Computation();
32
+ const oProto = new Observable();
33
+
34
+ export const root = <T>(fn: (destroy: () => void) => T): T => {
35
+ const backup = currentUpdate;
36
+ const rootUpdate: IUpdater = (() => {}) as any;
37
+ resetUpdate(rootUpdate);
38
+ rootUpdate._dirty = false;
39
+ currentUpdate = rootUpdate;
40
+ const result = fn(() => {
41
+ recycleUpdate(rootUpdate);
42
+ currentUpdate = undefined;
43
+ });
44
+ currentUpdate = backup;
45
+ return result;
46
+ };
47
+ export const S = <T>(fn: (pv: T | undefined) => T, value?: T): IS<T> => {
48
+ const data = () => {
49
+ if (update._dirty) {
50
+ update();
51
+ } else if (currentUpdate) {
52
+ update._whoINeed.forEach((t) => t._data());
53
+ }
54
+ return value as T;
55
+ };
56
+ Object.setPrototypeOf(data, cProto);
57
+ const update: IUpdater = (() => {
58
+ const backup = currentUpdate;
59
+ recycleUpdate(update);
60
+ update._dirty = false;
61
+ currentUpdate = update;
62
+ value = fn(value);
63
+ currentUpdate = backup;
64
+ }) as any;
65
+ if (currentUpdate) {
66
+ currentUpdate._whoIOwn.push(update);
67
+ }
68
+ resetUpdate(update);
69
+ update();
70
+ fun2s.set(fn, update);
71
+ return data;
72
+ };
73
+ export const o = <T>(value: T): IO<T> => {
74
+ function data(): T;
75
+ function data(nextValue: T): T;
76
+ function data(nextValue?: T) {
77
+ if (arguments.length === 0) {
78
+ if (currentUpdate) {
79
+ if (!currentUpdate._whoINeed.includes(trigger)) {
80
+ currentUpdate._whoINeed.push(trigger);
81
+ }
82
+ if (!trigger._whoNeedMe.includes(currentUpdate)) {
83
+ trigger._whoNeedMe.push(currentUpdate);
84
+ }
85
+ }
86
+ return value;
87
+ }
88
+
89
+ if (triggerQueue) {
90
+ if (trigger._pendingValue === NoValue) {
91
+ triggerQueue.push(trigger);
92
+ }
93
+ trigger._pendingValue = nextValue;
94
+ return nextValue;
95
+ }
96
+
97
+ value = nextValue!;
98
+
99
+ const backup = currentUpdate;
100
+ currentUpdate = undefined;
101
+
102
+ const whoNeedMeLasttime = trigger._whoNeedMe.filter((it) => {
103
+ return it._whoINeed.includes(trigger);
104
+ });
105
+ whoNeedMeLasttime.forEach((u) => {
106
+ u._dirty = true;
107
+ });
108
+ trigger._whoNeedMe = [];
109
+ whoNeedMeLasttime.forEach((u) => {
110
+ if (u._dirty) {
111
+ u();
112
+ }
113
+ });
114
+
115
+ currentUpdate = backup;
116
+
117
+ return value;
118
+ }
119
+
120
+ Object.setPrototypeOf(data, oProto);
121
+
122
+ const trigger: ITrigger = {
123
+ _whoNeedMe: [],
124
+ _data: data,
125
+ _pendingValue: NoValue,
126
+ };
127
+
128
+ return data;
129
+ };
130
+ const recycleUpdate = (update: IUpdater) => {
131
+ update._whoIOwn.forEach(recycleUpdate);
132
+ update._dirty = false;
133
+ update._onCleanupCallback.forEach((f) => f());
134
+ resetUpdate(update);
135
+ };
136
+
137
+ const resetUpdate = (update: IUpdater) => {
138
+ update._whoINeed = [];
139
+ update._whoIOwn = [];
140
+ update._onCleanupCallback = [];
141
+ };
142
+
143
+ export const cleanup = <T extends () => void>(f: T): T => {
144
+ if (currentUpdate) {
145
+ currentUpdate._onCleanupCallback.push(f);
146
+ }
147
+ return f;
148
+ };
149
+
150
+ export const transaction = <T>(f: () => T): T => {
151
+ const fallback = triggerQueue;
152
+ triggerQueue = [] as ITrigger[];
153
+ const result = f();
154
+ const workedQueue = triggerQueue;
155
+ triggerQueue = fallback;
156
+
157
+ workedQueue.forEach((t) => {
158
+ if (t._pendingValue !== NoValue) {
159
+ const pv = t._pendingValue;
160
+ t._pendingValue = NoValue;
161
+ t._data(pv);
162
+ }
163
+ });
164
+
165
+ return result;
166
+ };
167
+
168
+ export { o as observable };
169
+
170
+ export const sample = <T>(f: () => T): T => {
171
+ const fallback = currentUpdate;
172
+ currentUpdate = undefined;
173
+ const value = f();
174
+ currentUpdate = fallback;
175
+ return value;
176
+ };
177
+
178
+ export const isListening = () => !!currentUpdate;
179
+
180
+ export const subscribe = (f: () => void) => {
181
+ S(f);
182
+ return () => unsubscribe(f);
183
+ };
184
+ export const unsubscribe = (f: () => void) => {
185
+ const update = fun2s.get(f);
186
+ if (update) {
187
+ recycleUpdate(update);
188
+ }
189
+ };
190
+
191
+ export const isObservable = (o: any): o is IO<any> => o instanceof Observable;
192
+ export const isComputed = (o: any): o is IS<any> => o instanceof Computation;
193
+ export const isReactive = (o: any): o is IO<any> | IS<any> => o instanceof Reactive;