@alwatr/signal 6.2.0 → 9.1.0

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.
Files changed (42) hide show
  1. package/dist/core/computed-signal.d.ts +2 -1
  2. package/dist/core/computed-signal.d.ts.map +1 -1
  3. package/dist/core/effect-signal.d.ts +2 -1
  4. package/dist/core/effect-signal.d.ts.map +1 -1
  5. package/dist/core/event-signal.d.ts +2 -1
  6. package/dist/core/event-signal.d.ts.map +1 -1
  7. package/dist/core/signal-base.d.ts.map +1 -1
  8. package/dist/core/state-signal.d.ts +2 -1
  9. package/dist/core/state-signal.d.ts.map +1 -1
  10. package/dist/main.js +5 -0
  11. package/dist/main.js.map +25 -0
  12. package/dist/operators/debounce.d.ts.map +1 -1
  13. package/dist/type.d.ts.map +1 -1
  14. package/package.json +48 -53
  15. package/src/core/computed-signal.ts +216 -0
  16. package/src/core/effect-signal.ts +176 -0
  17. package/src/core/event-signal.ts +61 -0
  18. package/src/core/persistent-state-signal.ts +98 -0
  19. package/src/core/session-state-signal.ts +145 -0
  20. package/src/core/signal-base.ts +191 -0
  21. package/src/core/state-signal.ts +178 -0
  22. package/src/creators/computed.ts +37 -0
  23. package/src/creators/effect.ts +46 -0
  24. package/src/creators/event.ts +31 -0
  25. package/src/creators/persistent-state.ts +33 -0
  26. package/src/creators/session-state.ts +42 -0
  27. package/src/creators/state.ts +28 -0
  28. package/src/main.ts +20 -0
  29. package/src/operators/debounce.ts +91 -0
  30. package/src/operators/filter.ts +78 -0
  31. package/src/operators/map.ts +48 -0
  32. package/src/type.ts +357 -0
  33. package/CHANGELOG.md +0 -908
  34. package/dist/main.cjs +0 -4
  35. package/dist/main.cjs.map +0 -7
  36. package/dist/main.mjs +0 -4
  37. package/dist/main.mjs.map +0 -7
  38. package/src/core/computed-signal.test.js +0 -166
  39. package/src/core/effect-signal.test.js +0 -150
  40. package/src/core/event-signal.test.js +0 -210
  41. package/src/core/state-signal.test.js +0 -251
  42. package/src/operators/debounce.test.js +0 -206
@@ -0,0 +1,191 @@
1
+ import type {Observer_, SubscribeOptions, SubscribeResult, ListenerCallback, SignalConfig} from '../type.js';
2
+ import type {AlwatrLogger} from '@alwatr/logger';
3
+
4
+ /**
5
+ * An abstract base class for signal implementations.
6
+ * It provides the core functionality for managing subscriptions (observers).
7
+ *
8
+ * @template T The type of data that the signal holds or dispatches.
9
+ */
10
+ export abstract class SignalBase<T> {
11
+ /**
12
+ * The unique identifier for this signal instance.
13
+ * Useful for debugging and logging.
14
+ */
15
+ public readonly name: string;
16
+
17
+ /**
18
+ * The logger instance for this signal.
19
+ * @protected
20
+ */
21
+ protected abstract logger_: AlwatrLogger;
22
+
23
+ /**
24
+ * The list of observers (listeners) subscribed to this signal.
25
+ * @protected
26
+ */
27
+ protected readonly observers_: Observer_<T>[] = [];
28
+
29
+ /**
30
+ * A flag indicating whether the signal has been destroyed.
31
+ * @private
32
+ */
33
+ private isDestroyed__ = false;
34
+
35
+ /**
36
+ * Indicates whether the signal has been destroyed.
37
+ * A destroyed signal cannot be used and will throw an error if interacted with.
38
+ *
39
+ * @returns `true` if the signal is destroyed, `false` otherwise.
40
+ */
41
+ public get isDestroyed(): boolean {
42
+ return this.isDestroyed__;
43
+ }
44
+
45
+ constructor(protected config_: SignalConfig) {
46
+ this.name = config_.name;
47
+ }
48
+
49
+ /**
50
+ * Removes a specific observer from the observers list.
51
+ *
52
+ * @param observer The observer instance to remove.
53
+ * @protected
54
+ */
55
+ protected removeObserver_(observer: Observer_<T>): void {
56
+ this.logger_.logMethod?.('removeObserver_');
57
+
58
+ if (this.isDestroyed__) {
59
+ this.logger_.incident?.('removeObserver_', 'remove_observer_on_destroyed_signal');
60
+ return;
61
+ }
62
+
63
+ const index = this.observers_.indexOf(observer);
64
+ if (index !== -1) {
65
+ this.observers_.splice(index, 1);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Subscribes a listener function to this signal.
71
+ *
72
+ * The listener will be called whenever the signal is notified (e.g., when `dispatch` or `set` is called).
73
+ *
74
+ * @param callback The function to be called when the signal is dispatched.
75
+ * @param options Subscription options to customize the behavior (e.g., `once`, `priority`).
76
+ * @returns A `SubscribeResult` object with an `unsubscribe` method to remove the listener.
77
+ */
78
+ public subscribe(callback: ListenerCallback<T>, options?: SubscribeOptions): SubscribeResult {
79
+ this.logger_.logMethodArgs?.('subscribe.base', options);
80
+ this.checkDestroyed_();
81
+
82
+ const observer: Observer_<T> = {callback, options};
83
+
84
+ if (options?.priority) {
85
+ // High-priority observers are added to the front of the queue.
86
+ this.observers_.unshift(observer);
87
+ }
88
+ else {
89
+ this.observers_.push(observer);
90
+ }
91
+
92
+ // The returned unsubscribe function is a closure that calls the internal removal method.
93
+ return {
94
+ unsubscribe: (): void => this.removeObserver_(observer),
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Notifies all registered observers about a new value.
100
+ *
101
+ * This method iterates through a snapshot of the current observers to prevent issues
102
+ * with subscriptions changing during notification (e.g., an observer unsubscribing itself).
103
+ *
104
+ * @param value The new value to notify observers about.
105
+ * @protected
106
+ */
107
+ protected notify_(value: T): void {
108
+ this.logger_.logMethodArgs?.('notify_', value);
109
+
110
+ if (this.isDestroyed__) {
111
+ this.logger_.incident?.('notify_', 'notify_on_destroyed_signal');
112
+ return;
113
+ }
114
+
115
+ // Create a snapshot of the observers array to iterate over.
116
+ // This prevents issues if the observers_ array is modified during the loop.
117
+ const currentObservers = [...this.observers_];
118
+
119
+ for (const observer of currentObservers) {
120
+ if (observer.options?.once) {
121
+ this.removeObserver_(observer);
122
+ }
123
+
124
+ try {
125
+ const result = observer.callback(value);
126
+ if (result instanceof Promise) {
127
+ result.catch((err) => this.logger_.error('notify_', 'async_callback_failed', err, {observer}));
128
+ }
129
+ }
130
+ catch (err) {
131
+ this.logger_.error('notify_', 'sync_callback_failed', err);
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Returns a Promise that resolves with the next value dispatched by the signal.
138
+ * This provides an elegant way to wait for a single, future event using `async/await`.
139
+ *
140
+ * @returns A Promise that resolves with the next dispatched value.
141
+ *
142
+ * @example
143
+ * async function onButtonClick() {
144
+ * console.log('Waiting for the next signal...');
145
+ * const nextValue = await mySignal.untilNext();
146
+ * console.log('Signal received:', nextValue);
147
+ * }
148
+ */
149
+ public untilNext(): Promise<T> {
150
+ this.logger_.logMethod?.('untilNext');
151
+ this.checkDestroyed_();
152
+ return new Promise((resolve) => {
153
+ this.subscribe(resolve, {
154
+ once: true,
155
+ priority: true, // Resolve the promise before other listeners are called.
156
+ receivePrevious: false, // We only want the *next* value, not the current one.
157
+ });
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Destroys the signal, clearing all its listeners and making it inactive.
163
+ *
164
+ * After destruction, any interaction with the signal (like `subscribe` or `untilNext`)
165
+ * will throw an error. This is crucial for preventing memory leaks by allowing
166
+ * garbage collection of the signal and its observers.
167
+ */
168
+ public destroy(): void {
169
+ this.logger_.logMethod?.('destroy');
170
+ if (this.isDestroyed__) {
171
+ this.logger_.incident?.('destroy_', 'double_destroy_attempt');
172
+ return;
173
+ }
174
+ this.isDestroyed__ = true;
175
+ this.observers_.length = 0; // Clear all observers.
176
+ this.config_.onDestroy?.(); // Call the optional onDestroy callback.
177
+ this.config_ = null as unknown as SignalConfig; // Help GC by breaking references.
178
+ }
179
+
180
+ /**
181
+ * Throws an error if the signal has been destroyed.
182
+ * This is a safeguard to prevent interaction with a defunct signal.
183
+ * @protected
184
+ */
185
+ protected checkDestroyed_(): void {
186
+ if (this.isDestroyed__) {
187
+ this.logger_.accident('checkDestroyed_', 'attempt_to_use_destroyed_signal');
188
+ throw new Error(`Cannot interact with a destroyed signal (id: ${this.name})`);
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,178 @@
1
+ import { delay } from '@alwatr/delay';
2
+ import { createLogger, type AlwatrLogger } from '@alwatr/logger';
3
+
4
+ import { SignalBase } from './signal-base.js';
5
+
6
+ import type { StateSignalConfig, ListenerCallback, SubscribeOptions, SubscribeResult, IReadonlySignal } from '../type.js';
7
+
8
+ /**
9
+ * A stateful signal that holds a value and notifies listeners when the value changes.
10
+ *
11
+ * `StateSignal` is the core of the signal library, representing a piece of mutable state.
12
+ * It always has a value, and new subscribers immediately receive the current value by default.
13
+ *
14
+ * @template T The type of the state it holds.
15
+ * @implements {IReadonlySignal<T>}
16
+ *
17
+ * @example
18
+ * // Create a new state signal with an initial value.
19
+ * const counter = new StateSignal<number>({
20
+ * name: 'counter-signal',
21
+ * initialValue: 0,
22
+ * });
23
+ *
24
+ * // Get the current value.
25
+ * console.log(counter.get()); // Outputs: 0
26
+ *
27
+ * // Subscribe to changes.
28
+ * const subscription = counter.subscribe(newValue => {
29
+ * console.log(`Counter changed to: ${newValue}`);
30
+ * });
31
+ *
32
+ * // Set a new value, which triggers the notification.
33
+ * counter.set(1); // Outputs: "Counter changed to: 1"
34
+ *
35
+ * // Update value based on the previous value.
36
+ * counter.update(current => current + 1); // Outputs: "Counter changed to: 2"
37
+ *
38
+ * // Unsubscribe when no longer needed.
39
+ * subscription.unsubscribe();
40
+ */
41
+ export class StateSignal<T> extends SignalBase<T> implements IReadonlySignal<T> {
42
+ /**
43
+ * The current value of the signal.
44
+ * @private
45
+ */
46
+ private value__: T;
47
+
48
+ /**
49
+ * The logger instance for this signal.
50
+ * @protected
51
+ */
52
+ protected logger_: AlwatrLogger;
53
+
54
+ constructor(config: StateSignalConfig<T>) {
55
+ super({
56
+ name: config.name,
57
+ onDestroy: config.onDestroy,
58
+ });
59
+ this.logger_ = createLogger(`state-signal:${this.name}`);
60
+ this.value__ = config.initialValue;
61
+ this.logger_.logMethodArgs?.('constructor', { initialValue: this.value__ });
62
+ }
63
+
64
+ /**
65
+ * Retrieves the current value of the signal.
66
+ *
67
+ * @returns The current value.
68
+ *
69
+ * @example
70
+ * console.log(mySignal.get());
71
+ */
72
+ public get(): T {
73
+ this.checkDestroyed_();
74
+ return this.value__;
75
+ }
76
+
77
+ /**
78
+ * Updates the signal's value and notifies all active listeners.
79
+ *
80
+ * The notification is scheduled as a microtask, which means the update is deferred
81
+ * slightly to batch multiple synchronous changes.
82
+ *
83
+ * @param newValue The new value to set.
84
+ *
85
+ * @example
86
+ * // For primitive types
87
+ * mySignal.set(42);
88
+ *
89
+ * // For object types, it's best practice to set an immutable new object.
90
+ * mySignal.set({ ...mySignal.get(), property: 'new-value' });
91
+ */
92
+ public set(newValue: T): void {
93
+ this.logger_.logMethodArgs?.('set', { newValue });
94
+ this.checkDestroyed_();
95
+
96
+ // For primitives (including null), do not notify if the value is the same.
97
+ if (Object.is(this.value__, newValue) && (typeof newValue !== 'object' || newValue === null)) {
98
+ return;
99
+ }
100
+
101
+ this.value__ = newValue;
102
+
103
+ // Dispatch as a microtask to ensure consistent, non-blocking behavior.
104
+ delay.nextMicrotask().then(() => this.notify_(newValue));
105
+ }
106
+
107
+ /**
108
+ * Updates the signal's value based on its previous value.
109
+ *
110
+ * This method is particularly useful for state transitions that depend on the current value,
111
+ * especially for objects or arrays, as it promotes an immutable update pattern.
112
+ *
113
+ * @param updater A function that receives the current value and returns the new value.
114
+ *
115
+ * @example
116
+ * // For a counter
117
+ * counterSignal.update(current => current + 1);
118
+ *
119
+ * // For an object state
120
+ * userSignal.update(currentUser => ({ ...currentUser, loggedIn: true }));
121
+ */
122
+ public update(updater: (previousValue: T) => T): void {
123
+ this.checkDestroyed_();
124
+ const newValue = updater(this.value__);
125
+ this.logger_.logMethodFull?.('update', this.value__, newValue);
126
+ this.set(newValue);
127
+ }
128
+
129
+ /**
130
+ * Subscribes a listener to this signal.
131
+ *
132
+ * By default, the listener is immediately called with the signal's current value (`receivePrevious: true`).
133
+ * This behavior can be customized via the `options` parameter.
134
+ *
135
+ * @param callback The function to be called when the signal's value changes.
136
+ * @param options Subscription options, including `receivePrevious` and `once`.
137
+ * @returns An object with an `unsubscribe` method to remove the listener.
138
+ */
139
+ public override subscribe(callback: ListenerCallback<T>, options: SubscribeOptions = {}): SubscribeResult {
140
+ this.logger_.logMethodArgs?.('subscribe', options);
141
+ this.checkDestroyed_();
142
+
143
+ // By default, new subscribers to a StateSignal should receive the current value.
144
+ if (options.receivePrevious !== false) {
145
+ // Immediately (but asynchronously) call the listener with the current value.
146
+ // This is done in a microtask to ensure it happens after the subscription is fully registered.
147
+ delay
148
+ .nextMicrotask()
149
+ .then(() => {
150
+ this.logger_.logStep?.('subscribe', 'immediate_callback');
151
+ callback(this.value__);
152
+ })
153
+ .catch((err) => this.logger_.error('subscribe', 'immediate_callback_failed', err));
154
+
155
+ // If it's a 'once' subscription that receives the previous value, it's now fulfilled.
156
+ // We don't need to add it to the observers list for future updates.
157
+ if (options.once) {
158
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
159
+ return { unsubscribe: () => { } };
160
+ }
161
+ }
162
+
163
+ return super.subscribe(callback, options);
164
+ }
165
+
166
+ /**
167
+ * Destroys the signal, clearing its value and all listeners.
168
+ * This is crucial for memory management to prevent leaks.
169
+ */
170
+ public override destroy(): void {
171
+ this.value__ = null as T; // Clear the value to allow for garbage collection.
172
+ super.destroy();
173
+ }
174
+
175
+ public asReadonly(): IReadonlySignal<T> {
176
+ return this;
177
+ }
178
+ }
@@ -0,0 +1,37 @@
1
+ import {ComputedSignal} from '../core/computed-signal.js';
2
+
3
+ import type {ComputedSignalConfig} from '../type.js';
4
+
5
+ /**
6
+ * Creates a read-only signal that derives its value from a set of dependency signals.
7
+ *
8
+ * `ComputedSignal` is a powerful tool for creating values that reactively update when their underlying
9
+ * data sources change. Its value is memoized, meaning the `get` function is only re-evaluated when
10
+ * one of its dependencies has actually changed.
11
+ *
12
+ * A key feature is its lifecycle management: a `ComputedSignal` **must** be destroyed when no longer
13
+ * needed to prevent memory leaks from its subscriptions to dependency signals.
14
+ *
15
+ * @template T The type of the computed value.
16
+ *
17
+ * @param config The configuration for the computed signal.
18
+ * @returns A new, read-only computed signal.
19
+ *
20
+ * @example
21
+ * const firstName = createStateSignal({ name: 'firstName', initialValue: 'John' });
22
+ * const lastName = createStateSignal({ name: 'lastName', initialValue: 'Doe' });
23
+ *
24
+ * const fullName = createComputedSignal({
25
+ * name: 'fullName',
26
+ * deps: [firstName, lastName],
27
+ * get: () => `${firstName.get()} ${lastName.get()}`,
28
+ * });
29
+ *
30
+ * console.log(fullName.get()); // "John Doe"
31
+ *
32
+ * // IMPORTANT: Always destroy a computed signal when no longer needed.
33
+ * // fullName.destroy();
34
+ */
35
+ export function createComputedSignal<T>(config: ComputedSignalConfig<T>): ComputedSignal<T> {
36
+ return new ComputedSignal(config);
37
+ }
@@ -0,0 +1,46 @@
1
+ import {EffectSignal} from '../core/effect-signal.js';
2
+
3
+ import type {EffectSignalConfig} from '../type.js';
4
+
5
+ /**
6
+ * Creates a side-effect that runs in response to changes in dependency signals.
7
+ *
8
+ * `EffectSignal` is designed for running logic that interacts with the "outside world"—such as
9
+ * logging, network requests, or DOM manipulation—whenever its dependencies are updated.
10
+ * It encapsulates the subscription and cleanup logic, providing a robust and memory-safe
11
+ * way to handle reactive side-effects.
12
+ *
13
+ * A key feature is its lifecycle management: an `EffectSignal` **must** be destroyed when no longer
14
+ * needed to prevent memory leaks and stop the effect from running unnecessarily.
15
+ *
16
+ * @param config The configuration for the effect.
17
+ * @returns An object with a `destroy` method to stop the effect.
18
+ *
19
+ * @example
20
+ * // --- Create dependency signals ---
21
+ * const counter = createStateSignal({ initialValue: 0, name: 'counter' });
22
+ * const user = createStateSignal({ initialValue: 'guest', name: 'user' });
23
+ *
24
+ * // --- Create an effect ---
25
+ * const analyticsEffect = createEffect({
26
+ * deps: [counter, user],
27
+ * run: () => {
28
+ * console.log(`Analytics: User '${user.get()}' clicked ${counter.get()} times.`);
29
+ * },
30
+ * runImmediately: true, // Optional: run once on creation
31
+ * });
32
+ * // Immediately logs: "Analytics: User 'guest' clicked 0 times."
33
+ *
34
+ * // --- Trigger the effect by updating a dependency ---
35
+ * counter.set(1);
36
+ * // After a macrotask, logs: "Analytics: User 'guest' clicked 1 times."
37
+ *
38
+ * // --- IMPORTANT: Clean up when the effect is no longer needed ---
39
+ * analyticsEffect.destroy();
40
+ *
41
+ * // Further updates will not trigger the effect.
42
+ * counter.set(2); // Nothing is logged.
43
+ */
44
+ export function createEffect(config: EffectSignalConfig): EffectSignal {
45
+ return new EffectSignal(config);
46
+ }
@@ -0,0 +1,31 @@
1
+ import {EventSignal} from '../core/event-signal.js';
2
+
3
+ import type {SignalConfig} from '../type.js';
4
+
5
+ /**
6
+ * Creates a stateless signal for dispatching transient events.
7
+ *
8
+ * `EventSignal` is ideal for broadcasting events that do not have a persistent state.
9
+ * Unlike `StateSignal`, it does not hold a value. Listeners are only notified of new
10
+ * events as they are dispatched. This makes it suitable for modeling user interactions,
11
+ * system notifications, or any one-off message.
12
+ *
13
+ * @template T The type of the payload for the events.
14
+ *
15
+ * @param config The configuration for the event signal.
16
+ * @returns A new instance of EventSignal.
17
+ *
18
+ * @example
19
+ * const onUserClick = createEventSignal<{ x: number, y: number }>({
20
+ * name: 'on-user-click'
21
+ * });
22
+ *
23
+ * onUserClick.subscribe(pos => {
24
+ * console.log(`User clicked at: ${pos.x}, ${pos.y}`);
25
+ * });
26
+ *
27
+ * onUserClick.dispatch({ x: 100, y: 250 });
28
+ */
29
+ export function createEventSignal<T = void>(config: SignalConfig): EventSignal<T> {
30
+ return new EventSignal<T>(config);
31
+ }
@@ -0,0 +1,33 @@
1
+ import {PersistentStateSignal} from '../core/persistent-state-signal.js';
2
+
3
+ import type {PersistentStateSignalConfig} from '../type.js';
4
+
5
+ /**
6
+ * Creates a stateful signal that persists its value in localStorage.
7
+ *
8
+ * This function provides a clean, declarative API for creating state that
9
+ * survives page reloads. It automatically handles reading the initial state
10
+ * from localStorage and saving subsequent updates.
11
+ *
12
+ * @template T The type of the state it holds.
13
+ *
14
+ * @param {PersistentStateSignalConfig<T>} config The configuration for the persistent state signal.
15
+ * @returns {PersistentStateSignal<T>} A new instance of PersistentStateSignal.
16
+ *
17
+ * @example
18
+ * // Create a signal to store user's preferred theme.
19
+ * const userThemeSignal = createPersistentStateSignal<string>({
20
+ * name: 'user-theme',
21
+ * schemaVersion: 1,
22
+ * initialValue: 'light',
23
+ * });
24
+ *
25
+ * // The initial value is read from localStorage, or 'light' if not present.
26
+ * console.log(userThemeSignal.get());
27
+ *
28
+ * // Setting a new value updates the in-memory state and writes to localStorage.
29
+ * userThemeSignal.set('dark');
30
+ */
31
+ export function createPersistentStateSignal<T extends JsonValue>(config: PersistentStateSignalConfig<T>): PersistentStateSignal<T> {
32
+ return new PersistentStateSignal(config);
33
+ }
@@ -0,0 +1,42 @@
1
+ import { SessionStateSignal } from '../core/session-state-signal.js';
2
+
3
+ import type { SessionStateSignalConfig } from '../type.js';
4
+
5
+ /**
6
+ * Creates a stateful signal that persists its value in `sessionStorage`.
7
+ *
8
+ * The stored data survives soft navigations and page refreshes within the same browser tab,
9
+ * but is automatically cleared when the tab or window is closed.
10
+ *
11
+ * Use this for transient UI state such as:
12
+ * - Multi-step wizard progress
13
+ * - Unsaved form drafts
14
+ * - Scroll position or active tab indices
15
+ *
16
+ * @template T The type of the state it holds. Must be JSON-serializable.
17
+ *
18
+ * @param {SessionStateSignalConfig<T>} config The configuration for the session state signal.
19
+ * @returns {SessionStateSignal<T>} A new `SessionStateSignal` instance.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import {createSessionStateSignal} from '@alwatr/signal';
24
+ *
25
+ * const checkoutWizard = createSessionStateSignal<{step: number}>({
26
+ * name: 'checkout-wizard',
27
+ * initialValue: {step: 1},
28
+ * });
29
+ *
30
+ * // State is restored from sessionStorage if it exists.
31
+ * console.log(checkoutWizard.get()); // {step: 1} or last saved value
32
+ *
33
+ * // Update state — automatically saved to sessionStorage.
34
+ * checkoutWizard.set({step: 2});
35
+ *
36
+ * // Cleanup when no longer needed.
37
+ * checkoutWizard.destroy();
38
+ * ```
39
+ */
40
+ export function createSessionStateSignal<T extends JsonValue>(config: SessionStateSignalConfig<T>): SessionStateSignal<T> {
41
+ return new SessionStateSignal(config);
42
+ }
@@ -0,0 +1,28 @@
1
+ import {StateSignal} from '../core/state-signal.js';
2
+
3
+ import type {StateSignalConfig} from '../type.js';
4
+
5
+ /**
6
+ * Creates a stateful signal that holds a value and notifies listeners when the value changes.
7
+ *
8
+ * `StateSignal` is the core of the signal library, representing a piece of mutable state.
9
+ * It always has a value, and new subscribers immediately receive the current value by default.
10
+ *
11
+ * @template T The type of the state it holds.
12
+ *
13
+ * @param config The configuration for the state signal.
14
+ * @returns A new instance of StateSignal.
15
+ *
16
+ * @example
17
+ * const counter = createStateSignal({
18
+ * name: 'counter-signal',
19
+ * initialValue: 0,
20
+ * });
21
+ *
22
+ * console.log(counter.get()); // Outputs: 0
23
+ * counter.set(1);
24
+ * console.log(counter.get()); // Outputs: 1
25
+ */
26
+ export function createStateSignal<T>(config: StateSignalConfig<T>): StateSignal<T> {
27
+ return new StateSignal(config);
28
+ }
package/src/main.ts ADDED
@@ -0,0 +1,20 @@
1
+ export * from './core/signal-base.js';
2
+ export * from './core/event-signal.js';
3
+ export * from './core/state-signal.js';
4
+ export * from './core/computed-signal.js';
5
+ export * from './core/effect-signal.js';
6
+ export * from './core/persistent-state-signal.js';
7
+ export * from './core/session-state-signal.js';
8
+
9
+ export * from './creators/event.js';
10
+ export * from './creators/state.js';
11
+ export * from './creators/computed.js';
12
+ export * from './creators/effect.js';
13
+ export * from './creators/persistent-state.js';
14
+ export * from './creators/session-state.js';
15
+
16
+ export * from './operators/debounce.js';
17
+ export * from './operators/filter.js';
18
+ export * from './operators/map.js';
19
+
20
+ export type * from './type.js';