@tstdl/base 0.88.0-alpha1 → 0.88.0-alpha3
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/authentication/index.d.ts +1 -0
- package/authentication/index.js +1 -0
- package/browser/index.d.ts +1 -0
- package/browser/index.js +1 -0
- package/data-structures/index.d.ts +1 -0
- package/data-structures/index.js +1 -0
- package/http/client/adapters/index.d.ts +1 -0
- package/http/client/adapters/index.js +1 -0
- package/http/client/index.d.ts +2 -0
- package/http/client/index.js +2 -0
- package/mail/clients/index.d.ts +1 -0
- package/mail/clients/index.js +1 -0
- package/mail/index.d.ts +2 -0
- package/mail/index.js +2 -0
- package/mail/mail.service.d.ts +1 -1
- package/mail/mail.service.js +1 -1
- package/mail/module.d.ts +1 -1
- package/mail/module.js +1 -1
- package/mail/repositories/index.d.ts +1 -0
- package/mail/repositories/index.js +1 -0
- package/mail/repositories/mail-log.repository.d.ts +4 -0
- package/mail/{mail-log.repository.js → repositories/mail-log.repository.js} +1 -1
- package/mail/repositories/mongo/index.d.ts +1 -0
- package/mail/repositories/mongo/index.js +1 -0
- package/mail/repositories/{mongo-mail-log.repository.d.ts → mongo/mongo-mail-log.repository.d.ts} +6 -6
- package/mail/repositories/{mongo-mail-log.repository.js → mongo/mongo-mail-log.repository.js} +3 -3
- package/package.json +103 -6
- package/random/number-generator/index.d.ts +2 -0
- package/random/number-generator/index.js +2 -0
- package/random/number-generator/utils.d.ts +1 -0
- package/random/number-generator/utils.js +5 -0
- package/rpc/endpoints/index.d.ts +1 -0
- package/rpc/endpoints/index.js +1 -0
- package/rpc/endpoints/message-port.rpc-endpoint.js +1 -1
- package/signals/implementation/api.d.ts +1 -18
- package/signals/implementation/api.js +1 -10
- package/signals/implementation/computed.js +42 -94
- package/signals/implementation/effect.d.ts +1 -1
- package/signals/implementation/effect.js +16 -15
- package/signals/implementation/graph.d.ts +118 -73
- package/signals/implementation/graph.js +235 -156
- package/signals/implementation/signal.js +58 -81
- package/signals/implementation/watch.d.ts +2 -18
- package/signals/implementation/watch.js +37 -54
- package/sse/index.d.ts +1 -0
- package/sse/index.js +1 -0
- package/templates/providers/index.d.ts +2 -0
- package/templates/providers/index.js +2 -0
- package/templates/renderers/index.d.ts +4 -0
- package/templates/renderers/index.js +4 -0
- package/templates/resolvers/index.d.ts +3 -0
- package/templates/resolvers/index.js +3 -0
- package/templates/types/index.d.ts +1 -0
- package/templates/types/index.js +1 -0
- package/text/dynamic-text.model.js +3 -5
- package/theme/adapters/index.d.ts +2 -0
- package/theme/adapters/index.js +2 -0
- package/utils/index.d.ts +8 -0
- package/utils/index.js +8 -0
- package/async-iterator-symbol.d.ts +0 -1
- package/async-iterator-symbol.js +0 -6
- package/mail/mail-log.repository.d.ts +0 -4
- package/notification/api.d.ts +0 -15
- package/notification/api.js +0 -28
- package/notification/models/index.d.ts +0 -2
- package/notification/models/index.js +0 -2
- package/notification/models/notification-channel-job.model.d.ts +0 -3
- package/notification/models/notification-channel-job.model.js +0 -1
- package/notification/models/notification.model.d.ts +0 -45
- package/notification/models/notification.model.js +0 -103
- package/notification/module.d.ts +0 -9
- package/notification/module.js +0 -10
- package/notification/notification-channel.service.d.ts +0 -5
- package/notification/notification-channel.service.js +0 -2
- package/notification/notification.repository.d.ts +0 -5
- package/notification/notification.repository.js +0 -2
- package/notification/notification.service.d.ts +0 -8
- package/notification/notification.service.js +0 -36
|
@@ -6,182 +6,261 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Tracks the currently active reactive consumer (or `null` if there is no active
|
|
14
|
-
* consumer).
|
|
9
|
+
* The currently active consumer `ReactiveNode`, if running code in a reactive context.
|
|
10
|
+
*
|
|
11
|
+
* Change this via `setActiveConsumer`.
|
|
15
12
|
*/
|
|
16
13
|
let activeConsumer = null;
|
|
17
|
-
/**
|
|
18
|
-
* Whether the graph is currently propagating change notifications.
|
|
19
|
-
*/
|
|
20
14
|
let inNotificationPhase = false;
|
|
21
15
|
export function setActiveConsumer(consumer) {
|
|
22
16
|
const prev = activeConsumer;
|
|
23
17
|
activeConsumer = consumer;
|
|
24
18
|
return prev;
|
|
25
19
|
}
|
|
20
|
+
export const REACTIVE_NODE = {
|
|
21
|
+
version: 0,
|
|
22
|
+
dirty: false,
|
|
23
|
+
producerNode: undefined,
|
|
24
|
+
producerLastReadVersion: undefined,
|
|
25
|
+
producerIndexOfThis: undefined,
|
|
26
|
+
nextProducerIndex: 0,
|
|
27
|
+
liveConsumerNode: undefined,
|
|
28
|
+
liveConsumerIndexOfThis: undefined,
|
|
29
|
+
consumerAllowSignalWrites: false,
|
|
30
|
+
consumerIsAlwaysLive: false,
|
|
31
|
+
producerMustRecompute: () => false,
|
|
32
|
+
producerRecomputeValue: () => { },
|
|
33
|
+
consumerMarkedDirty: () => { },
|
|
34
|
+
};
|
|
26
35
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* Nodes can be producers of reactive values, consumers of other reactive values, or both.
|
|
30
|
-
*
|
|
31
|
-
* Producers are nodes that produce values, and can be depended upon by consumer nodes.
|
|
32
|
-
*
|
|
33
|
-
* Producers expose a monotonic `valueVersion` counter, and are responsible for incrementing this
|
|
34
|
-
* version when their value semantically changes. Some producers may produce their values lazily and
|
|
35
|
-
* thus at times need to be polled for potential updates to their value (and by extension their
|
|
36
|
-
* `valueVersion`). This is accomplished via the `onProducerUpdateValueVersion` method for
|
|
37
|
-
* implemented by producers, which should perform whatever calculations are necessary to ensure
|
|
38
|
-
* `valueVersion` is up to date.
|
|
39
|
-
*
|
|
40
|
-
* Consumers are nodes that depend on the values of producers and are notified when those values
|
|
41
|
-
* might have changed.
|
|
42
|
-
*
|
|
43
|
-
* Consumers do not wrap the reads they consume themselves, but rather can be set as the active
|
|
44
|
-
* reader via `setActiveConsumer`. Reads of producers that happen while a consumer is active will
|
|
45
|
-
* result in those producers being added as dependencies of that consumer node.
|
|
46
|
-
*
|
|
47
|
-
* The set of dependencies of a consumer is dynamic. Implementers expose a monotonically increasing
|
|
48
|
-
* `trackingVersion` counter, which increments whenever the consumer is about to re-run any reactive
|
|
49
|
-
* reads it needs and establish a new set of dependencies as a result.
|
|
50
|
-
*
|
|
51
|
-
* Producers store the last `trackingVersion` they've seen from `Consumer`s which have read them.
|
|
52
|
-
* This allows a producer to identify whether its record of the dependency is current or stale, by
|
|
53
|
-
* comparing the consumer's `trackingVersion` to the version at which the dependency was
|
|
54
|
-
* last observed.
|
|
36
|
+
* Called by implementations when a producer's signal is read.
|
|
55
37
|
*/
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Polls dependencies of a consumer to determine if they have actually changed.
|
|
82
|
-
*
|
|
83
|
-
* If this returns `false`, then even though the consumer may have previously been notified of a
|
|
84
|
-
* change, the values of its dependencies have not actually changed and the consumer should not
|
|
85
|
-
* rerun any reactions.
|
|
86
|
-
*/
|
|
87
|
-
consumerPollProducersForChange() {
|
|
88
|
-
for (const [producerId, edge] of this.producers) {
|
|
89
|
-
const producer = edge.producerNode.deref();
|
|
90
|
-
// On Safari < 16.1 deref can return null, we need to check for null also.
|
|
91
|
-
// See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
|
|
92
|
-
if (producer == null || edge.atTrackingVersion !== this.trackingVersion) {
|
|
93
|
-
// This dependency edge is stale, so remove it.
|
|
94
|
-
this.producers.delete(producerId);
|
|
95
|
-
producer?.consumers.delete(this.id);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (producer.producerPollStatus(edge.seenValueVersion)) {
|
|
99
|
-
// One of the dependencies reports a real value change.
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
38
|
+
export function producerAccessed(node) {
|
|
39
|
+
if (inNotificationPhase) {
|
|
40
|
+
throw new Error('Assertion error: signal read during notification phase');
|
|
41
|
+
}
|
|
42
|
+
if (activeConsumer === null) {
|
|
43
|
+
// Accessed outside of a reactive context, so nothing to record.
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// This producer is the `idx`th dependency of `activeConsumer`.
|
|
47
|
+
const idx = activeConsumer.nextProducerIndex++;
|
|
48
|
+
assertConsumerNode(activeConsumer);
|
|
49
|
+
if (idx < activeConsumer.producerNode.length && activeConsumer.producerNode[idx] !== node) {
|
|
50
|
+
// There's been a change in producers since the last execution of `activeConsumer`.
|
|
51
|
+
// `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
|
|
52
|
+
// replaced with `this`.
|
|
53
|
+
//
|
|
54
|
+
// If `activeConsumer` isn't live, then this is a no-op, since we can replace the producer in
|
|
55
|
+
// `activeConsumer.producerNode` directly. However, if `activeConsumer` is live, then we need
|
|
56
|
+
// to remove it from the stale producer's `liveConsumer`s.
|
|
57
|
+
if (consumerIsLive(activeConsumer)) {
|
|
58
|
+
const staleProducer = activeConsumer.producerNode[idx];
|
|
59
|
+
producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]);
|
|
60
|
+
// At this point, the only record of `staleProducer` is the reference at
|
|
61
|
+
// `activeConsumer.producerNode[idx]` which will be overwritten below.
|
|
102
62
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
63
|
+
}
|
|
64
|
+
if (activeConsumer.producerNode[idx] !== node) {
|
|
65
|
+
// We're a new dependency of the consumer (at `idx`).
|
|
66
|
+
activeConsumer.producerNode[idx] = node;
|
|
67
|
+
// If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
|
|
68
|
+
// placeholder value.
|
|
69
|
+
activeConsumer.producerIndexOfThis[idx] =
|
|
70
|
+
consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
|
|
71
|
+
}
|
|
72
|
+
activeConsumer.producerLastReadVersion[idx] = node.version;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Ensure this producer's `version` is up-to-date.
|
|
76
|
+
*/
|
|
77
|
+
export function producerUpdateValueVersion(node) {
|
|
78
|
+
if (consumerIsLive(node) && !node.dirty) {
|
|
79
|
+
// A live consumer will be marked dirty by producers, so a clean state means that its version
|
|
80
|
+
// is guaranteed to be up-to-date.
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
|
|
84
|
+
// None of our producers report a change since the last time they were read, so no
|
|
85
|
+
// recomputation of our value is necessary, and we can consider ourselves clean.
|
|
86
|
+
node.dirty = false;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
node.producerRecomputeValue(node);
|
|
90
|
+
// After recomputing the value, we're no longer dirty.
|
|
91
|
+
node.dirty = false;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Propagate a dirty notification to live consumers of this producer.
|
|
95
|
+
*/
|
|
96
|
+
export function producerNotifyConsumers(node) {
|
|
97
|
+
if (node.liveConsumerNode === undefined) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Prevent signal reads when we're updating the graph
|
|
101
|
+
const prev = inNotificationPhase;
|
|
102
|
+
inNotificationPhase = true;
|
|
103
|
+
try {
|
|
104
|
+
for (const consumer of node.liveConsumerNode) {
|
|
105
|
+
if (!consumer.dirty) {
|
|
106
|
+
consumerMarkDirty(consumer);
|
|
125
107
|
}
|
|
126
108
|
}
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
inNotificationPhase = prev;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
|
|
116
|
+
* based on the current consumer context.
|
|
117
|
+
*/
|
|
118
|
+
export function producerUpdatesAllowed() {
|
|
119
|
+
return activeConsumer?.consumerAllowSignalWrites !== false;
|
|
120
|
+
}
|
|
121
|
+
export function consumerMarkDirty(node) {
|
|
122
|
+
node.dirty = true;
|
|
123
|
+
producerNotifyConsumers(node);
|
|
124
|
+
node.consumerMarkedDirty?.(node);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Prepare this consumer to run a computation in its reactive context.
|
|
128
|
+
*
|
|
129
|
+
* Must be called by subclasses which represent reactive computations, before those computations
|
|
130
|
+
* begin.
|
|
131
|
+
*/
|
|
132
|
+
export function consumerBeforeComputation(node) {
|
|
133
|
+
node && (node.nextProducerIndex = 0);
|
|
134
|
+
return setActiveConsumer(node);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Finalize this consumer's state after a reactive computation has run.
|
|
138
|
+
*
|
|
139
|
+
* Must be called by subclasses which represent reactive computations, after those computations
|
|
140
|
+
* have finished.
|
|
141
|
+
*/
|
|
142
|
+
export function consumerAfterComputation(node, prevConsumer) {
|
|
143
|
+
setActiveConsumer(prevConsumer);
|
|
144
|
+
if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
|
|
145
|
+
node.producerLastReadVersion === undefined) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (consumerIsLive(node)) {
|
|
149
|
+
// For live consumers, we need to remove the producer -> consumer edge for any stale producers
|
|
150
|
+
// which weren't dependencies after the recomputation.
|
|
151
|
+
for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
|
|
152
|
+
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
|
|
129
153
|
}
|
|
130
154
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
155
|
+
// Truncate the producer tracking arrays.
|
|
156
|
+
for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
|
|
157
|
+
node.producerNode.pop();
|
|
158
|
+
node.producerLastReadVersion.pop();
|
|
159
|
+
node.producerIndexOfThis.pop();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Determine whether this consumer has any dependencies which have changed since the last time
|
|
164
|
+
* they were read.
|
|
165
|
+
*/
|
|
166
|
+
export function consumerPollProducersForChange(node) {
|
|
167
|
+
assertConsumerNode(node);
|
|
168
|
+
// Poll producers for change.
|
|
169
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
170
|
+
const producer = node.producerNode[i];
|
|
171
|
+
const seenVersion = node.producerLastReadVersion[i];
|
|
172
|
+
// First check the versions. A mismatch means that the producer's value is known to have
|
|
173
|
+
// changed since the last time we read it.
|
|
174
|
+
if (seenVersion !== producer.version) {
|
|
175
|
+
return true;
|
|
137
176
|
}
|
|
138
|
-
|
|
139
|
-
|
|
177
|
+
// The producer's version is the same as the last time we read it, but it might itself be
|
|
178
|
+
// stale. Force the producer to recompute its version (calculating a new value if necessary).
|
|
179
|
+
producerUpdateValueVersion(producer);
|
|
180
|
+
// Now when we do this check, `producer.version` is guaranteed to be up to date, so if the
|
|
181
|
+
// versions still match then it has not changed since the last time we read it.
|
|
182
|
+
if (seenVersion !== producer.version) {
|
|
183
|
+
return true;
|
|
140
184
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Disconnect this consumer from the graph.
|
|
190
|
+
*/
|
|
191
|
+
export function consumerDestroy(node) {
|
|
192
|
+
assertConsumerNode(node);
|
|
193
|
+
if (consumerIsLive(node)) {
|
|
194
|
+
// Drop all connections from the graph to this node.
|
|
195
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
196
|
+
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
|
|
152
197
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
}
|
|
199
|
+
// Truncate all the arrays to drop all connection from this node to the graph.
|
|
200
|
+
node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
|
|
201
|
+
0;
|
|
202
|
+
if (node.liveConsumerNode) {
|
|
203
|
+
node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Add `consumer` as a live consumer of this node.
|
|
208
|
+
*
|
|
209
|
+
* Note that this operation is potentially transitive. If this node becomes live, then it becomes
|
|
210
|
+
* a live consumer of all of its current producers.
|
|
211
|
+
*/
|
|
212
|
+
function producerAddLiveConsumer(node, consumer, indexOfThis) {
|
|
213
|
+
assertProducerNode(node);
|
|
214
|
+
assertConsumerNode(node);
|
|
215
|
+
if (node.liveConsumerNode.length === 0) {
|
|
216
|
+
// When going from 0 to 1 live consumers, we become a live consumer to our producers.
|
|
217
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
218
|
+
node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
|
|
156
219
|
}
|
|
157
220
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
* last seen at a specific version by a `Consumer` which recorded a dependency on
|
|
174
|
-
* this `Producer`.
|
|
175
|
-
*/
|
|
176
|
-
producerPollStatus(lastSeenValueVersion) {
|
|
177
|
-
// `producer.valueVersion` may be stale, but a mismatch still means that the value
|
|
178
|
-
// last seen by the `Consumer` is also stale.
|
|
179
|
-
if (this.valueVersion !== lastSeenValueVersion) {
|
|
180
|
-
return true;
|
|
221
|
+
node.liveConsumerIndexOfThis.push(indexOfThis);
|
|
222
|
+
return node.liveConsumerNode.push(consumer) - 1;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Remove the live consumer at `idx`.
|
|
226
|
+
*/
|
|
227
|
+
function producerRemoveLiveConsumerAtIndex(node, idx) {
|
|
228
|
+
assertProducerNode(node);
|
|
229
|
+
assertConsumerNode(node);
|
|
230
|
+
if (node.liveConsumerNode.length === 1) {
|
|
231
|
+
// When removing the last live consumer, we will no longer be live. We need to remove
|
|
232
|
+
// ourselves from our producers' tracking (which may cause consumer-producers to lose
|
|
233
|
+
// liveness as well).
|
|
234
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
235
|
+
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
|
|
181
236
|
}
|
|
182
|
-
// Trigger the `Producer` to update its `valueVersion` if necessary.
|
|
183
|
-
this.onProducerUpdateValueVersion();
|
|
184
|
-
// At this point, we can trust `producer.valueVersion`.
|
|
185
|
-
return this.valueVersion !== lastSeenValueVersion;
|
|
186
237
|
}
|
|
238
|
+
// Move the last value of `liveConsumers` into `idx`. Note that if there's only a single
|
|
239
|
+
// live consumer, this is a no-op.
|
|
240
|
+
const lastIdx = node.liveConsumerNode.length - 1;
|
|
241
|
+
node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx];
|
|
242
|
+
node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx];
|
|
243
|
+
// Truncate the array.
|
|
244
|
+
node.liveConsumerNode.length--;
|
|
245
|
+
node.liveConsumerIndexOfThis.length--;
|
|
246
|
+
// If the index is still valid, then we need to fix the index pointer from the producer to this
|
|
247
|
+
// consumer, and update it from `lastIdx` to `idx` (accounting for the move above).
|
|
248
|
+
if (idx < node.liveConsumerNode.length) {
|
|
249
|
+
const idxProducer = node.liveConsumerIndexOfThis[idx];
|
|
250
|
+
const consumer = node.liveConsumerNode[idx];
|
|
251
|
+
assertConsumerNode(consumer);
|
|
252
|
+
consumer.producerIndexOfThis[idxProducer] = idx;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function consumerIsLive(node) {
|
|
256
|
+
return node.consumerIsAlwaysLive || (node?.liveConsumerNode?.length ?? 0) > 0;
|
|
257
|
+
}
|
|
258
|
+
function assertConsumerNode(node) {
|
|
259
|
+
node.producerNode ??= [];
|
|
260
|
+
node.producerIndexOfThis ??= [];
|
|
261
|
+
node.producerLastReadVersion ??= [];
|
|
262
|
+
}
|
|
263
|
+
function assertProducerNode(node) {
|
|
264
|
+
node.liveConsumerNode ??= [];
|
|
265
|
+
node.liveConsumerIndexOfThis ??= [];
|
|
187
266
|
}
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { SIGNAL, defaultEquals } from './api.js';
|
|
9
9
|
import { throwInvalidWriteToSignalError } from './errors.js';
|
|
10
|
-
import {
|
|
10
|
+
import { REACTIVE_NODE, producerAccessed, producerNotifyConsumers, producerUpdatesAllowed } from './graph.js';
|
|
11
11
|
/**
|
|
12
12
|
* If set, called after `WritableSignal`s are updated.
|
|
13
13
|
*
|
|
@@ -15,89 +15,22 @@ import { ReactiveNode } from './graph.js';
|
|
|
15
15
|
* of setting a signal.
|
|
16
16
|
*/
|
|
17
17
|
let postSignalSetFn = null;
|
|
18
|
-
class WritableSignalImpl extends ReactiveNode {
|
|
19
|
-
value;
|
|
20
|
-
equal;
|
|
21
|
-
readonlySignal;
|
|
22
|
-
consumerAllowSignalWrites = false;
|
|
23
|
-
constructor(value, equal) {
|
|
24
|
-
super();
|
|
25
|
-
this.value = value;
|
|
26
|
-
this.equal = equal;
|
|
27
|
-
}
|
|
28
|
-
onConsumerDependencyMayHaveChanged() {
|
|
29
|
-
// This never happens for writable signals as they're not consumers.
|
|
30
|
-
}
|
|
31
|
-
onProducerUpdateValueVersion() {
|
|
32
|
-
// Writable signal value versions are always up to date.
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Directly update the value of the signal to a new value, which may or may not be
|
|
36
|
-
* equal to the previous.
|
|
37
|
-
*
|
|
38
|
-
* In the event that `newValue` is semantically equal to the current value, `set` is
|
|
39
|
-
* a no-op.
|
|
40
|
-
*/
|
|
41
|
-
set(newValue) {
|
|
42
|
-
if (!this.producerUpdatesAllowed) {
|
|
43
|
-
throwInvalidWriteToSignalError();
|
|
44
|
-
}
|
|
45
|
-
if (!this.equal(this.value, newValue)) {
|
|
46
|
-
this.value = newValue;
|
|
47
|
-
this.valueVersion++;
|
|
48
|
-
this.producerMayHaveChanged();
|
|
49
|
-
postSignalSetFn?.();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Derive a new value for the signal from its current value using the `updater` function.
|
|
54
|
-
*
|
|
55
|
-
* This is equivalent to calling `set` on the result of running `updater` on the current
|
|
56
|
-
* value.
|
|
57
|
-
*/
|
|
58
|
-
update(updater) {
|
|
59
|
-
if (!this.producerUpdatesAllowed) {
|
|
60
|
-
throwInvalidWriteToSignalError();
|
|
61
|
-
}
|
|
62
|
-
this.set(updater(this.value));
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Calls `mutator` on the current value and assumes that it has been mutated.
|
|
66
|
-
*/
|
|
67
|
-
mutate(mutator) {
|
|
68
|
-
if (!this.producerUpdatesAllowed) {
|
|
69
|
-
throwInvalidWriteToSignalError();
|
|
70
|
-
}
|
|
71
|
-
// Mutate bypasses equality checks as it's by definition changing the value.
|
|
72
|
-
mutator(this.value);
|
|
73
|
-
this.valueVersion++;
|
|
74
|
-
this.producerMayHaveChanged();
|
|
75
|
-
postSignalSetFn?.();
|
|
76
|
-
}
|
|
77
|
-
asReadonly() {
|
|
78
|
-
if (this.readonlySignal === undefined) {
|
|
79
|
-
this.readonlySignal = createSignalFromFunction(this, () => this.signal());
|
|
80
|
-
}
|
|
81
|
-
return this.readonlySignal;
|
|
82
|
-
}
|
|
83
|
-
signal() {
|
|
84
|
-
this.producerAccessed();
|
|
85
|
-
return this.value;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
18
|
/**
|
|
89
19
|
* Create a `Signal` that can be set or updated directly.
|
|
90
20
|
*/
|
|
91
21
|
export function signal(initialValue, options) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
22
|
+
const node = Object.create(SIGNAL_NODE);
|
|
23
|
+
node.value = initialValue;
|
|
24
|
+
options?.equal && (node.equal = options.equal);
|
|
25
|
+
function signalFn() {
|
|
26
|
+
producerAccessed(node);
|
|
27
|
+
return node.value;
|
|
28
|
+
}
|
|
29
|
+
signalFn.set = signalSetFn;
|
|
30
|
+
signalFn.update = signalUpdateFn;
|
|
31
|
+
signalFn.mutate = signalMutateFn;
|
|
32
|
+
signalFn.asReadonly = signalAsReadonlyFn;
|
|
33
|
+
signalFn[SIGNAL] = node;
|
|
101
34
|
return signalFn;
|
|
102
35
|
}
|
|
103
36
|
export function setPostSignalSetFn(fn) {
|
|
@@ -105,3 +38,47 @@ export function setPostSignalSetFn(fn) {
|
|
|
105
38
|
postSignalSetFn = fn;
|
|
106
39
|
return prev;
|
|
107
40
|
}
|
|
41
|
+
const SIGNAL_NODE = {
|
|
42
|
+
...REACTIVE_NODE,
|
|
43
|
+
equal: defaultEquals,
|
|
44
|
+
readonlyFn: undefined,
|
|
45
|
+
};
|
|
46
|
+
function signalValueChanged(node) {
|
|
47
|
+
node.version++;
|
|
48
|
+
producerNotifyConsumers(node);
|
|
49
|
+
postSignalSetFn?.();
|
|
50
|
+
}
|
|
51
|
+
function signalSetFn(newValue) {
|
|
52
|
+
const node = this[SIGNAL];
|
|
53
|
+
if (!producerUpdatesAllowed()) {
|
|
54
|
+
throwInvalidWriteToSignalError();
|
|
55
|
+
}
|
|
56
|
+
if (!node.equal(node.value, newValue)) {
|
|
57
|
+
node.value = newValue;
|
|
58
|
+
signalValueChanged(node);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function signalUpdateFn(updater) {
|
|
62
|
+
if (!producerUpdatesAllowed()) {
|
|
63
|
+
throwInvalidWriteToSignalError();
|
|
64
|
+
}
|
|
65
|
+
signalSetFn.call(this, updater(this[SIGNAL].value));
|
|
66
|
+
}
|
|
67
|
+
function signalMutateFn(mutator) {
|
|
68
|
+
const node = this[SIGNAL];
|
|
69
|
+
if (!producerUpdatesAllowed()) {
|
|
70
|
+
throwInvalidWriteToSignalError();
|
|
71
|
+
}
|
|
72
|
+
// Mutate bypasses equality checks as it's by definition changing the value.
|
|
73
|
+
mutator(node.value);
|
|
74
|
+
signalValueChanged(node);
|
|
75
|
+
}
|
|
76
|
+
function signalAsReadonlyFn() {
|
|
77
|
+
const node = this[SIGNAL];
|
|
78
|
+
if (node.readonlyFn === undefined) {
|
|
79
|
+
const readonlyFn = () => this();
|
|
80
|
+
readonlyFn[SIGNAL] = node;
|
|
81
|
+
node.readonlyFn = readonlyFn;
|
|
82
|
+
}
|
|
83
|
+
return node.readonlyFn;
|
|
84
|
+
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
|
-
import { ReactiveNode } from './graph.js';
|
|
9
8
|
/**
|
|
10
9
|
* A cleanup function that can be optionally registered from the watch logic. If registered, the
|
|
11
10
|
* cleanup logic runs before the next watch execution.
|
|
@@ -15,24 +14,8 @@ export type WatchCleanupFn = () => void;
|
|
|
15
14
|
* A callback passed to the watch function that makes it possible to register cleanup logic.
|
|
16
15
|
*/
|
|
17
16
|
export type WatchCleanupRegisterFn = (cleanupFn: WatchCleanupFn) => void;
|
|
18
|
-
|
|
19
|
-
* Watches a reactive expression and allows it to be scheduled to re-run
|
|
20
|
-
* when any dependencies notify of a change.
|
|
21
|
-
*
|
|
22
|
-
* `Watch` doesn't run reactive expressions itself, but relies on a consumer-
|
|
23
|
-
* provided scheduling operation to coordinate calling `Watch.run()`.
|
|
24
|
-
*/
|
|
25
|
-
export declare class Watch extends ReactiveNode {
|
|
26
|
-
private watch;
|
|
27
|
-
private schedule;
|
|
28
|
-
protected readonly consumerAllowSignalWrites: boolean;
|
|
29
|
-
private dirty;
|
|
30
|
-
private cleanupFn;
|
|
31
|
-
private registerOnCleanup;
|
|
32
|
-
constructor(watch: (onCleanup: WatchCleanupRegisterFn) => void, schedule: (watch: Watch) => void, allowSignalWrites: boolean);
|
|
17
|
+
export interface Watch {
|
|
33
18
|
notify(): void;
|
|
34
|
-
protected onConsumerDependencyMayHaveChanged(): void;
|
|
35
|
-
protected onProducerUpdateValueVersion(): void;
|
|
36
19
|
/**
|
|
37
20
|
* Execute the reactive expression in the context of this `Watch` consumer.
|
|
38
21
|
*
|
|
@@ -42,3 +25,4 @@ export declare class Watch extends ReactiveNode {
|
|
|
42
25
|
run(): void;
|
|
43
26
|
cleanup(): void;
|
|
44
27
|
}
|
|
28
|
+
export declare function watch(fn: (onCleanup: WatchCleanupRegisterFn) => void, schedule: (watch: Watch) => void, allowSignalWrites: boolean): Watch;
|