lume-js 1.0.0 → 2.0.0-alpha.2

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/index.d.ts CHANGED
@@ -14,6 +14,94 @@ export type Unsubscribe = () => void;
14
14
  */
15
15
  export type Subscriber<T> = (value: T) => void;
16
16
 
17
+ /**
18
+ * Plugin interface for extending state behavior
19
+ *
20
+ * All hooks are optional. Hooks execute in the order plugins are registered.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const debugPlugin: Plugin = {
25
+ * name: 'debug',
26
+ * onGet: (key, value) => {
27
+ * console.log(`GET ${key}:`, value);
28
+ * return value; // Return value to pass to next plugin
29
+ * },
30
+ * onSet: (key, newValue, oldValue) => {
31
+ * console.log(`SET ${key}:`, oldValue, '→', newValue);
32
+ * return newValue; // Return value to pass to next plugin
33
+ * }
34
+ * };
35
+ *
36
+ * const store = state({ count: 0 }, { plugins: [debugPlugin] });
37
+ * ```
38
+ */
39
+ export interface Plugin {
40
+ /**
41
+ * Plugin name (for debugging)
42
+ */
43
+ name: string;
44
+
45
+ /**
46
+ * Called when state object is created
47
+ * Runs synchronously before Proxy is returned
48
+ */
49
+ onInit?(): void;
50
+
51
+ /**
52
+ * Called when a property is accessed (before value returned)
53
+ * Can transform the value by returning a new value
54
+ *
55
+ * Chain pattern: Each plugin receives the output of the previous plugin
56
+ *
57
+ * @param key - Property key being accessed
58
+ * @param value - Current value (possibly transformed by previous plugins)
59
+ * @returns Transformed value, or undefined to keep current value
60
+ */
61
+ onGet?(key: string, value: any): any;
62
+
63
+ /**
64
+ * Called when a property is updated (before subscribers notified)
65
+ * Can transform or validate the new value
66
+ *
67
+ * Chain pattern: Each plugin receives the output of the previous plugin
68
+ *
69
+ * @param key - Property key being updated
70
+ * @param newValue - New value being set (possibly transformed by previous plugins)
71
+ * @param oldValue - Previous value
72
+ * @returns Transformed value, or undefined to keep current value
73
+ */
74
+ onSet?(key: string, newValue: any, oldValue: any): any;
75
+
76
+ /**
77
+ * Called when a subscriber is added
78
+ * Useful for tracking active subscriptions
79
+ *
80
+ * @param key - Property key being subscribed to
81
+ */
82
+ onSubscribe?(key: string): void;
83
+
84
+ /**
85
+ * Called when subscribers are about to be notified
86
+ * Runs in microtask, before subscribers receive value
87
+ *
88
+ * @param key - Property key that changed
89
+ * @param value - New value being notified
90
+ */
91
+ onNotify?(key: string, value: any): void;
92
+ }
93
+
94
+ /**
95
+ * Options for state creation
96
+ */
97
+ export interface StateOptions {
98
+ /**
99
+ * Array of plugins to apply to this state object
100
+ * Plugins execute in the order they are registered
101
+ */
102
+ plugins?: Plugin[];
103
+ }
104
+
17
105
  /**
18
106
  * Reactive state object with $subscribe method
19
107
  */
@@ -55,8 +143,30 @@ export type ReactiveState<T extends object> = T & {
55
143
  * // Cleanup
56
144
  * unsub();
57
145
  * ```
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * // With plugins
150
+ * const store = state(
151
+ * { count: 0 },
152
+ * {
153
+ * plugins: [
154
+ * {
155
+ * name: 'logger',
156
+ * onGet: (key, value) => {
157
+ * console.log(`GET ${key}:`, value);
158
+ * return value;
159
+ * }
160
+ * }
161
+ * ]
162
+ * }
163
+ * );
164
+ * ```
58
165
  */
59
- export function state<T extends object>(obj: T): ReactiveState<T>;
166
+ export function state<T extends object>(
167
+ obj: T,
168
+ options?: StateOptions
169
+ ): ReactiveState<T>;
60
170
 
61
171
  /**
62
172
  * Options for bindDom function
@@ -108,7 +218,13 @@ export function bindDom(
108
218
  ): Unsubscribe;
109
219
 
110
220
  /**
111
- * Create an effect that automatically tracks dependencies
221
+ * Dependency tuple for explicit effect tracking
222
+ * Format: [store, key1, key2, ...] where store is a ReactiveState and keys are property names
223
+ */
224
+ export type EffectDependency = [ReactiveState<any>, ...string[]];
225
+
226
+ /**
227
+ * Create an effect with auto-tracking (default mode)
112
228
  *
113
229
  * The effect runs immediately and re-runs when any accessed state properties change.
114
230
  * Only tracks properties that are actually accessed during execution.
@@ -134,6 +250,31 @@ export function bindDom(
134
250
  */
135
251
  export function effect(fn: () => void): Unsubscribe;
136
252
 
253
+ /**
254
+ * Create an effect with explicit dependencies (no magic)
255
+ *
256
+ * The effect runs immediately and re-runs ONLY when specified dependencies change.
257
+ * Does not auto-track any state access. Ideal for side-effects like logging.
258
+ *
259
+ * @param fn - Function to run reactively
260
+ * @param deps - Array of [store, key] tuples specifying exact dependencies
261
+ * @returns Cleanup function to stop the effect
262
+ * @throws {Error} If fn is not a function
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const store = state({ count: 0 });
267
+ *
268
+ * // Explicit: only re-runs when store.count changes
269
+ * const cleanup = effect(() => {
270
+ * analytics.track('count', store.count);
271
+ * }, [[store, 'count']]);
272
+ *
273
+ * cleanup(); // Stop the effect
274
+ * ```
275
+ */
276
+ export function effect(fn: () => void, deps: EffectDependency[]): Unsubscribe;
277
+
137
278
  /**
138
279
  * Check if a value is a Lume reactive proxy produced by state().
139
280
  * Returns true only for objects created by state().