lume-js 0.2.1 → 0.4.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.
package/src/core/utils.js CHANGED
@@ -1,14 +1,41 @@
1
+ /**
2
+ * Utility functions for Lume.js
3
+ */
4
+
1
5
  /**
2
6
  * Resolve a nested path in an object.
3
- * Example: path "user.name" returns obj.user.name
7
+ * Example: resolvePath(obj, ['user', 'address']) returns obj.user.address
4
8
  *
5
9
  * @param {object} obj - The root object
6
10
  * @param {string[]} pathArr - Array of keys
7
11
  * @returns {object} Last object in the path
12
+ * @throws {Error} If path is invalid or doesn't exist
8
13
  */
9
14
  export function resolvePath(obj, pathArr) {
10
- return pathArr.reduce((acc, key) => {
11
- if (!acc) throw new Error(`Invalid path: ${pathArr.join(".")}`);
12
- return acc[key];
13
- }, obj);
14
- }
15
+ // If no path, return the object itself
16
+ if (!pathArr || pathArr.length === 0) {
17
+ return obj;
18
+ }
19
+
20
+ let current = obj;
21
+
22
+ for (let i = 0; i < pathArr.length; i++) {
23
+ const key = pathArr[i];
24
+
25
+ if (current === null || current === undefined) {
26
+ throw new Error(
27
+ `Cannot access property "${key}" of ${current} at path: ${pathArr.slice(0, i + 1).join('.')}`
28
+ );
29
+ }
30
+
31
+ if (!(key in current)) {
32
+ throw new Error(
33
+ `Property "${key}" does not exist at path: ${pathArr.slice(0, i + 1).join('.')}`
34
+ );
35
+ }
36
+
37
+ current = current[key];
38
+ }
39
+
40
+ return current;
41
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Lume.js TypeScript Definitions
3
+ *
4
+ * Provides type safety for reactive state management
5
+ */
6
+
7
+ /**
8
+ * Unsubscribe function returned by $subscribe
9
+ */
10
+ export type Unsubscribe = () => void;
11
+
12
+ /**
13
+ * Subscriber callback function
14
+ */
15
+ export type Subscriber<T> = (value: T) => void;
16
+
17
+ /**
18
+ * Reactive state object with $subscribe method
19
+ */
20
+ export type ReactiveState<T extends object> = T & {
21
+ /**
22
+ * Subscribe to changes on a specific property key
23
+ * @param key - Property key to watch
24
+ * @param callback - Function called when property changes
25
+ * @returns Unsubscribe function for cleanup
26
+ */
27
+ $subscribe<K extends keyof T>(
28
+ key: K,
29
+ callback: Subscriber<T[K]>
30
+ ): Unsubscribe;
31
+ };
32
+
33
+ /**
34
+ * Create a reactive state object
35
+ *
36
+ * @param obj - Plain object to make reactive
37
+ * @returns Reactive proxy with $subscribe method
38
+ * @throws {Error} If obj is not a plain object
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const store = state({
43
+ * count: 0,
44
+ * user: state({
45
+ * name: 'Alice'
46
+ * })
47
+ * });
48
+ *
49
+ * store.count++; // Triggers reactivity
50
+ *
51
+ * const unsub = store.$subscribe('count', (val) => {
52
+ * console.log('Count:', val);
53
+ * });
54
+ *
55
+ * // Cleanup
56
+ * unsub();
57
+ * ```
58
+ */
59
+ export function state<T extends object>(obj: T): ReactiveState<T>;
60
+
61
+ /**
62
+ * Bind reactive state to DOM elements
63
+ *
64
+ * @param root - Root element to scan for [data-bind] attributes
65
+ * @param store - Reactive state object
66
+ * @returns Cleanup function to remove all bindings
67
+ * @throws {Error} If root is not an HTMLElement
68
+ * @throws {Error} If store is not a reactive state object
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const store = state({ count: 0 });
73
+ * const cleanup = bindDom(document.body, store);
74
+ *
75
+ * // Later: cleanup all bindings
76
+ * cleanup();
77
+ * ```
78
+ *
79
+ * HTML:
80
+ * ```html
81
+ * <span data-bind="count"></span>
82
+ * <input data-bind="name">
83
+ * ```
84
+ */
85
+ export function bindDom(
86
+ root: HTMLElement,
87
+ store: ReactiveState<any>
88
+ ): Unsubscribe;
89
+
90
+ /**
91
+ * Create an effect that automatically tracks dependencies
92
+ *
93
+ * The effect runs immediately and re-runs when any accessed state properties change.
94
+ * Only tracks properties that are actually accessed during execution.
95
+ *
96
+ * @param fn - Function to run reactively
97
+ * @returns Cleanup function to stop the effect
98
+ * @throws {Error} If fn is not a function
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const store = state({ count: 0, name: 'Alice' });
103
+ *
104
+ * const cleanup = effect(() => {
105
+ * // Only tracks 'count' (name not accessed)
106
+ * console.log(`Count: ${store.count}`);
107
+ * });
108
+ *
109
+ * store.count = 5; // Effect re-runs
110
+ * store.name = 'Bob'; // Effect does NOT re-run
111
+ *
112
+ * cleanup(); // Stop the effect
113
+ * ```
114
+ */
115
+ export function effect(fn: () => void): Unsubscribe;
package/src/index.js CHANGED
@@ -4,10 +4,12 @@
4
4
  * Exposes:
5
5
  * - state(): create reactive state
6
6
  * - bindDom(): zero-runtime DOM binding
7
+ * - effect(): reactive effect with automatic dependency tracking
7
8
  *
8
9
  * Usage:
9
- * import { state, bindDom } from "lume-js";
10
+ * import { state, bindDom, effect } from "lume-js";
10
11
  */
11
12
 
12
13
  export { state } from "./core/state.js";
13
14
  export { bindDom } from "./core/bindDom.js";
15
+ export { effect } from "./core/effect.js";