/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import { defaultEquals, SIGNAL } from './api';
import { throwInvalidWriteToSignalError } from './errors';
import { producerAccessed, producerNotifyConsumers, producerUpdatesAllowed, REACTIVE_NODE, } from './graph';
/**
 * If set, called after `WritableSignal`s are updated.
 *
 * This hook can be used to achieve various effects, such as running effects synchronously as part
 * of setting a signal.
 */
let postSignalSetFn = null;
/**
 * Create a `Signal` that can be set or updated directly.
 *
 * @developerPreview
 */
export function signal(initialValue, options) {
    const node = Object.create(SIGNAL_NODE);
    node.value = initialValue;
    options?.equal && (node.equal = options.equal);
    function signalFn() {
        producerAccessed(node);
        return node.value;
    }
    signalFn.set = signalSetFn;
    signalFn.update = signalUpdateFn;
    signalFn.mutate = signalMutateFn;
    signalFn.asReadonly = signalAsReadonlyFn;
    signalFn[SIGNAL] = node;
    return signalFn;
}
export function setPostSignalSetFn(fn) {
    const prev = postSignalSetFn;
    postSignalSetFn = fn;
    return prev;
}
// Note: Using an IIFE here to ensure that the spread assignment is not considered
// a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
// TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
const SIGNAL_NODE = /* @__PURE__ */ (() => {
    return {
        ...REACTIVE_NODE,
        equal: defaultEquals,
        readonlyFn: undefined,
    };
})();
function signalValueChanged(node) {
    node.version++;
    producerNotifyConsumers(node);
    postSignalSetFn?.();
}
function signalSetFn(newValue) {
    const node = this[SIGNAL];
    if (!producerUpdatesAllowed()) {
        throwInvalidWriteToSignalError();
    }
    if (!node.equal(node.value, newValue)) {
        node.value = newValue;
        signalValueChanged(node);
    }
}
function signalUpdateFn(updater) {
    if (!producerUpdatesAllowed()) {
        throwInvalidWriteToSignalError();
    }
    signalSetFn.call(this, updater(this[SIGNAL].value));
}
function signalMutateFn(mutator) {
    const node = this[SIGNAL];
    if (!producerUpdatesAllowed()) {
        throwInvalidWriteToSignalError();
    }
    // Mutate bypasses equality checks as it's by definition changing the value.
    mutator(node.value);
    signalValueChanged(node);
}
function signalAsReadonlyFn() {
    const node = this[SIGNAL];
    if (node.readonlyFn === undefined) {
        const readonlyFn = () => this();
        readonlyFn[SIGNAL] = node;
        node.readonlyFn = readonlyFn;
    }
    return node.readonlyFn;
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 9256 characters]