@tstdl/base 0.90.30 → 0.90.32
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/api/server/gateway.js +18 -10
- package/http/server/node/node-http-server.js +0 -15
- package/package.json +2 -2
- package/schema/schemas/literal.d.ts +1 -1
- package/signals/computed-with-dependencies.js +4 -1
- package/signals/defer.js +5 -8
- package/signals/effect-with-dependencies.js +4 -1
- package/signals/implementation/asserts.d.ts +0 -2
- package/signals/implementation/asserts.js +0 -2
- package/signals/implementation/computed.d.ts +1 -1
- package/signals/implementation/computed.js +2 -9
- package/signals/implementation/effect.js +10 -7
- package/signals/implementation/equality.js +1 -7
- package/signals/implementation/errors.d.ts +0 -7
- package/signals/implementation/errors.js +1 -0
- package/signals/implementation/graph.d.ts +14 -8
- package/signals/implementation/graph.js +25 -2
- package/signals/implementation/signal.d.ts +6 -3
- package/signals/implementation/signal.js +14 -20
- package/signals/implementation/to-signal.d.ts +17 -1
- package/signals/implementation/to-signal.js +12 -9
- package/signals/implementation/watch.d.ts +1 -1
- package/signals/implementation/watch.js +1 -9
- package/signals/index.d.ts +0 -1
- package/signals/index.js +0 -1
- package/signals/switch-map.js +1 -1
- package/signals/symbol.js +1 -1
- package/signals/to-lazy-signal.d.ts +1 -23
- package/signals/to-lazy-signal.js +12 -13
- package/signals/to-observable-2.d.ts +0 -8
- package/signals/to-observable-2.js +0 -9
package/api/server/gateway.js
CHANGED
|
@@ -186,17 +186,25 @@ let ApiGateway = class ApiGateway {
|
|
|
186
186
|
getToken: async () => this.requestTokenProvider.getToken(requestContext)
|
|
187
187
|
};
|
|
188
188
|
const result = await context.endpoint.implementation(requestContext);
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
const response = (result instanceof HttpServerResponse)
|
|
190
|
+
? result
|
|
191
|
+
: new HttpServerResponse({
|
|
192
|
+
body: isUint8Array(result) ? { buffer: result }
|
|
193
|
+
: isBlob(result) ? { stream: result.stream() }
|
|
194
|
+
: isReadableStream(result) ? { stream: result }
|
|
195
|
+
: (result instanceof ServerSentEventsSource) ? { events: result }
|
|
196
|
+
: (context.endpoint.definition.result == String) ? { text: result }
|
|
197
|
+
: { json: result }
|
|
198
|
+
});
|
|
199
|
+
if (isUndefined(response.headers.contentType)) {
|
|
200
|
+
response.headers.contentType =
|
|
201
|
+
(isDefined(response.body?.json)) ? 'application/json; charset=utf-8'
|
|
202
|
+
: (isDefined(response.body?.text)) ? 'text/plain; charset=utf-8'
|
|
203
|
+
: (isDefined(response.body?.buffer)) ? 'application/octet-stream'
|
|
204
|
+
: (isDefined(response.body?.stream)) ? 'application/octet-stream'
|
|
205
|
+
: (isDefined(response.body?.events)) ? 'text/event-stream'
|
|
206
|
+
: undefined;
|
|
191
207
|
}
|
|
192
|
-
const response = new HttpServerResponse({
|
|
193
|
-
body: isUint8Array(result) ? { buffer: result }
|
|
194
|
-
: isBlob(result) ? { stream: result.stream() }
|
|
195
|
-
: isReadableStream(result) ? { stream: result }
|
|
196
|
-
: (result instanceof ServerSentEventsSource) ? { events: result }
|
|
197
|
-
: (context.endpoint.definition.result == String) ? { text: result }
|
|
198
|
-
: { json: result }
|
|
199
|
-
});
|
|
200
208
|
return response;
|
|
201
209
|
}
|
|
202
210
|
async getBody(request, options, schema) {
|
|
@@ -141,21 +141,6 @@ function getResponder(httpResponse) {
|
|
|
141
141
|
return respond;
|
|
142
142
|
}
|
|
143
143
|
function writeHeaders(response, httpResponse) {
|
|
144
|
-
if (isDefined(response.body?.json)) {
|
|
145
|
-
httpResponse.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
146
|
-
}
|
|
147
|
-
else if (isDefined(response.body?.text)) {
|
|
148
|
-
httpResponse.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
149
|
-
}
|
|
150
|
-
else if (isDefined(response.body?.buffer)) {
|
|
151
|
-
httpResponse.setHeader('Content-Type', 'application/octet-stream');
|
|
152
|
-
}
|
|
153
|
-
else if (isDefined(response.body?.stream)) {
|
|
154
|
-
httpResponse.setHeader('Content-Type', 'application/octet-stream');
|
|
155
|
-
}
|
|
156
|
-
else if (isDefined(response.body?.events)) {
|
|
157
|
-
httpResponse.setHeader('Content-Type', 'text/event-stream');
|
|
158
|
-
}
|
|
159
144
|
for (const [name, value] of response.headers.normalizedEntries()) {
|
|
160
145
|
httpResponse.setHeader(name, value);
|
|
161
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.90.
|
|
3
|
+
"version": "0.90.32",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
"mongodb": "^6.2",
|
|
150
150
|
"nodemailer": "^6.9",
|
|
151
151
|
"playwright": "^1.39",
|
|
152
|
-
"preact": "^10.
|
|
152
|
+
"preact": "^10.19",
|
|
153
153
|
"preact-render-to-string": "^6.3",
|
|
154
154
|
"undici": "^5.27",
|
|
155
155
|
"urlpattern-polyfill": "^9.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Decorator } from '../../reflection/index.js';
|
|
2
2
|
import type { ValueSchema, ValueSchemaOptions } from '../types/types.js';
|
|
3
3
|
export type LiteralOptions = ValueSchemaOptions;
|
|
4
|
-
export declare function literal<T>(value: T, options?: LiteralOptions): ValueSchema<T>;
|
|
4
|
+
export declare function literal<const T>(value: T, options?: LiteralOptions): ValueSchema<T>;
|
|
5
5
|
export declare function Literal(value: any): Decorator<'property' | 'accessor'>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { computed as actualComputed } from './api.js';
|
|
2
2
|
export function computedWithDependencies(computation, dependencies, options = {}) {
|
|
3
|
+
if (dependencies.length == 0) {
|
|
4
|
+
return actualComputed(computation, options);
|
|
5
|
+
}
|
|
3
6
|
function computationWithDependencies() {
|
|
4
7
|
for (let i = 0; i < dependencies.length; i++) { // eslint-disable-line @typescript-eslint/prefer-for-of
|
|
5
8
|
dependencies[i]();
|
|
6
9
|
}
|
|
7
10
|
return computation();
|
|
8
11
|
}
|
|
9
|
-
return actualComputed(
|
|
12
|
+
return actualComputed(computationWithDependencies, options);
|
|
10
13
|
}
|
package/signals/defer.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { isUndefined } from '../utils/type-guards.js';
|
|
2
1
|
import { computed, untracked } from './api.js';
|
|
3
2
|
export function defer(signalFactory) {
|
|
4
|
-
let
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return source();
|
|
10
|
-
});
|
|
3
|
+
let computation = () => {
|
|
4
|
+
computation = untracked(signalFactory);
|
|
5
|
+
return computation();
|
|
6
|
+
};
|
|
7
|
+
return computed(() => computation());
|
|
11
8
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { effect as actualEffect } from './api.js';
|
|
2
2
|
export function effectWithDependencies(effectFn, dependencies, options) {
|
|
3
|
+
if (dependencies.length == 0) {
|
|
4
|
+
return actualEffect(effectFn, options);
|
|
5
|
+
}
|
|
3
6
|
function effectFnWithDependencies(onCleanup) {
|
|
4
7
|
for (let i = 0; i < dependencies.length; i++) { // eslint-disable-line @typescript-eslint/prefer-for-of
|
|
5
8
|
dependencies[i]();
|
|
6
9
|
}
|
|
7
10
|
effectFn(onCleanup);
|
|
8
11
|
}
|
|
9
|
-
return actualEffect(
|
|
12
|
+
return actualEffect(effectFnWithDependencies, options);
|
|
10
13
|
}
|
|
@@ -10,7 +10,5 @@
|
|
|
10
10
|
* to disallow certain code from running inside a reactive context (see {@link toSignal}).
|
|
11
11
|
*
|
|
12
12
|
* @param debugFn a reference to the function making the assertion (used for the error message).
|
|
13
|
-
*
|
|
14
|
-
* @publicApi
|
|
15
13
|
*/
|
|
16
14
|
export declare function assertNotInReactiveContext(debugFn: Function, extraContext?: string): void;
|
|
@@ -11,8 +11,6 @@ import { getActiveConsumer } from './graph.js';
|
|
|
11
11
|
* to disallow certain code from running inside a reactive context (see {@link toSignal}).
|
|
12
12
|
*
|
|
13
13
|
* @param debugFn a reference to the function making the assertion (used for the error message).
|
|
14
|
-
*
|
|
15
|
-
* @publicApi
|
|
16
14
|
*/
|
|
17
15
|
export function assertNotInReactiveContext(debugFn, extraContext) {
|
|
18
16
|
// Taking a `Function` instead of a string name here prevents the un-minified name of the function
|
|
@@ -5,10 +5,10 @@
|
|
|
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 { SIGNAL } from '../symbol.js';
|
|
9
8
|
import type { Signal } from './api.js';
|
|
10
9
|
import type { ValueEqualityFn } from './equality.js';
|
|
11
10
|
import type { ReactiveNode } from './graph.js';
|
|
11
|
+
import { SIGNAL } from './graph.js';
|
|
12
12
|
/**
|
|
13
13
|
* A computation, which derives a value from a declarative reactive expression.
|
|
14
14
|
*
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
7
|
-
*/
|
|
8
|
-
import { SIGNAL } from '../symbol.js';
|
|
1
|
+
/* eslint-disable */
|
|
9
2
|
import { defaultEquals } from './equality.js';
|
|
10
|
-
import { consumerAfterComputation, consumerBeforeComputation, producerAccessed, producerUpdateValueVersion, REACTIVE_NODE } from './graph.js';
|
|
3
|
+
import { consumerAfterComputation, consumerBeforeComputation, producerAccessed, producerUpdateValueVersion, REACTIVE_NODE, SIGNAL } from './graph.js';
|
|
11
4
|
/**
|
|
12
5
|
* Create a computed signal which derives a reactive value from an expression.
|
|
13
6
|
*/
|
|
@@ -34,7 +34,7 @@ export class TstdlEffectScheduler {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* Core reactive node for an
|
|
37
|
+
* Core reactive node for an effect.
|
|
38
38
|
*
|
|
39
39
|
* `EffectHandle` combines the reactive graph's `Watch` base node for effects with the framework's
|
|
40
40
|
* scheduling abstraction (`EffectScheduler`) as well as automatic cleanup via `DestroyRef` if
|
|
@@ -50,7 +50,12 @@ class EffectHandle {
|
|
|
50
50
|
this.watcher = createWatch((onCleanup) => this.runEffect(onCleanup), () => this.schedule(), allowSignalWrites);
|
|
51
51
|
}
|
|
52
52
|
runEffect(onCleanup) {
|
|
53
|
-
|
|
53
|
+
try {
|
|
54
|
+
this.effectFn(onCleanup);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
queueMicrotask(() => { throw err; });
|
|
58
|
+
}
|
|
54
59
|
}
|
|
55
60
|
run() {
|
|
56
61
|
this.watcher.run();
|
|
@@ -58,9 +63,6 @@ class EffectHandle {
|
|
|
58
63
|
schedule() {
|
|
59
64
|
this.scheduler.scheduleEffect(this);
|
|
60
65
|
}
|
|
61
|
-
notify() {
|
|
62
|
-
this.watcher.notify();
|
|
63
|
-
}
|
|
64
66
|
destroy() {
|
|
65
67
|
this.watcher.destroy();
|
|
66
68
|
// Note: if the effect is currently scheduled, it's not un-scheduled, and so the scheduler will
|
|
@@ -72,8 +74,9 @@ const effectScheduler = new TstdlEffectScheduler();
|
|
|
72
74
|
* Create a global `Effect` for the given reactive function.
|
|
73
75
|
*/
|
|
74
76
|
export function effect(effectFn, options) {
|
|
75
|
-
assertNotInReactiveContext(effect, 'Call `effect` outside of a reactive context. For example, schedule the
|
|
77
|
+
assertNotInReactiveContext(effect, 'Call `effect` outside of a reactive context. For example, schedule the ' +
|
|
78
|
+
'effect inside the component constructor.');
|
|
76
79
|
const handle = new EffectHandle(effectScheduler, effectFn, options?.allowSignalWrites ?? false);
|
|
77
|
-
handle.notify();
|
|
80
|
+
handle.watcher.notify();
|
|
78
81
|
return handle;
|
|
79
82
|
}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
7
|
-
*/
|
|
1
|
+
/* eslint-disable */
|
|
8
2
|
/**
|
|
9
3
|
* The default equality function used for `signal` and `computed`, which uses referential equality.
|
|
10
4
|
*/
|
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
7
|
-
*/
|
|
8
1
|
export declare function throwInvalidWriteToSignalError(): void;
|
|
9
2
|
export declare function setThrowInvalidWriteToSignalError(fn: () => never): void;
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
7
|
-
*/
|
|
8
1
|
import { SIGNAL } from '../symbol.js';
|
|
2
|
+
export { SIGNAL };
|
|
9
3
|
type Version = number & {
|
|
10
4
|
__brand: 'Version';
|
|
11
5
|
};
|
|
@@ -37,6 +31,13 @@ export interface ReactiveNode {
|
|
|
37
31
|
* previous value (by whatever definition of equality is in use).
|
|
38
32
|
*/
|
|
39
33
|
version: Version;
|
|
34
|
+
/**
|
|
35
|
+
* Epoch at which this node is verified to be clean.
|
|
36
|
+
*
|
|
37
|
+
* This allows skipping of some polling operations in the case where no signals have been set
|
|
38
|
+
* since this node was last read.
|
|
39
|
+
*/
|
|
40
|
+
lastCleanEpoch: Version;
|
|
40
41
|
/**
|
|
41
42
|
* Whether this node (in its consumer capacity) is dirty.
|
|
42
43
|
*
|
|
@@ -110,6 +111,12 @@ export interface ReactiveNode {
|
|
|
110
111
|
* Called by implementations when a producer's signal is read.
|
|
111
112
|
*/
|
|
112
113
|
export declare function producerAccessed(node: ReactiveNode): void;
|
|
114
|
+
/**
|
|
115
|
+
* Increment the global epoch counter.
|
|
116
|
+
*
|
|
117
|
+
* Called by source producers (that is, not computeds) whenever their values change.
|
|
118
|
+
*/
|
|
119
|
+
export declare function producerIncrementEpoch(): void;
|
|
113
120
|
/**
|
|
114
121
|
* Ensure this producer's `version` is up-to-date.
|
|
115
122
|
*/
|
|
@@ -147,4 +154,3 @@ export declare function consumerPollProducersForChange(node: ReactiveNode): bool
|
|
|
147
154
|
* Disconnect this consumer from the graph.
|
|
148
155
|
*/
|
|
149
156
|
export declare function consumerDestroy(node: ReactiveNode): void;
|
|
150
|
-
export {};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { SIGNAL } from '../symbol.js';
|
|
3
|
+
export { SIGNAL };
|
|
1
4
|
/**
|
|
2
5
|
* @license
|
|
3
6
|
* Copyright Google LLC All Rights Reserved.
|
|
@@ -5,7 +8,6 @@
|
|
|
5
8
|
* Use of this source code is governed by an MIT-style license that can be
|
|
6
9
|
* found in the LICENSE file at https://angular.io/license
|
|
7
10
|
*/
|
|
8
|
-
import { SIGNAL } from '../symbol.js';
|
|
9
11
|
/**
|
|
10
12
|
* The currently active consumer `ReactiveNode`, if running code in a reactive context.
|
|
11
13
|
*
|
|
@@ -13,6 +15,10 @@ import { SIGNAL } from '../symbol.js';
|
|
|
13
15
|
*/
|
|
14
16
|
let activeConsumer = null;
|
|
15
17
|
let inNotificationPhase = false;
|
|
18
|
+
/**
|
|
19
|
+
* Global epoch counter. Incremented whenever a source signal is set.
|
|
20
|
+
*/
|
|
21
|
+
let epoch = 1;
|
|
16
22
|
export function setActiveConsumer(consumer) {
|
|
17
23
|
const prev = activeConsumer;
|
|
18
24
|
activeConsumer = consumer;
|
|
@@ -29,6 +35,7 @@ export function isReactive(value) {
|
|
|
29
35
|
}
|
|
30
36
|
export const REACTIVE_NODE = {
|
|
31
37
|
version: 0,
|
|
38
|
+
lastCleanEpoch: 0,
|
|
32
39
|
dirty: false,
|
|
33
40
|
producerNode: undefined,
|
|
34
41
|
producerLastReadVersion: undefined,
|
|
@@ -48,7 +55,7 @@ export const REACTIVE_NODE = {
|
|
|
48
55
|
*/
|
|
49
56
|
export function producerAccessed(node) {
|
|
50
57
|
if (inNotificationPhase) {
|
|
51
|
-
throw new Error(
|
|
58
|
+
throw new Error(`Assertion error: signal read during notification phase`);
|
|
52
59
|
}
|
|
53
60
|
if (activeConsumer === null) {
|
|
54
61
|
// Accessed outside of a reactive context, so nothing to record.
|
|
@@ -83,6 +90,14 @@ export function producerAccessed(node) {
|
|
|
83
90
|
}
|
|
84
91
|
activeConsumer.producerLastReadVersion[idx] = node.version;
|
|
85
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Increment the global epoch counter.
|
|
95
|
+
*
|
|
96
|
+
* Called by source producers (that is, not computeds) whenever their values change.
|
|
97
|
+
*/
|
|
98
|
+
export function producerIncrementEpoch() {
|
|
99
|
+
epoch++;
|
|
100
|
+
}
|
|
86
101
|
/**
|
|
87
102
|
* Ensure this producer's `version` is up-to-date.
|
|
88
103
|
*/
|
|
@@ -92,15 +107,23 @@ export function producerUpdateValueVersion(node) {
|
|
|
92
107
|
// is guaranteed to be up-to-date.
|
|
93
108
|
return;
|
|
94
109
|
}
|
|
110
|
+
if (!node.dirty && node.lastCleanEpoch === epoch) {
|
|
111
|
+
// Even non-live consumers can skip polling if they previously found themselves to be clean at
|
|
112
|
+
// the current epoch, since their dependencies could not possibly have changed (such a change
|
|
113
|
+
// would've increased the epoch).
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
95
116
|
if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
|
|
96
117
|
// None of our producers report a change since the last time they were read, so no
|
|
97
118
|
// recomputation of our value is necessary, and we can consider ourselves clean.
|
|
98
119
|
node.dirty = false;
|
|
120
|
+
node.lastCleanEpoch = epoch;
|
|
99
121
|
return;
|
|
100
122
|
}
|
|
101
123
|
node.producerRecomputeValue(node);
|
|
102
124
|
// After recomputing the value, we're no longer dirty.
|
|
103
125
|
node.dirty = false;
|
|
126
|
+
node.lastCleanEpoch = epoch;
|
|
104
127
|
}
|
|
105
128
|
/**
|
|
106
129
|
* Propagate a dirty notification to live consumers of this producer.
|
|
@@ -5,10 +5,10 @@
|
|
|
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 {
|
|
9
|
-
import { Signal } from './api.js';
|
|
8
|
+
import type { Signal } from './api.js';
|
|
10
9
|
import type { ValueEqualityFn } from './equality.js';
|
|
11
10
|
import type { ReactiveNode } from './graph.js';
|
|
11
|
+
import { SIGNAL } from './graph.js';
|
|
12
12
|
export interface SignalNode<T> extends ReactiveNode {
|
|
13
13
|
value: T;
|
|
14
14
|
equal: ValueEqualityFn<T>;
|
|
@@ -29,13 +29,16 @@ export declare function signalGetFn<T>(this: SignalNode<T>): T;
|
|
|
29
29
|
export declare function signalSetFn<T>(node: SignalNode<T>, newValue: T): void;
|
|
30
30
|
export declare function signalUpdateFn<T>(node: SignalNode<T>, updater: (value: T) => T): void;
|
|
31
31
|
export declare function signalMutateFn<T>(node: SignalNode<T>, mutator: (value: T) => void): void;
|
|
32
|
+
/**
|
|
33
|
+
* A `Signal` with a value that can be mutated via a setter interface.
|
|
34
|
+
*/
|
|
32
35
|
export interface WritableSignal<T> extends Signal<T> {
|
|
33
36
|
/**
|
|
34
37
|
* Directly set the signal to a new value, and notify any dependents.
|
|
35
38
|
*/
|
|
36
39
|
set(value: T): void;
|
|
37
40
|
/**
|
|
38
|
-
* Update the value of the signal based on
|
|
41
|
+
* Update the value of the signal based on its current value, and
|
|
39
42
|
* notify any dependents.
|
|
40
43
|
*/
|
|
41
44
|
update(updateFn: (value: T) => T): void;
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright Google LLC All Rights Reserved.
|
|
5
|
-
*
|
|
6
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
-
* found in the LICENSE file at https://angular.io/license
|
|
8
|
-
*/
|
|
9
|
-
import { SIGNAL } from '../symbol.js';
|
|
10
2
|
import { defaultEquals } from './equality.js';
|
|
11
3
|
import { throwInvalidWriteToSignalError } from './errors.js';
|
|
12
|
-
import { producerAccessed, producerNotifyConsumers, producerUpdatesAllowed, REACTIVE_NODE } from './graph.js';
|
|
4
|
+
import { producerAccessed, producerIncrementEpoch, producerNotifyConsumers, producerUpdatesAllowed, REACTIVE_NODE, SIGNAL } from './graph.js';
|
|
13
5
|
/**
|
|
14
6
|
* If set, called after `WritableSignal`s are updated.
|
|
15
7
|
*
|
|
@@ -43,7 +35,13 @@ export function signalSetFn(node, newValue) {
|
|
|
43
35
|
if (!producerUpdatesAllowed()) {
|
|
44
36
|
throwInvalidWriteToSignalError();
|
|
45
37
|
}
|
|
46
|
-
|
|
38
|
+
const value = node.value;
|
|
39
|
+
if (Object.is(value, newValue)) {
|
|
40
|
+
if (!node.equal(value, newValue)) {
|
|
41
|
+
console.warn('Signal value equality implementations should always return `true` for values that are the same according to `Object.is` but returned `false` instead.');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (!node.equal(value, newValue)) {
|
|
47
45
|
node.value = newValue;
|
|
48
46
|
signalValueChanged(node);
|
|
49
47
|
}
|
|
@@ -62,18 +60,14 @@ export function signalMutateFn(node, mutator) {
|
|
|
62
60
|
mutator(node.value);
|
|
63
61
|
signalValueChanged(node);
|
|
64
62
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
...REACTIVE_NODE,
|
|
71
|
-
equal: defaultEquals,
|
|
72
|
-
value: undefined,
|
|
73
|
-
};
|
|
74
|
-
})();
|
|
63
|
+
const SIGNAL_NODE = {
|
|
64
|
+
...REACTIVE_NODE,
|
|
65
|
+
equal: defaultEquals,
|
|
66
|
+
value: undefined,
|
|
67
|
+
};
|
|
75
68
|
function signalValueChanged(node) {
|
|
76
69
|
node.version++;
|
|
70
|
+
producerIncrementEpoch();
|
|
77
71
|
producerNotifyConsumers(node);
|
|
78
72
|
postSignalSetFn?.();
|
|
79
73
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.io/license
|
|
7
|
+
*/
|
|
1
8
|
import type { Observable, Subscribable } from 'rxjs';
|
|
2
|
-
import { Signal } from './api.js';
|
|
9
|
+
import type { Signal } from './api.js';
|
|
3
10
|
/**
|
|
4
11
|
* Options for `toSignal`.
|
|
5
12
|
*/
|
|
@@ -19,6 +26,15 @@ export interface ToSignalOptions {
|
|
|
19
26
|
* not met.
|
|
20
27
|
*/
|
|
21
28
|
requireSync?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether `toSignal` should throw errors from the Observable error channel back to RxJS, where
|
|
31
|
+
* they'll be processed as uncaught exceptions.
|
|
32
|
+
*
|
|
33
|
+
* In practice, this means that the signal returned by `toSignal` will keep returning the last
|
|
34
|
+
* good value forever, as Observables which error produce no further values. This option emulates
|
|
35
|
+
* the behavior of the `async` pipe.
|
|
36
|
+
*/
|
|
37
|
+
rejectErrors?: boolean;
|
|
22
38
|
}
|
|
23
39
|
export declare function toSignal<T>(source: Observable<T> | Subscribable<T>): Signal<T | undefined>;
|
|
24
40
|
export declare function toSignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright Google LLC All Rights Reserved.
|
|
5
|
-
*
|
|
6
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
-
* found in the LICENSE file at https://angular.io/license
|
|
8
|
-
*/
|
|
9
2
|
import { registerFinalization } from '../../memory/finalization.js';
|
|
10
3
|
import { assertNotInReactiveContext } from './asserts.js';
|
|
11
4
|
import { computed } from './computed.js';
|
|
@@ -23,7 +16,8 @@ import { signal } from './signal.js';
|
|
|
23
16
|
* does not include an `undefined` type.
|
|
24
17
|
*/
|
|
25
18
|
export function toSignal(source, options) {
|
|
26
|
-
assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time.
|
|
19
|
+
assertNotInReactiveContext(toSignal, 'Invoking `toSignal` causes new subscriptions every time. ' +
|
|
20
|
+
'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.');
|
|
27
21
|
// Note: T is the Observable value type, and U is the initial value type. They don't have to be
|
|
28
22
|
// the same - the returned signal gives values of type `T`.
|
|
29
23
|
let state;
|
|
@@ -43,7 +37,14 @@ export function toSignal(source, options) {
|
|
|
43
37
|
// https://github.com/angular/angular/pull/50522.
|
|
44
38
|
const sub = source.subscribe({
|
|
45
39
|
next: value => state.set({ kind: StateKind.Value, value }),
|
|
46
|
-
error: error =>
|
|
40
|
+
error: error => {
|
|
41
|
+
if (options?.rejectErrors) {
|
|
42
|
+
// Kick the error back to RxJS. It will be caught and rethrown in a macrotask, which causes
|
|
43
|
+
// the error to end up as an uncaught exception.
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
state.set({ kind: StateKind.Error, error });
|
|
47
|
+
},
|
|
47
48
|
// Completion of the Observable is meaningless to the signal. Signals don't have a concept of
|
|
48
49
|
// "complete".
|
|
49
50
|
});
|
|
@@ -60,6 +61,8 @@ export function toSignal(source, options) {
|
|
|
60
61
|
case StateKind.Error:
|
|
61
62
|
throw current.error;
|
|
62
63
|
case StateKind.NoValue:
|
|
64
|
+
// This shouldn't really happen because the error is thrown on creation.
|
|
65
|
+
// TODO(alxhub): use a RuntimeError when we finalize the error semantics
|
|
63
66
|
throw new Error('`toSignal()` called with `requireSync` but `Observable` did not emit synchronously.');
|
|
64
67
|
}
|
|
65
68
|
});
|
|
@@ -5,8 +5,8 @@
|
|
|
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 { SIGNAL } from '../symbol.js';
|
|
9
8
|
import type { ReactiveNode } from './graph.js';
|
|
9
|
+
import { SIGNAL } from './graph.js';
|
|
10
10
|
/**
|
|
11
11
|
* A cleanup function that can be optionally registered from the watch logic. If registered, the
|
|
12
12
|
* cleanup logic runs before the next watch execution.
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright Google LLC All Rights Reserved.
|
|
5
|
-
*
|
|
6
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
-
* found in the LICENSE file at https://angular.io/license
|
|
8
|
-
*/
|
|
9
|
-
import { SIGNAL } from '../symbol.js';
|
|
10
|
-
import { consumerAfterComputation, consumerBeforeComputation, consumerDestroy, consumerMarkDirty, consumerPollProducersForChange, isInNotificationPhase, REACTIVE_NODE } from './graph.js';
|
|
2
|
+
import { consumerAfterComputation, consumerBeforeComputation, consumerDestroy, consumerMarkDirty, consumerPollProducersForChange, isInNotificationPhase, REACTIVE_NODE, SIGNAL } from './graph.js';
|
|
11
3
|
export function createWatch(fn, schedule, allowSignalWrites) {
|
|
12
4
|
const node = Object.create(WATCH_NODE);
|
|
13
5
|
if (allowSignalWrites) {
|
package/signals/index.d.ts
CHANGED
package/signals/index.js
CHANGED
package/signals/switch-map.js
CHANGED
package/signals/symbol.js
CHANGED
|
@@ -1,23 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Signal, ToSignalOptions } from './api.js';
|
|
3
|
-
/**
|
|
4
|
-
* Like `toSignal`, except that it uses untracked internal operations (required for some scenarios, but might be less safe in terms of bugs catching) and has the ability to subscribe lazily.
|
|
5
|
-
* Subscription to observable is cleaned up using finalization (garbage collection) of the signal.
|
|
6
|
-
*/
|
|
7
|
-
export declare function toLazySignal<T>(source: Observable<T>): Signal<T | undefined>;
|
|
8
|
-
export declare function toLazySignal<T>(source: Observable<T>, options: ToSignalOptions & {
|
|
9
|
-
initialValue?: undefined;
|
|
10
|
-
requireSync?: false;
|
|
11
|
-
}): Signal<T | undefined>;
|
|
12
|
-
export declare function toLazySignal<T>(source: Observable<T>, options: ToSignalOptions & {
|
|
13
|
-
initialValue?: null;
|
|
14
|
-
requireSync?: false;
|
|
15
|
-
}): Signal<T | null>;
|
|
16
|
-
export declare function toLazySignal<T>(source: Observable<T>, options: ToSignalOptions & {
|
|
17
|
-
initialValue?: undefined;
|
|
18
|
-
requireSync: true;
|
|
19
|
-
}): Signal<T>;
|
|
20
|
-
export declare function toLazySignal<T, const I>(source: Observable<T>, options: ToSignalOptions & {
|
|
21
|
-
initialValue: I;
|
|
22
|
-
requireSync?: false;
|
|
23
|
-
}): Signal<T | I>;
|
|
1
|
+
export declare const toLazySignal: typeof import("./implementation/to-signal.js").toSignal;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { Subject, switchMap } from 'rxjs';
|
|
2
2
|
import { computed, toSignal } from './api.js';
|
|
3
3
|
const LAZY = Symbol('LAZY');
|
|
4
|
-
export function toLazySignal(source, options) {
|
|
4
|
+
export const toLazySignal = function toLazySignal(source, options) {
|
|
5
5
|
const subscribe$ = new Subject();
|
|
6
6
|
const lazySource = subscribe$.pipe(switchMap(() => source));
|
|
7
7
|
const signal = toSignal(lazySource, { initialValue: LAZY, ...options }); // eslint-disable-line @typescript-eslint/no-unsafe-argument
|
|
8
|
-
let
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new Error('`toLazySignal()` called with `requireSync` but `Observable` did not emit synchronously.');
|
|
16
|
-
}
|
|
8
|
+
let computation = () => {
|
|
9
|
+
subscribe$.next();
|
|
10
|
+
subscribe$.complete();
|
|
11
|
+
const value = signal();
|
|
12
|
+
computation = signal;
|
|
13
|
+
if (value == LAZY) {
|
|
14
|
+
throw new Error('`toLazySignal()` called with `requireSync` but `Observable` did not emit synchronously.');
|
|
17
15
|
}
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
|
|
16
|
+
return value;
|
|
17
|
+
};
|
|
18
|
+
return computed(() => computation());
|
|
19
|
+
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Observable } from 'rxjs';
|
|
2
|
-
import type { Signal } from './api.js';
|
|
3
|
-
/**
|
|
4
|
-
* Exposes the value of an `Signal` as an RxJS `Observable`.
|
|
5
|
-
*
|
|
6
|
-
* The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
|
|
7
|
-
*/
|
|
8
|
-
export declare function toObservable2<T>(source: Signal<T>): Observable<T>;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { toObservable } from './implementation/to-observable.js';
|
|
2
|
-
/**
|
|
3
|
-
* Exposes the value of an `Signal` as an RxJS `Observable`.
|
|
4
|
-
*
|
|
5
|
-
* The signal's value will be propagated into the `Observable`'s subscribers using an `effect`.
|
|
6
|
-
*/
|
|
7
|
-
export function toObservable2(source) {
|
|
8
|
-
return toObservable(source);
|
|
9
|
-
}
|