@fynixorg/ui 1.0.5 → 1.0.7

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.
@@ -1,2 +1,99 @@
1
- export function setActiveContext(ctx: any): void;
2
- export let activeContext: any;
1
+ /**
2
+ * Set the active component context.
3
+ * Called internally by the runtime when entering/exiting component rendering.
4
+ *
5
+ * @param {ComponentContext|null} ctx - The component context to set as active, or null to clear
6
+ * @returns {void}
7
+ *
8
+ * @example
9
+ * // Internal usage in runtime
10
+ * setActiveContext(componentContext);
11
+ * const result = Component(props);
12
+ * setActiveContext(null);
13
+ */
14
+ export function setActiveContext(ctx: ComponentContext | null): void;
15
+ /**
16
+ * @fileoverview Component context management for Fynix framework.
17
+ * Tracks the currently rendering component to enable hooks to access component state.
18
+ */
19
+ /**
20
+ * @typedef {Object} ComponentContext
21
+ * @property {Array<any>} hooks - Array of hook states
22
+ * @property {number} hookIndex - Current hook index during render
23
+ * @property {Array<Function>} effects - Array of effect cleanup functions
24
+ * @property {Array<Function>} cleanups - Array of cleanup functions
25
+ * @property {Array<Function>} [stateCleanups] - Array of state cleanup functions
26
+ * @property {Object} _vnode - Associated virtual node
27
+ * @property {Set<any>} _accessedStates - Set of states accessed during render
28
+ * @property {Set<any>} _subscriptions - Set of active subscriptions
29
+ * @property {Array<Function>} _subscriptionCleanups - Array of subscription cleanup functions
30
+ * @property {number} version - Component version for tracking updates
31
+ * @property {Function|null} rerender - Function to trigger component re-render
32
+ * @property {Function} Component - Component function
33
+ * @property {boolean} _isMounted - Whether component is mounted
34
+ * @property {boolean} _isRerendering - Whether component is currently re-rendering
35
+ */
36
+ /**
37
+ * The currently active component context.
38
+ * This is set during component rendering to allow hooks to access the component's state.
39
+ * @type {ComponentContext|null}
40
+ */
41
+ export let activeContext: ComponentContext | null;
42
+ export type ComponentContext = {
43
+ /**
44
+ * - Array of hook states
45
+ */
46
+ hooks: Array<any>;
47
+ /**
48
+ * - Current hook index during render
49
+ */
50
+ hookIndex: number;
51
+ /**
52
+ * - Array of effect cleanup functions
53
+ */
54
+ effects: Array<Function>;
55
+ /**
56
+ * - Array of cleanup functions
57
+ */
58
+ cleanups: Array<Function>;
59
+ /**
60
+ * - Array of state cleanup functions
61
+ */
62
+ stateCleanups?: Array<Function>;
63
+ /**
64
+ * - Associated virtual node
65
+ */
66
+ _vnode: any;
67
+ /**
68
+ * - Set of states accessed during render
69
+ */
70
+ _accessedStates: Set<any>;
71
+ /**
72
+ * - Set of active subscriptions
73
+ */
74
+ _subscriptions: Set<any>;
75
+ /**
76
+ * - Array of subscription cleanup functions
77
+ */
78
+ _subscriptionCleanups: Array<Function>;
79
+ /**
80
+ * - Component version for tracking updates
81
+ */
82
+ version: number;
83
+ /**
84
+ * - Function to trigger component re-render
85
+ */
86
+ rerender: Function | null;
87
+ /**
88
+ * - Component function
89
+ */
90
+ Component: Function;
91
+ /**
92
+ * - Whether component is mounted
93
+ */
94
+ _isMounted: boolean;
95
+ /**
96
+ * - Whether component is currently re-rendering
97
+ */
98
+ _isRerendering: boolean;
99
+ };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../context/context.js"],
4
- "sourcesContent": ["// runtime/context.js\r\nexport let activeContext = null;\r\n\r\nexport function setActiveContext(ctx) {\r\n activeContext = ctx;\r\n}\r\n"],
5
- "mappings": ";;AACO,IAAI,gBAAgB;AAEpB,SAAS,iBAAiB,KAAK;AACpC,kBAAgB;AAClB;AAFgB;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Component context management for Fynix framework.\r\n * Tracks the currently rendering component to enable hooks to access component state.\r\n */\r\n\r\n/**\r\n * @typedef {Object} ComponentContext\r\n * @property {Array<any>} hooks - Array of hook states\r\n * @property {number} hookIndex - Current hook index during render\r\n * @property {Array<Function>} effects - Array of effect cleanup functions\r\n * @property {Array<Function>} cleanups - Array of cleanup functions\r\n * @property {Array<Function>} [stateCleanups] - Array of state cleanup functions\r\n * @property {Object} _vnode - Associated virtual node\r\n * @property {Set<any>} _accessedStates - Set of states accessed during render\r\n * @property {Set<any>} _subscriptions - Set of active subscriptions\r\n * @property {Array<Function>} _subscriptionCleanups - Array of subscription cleanup functions\r\n * @property {number} version - Component version for tracking updates\r\n * @property {Function|null} rerender - Function to trigger component re-render\r\n * @property {Function} Component - Component function\r\n * @property {boolean} _isMounted - Whether component is mounted\r\n * @property {boolean} _isRerendering - Whether component is currently re-rendering\r\n */\r\n\r\n/**\r\n * The currently active component context.\r\n * This is set during component rendering to allow hooks to access the component's state.\r\n * @type {ComponentContext|null}\r\n */\r\nexport let activeContext = null;\r\n\r\n/**\r\n * Set the active component context.\r\n * Called internally by the runtime when entering/exiting component rendering.\r\n * \r\n * @param {ComponentContext|null} ctx - The component context to set as active, or null to clear\r\n * @returns {void}\r\n * \r\n * @example\r\n * // Internal usage in runtime\r\n * setActiveContext(componentContext);\r\n * const result = Component(props);\r\n * setActiveContext(null);\r\n */\r\nexport function setActiveContext(ctx) {\r\n activeContext = ctx;\r\n}\r\n"],
5
+ "mappings": ";;AA4BO,IAAI,gBAAgB;AAepB,SAAS,iBAAiB,KAAK;AACpC,kBAAgB;AAClB;AAFgB;",
6
6
  "names": []
7
7
  }
File without changes
@@ -1,19 +1,62 @@
1
1
  /**
2
2
  * @typedef {Object} ButtonProps
3
- * @property {string} [value] - Inner text of the button (default: "")
4
- * @property {Record<string, any>} [props] - Other attributes
3
+ * @property {string} [value=""] - Button text content
4
+ * @property {string} [type="button"] - Button type (button, submit, reset)
5
+ * @property {boolean} [disabled=false] - Whether button is disabled
6
+ * @property {string} [rc] - Reactive class attribute (Fynix-specific)
7
+ * @property {string} [class] - CSS class names
8
+ * @property {(this: HTMLElement, event: MouseEvent) => void} [r-click] - Click event handler
5
9
  */
6
10
  /**
7
- * @param {ButtonProps} props
11
+ * Button component for Fynix applications.
12
+ * Renders a native HTML button element with Fynix event handling.
13
+ *
14
+ * @param {ButtonProps & Record<string, any>} props - Component props
15
+ * @returns {VNode} Button element virtual node
16
+ *
17
+ * @example
18
+ * // Basic button
19
+ * <Button value="Click Me" />
20
+ *
21
+ * @example
22
+ * // With click handler
23
+ * <Button
24
+ * value="Submit"
25
+ * r-click={() => console.log('Clicked!')}
26
+ * />
27
+ *
28
+ * @example
29
+ * // Submit button with styling
30
+ * <Button
31
+ * value="Save"
32
+ * type="submit"
33
+ * rc="px-4 py-2 bg-blue-500 text-white rounded"
34
+ * />
8
35
  */
9
- export function Button({ value, ...props }: ButtonProps): import("../runtime.js").VNode;
36
+ export function Button({ value, ...props }: ButtonProps & Record<string, any>): VNode;
10
37
  export type ButtonProps = {
11
38
  /**
12
- * - Inner text of the button (default: "")
39
+ * - Button text content
13
40
  */
14
41
  value?: string;
15
42
  /**
16
- * - Other attributes
43
+ * - Button type (button, submit, reset)
17
44
  */
18
- props?: Record<string, any>;
45
+ type?: string;
46
+ /**
47
+ * - Whether button is disabled
48
+ */
49
+ disabled?: boolean;
50
+ /**
51
+ * - Reactive class attribute (Fynix-specific)
52
+ */
53
+ rc?: string;
54
+ /**
55
+ * - CSS class names
56
+ */
57
+ class?: string;
58
+ /**
59
+ * - Click event handler
60
+ */
61
+ "r-click"?: (this: HTMLElement, event: MouseEvent) => void;
19
62
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../custom/button.js"],
4
- "sourcesContent": ["import { Fynix } from \"../runtime.js\";\r\n\r\n/**\r\n * @typedef {Object} ButtonProps\r\n * @property {string} [value] - Inner text of the button (default: \"\")\r\n * @property {Record<string, any>} [props] - Other attributes\r\n */\r\n\r\n/**\r\n * @param {ButtonProps} props\r\n */\r\nexport function Button({ value = \"\", ...props }) {\r\n return Fynix(\"button\", props, value);\r\n}\r\n"],
5
- "mappings": ";;AAAA,SAAS,aAAa;AAWf,SAAS,OAAO,EAAE,QAAQ,IAAI,GAAG,MAAM,GAAG;AAC/C,SAAO,MAAM,UAAU,OAAO,KAAK;AACrC;AAFgB;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Button component for Fynix applications.\r\n * Simple wrapper around native button element with Fynix integration.\r\n */\r\n\r\nimport { Fynix } from \"../runtime.js\";\r\n\r\n/**\r\n * @typedef {Object} ButtonProps\r\n * @property {string} [value=\"\"] - Button text content\r\n * @property {string} [type=\"button\"] - Button type (button, submit, reset)\r\n * @property {boolean} [disabled=false] - Whether button is disabled\r\n * @property {string} [rc] - Reactive class attribute (Fynix-specific)\r\n * @property {string} [class] - CSS class names\r\n * @property {(this: HTMLElement, event: MouseEvent) => void} [r-click] - Click event handler\r\n */\r\n\r\n/**\r\n * Button component for Fynix applications.\r\n * Renders a native HTML button element with Fynix event handling.\r\n * \r\n * @param {ButtonProps & Record<string, any>} props - Component props\r\n * @returns {VNode} Button element virtual node\r\n * \r\n * @example\r\n * // Basic button\r\n * <Button value=\"Click Me\" />\r\n * \r\n * @example\r\n * // With click handler\r\n * <Button \r\n * value=\"Submit\"\r\n * r-click={() => console.log('Clicked!')}\r\n * />\r\n * \r\n * @example\r\n * // Submit button with styling\r\n * <Button\r\n * value=\"Save\"\r\n * type=\"submit\"\r\n * rc=\"px-4 py-2 bg-blue-500 text-white rounded\"\r\n * />\r\n */\r\nexport function Button({ value = \"\", ...props }) {\r\n return Fynix(\"button\", props, value);\r\n}\r\n"],
5
+ "mappings": ";;AAKA,SAAS,aAAa;AAsCf,SAAS,OAAO,EAAE,QAAQ,IAAI,GAAG,MAAM,GAAG;AAC/C,SAAO,MAAM,UAAU,OAAO,KAAK;AACrC;AAFgB;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../custom/index.js"],
4
- "sourcesContent": ["// core/custom/index.js\r\nexport { Button } from \"./button\";\r\nexport { Path } from \"./path\";\r\n"],
5
- "mappings": "AACA,SAAS,cAAc;AACvB,SAAS,YAAY;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Custom Fynix components export module.\r\n * Re-exports Button and Path components for convenient importing.\r\n * \r\n * @example\r\n * import { Button, Path } from '@fynixorg/ui/custom';\r\n */\r\n\r\nexport { Button } from \"./button\";\r\nexport { Path } from \"./path\";\r\n"],
5
+ "mappings": "AAQA,SAAS,cAAc;AACvB,SAAS,YAAY;",
6
6
  "names": []
7
7
  }
@@ -1,20 +1,71 @@
1
1
  /**
2
- * SPA navigation link for Fynix router
3
- * @param {PathProps & Record<string, any>} options
4
- * @returns {VNode} anchor virtual node
2
+ * SPA navigation link component for Fynix router.
3
+ * Automatically handles client-side navigation and prop passing between routes.
4
+ *
5
+ * @param {PathProps & Record<string, any>} options - Component props
6
+ * @returns {VNode} Anchor element virtual node
7
+ *
8
+ * @example
9
+ * // Basic navigation
10
+ * <Path to="/about" value="About Us" />
11
+ *
12
+ * @example
13
+ * // Navigate with props
14
+ * <Path
15
+ * to="/user/123"
16
+ * value="View Profile"
17
+ * props={{ userId: 123, fromSearch: true }}
18
+ * />
19
+ *
20
+ * @example
21
+ * // External link
22
+ * <Path
23
+ * to="https://github.com"
24
+ * value="GitHub"
25
+ * target="_blank"
26
+ * rel="noopener noreferrer"
27
+ * />
28
+ *
29
+ * @example
30
+ * // With styling
31
+ * <Path
32
+ * to="/dashboard"
33
+ * value="Dashboard"
34
+ * rc="px-4 py-2 bg-blue-500 text-white rounded"
35
+ * />
5
36
  */
6
37
  export function Path({ to, value, props: routeProps, ...attrs }: PathProps & Record<string, any>): VNode;
7
38
  export type PathProps = {
8
39
  /**
9
- * - URL to navigate to
40
+ * - URL to navigate to (relative or absolute)
10
41
  */
11
42
  to?: string;
12
43
  /**
13
- * - Link text
44
+ * - Link text content
14
45
  */
15
46
  value?: string;
16
47
  /**
17
- * - Props to pass to the next route
48
+ * - Props to pass to the destination route component
18
49
  */
19
- props?: any;
50
+ props?: Record<string, any>;
51
+ /**
52
+ * - Link target (_blank, _self, etc.)
53
+ */
54
+ target?: string;
55
+ /**
56
+ * - Link relationship (noopener, noreferrer, etc.)
57
+ */
58
+ rel?: string;
59
+ /**
60
+ * - Reactive class attribute (Fynix-specific)
61
+ */
62
+ rc?: string;
63
+ /**
64
+ * - CSS class names
65
+ */
66
+ class?: string;
67
+ /**
68
+ * - Element ID
69
+ */
70
+ id?: string;
20
71
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../custom/path.js"],
4
- "sourcesContent": ["import { Fynix, nixState } from \"../runtime.js\";\r\n\r\n/**\r\n * @typedef {Object} PathProps\r\n * @property {string} [to] - URL to navigate to\r\n * @property {string} [value] - Link text\r\n * @property {Object} [props] - Props to pass to the next route\r\n */\r\n\r\n/** Unique counter for props keys */\r\nlet propsCounter = 0;\r\n\r\n/**\r\n * SPA navigation link for Fynix router\r\n * @param {PathProps & Record<string, any>} options\r\n * @returns {VNode} anchor virtual node\r\n */\r\nexport function Path({\r\n to = \"#\",\r\n value = \"\",\r\n props: routeProps = {},\r\n ...attrs\r\n}) {\r\n ///console.log(\"[Path Component] Received routeProps:\", routeProps);\r\n\r\n // Wrap plain props in nixState if not already\r\n const wrappedProps = {};\r\n for (const [k, v] of Object.entries(routeProps)) {\r\n wrappedProps[k] = v && v._isNixState ? v : nixState(v);\r\n }\r\n\r\n // Generate a unique key for this props object\r\n // FIX: Do not use __ prefix as router blocks it for security\r\n const propsKey = `fynixProp_${Date.now()}_${propsCounter++}`;\r\n\r\n // FIX: Store props in the namespace expected by the router\r\n if (!window.__fynixLinkProps__) {\r\n window.__fynixLinkProps__ = {};\r\n }\r\n\r\n // Debug log\r\n // console.log(\"[Path] Storage props:\", propsKey, wrappedProps);\r\n\r\n window.__fynixLinkProps__[propsKey] = wrappedProps;\r\n\r\n // Create the anchor element\r\n const el = Fynix(\r\n \"a\",\r\n {\r\n href: to,\r\n \"data-fynix-link\": true, // SPA link detection\r\n \"data-props-key\": propsKey,\r\n ...attrs,\r\n },\r\n value\r\n );\r\n\r\n return el;\r\n}\r\n"],
5
- "mappings": ";;AAAA,SAAS,OAAO,gBAAgB;AAUhC,IAAI,eAAe;AAOZ,SAAS,KAAK;AAAA,EACnB,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,OAAO,aAAa,CAAC;AAAA,EACrB,GAAG;AACL,GAAG;AAID,QAAM,eAAe,CAAC;AACtB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,UAAU,GAAG;AAC/C,iBAAa,CAAC,IAAI,KAAK,EAAE,cAAc,IAAI,SAAS,CAAC;AAAA,EACvD;AAIA,QAAM,WAAW,aAAa,KAAK,IAAI,CAAC,IAAI,cAAc;AAG1D,MAAI,CAAC,OAAO,oBAAoB;AAC9B,WAAO,qBAAqB,CAAC;AAAA,EAC/B;AAKA,SAAO,mBAAmB,QAAQ,IAAI;AAGtC,QAAM,KAAK;AAAA,IACT;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,mBAAmB;AAAA;AAAA,MACnB,kBAAkB;AAAA,MAClB,GAAG;AAAA,IACL;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AACT;AAzCgB;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Path component for SPA navigation in Fynix applications.\r\n * Provides client-side routing with prop passing between routes.\r\n */\r\n\r\nimport { Fynix, nixState } from \"../runtime.js\";\r\n\r\n/**\r\n * @typedef {Object} PathProps\r\n * @property {string} [to=\"#\"] - URL to navigate to (relative or absolute)\r\n * @property {string} [value=\"\"] - Link text content\r\n * @property {Record<string, any>} [props={}] - Props to pass to the destination route component\r\n * @property {string} [target] - Link target (_blank, _self, etc.)\r\n * @property {string} [rel] - Link relationship (noopener, noreferrer, etc.)\r\n * @property {string} [rc] - Reactive class attribute (Fynix-specific)\r\n * @property {string} [class] - CSS class names\r\n * @property {string} [id] - Element ID\r\n */\r\n\r\n/** Unique counter for props keys */\r\nlet propsCounter = 0;\r\n\r\n/**\r\n * SPA navigation link component for Fynix router.\r\n * Automatically handles client-side navigation and prop passing between routes.\r\n * \r\n * @param {PathProps & Record<string, any>} options - Component props\r\n * @returns {VNode} Anchor element virtual node\r\n * \r\n * @example\r\n * // Basic navigation\r\n * <Path to=\"/about\" value=\"About Us\" />\r\n * \r\n * @example\r\n * // Navigate with props\r\n * <Path \r\n * to=\"/user/123\" \r\n * value=\"View Profile\"\r\n * props={{ userId: 123, fromSearch: true }}\r\n * />\r\n * \r\n * @example\r\n * // External link\r\n * <Path\r\n * to=\"https://github.com\"\r\n * value=\"GitHub\"\r\n * target=\"_blank\"\r\n * rel=\"noopener noreferrer\"\r\n * />\r\n * \r\n * @example\r\n * // With styling\r\n * <Path\r\n * to=\"/dashboard\"\r\n * value=\"Dashboard\"\r\n * rc=\"px-4 py-2 bg-blue-500 text-white rounded\"\r\n * />\r\n */\r\nexport function Path({\r\n to = \"#\",\r\n value = \"\",\r\n props: routeProps = {},\r\n ...attrs\r\n}) {\r\n ///console.log(\"[Path Component] Received routeProps:\", routeProps);\r\n\r\n // Wrap plain props in nixState if not already\r\n const wrappedProps = {};\r\n for (const [k, v] of Object.entries(routeProps)) {\r\n wrappedProps[k] = v && v._isNixState ? v : nixState(v);\r\n }\r\n\r\n // Generate a unique key for this props object\r\n // FIX: Do not use __ prefix as router blocks it for security\r\n const propsKey = `fynixProp_${Date.now()}_${propsCounter++}`;\r\n\r\n // FIX: Store props in the namespace expected by the router\r\n if (!window.__fynixLinkProps__) {\r\n window.__fynixLinkProps__ = {};\r\n }\r\n\r\n // Debug log\r\n // console.log(\"[Path] Storage props:\", propsKey, wrappedProps);\r\n\r\n window.__fynixLinkProps__[propsKey] = wrappedProps;\r\n\r\n // Create the anchor element\r\n const el = Fynix(\r\n \"a\",\r\n {\r\n href: to,\r\n \"data-fynix-link\": true, // SPA link detection\r\n \"data-props-key\": propsKey,\r\n ...attrs,\r\n },\r\n value\r\n );\r\n\r\n return el;\r\n}\r\n"],
5
+ "mappings": ";;AAKA,SAAS,OAAO,gBAAgB;AAehC,IAAI,eAAe;AAsCZ,SAAS,KAAK;AAAA,EACnB,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,OAAO,aAAa,CAAC;AAAA,EACrB,GAAG;AACL,GAAG;AAID,QAAM,eAAe,CAAC;AACtB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,UAAU,GAAG;AAC/C,iBAAa,CAAC,IAAI,KAAK,EAAE,cAAc,IAAI,SAAS,CAAC;AAAA,EACvD;AAIA,QAAM,WAAW,aAAa,KAAK,IAAI,CAAC,IAAI,cAAc;AAG1D,MAAI,CAAC,OAAO,oBAAoB;AAC9B,WAAO,qBAAqB,CAAC;AAAA,EAC/B;AAKA,SAAO,mBAAmB,QAAQ,IAAI;AAGtC,QAAM,KAAK;AAAA,IACT;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,mBAAmB;AAAA;AAAA,MACnB,kBAAkB;AAAA,MAClB,GAAG;AAAA,IACL;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AACT;AAzCgB;",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,26 @@
1
- export function showErrorOverlay(error: any): void;
1
+ /**
2
+ * @fileoverview Development error overlay for Fynix applications.
3
+ * Displays runtime errors in a user-friendly overlay during development.
4
+ */
5
+ /**
6
+ * Display an error overlay with error details.
7
+ * Shows error message, stack trace, and a close button.
8
+ *
9
+ * @param {Error} error - The error object to display
10
+ * @returns {void}
11
+ *
12
+ * @example
13
+ * try {
14
+ * // some code
15
+ * } catch (err) {
16
+ * showErrorOverlay(err);
17
+ * }
18
+ */
19
+ export function showErrorOverlay(error: Error): void;
20
+ /**
21
+ * Remove the error overlay from the DOM.
22
+ * Fades out the overlay before removing it.
23
+ *
24
+ * @returns {void}
25
+ */
2
26
  export function removeErrorOverlay(): void;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../error/errorOverlay.js"],
4
- "sourcesContent": ["// errorOverlay.js\r\nexport function showErrorOverlay(error) {\r\n removeErrorOverlay();\r\n\r\n const overlay = document.createElement(\"div\");\r\n overlay.id = \"dev-error-overlay\";\r\n Object.assign(overlay.style, {\r\n position: \"fixed\",\r\n inset: 0,\r\n width: \"100%\",\r\n height: \"100%\",\r\n backgroundColor: \"rgba(0, 0, 0, 0.9)\",\r\n color: \"#fff\",\r\n fontFamily: \"Consolas, monospace\",\r\n padding: \"30px\",\r\n zIndex: 99999,\r\n overflowY: \"auto\",\r\n transition: \"opacity 0.25s ease\",\r\n opacity: \"0\",\r\n });\r\n\r\n const container = document.createElement(\"div\");\r\n container.style.position = \"relative\"; // important for absolute button\r\n container.style.maxWidth = \"900px\";\r\n container.style.margin = \"50px auto\";\r\n container.style.background = \"rgba(255, 255, 255, 0.05)\";\r\n container.style.borderRadius = \"8px\";\r\n container.style.padding = \"20px 30px\";\r\n container.style.boxShadow = \"0 0 10px rgba(255, 0, 0, 0.3)\";\r\n\r\n // Close Button\r\n const closeBtn = document.createElement(\"button\");\r\n closeBtn.textContent = \"\u00D7 Close\";\r\n Object.assign(closeBtn.style, {\r\n position: \"absolute\",\r\n top: \"10px\",\r\n right: \"10px\",\r\n background: \"#ff5555\",\r\n color: \"#fff\",\r\n border: \"none\",\r\n padding: \"6px 12px\",\r\n fontSize: \"16px\",\r\n borderRadius: \"4px\",\r\n cursor: \"pointer\",\r\n zIndex: 10,\r\n });\r\n closeBtn.addEventListener(\"click\", () => removeErrorOverlay());\r\n container.appendChild(closeBtn);\r\n\r\n // Title\r\n const title = document.createElement(\"h1\");\r\n title.textContent = \"Runtime Error\";\r\n Object.assign(title.style, {\r\n color: \"#ff5555\",\r\n fontSize: \"24px\",\r\n marginBottom: \"10px\",\r\n });\r\n\r\n // Message\r\n const message = document.createElement(\"pre\");\r\n message.textContent = error?.message || \"Unknown error\";\r\n Object.assign(message.style, {\r\n whiteSpace: \"pre-wrap\",\r\n background: \"rgba(255, 255, 255, 0.1)\",\r\n padding: \"10px\",\r\n borderRadius: \"6px\",\r\n marginTop: \"10px\",\r\n color: \"#ffaaaa\",\r\n });\r\n\r\n // Stack trace\r\n const stack = document.createElement(\"pre\");\r\n stack.textContent = error?.stack || \"\";\r\n Object.assign(stack.style, {\r\n whiteSpace: \"pre-wrap\",\r\n marginTop: \"15px\",\r\n color: \"#ccc\",\r\n });\r\n\r\n container.appendChild(title);\r\n container.appendChild(message);\r\n container.appendChild(stack);\r\n overlay.appendChild(container);\r\n document.body.appendChild(overlay);\r\n\r\n // Smooth fade-in\r\n requestAnimationFrame(() => {\r\n overlay.style.opacity = \"1\";\r\n });\r\n\r\n // Console log\r\n console.error(\"FynixJS Runtime Error]\", error);\r\n}\r\n\r\nexport function removeErrorOverlay() {\r\n const existing = document.getElementById(\"dev-error-overlay\");\r\n if (existing) {\r\n existing.style.opacity = \"0\";\r\n setTimeout(() => existing.remove(), 200);\r\n }\r\n}\r\n"],
5
- "mappings": ";;AACO,SAAS,iBAAiB,OAAO;AACtC,qBAAmB;AAEnB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,KAAK;AACb,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,EACX,CAAC;AAED,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,SAAS;AACzB,YAAU,MAAM,aAAa;AAC7B,YAAU,MAAM,eAAe;AAC/B,YAAU,MAAM,UAAU;AAC1B,YAAU,MAAM,YAAY;AAG5B,QAAM,WAAW,SAAS,cAAc,QAAQ;AAChD,WAAS,cAAc;AACvB,SAAO,OAAO,SAAS,OAAO;AAAA,IAC5B,UAAU;AAAA,IACV,KAAK;AAAA,IACL,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,iBAAiB,SAAS,MAAM,mBAAmB,CAAC;AAC7D,YAAU,YAAY,QAAQ;AAG9B,QAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,QAAM,cAAc;AACpB,SAAO,OAAO,MAAM,OAAO;AAAA,IACzB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AAGD,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,cAAc,OAAO,WAAW;AACxC,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAGD,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,cAAc,OAAO,SAAS;AACpC,SAAO,OAAO,MAAM,OAAO;AAAA,IACzB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,YAAU,YAAY,KAAK;AAC3B,YAAU,YAAY,OAAO;AAC7B,YAAU,YAAY,KAAK;AAC3B,UAAQ,YAAY,SAAS;AAC7B,WAAS,KAAK,YAAY,OAAO;AAGjC,wBAAsB,MAAM;AAC1B,YAAQ,MAAM,UAAU;AAAA,EAC1B,CAAC;AAGD,UAAQ,MAAM,0BAA0B,KAAK;AAC/C;AA3FgB;AA6FT,SAAS,qBAAqB;AACnC,QAAM,WAAW,SAAS,eAAe,mBAAmB;AAC5D,MAAI,UAAU;AACZ,aAAS,MAAM,UAAU;AACzB,eAAW,MAAM,SAAS,OAAO,GAAG,GAAG;AAAA,EACzC;AACF;AANgB;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Development error overlay for Fynix applications.\r\n * Displays runtime errors in a user-friendly overlay during development.\r\n */\r\n\r\n/**\r\n * Display an error overlay with error details.\r\n * Shows error message, stack trace, and a close button.\r\n * \r\n * @param {Error} error - The error object to display\r\n * @returns {void}\r\n * \r\n * @example\r\n * try {\r\n * // some code\r\n * } catch (err) {\r\n * showErrorOverlay(err);\r\n * }\r\n */\r\nexport function showErrorOverlay(error) {\r\n removeErrorOverlay();\r\n\r\n const overlay = document.createElement(\"div\");\r\n overlay.id = \"dev-error-overlay\";\r\n Object.assign(overlay.style, {\r\n position: \"fixed\",\r\n inset: 0,\r\n width: \"100%\",\r\n height: \"100%\",\r\n backgroundColor: \"rgba(0, 0, 0, 0.9)\",\r\n color: \"#fff\",\r\n fontFamily: \"Consolas, monospace\",\r\n padding: \"30px\",\r\n zIndex: 99999,\r\n overflowY: \"auto\",\r\n transition: \"opacity 0.25s ease\",\r\n opacity: \"0\",\r\n });\r\n\r\n const container = document.createElement(\"div\");\r\n container.style.position = \"relative\"; // important for absolute button\r\n container.style.maxWidth = \"900px\";\r\n container.style.margin = \"50px auto\";\r\n container.style.background = \"rgba(255, 255, 255, 0.05)\";\r\n container.style.borderRadius = \"8px\";\r\n container.style.padding = \"20px 30px\";\r\n container.style.boxShadow = \"0 0 10px rgba(255, 0, 0, 0.3)\";\r\n\r\n // Close Button\r\n const closeBtn = document.createElement(\"button\");\r\n closeBtn.textContent = \"\u00D7 Close\";\r\n Object.assign(closeBtn.style, {\r\n position: \"absolute\",\r\n top: \"10px\",\r\n right: \"10px\",\r\n background: \"#ff5555\",\r\n color: \"#fff\",\r\n border: \"none\",\r\n padding: \"6px 12px\",\r\n fontSize: \"16px\",\r\n borderRadius: \"4px\",\r\n cursor: \"pointer\",\r\n zIndex: 10,\r\n });\r\n closeBtn.addEventListener(\"click\", () => removeErrorOverlay());\r\n container.appendChild(closeBtn);\r\n\r\n // Title\r\n const title = document.createElement(\"h1\");\r\n title.textContent = \"Runtime Error\";\r\n Object.assign(title.style, {\r\n color: \"#ff5555\",\r\n fontSize: \"24px\",\r\n marginBottom: \"10px\",\r\n });\r\n\r\n // Message\r\n const message = document.createElement(\"pre\");\r\n message.textContent = error?.message || \"Unknown error\";\r\n Object.assign(message.style, {\r\n whiteSpace: \"pre-wrap\",\r\n background: \"rgba(255, 255, 255, 0.1)\",\r\n padding: \"10px\",\r\n borderRadius: \"6px\",\r\n marginTop: \"10px\",\r\n color: \"#ffaaaa\",\r\n });\r\n\r\n // Stack trace\r\n const stack = document.createElement(\"pre\");\r\n stack.textContent = error?.stack || \"\";\r\n Object.assign(stack.style, {\r\n whiteSpace: \"pre-wrap\",\r\n marginTop: \"15px\",\r\n color: \"#ccc\",\r\n });\r\n\r\n container.appendChild(title);\r\n container.appendChild(message);\r\n container.appendChild(stack);\r\n overlay.appendChild(container);\r\n document.body.appendChild(overlay);\r\n\r\n // Smooth fade-in\r\n requestAnimationFrame(() => {\r\n overlay.style.opacity = \"1\";\r\n });\r\n\r\n // Console log\r\n console.error(\"FynixJS Runtime Error]\", error);\r\n}\r\n\r\n/**\r\n * Remove the error overlay from the DOM.\r\n * Fades out the overlay before removing it.\r\n * \r\n * @returns {void}\r\n */\r\nexport function removeErrorOverlay() {\r\n const existing = document.getElementById(\"dev-error-overlay\");\r\n if (existing) {\r\n existing.style.opacity = \"0\";\r\n setTimeout(() => existing.remove(), 200);\r\n }\r\n}\r\n"],
5
+ "mappings": ";;AAmBO,SAAS,iBAAiB,OAAO;AACtC,qBAAmB;AAEnB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,KAAK;AACb,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,EACX,CAAC;AAED,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,SAAS;AACzB,YAAU,MAAM,aAAa;AAC7B,YAAU,MAAM,eAAe;AAC/B,YAAU,MAAM,UAAU;AAC1B,YAAU,MAAM,YAAY;AAG5B,QAAM,WAAW,SAAS,cAAc,QAAQ;AAChD,WAAS,cAAc;AACvB,SAAO,OAAO,SAAS,OAAO;AAAA,IAC5B,UAAU;AAAA,IACV,KAAK;AAAA,IACL,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,iBAAiB,SAAS,MAAM,mBAAmB,CAAC;AAC7D,YAAU,YAAY,QAAQ;AAG9B,QAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,QAAM,cAAc;AACpB,SAAO,OAAO,MAAM,OAAO;AAAA,IACzB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AAGD,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,cAAc,OAAO,WAAW;AACxC,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAGD,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,cAAc,OAAO,SAAS;AACpC,SAAO,OAAO,MAAM,OAAO;AAAA,IACzB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,YAAU,YAAY,KAAK;AAC3B,YAAU,YAAY,OAAO;AAC7B,YAAU,YAAY,KAAK;AAC3B,UAAQ,YAAY,SAAS;AAC7B,WAAS,KAAK,YAAY,OAAO;AAGjC,wBAAsB,MAAM;AAC1B,YAAQ,MAAM,UAAU;AAAA,EAC1B,CAAC;AAGD,UAAQ,MAAM,0BAA0B,KAAK;AAC/C;AA3FgB;AAmGT,SAAS,qBAAqB;AACnC,QAAM,WAAW,SAAS,eAAe,mBAAmB;AAC5D,MAAI,UAAU;AACZ,aAAS,MAAM,UAAU;AACzB,eAAW,MAAM,SAAS,OAAO,GAAG,GAAG;AAAA,EACzC;AACF;AANgB;",
6
6
  "names": []
7
7
  }
@@ -1,19 +1,22 @@
1
1
  /**
2
- * Create a derived/computed state from other states.
3
- * Automatically tracks dependencies and updates when any dependency changes.
4
- *
5
- * @param {() => any} computeFn - Function that computes the derived value
6
- * @returns {ComputedState} A reactive state object with the computed value
7
- *
2
+ * @template T
8
3
  * @typedef {Object} ComputedState
9
- * @property {any} value - Get the computed value (read-only)
10
- * @property {(fn: Function) => Function} subscribe - Subscribe to computed value changes
4
+ * @property {T} value - Get the computed value (read-only)
5
+ * @property {(fn: (value: T) => void) => (() => void)} subscribe - Subscribe to computed value changes
11
6
  * @property {() => void} cleanup - Cleanup all subscriptions and dependencies
12
7
  * @property {() => number} getSubscriberCount - Get number of active subscribers (debugging)
13
8
  * @property {() => number} getDependencyCount - Get number of tracked dependencies (debugging)
14
9
  * @property {() => boolean} isDestroyed - Check if computed state has been destroyed
15
10
  * @property {boolean} _isNixState - Internal flag (computed states behave like states)
16
11
  * @property {boolean} _isComputed - Internal flag to identify computed states
12
+ */
13
+ /**
14
+ * Create a derived/computed state from other states.
15
+ * Automatically tracks dependencies and updates when any dependency changes.
16
+ *
17
+ * @template T
18
+ * @param {() => T} computeFn - Function that computes the derived value
19
+ * @returns {ComputedState<T>} A reactive state object with the computed value
17
20
  *
18
21
  * @example
19
22
  * const count = nixState(5);
@@ -53,20 +56,16 @@
53
56
  * @throws {Error} If called outside a component context
54
57
  * @throws {TypeError} If computeFn is not a function
55
58
  */
56
- export function nixComputed(computeFn: () => any): ComputedState;
57
- /**
58
- * Create a derived/computed state from other states.
59
- * Automatically tracks dependencies and updates when any dependency changes.
60
- */
61
- export type ComputedState = {
59
+ export function nixComputed<T>(computeFn: () => T): ComputedState<T>;
60
+ export type ComputedState<T> = {
62
61
  /**
63
62
  * - Get the computed value (read-only)
64
63
  */
65
- value: any;
64
+ value: T;
66
65
  /**
67
66
  * - Subscribe to computed value changes
68
67
  */
69
- subscribe: (fn: Function) => Function;
68
+ subscribe: (fn: (value: T) => void) => (() => void);
70
69
  /**
71
70
  * - Cleanup all subscriptions and dependencies
72
71
  */
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../hooks/nixComputed.js"],
4
- "sourcesContent": ["/* ----------------------\r\n nixComputed - Computed/Derived State\r\n Memory Leaks & Security Issues Resolved\r\n---------------------- */\r\nimport { activeContext, setActiveContext } from \"../context/context\";\r\n\r\n/**\r\n * Create a derived/computed state from other states.\r\n * Automatically tracks dependencies and updates when any dependency changes.\r\n * \r\n * @param {() => any} computeFn - Function that computes the derived value\r\n * @returns {ComputedState} A reactive state object with the computed value\r\n * \r\n * @typedef {Object} ComputedState\r\n * @property {any} value - Get the computed value (read-only)\r\n * @property {(fn: Function) => Function} subscribe - Subscribe to computed value changes\r\n * @property {() => void} cleanup - Cleanup all subscriptions and dependencies\r\n * @property {() => number} getSubscriberCount - Get number of active subscribers (debugging)\r\n * @property {() => number} getDependencyCount - Get number of tracked dependencies (debugging)\r\n * @property {() => boolean} isDestroyed - Check if computed state has been destroyed\r\n * @property {boolean} _isNixState - Internal flag (computed states behave like states)\r\n * @property {boolean} _isComputed - Internal flag to identify computed states\r\n * \r\n * @example\r\n * const count = nixState(5);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * console.log(doubled.value); // 10\r\n * count.value = 10;\r\n * console.log(doubled.value); // 20\r\n * \r\n * @example\r\n * // Multiple dependencies\r\n * const a = nixState(5);\r\n * const b = nixState(10);\r\n * const sum = nixComputed(() => a.value + b.value);\r\n * console.log(sum.value); // 15\r\n * \r\n * @example\r\n * // Conditional dependencies\r\n * const flag = nixState(true);\r\n * const x = nixState(1);\r\n * const y = nixState(2);\r\n * const result = nixComputed(() => flag.value ? x.value : y.value);\r\n * \r\n * @example\r\n * // With cleanup\r\n * const MyComponent = () => {\r\n * const count = nixState(0);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * \r\n * nixEffect(() => {\r\n * return () => {\r\n * doubled.cleanup();\r\n * count.cleanup();\r\n * };\r\n * }, []);\r\n * };\r\n * \r\n * @throws {Error} If called outside a component context\r\n * @throws {TypeError} If computeFn is not a function\r\n */\r\nexport function nixComputed(computeFn) {\r\n const ctx = activeContext;\r\n if (!ctx) throw new Error(\"nixComputed() called outside component\");\r\n \r\n // Security: Validate compute function\r\n if (typeof computeFn !== 'function') {\r\n throw new TypeError('[nixComputed] First argument must be a function');\r\n }\r\n\r\n const idx = ctx.hookIndex++;\r\n if (!ctx.hooks[idx]) {\r\n const subscribers = new Set();\r\n const dependencies = new Set();\r\n const unsubscribers = new Map(); // Map<dependency, unsubscribe function>\r\n let cachedValue;\r\n let isStale = true;\r\n let isDestroyed = false;\r\n let isComputing = false; // Prevent infinite loops\r\n\r\n /**\r\n * Compute the value and track dependencies\r\n */\r\n function compute() {\r\n if (isDestroyed) return cachedValue;\r\n \r\n // Prevent infinite computation loops\r\n if (isComputing) {\r\n console.error('[nixComputed] Circular dependency detected');\r\n return cachedValue;\r\n }\r\n \r\n isComputing = true;\r\n \r\n // Create a mock context for dependency tracking\r\n const trackingContext = {\r\n _accessedStates: new Set(),\r\n hookIndex: 0,\r\n hooks: [],\r\n _subscriptions: new Set(),\r\n _subscriptionCleanups: [],\r\n };\r\n \r\n // Save the previous context\r\n const prevContext = activeContext;\r\n \r\n try {\r\n // Temporarily set the tracking context\r\n setActiveContext(trackingContext);\r\n \r\n // Compute the value - this will trigger state.value getters\r\n // which will add themselves to trackingContext._accessedStates\r\n cachedValue = computeFn();\r\n \r\n // Find dependencies that are no longer needed\r\n const oldDeps = Array.from(dependencies);\r\n oldDeps.forEach(dep => {\r\n if (!trackingContext._accessedStates.has(dep)) {\r\n // This dependency is no longer accessed - unsubscribe\r\n if (unsubscribers.has(dep)) {\r\n try {\r\n unsubscribers.get(dep)();\r\n } catch (e) {\r\n console.error('[nixComputed] Error unsubscribing from old dependency:', e);\r\n }\r\n unsubscribers.delete(dep);\r\n }\r\n dependencies.delete(dep);\r\n }\r\n });\r\n \r\n // Subscribe to new dependencies\r\n trackingContext._accessedStates.forEach(state => {\r\n if (!dependencies.has(state)) {\r\n // New dependency found - subscribe to it\r\n const unsub = state.subscribe(() => {\r\n // Mark computed value as stale when dependency changes\r\n isStale = true;\r\n \r\n // Notify computed subscribers\r\n const subsArray = Array.from(subscribers);\r\n subsArray.forEach(fn => {\r\n try { \r\n fn(s.value); \r\n } catch (e) { \r\n console.error('[nixComputed] Subscriber error:', e);\r\n subscribers.delete(fn);\r\n }\r\n });\r\n });\r\n \r\n // Store unsubscribe function\r\n unsubscribers.set(state, unsub);\r\n dependencies.add(state);\r\n }\r\n });\r\n \r\n isStale = false;\r\n } catch (err) {\r\n console.error('[nixComputed] Compute error:', err);\r\n isStale = false; // Don't retry immediately\r\n } finally {\r\n // Restore the previous context\r\n setActiveContext(prevContext);\r\n isComputing = false;\r\n }\r\n \r\n return cachedValue;\r\n }\r\n\r\n const s = {\r\n /**\r\n * Get the computed value (read-only).\r\n * Automatically recomputes if dependencies have changed.\r\n * @returns {any} The computed value\r\n */\r\n get value() {\r\n if (isDestroyed) {\r\n console.warn('[nixComputed] Accessing destroyed computed state');\r\n return cachedValue;\r\n }\r\n \r\n // Recompute if stale\r\n if (isStale) {\r\n compute();\r\n }\r\n \r\n // Track this computed state as a dependency if accessed in another computed\r\n if (activeContext && activeContext._accessedStates) {\r\n activeContext._accessedStates.add(s);\r\n }\r\n \r\n return cachedValue;\r\n },\r\n \r\n /**\r\n * Subscribe to computed value changes.\r\n * @param {(value: T) => void} fn - Callback function\r\n * @returns {() => void} Unsubscribe function\r\n */\r\n subscribe(fn) {\r\n // Security: Validate subscriber function\r\n if (typeof fn !== 'function') {\r\n console.error('[nixComputed] subscribe() requires a function');\r\n return () => {};\r\n }\r\n \r\n // Memory Leak Fix: Don't add subscribers to destroyed state\r\n if (isDestroyed) {\r\n console.warn('[nixComputed] Cannot subscribe to destroyed computed state');\r\n return () => {};\r\n }\r\n \r\n // Security: Limit number of subscribers to prevent DOS\r\n const MAX_SUBSCRIBERS = 1000;\r\n if (subscribers.size >= MAX_SUBSCRIBERS) {\r\n console.error('[nixComputed] Maximum subscriber limit reached');\r\n return () => {};\r\n }\r\n \r\n subscribers.add(fn);\r\n \r\n // Return unsubscribe function\r\n return () => {\r\n subscribers.delete(fn);\r\n };\r\n },\r\n \r\n /**\r\n * Cleanup all subscriptions and dependencies.\r\n * Call this when component unmounts to prevent memory leaks.\r\n * \r\n * @example\r\n * nixEffect(() => {\r\n * return () => myComputed.cleanup();\r\n * }, []);\r\n */\r\n cleanup() {\r\n if (isDestroyed) return;\r\n \r\n // Mark as destroyed to prevent further operations\r\n isDestroyed = true;\r\n \r\n // Unsubscribe from all dependencies\r\n unsubscribers.forEach((unsub, dep) => {\r\n try {\r\n unsub();\r\n } catch (e) {\r\n console.error('[nixComputed] Cleanup error:', e);\r\n }\r\n });\r\n \r\n // Clear all collections\r\n unsubscribers.clear();\r\n dependencies.clear();\r\n subscribers.clear();\r\n \r\n // Clear cached value to help garbage collection\r\n cachedValue = null;\r\n \r\n console.log('[nixComputed] Computed state cleaned up');\r\n },\r\n \r\n /**\r\n * Get the number of active subscribers (useful for debugging)\r\n * @returns {number} Number of active subscribers\r\n */\r\n getSubscriberCount() {\r\n return subscribers.size;\r\n },\r\n \r\n /**\r\n * Get the number of tracked dependencies (useful for debugging)\r\n * @returns {number} Number of dependencies\r\n */\r\n getDependencyCount() {\r\n return dependencies.size;\r\n },\r\n \r\n /**\r\n * Check if computed state has been destroyed\r\n * @returns {boolean} True if state is destroyed\r\n */\r\n isDestroyed() {\r\n return isDestroyed;\r\n },\r\n \r\n /**\r\n * Get information about current dependencies (debugging)\r\n * @returns {Array<{state: Object, hasCleanup: boolean}>} Dependency info\r\n */\r\n getDependencyInfo() {\r\n return Array.from(dependencies).map(dep => ({\r\n state: dep,\r\n hasCleanup: unsubscribers.has(dep),\r\n isComputed: !!dep._isComputed,\r\n }));\r\n },\r\n \r\n // Internal flags\r\n _isNixState: true, // Computed states behave like states\r\n _isComputed: true, // Flag to identify computed states\r\n };\r\n\r\n // Initial computation\r\n compute();\r\n \r\n // Store in hooks\r\n ctx.hooks[idx] = s;\r\n \r\n // Memory Leak Fix: Track for cleanup\r\n if (ctx.stateCleanups) {\r\n ctx.stateCleanups.push(() => s.cleanup());\r\n }\r\n }\r\n\r\n return ctx.hooks[idx];\r\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* ----------------------\r\n nixComputed - Computed/Derived State\r\n Memory Leaks & Security Issues Resolved\r\n---------------------- */\r\nimport { activeContext, setActiveContext } from \"../context/context\";\r\n\r\n/**\r\n * @template T\r\n * @typedef {Object} ComputedState\r\n * @property {T} value - Get the computed value (read-only)\r\n * @property {(fn: (value: T) => void) => (() => void)} subscribe - Subscribe to computed value changes\r\n * @property {() => void} cleanup - Cleanup all subscriptions and dependencies\r\n * @property {() => number} getSubscriberCount - Get number of active subscribers (debugging)\r\n * @property {() => number} getDependencyCount - Get number of tracked dependencies (debugging)\r\n * @property {() => boolean} isDestroyed - Check if computed state has been destroyed\r\n * @property {boolean} _isNixState - Internal flag (computed states behave like states)\r\n * @property {boolean} _isComputed - Internal flag to identify computed states\r\n */\r\n\r\n/**\r\n * Create a derived/computed state from other states.\r\n * Automatically tracks dependencies and updates when any dependency changes.\r\n * \r\n * @template T\r\n * @param {() => T} computeFn - Function that computes the derived value\r\n * @returns {ComputedState<T>} A reactive state object with the computed value\r\n * \r\n * @example\r\n * const count = nixState(5);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * console.log(doubled.value); // 10\r\n * count.value = 10;\r\n * console.log(doubled.value); // 20\r\n * \r\n * @example\r\n * // Multiple dependencies\r\n * const a = nixState(5);\r\n * const b = nixState(10);\r\n * const sum = nixComputed(() => a.value + b.value);\r\n * console.log(sum.value); // 15\r\n * \r\n * @example\r\n * // Conditional dependencies\r\n * const flag = nixState(true);\r\n * const x = nixState(1);\r\n * const y = nixState(2);\r\n * const result = nixComputed(() => flag.value ? x.value : y.value);\r\n * \r\n * @example\r\n * // With cleanup\r\n * const MyComponent = () => {\r\n * const count = nixState(0);\r\n * const doubled = nixComputed(() => count.value * 2);\r\n * \r\n * nixEffect(() => {\r\n * return () => {\r\n * doubled.cleanup();\r\n * count.cleanup();\r\n * };\r\n * }, []);\r\n * };\r\n * \r\n * @throws {Error} If called outside a component context\r\n * @throws {TypeError} If computeFn is not a function\r\n */\r\nexport function nixComputed(computeFn) {\r\n const ctx = activeContext;\r\n if (!ctx) throw new Error(\"nixComputed() called outside component\");\r\n\r\n // Security: Validate compute function\r\n if (typeof computeFn !== 'function') {\r\n throw new TypeError('[nixComputed] First argument must be a function');\r\n }\r\n\r\n const idx = ctx.hookIndex++;\r\n if (!ctx.hooks[idx]) {\r\n const subscribers = new Set();\r\n const dependencies = new Set();\r\n const unsubscribers = new Map(); // Map<dependency, unsubscribe function>\r\n let cachedValue;\r\n let isStale = true;\r\n let isDestroyed = false;\r\n let isComputing = false; // Prevent infinite loops\r\n\r\n /**\r\n * Compute the value and track dependencies\r\n */\r\n function compute() {\r\n if (isDestroyed) return cachedValue;\r\n\r\n // Prevent infinite computation loops\r\n if (isComputing) {\r\n console.error('[nixComputed] Circular dependency detected');\r\n return cachedValue;\r\n }\r\n\r\n isComputing = true;\r\n\r\n // Create a mock context for dependency tracking\r\n const trackingContext = {\r\n _accessedStates: new Set(),\r\n hookIndex: 0,\r\n hooks: [],\r\n _subscriptions: new Set(),\r\n _subscriptionCleanups: [],\r\n };\r\n\r\n // Save the previous context\r\n const prevContext = activeContext;\r\n\r\n try {\r\n // Temporarily set the tracking context\r\n // @ts-ignore - Internal tracking context doesn't need full ComponentContext properties\r\n setActiveContext(trackingContext);\r\n\r\n // Compute the value - this will trigger state.value getters\r\n // which will add themselves to trackingContext._accessedStates\r\n cachedValue = computeFn();\r\n\r\n // Find dependencies that are no longer needed\r\n const oldDeps = Array.from(dependencies);\r\n oldDeps.forEach(dep => {\r\n if (!trackingContext._accessedStates.has(dep)) {\r\n // This dependency is no longer accessed - unsubscribe\r\n if (unsubscribers.has(dep)) {\r\n try {\r\n unsubscribers.get(dep)();\r\n } catch (e) {\r\n console.error('[nixComputed] Error unsubscribing from old dependency:', e);\r\n }\r\n unsubscribers.delete(dep);\r\n }\r\n dependencies.delete(dep);\r\n }\r\n });\r\n\r\n // Subscribe to new dependencies\r\n trackingContext._accessedStates.forEach(state => {\r\n if (!dependencies.has(state)) {\r\n // New dependency found - subscribe to it\r\n const unsub = state.subscribe(() => {\r\n // Mark computed value as stale when dependency changes\r\n isStale = true;\r\n\r\n // Notify computed subscribers\r\n const subsArray = Array.from(subscribers);\r\n subsArray.forEach(fn => {\r\n try {\r\n fn(s.value);\r\n } catch (e) {\r\n console.error('[nixComputed] Subscriber error:', e);\r\n subscribers.delete(fn);\r\n }\r\n });\r\n });\r\n\r\n // Store unsubscribe function\r\n unsubscribers.set(state, unsub);\r\n dependencies.add(state);\r\n }\r\n });\r\n\r\n isStale = false;\r\n } catch (err) {\r\n console.error('[nixComputed] Compute error:', err);\r\n isStale = false; // Don't retry immediately\r\n } finally {\r\n // Restore the previous context\r\n setActiveContext(prevContext);\r\n isComputing = false;\r\n }\r\n\r\n return cachedValue;\r\n }\r\n\r\n const s = {\r\n /**\r\n * Get the computed value (read-only).\r\n * Automatically recomputes if dependencies have changed.\r\n * @returns {any} The computed value\r\n */\r\n get value() {\r\n if (isDestroyed) {\r\n console.warn('[nixComputed] Accessing destroyed computed state');\r\n return cachedValue;\r\n }\r\n\r\n // Recompute if stale\r\n if (isStale) {\r\n compute();\r\n }\r\n\r\n // Track this computed state as a dependency if accessed in another computed\r\n if (activeContext && activeContext._accessedStates) {\r\n activeContext._accessedStates.add(s);\r\n }\r\n\r\n return cachedValue;\r\n },\r\n\r\n /**\r\n * Subscribe to computed value changes.\r\n * @param {(value: T) => void} fn - Callback function\r\n * @returns {() => void} Unsubscribe function\r\n */\r\n subscribe(fn) {\r\n // Security: Validate subscriber function\r\n if (typeof fn !== 'function') {\r\n console.error('[nixComputed] subscribe() requires a function');\r\n return () => { };\r\n }\r\n\r\n // Memory Leak Fix: Don't add subscribers to destroyed state\r\n if (isDestroyed) {\r\n console.warn('[nixComputed] Cannot subscribe to destroyed computed state');\r\n return () => { };\r\n }\r\n\r\n // Security: Limit number of subscribers to prevent DOS\r\n const MAX_SUBSCRIBERS = 1000;\r\n if (subscribers.size >= MAX_SUBSCRIBERS) {\r\n console.error('[nixComputed] Maximum subscriber limit reached');\r\n return () => { };\r\n }\r\n\r\n subscribers.add(fn);\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n subscribers.delete(fn);\r\n };\r\n },\r\n\r\n /**\r\n * Cleanup all subscriptions and dependencies.\r\n * Call this when component unmounts to prevent memory leaks.\r\n * \r\n * @example\r\n * nixEffect(() => {\r\n * return () => myComputed.cleanup();\r\n * }, []);\r\n */\r\n cleanup() {\r\n if (isDestroyed) return;\r\n\r\n // Mark as destroyed to prevent further operations\r\n isDestroyed = true;\r\n\r\n // Unsubscribe from all dependencies\r\n unsubscribers.forEach((unsub, dep) => {\r\n try {\r\n unsub();\r\n } catch (e) {\r\n console.error('[nixComputed] Cleanup error:', e);\r\n }\r\n });\r\n\r\n // Clear all collections\r\n unsubscribers.clear();\r\n dependencies.clear();\r\n subscribers.clear();\r\n\r\n // Clear cached value to help garbage collection\r\n cachedValue = null;\r\n\r\n console.log('[nixComputed] Computed state cleaned up');\r\n },\r\n\r\n /**\r\n * Get the number of active subscribers (useful for debugging)\r\n * @returns {number} Number of active subscribers\r\n */\r\n getSubscriberCount() {\r\n return subscribers.size;\r\n },\r\n\r\n /**\r\n * Get the number of tracked dependencies (useful for debugging)\r\n * @returns {number} Number of dependencies\r\n */\r\n getDependencyCount() {\r\n return dependencies.size;\r\n },\r\n\r\n /**\r\n * Check if computed state has been destroyed\r\n * @returns {boolean} True if state is destroyed\r\n */\r\n isDestroyed() {\r\n return isDestroyed;\r\n },\r\n\r\n /**\r\n * Get information about current dependencies (debugging)\r\n * @returns {Array<{state: Object, hasCleanup: boolean}>} Dependency info\r\n */\r\n getDependencyInfo() {\r\n return Array.from(dependencies).map(dep => ({\r\n state: dep,\r\n hasCleanup: unsubscribers.has(dep),\r\n isComputed: !!dep._isComputed,\r\n }));\r\n },\r\n\r\n // Internal flags\r\n _isNixState: true, // Computed states behave like states\r\n _isComputed: true, // Flag to identify computed states\r\n };\r\n\r\n // Initial computation\r\n compute();\r\n\r\n // Store in hooks\r\n ctx.hooks[idx] = s;\r\n\r\n // Memory Leak Fix: Track for cleanup\r\n if (ctx.stateCleanups) {\r\n ctx.stateCleanups.push(() => s.cleanup());\r\n }\r\n }\r\n\r\n return ctx.hooks[idx];\r\n}"],
5
+ "mappings": ";;AAIA,SAAS,eAAe,wBAAwB;AA6DzC,SAAS,YAAY,WAAW;AACrC,QAAM,MAAM;AACZ,MAAI,CAAC;AAAK,UAAM,IAAI,MAAM,wCAAwC;AAGlE,MAAI,OAAO,cAAc,YAAY;AACnC,UAAM,IAAI,UAAU,iDAAiD;AAAA,EACvE;AAEA,QAAM,MAAM,IAAI;AAChB,MAAI,CAAC,IAAI,MAAM,GAAG,GAAG;AAYnB,QAAS,UAAT,WAAmB;AACjB,UAAI;AAAa,eAAO;AAGxB,UAAI,aAAa;AACf,gBAAQ,MAAM,4CAA4C;AAC1D,eAAO;AAAA,MACT;AAEA,oBAAc;AAGd,YAAM,kBAAkB;AAAA,QACtB,iBAAiB,oBAAI,IAAI;AAAA,QACzB,WAAW;AAAA,QACX,OAAO,CAAC;AAAA,QACR,gBAAgB,oBAAI,IAAI;AAAA,QACxB,uBAAuB,CAAC;AAAA,MAC1B;AAGA,YAAM,cAAc;AAEpB,UAAI;AAGF,yBAAiB,eAAe;AAIhC,sBAAc,UAAU;AAGxB,cAAM,UAAU,MAAM,KAAK,YAAY;AACvC,gBAAQ,QAAQ,SAAO;AACrB,cAAI,CAAC,gBAAgB,gBAAgB,IAAI,GAAG,GAAG;AAE7C,gBAAI,cAAc,IAAI,GAAG,GAAG;AAC1B,kBAAI;AACF,8BAAc,IAAI,GAAG,EAAE;AAAA,cACzB,SAAS,GAAG;AACV,wBAAQ,MAAM,0DAA0D,CAAC;AAAA,cAC3E;AACA,4BAAc,OAAO,GAAG;AAAA,YAC1B;AACA,yBAAa,OAAO,GAAG;AAAA,UACzB;AAAA,QACF,CAAC;AAGD,wBAAgB,gBAAgB,QAAQ,WAAS;AAC/C,cAAI,CAAC,aAAa,IAAI,KAAK,GAAG;AAE5B,kBAAM,QAAQ,MAAM,UAAU,MAAM;AAElC,wBAAU;AAGV,oBAAM,YAAY,MAAM,KAAK,WAAW;AACxC,wBAAU,QAAQ,QAAM;AACtB,oBAAI;AACF,qBAAG,EAAE,KAAK;AAAA,gBACZ,SAAS,GAAG;AACV,0BAAQ,MAAM,mCAAmC,CAAC;AAClD,8BAAY,OAAO,EAAE;AAAA,gBACvB;AAAA,cACF,CAAC;AAAA,YACH,CAAC;AAGD,0BAAc,IAAI,OAAO,KAAK;AAC9B,yBAAa,IAAI,KAAK;AAAA,UACxB;AAAA,QACF,CAAC;AAED,kBAAU;AAAA,MACZ,SAAS,KAAK;AACZ,gBAAQ,MAAM,gCAAgC,GAAG;AACjD,kBAAU;AAAA,MACZ,UAAE;AAEA,yBAAiB,WAAW;AAC5B,sBAAc;AAAA,MAChB;AAEA,aAAO;AAAA,IACT;AAtFS;AAXT,UAAM,cAAc,oBAAI,IAAI;AAC5B,UAAM,eAAe,oBAAI,IAAI;AAC7B,UAAM,gBAAgB,oBAAI,IAAI;AAC9B,QAAI;AACJ,QAAI,UAAU;AACd,QAAI,cAAc;AAClB,QAAI,cAAc;AA6FlB,UAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMR,IAAI,QAAQ;AACV,YAAI,aAAa;AACf,kBAAQ,KAAK,kDAAkD;AAC/D,iBAAO;AAAA,QACT;AAGA,YAAI,SAAS;AACX,kBAAQ;AAAA,QACV;AAGA,YAAI,iBAAiB,cAAc,iBAAiB;AAClD,wBAAc,gBAAgB,IAAI,CAAC;AAAA,QACrC;AAEA,eAAO;AAAA,MACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,UAAU,IAAI;AAEZ,YAAI,OAAO,OAAO,YAAY;AAC5B,kBAAQ,MAAM,+CAA+C;AAC7D,iBAAO,MAAM;AAAA,UAAE;AAAA,QACjB;AAGA,YAAI,aAAa;AACf,kBAAQ,KAAK,4DAA4D;AACzE,iBAAO,MAAM;AAAA,UAAE;AAAA,QACjB;AAGA,cAAM,kBAAkB;AACxB,YAAI,YAAY,QAAQ,iBAAiB;AACvC,kBAAQ,MAAM,gDAAgD;AAC9D,iBAAO,MAAM;AAAA,UAAE;AAAA,QACjB;AAEA,oBAAY,IAAI,EAAE;AAGlB,eAAO,MAAM;AACX,sBAAY,OAAO,EAAE;AAAA,QACvB;AAAA,MACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAWA,UAAU;AACR,YAAI;AAAa;AAGjB,sBAAc;AAGd,sBAAc,QAAQ,CAAC,OAAO,QAAQ;AACpC,cAAI;AACF,kBAAM;AAAA,UACR,SAAS,GAAG;AACV,oBAAQ,MAAM,gCAAgC,CAAC;AAAA,UACjD;AAAA,QACF,CAAC;AAGD,sBAAc,MAAM;AACpB,qBAAa,MAAM;AACnB,oBAAY,MAAM;AAGlB,sBAAc;AAEd,gBAAQ,IAAI,yCAAyC;AAAA,MACvD;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,qBAAqB;AACnB,eAAO,YAAY;AAAA,MACrB;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,qBAAqB;AACnB,eAAO,aAAa;AAAA,MACtB;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,cAAc;AACZ,eAAO;AAAA,MACT;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,oBAAoB;AAClB,eAAO,MAAM,KAAK,YAAY,EAAE,IAAI,UAAQ;AAAA,UAC1C,OAAO;AAAA,UACP,YAAY,cAAc,IAAI,GAAG;AAAA,UACjC,YAAY,CAAC,CAAC,IAAI;AAAA,QACpB,EAAE;AAAA,MACJ;AAAA;AAAA,MAGA,aAAa;AAAA;AAAA,MACb,aAAa;AAAA;AAAA,IACf;AAGA,YAAQ;AAGR,QAAI,MAAM,GAAG,IAAI;AAGjB,QAAI,IAAI,eAAe;AACrB,UAAI,cAAc,KAAK,MAAM,EAAE,QAAQ,CAAC;AAAA,IAC1C;AAAA,EACF;AAEA,SAAO,IAAI,MAAM,GAAG;AACtB;AAjQgB;",
6
6
  "names": []
7
7
  }
@@ -1,14 +1,18 @@
1
+ /**
2
+ * @fileoverview Debounce utility function with advanced options.
3
+ * Supports leading/trailing edge invocation, maxWait, and AbortController.
4
+ */
1
5
  /**
2
6
  * Debounce a function with options for leading, trailing, maxWait, and AbortController support.
3
7
  *
4
- * @param {Function} fn - The function to debounce.
5
- * @param {number} [delay=300] - Delay in milliseconds.
6
- * @param {Object} [options={}] - Debounce options.
7
- * @param {boolean} [options.leading=false] - Invoke on the leading edge.
8
- * @param {boolean} [options.trailing=true] - Invoke on the trailing edge.
9
- * @param {number} [options.maxWait] - Maximum wait time before forced invocation.
10
- * @param {AbortSignal} [options.signal] - Optional AbortSignal to cancel pending calls.
11
- * @returns {Function} Debounced function with `.cancel()` method.
8
+ * @param {Function} fn - The function to debounce
9
+ * @param {number} [delay=300] - Delay in milliseconds
10
+ * @param {Object} [options={}] - Debounce options
11
+ * @param {boolean} [options.leading=false] - Invoke on the leading edge
12
+ * @param {boolean} [options.trailing=true] - Invoke on the trailing edge
13
+ * @param {number} [options.maxWait] - Maximum wait time before forced invocation
14
+ * @param {AbortSignal} [options.signal] - Optional AbortSignal to cancel pending calls
15
+ * @returns {Function} Debounced function with `.cancel()` method
12
16
  *
13
17
  * @example
14
18
  * const controller = new AbortController();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../hooks/nixDebounce.js"],
4
- "sourcesContent": ["/**\r\n * Debounce a function with options for leading, trailing, maxWait, and AbortController support.\r\n *\r\n * @param {Function} fn - The function to debounce.\r\n * @param {number} [delay=300] - Delay in milliseconds.\r\n * @param {Object} [options={}] - Debounce options.\r\n * @param {boolean} [options.leading=false] - Invoke on the leading edge.\r\n * @param {boolean} [options.trailing=true] - Invoke on the trailing edge.\r\n * @param {number} [options.maxWait] - Maximum wait time before forced invocation.\r\n * @param {AbortSignal} [options.signal] - Optional AbortSignal to cancel pending calls.\r\n * @returns {Function} Debounced function with `.cancel()` method.\r\n *\r\n * @example\r\n * const controller = new AbortController();\r\n * const debounced = nixDebounce(() => console.log('Hello'), 500, {\r\n * leading: true,\r\n * maxWait: 2000,\r\n * signal: controller.signal\r\n * });\r\n * debounced();\r\n * controller.abort(); // Cancel pending invocation\r\n */\r\nexport function nixDebounce(fn, delay = 300, options = {}) {\r\n let timerId = null;\r\n let lastInvokeTime = 0;\r\n let lastArgs;\r\n let lastThis;\r\n\r\n const { leading = false, trailing = true, maxWait, signal } = options;\r\n\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n if (timerId) {\r\n clearTimeout(timerId);\r\n timerId = null;\r\n }\r\n lastArgs = null;\r\n lastThis = null;\r\n });\r\n }\r\n\r\n const invoke = () => {\r\n lastInvokeTime = Date.now();\r\n if (lastArgs) {\r\n fn.apply(lastThis, lastArgs);\r\n lastArgs = lastThis = null;\r\n }\r\n };\r\n\r\n const debounced = function (...args) {\r\n const now = Date.now();\r\n lastArgs = args;\r\n lastThis = this;\r\n\r\n const shouldInvokeLeading = leading && !timerId;\r\n const timeSinceLastInvoke = now - lastInvokeTime;\r\n const remainingTime = delay - timeSinceLastInvoke;\r\n\r\n if (maxWait !== undefined && timeSinceLastInvoke >= maxWait) {\r\n if (timerId) clearTimeout(timerId);\r\n timerId = null;\r\n invoke();\r\n return;\r\n }\r\n\r\n if (timerId) clearTimeout(timerId);\r\n\r\n if (shouldInvokeLeading) {\r\n invoke();\r\n }\r\n\r\n if (trailing) {\r\n timerId = setTimeout(invoke, remainingTime > 0 ? remainingTime : delay);\r\n }\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timerId) clearTimeout(timerId);\r\n timerId = lastArgs = lastThis = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n"],
5
- "mappings": ";;AAsBO,SAAS,YAAY,IAAI,QAAQ,KAAK,UAAU,CAAC,GAAG;AACzD,MAAI,UAAU;AACd,MAAI,iBAAiB;AACrB,MAAI;AACJ,MAAI;AAEJ,QAAM,EAAE,UAAU,OAAO,WAAW,MAAM,SAAS,OAAO,IAAI;AAE9D,MAAI,QAAQ;AACV,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,SAAS;AACX,qBAAa,OAAO;AACpB,kBAAU;AAAA,MACZ;AACA,iBAAW;AACX,iBAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,6BAAM;AACnB,qBAAiB,KAAK,IAAI;AAC1B,QAAI,UAAU;AACZ,SAAG,MAAM,UAAU,QAAQ;AAC3B,iBAAW,WAAW;AAAA,IACxB;AAAA,EACF,GANe;AAQf,QAAM,YAAY,mCAAa,MAAM;AACnC,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW;AACX,eAAW;AAEX,UAAM,sBAAsB,WAAW,CAAC;AACxC,UAAM,sBAAsB,MAAM;AAClC,UAAM,gBAAgB,QAAQ;AAE9B,QAAI,YAAY,UAAa,uBAAuB,SAAS;AAC3D,UAAI;AAAS,qBAAa,OAAO;AACjC,gBAAU;AACV,aAAO;AACP;AAAA,IACF;AAEA,QAAI;AAAS,mBAAa,OAAO;AAEjC,QAAI,qBAAqB;AACvB,aAAO;AAAA,IACT;AAEA,QAAI,UAAU;AACZ,gBAAU,WAAW,QAAQ,gBAAgB,IAAI,gBAAgB,KAAK;AAAA,IACxE;AAAA,EACF,GAzBkB;AA2BlB,YAAU,SAAS,MAAM;AACvB,QAAI;AAAS,mBAAa,OAAO;AACjC,cAAU,WAAW,WAAW;AAAA,EAClC;AAEA,SAAO;AACT;AA5DgB;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Debounce utility function with advanced options.\r\n * Supports leading/trailing edge invocation, maxWait, and AbortController.\r\n */\r\n\r\n/**\r\n * Debounce a function with options for leading, trailing, maxWait, and AbortController support.\r\n *\r\n * @param {Function} fn - The function to debounce\r\n * @param {number} [delay=300] - Delay in milliseconds\r\n * @param {Object} [options={}] - Debounce options\r\n * @param {boolean} [options.leading=false] - Invoke on the leading edge\r\n * @param {boolean} [options.trailing=true] - Invoke on the trailing edge\r\n * @param {number} [options.maxWait] - Maximum wait time before forced invocation\r\n * @param {AbortSignal} [options.signal] - Optional AbortSignal to cancel pending calls\r\n * @returns {Function} Debounced function with `.cancel()` method\r\n *\r\n * @example\r\n * const controller = new AbortController();\r\n * const debounced = nixDebounce(() => console.log('Hello'), 500, {\r\n * leading: true,\r\n * maxWait: 2000,\r\n * signal: controller.signal\r\n * });\r\n * debounced();\r\n * controller.abort(); // Cancel pending invocation\r\n */\r\nexport function nixDebounce(fn, delay = 300, options = {}) {\r\n let timerId = null;\r\n let lastInvokeTime = 0;\r\n let lastArgs;\r\n let lastThis;\r\n\r\n const { leading = false, trailing = true, maxWait, signal } = options;\r\n\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n if (timerId) {\r\n clearTimeout(timerId);\r\n timerId = null;\r\n }\r\n lastArgs = null;\r\n lastThis = null;\r\n });\r\n }\r\n\r\n const invoke = () => {\r\n lastInvokeTime = Date.now();\r\n if (lastArgs) {\r\n fn.apply(lastThis, lastArgs);\r\n lastArgs = lastThis = null;\r\n }\r\n };\r\n\r\n const debounced = function (...args) {\r\n const now = Date.now();\r\n lastArgs = args;\r\n lastThis = this;\r\n\r\n const shouldInvokeLeading = leading && !timerId;\r\n const timeSinceLastInvoke = now - lastInvokeTime;\r\n const remainingTime = delay - timeSinceLastInvoke;\r\n\r\n if (maxWait !== undefined && timeSinceLastInvoke >= maxWait) {\r\n if (timerId) clearTimeout(timerId);\r\n timerId = null;\r\n invoke();\r\n return;\r\n }\r\n\r\n if (timerId) clearTimeout(timerId);\r\n\r\n if (shouldInvokeLeading) {\r\n invoke();\r\n }\r\n\r\n if (trailing) {\r\n timerId = setTimeout(invoke, remainingTime > 0 ? remainingTime : delay);\r\n }\r\n };\r\n\r\n debounced.cancel = () => {\r\n if (timerId) clearTimeout(timerId);\r\n timerId = lastArgs = lastThis = null;\r\n };\r\n\r\n return debounced;\r\n}\r\n"],
5
+ "mappings": ";;AA2BO,SAAS,YAAY,IAAI,QAAQ,KAAK,UAAU,CAAC,GAAG;AACzD,MAAI,UAAU;AACd,MAAI,iBAAiB;AACrB,MAAI;AACJ,MAAI;AAEJ,QAAM,EAAE,UAAU,OAAO,WAAW,MAAM,SAAS,OAAO,IAAI;AAE9D,MAAI,QAAQ;AACV,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,SAAS;AACX,qBAAa,OAAO;AACpB,kBAAU;AAAA,MACZ;AACA,iBAAW;AACX,iBAAW;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,6BAAM;AACnB,qBAAiB,KAAK,IAAI;AAC1B,QAAI,UAAU;AACZ,SAAG,MAAM,UAAU,QAAQ;AAC3B,iBAAW,WAAW;AAAA,IACxB;AAAA,EACF,GANe;AAQf,QAAM,YAAY,mCAAa,MAAM;AACnC,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW;AACX,eAAW;AAEX,UAAM,sBAAsB,WAAW,CAAC;AACxC,UAAM,sBAAsB,MAAM;AAClC,UAAM,gBAAgB,QAAQ;AAE9B,QAAI,YAAY,UAAa,uBAAuB,SAAS;AAC3D,UAAI;AAAS,qBAAa,OAAO;AACjC,gBAAU;AACV,aAAO;AACP;AAAA,IACF;AAEA,QAAI;AAAS,mBAAa,OAAO;AAEjC,QAAI,qBAAqB;AACvB,aAAO;AAAA,IACT;AAEA,QAAI,UAAU;AACZ,gBAAU,WAAW,QAAQ,gBAAgB,IAAI,gBAAgB,KAAK;AAAA,IACxE;AAAA,EACF,GAzBkB;AA2BlB,YAAU,SAAS,MAAM;AACvB,QAAI;AAAS,mBAAa,OAAO;AACjC,cAAU,WAAW,WAAW;AAAA,EAClC;AAEA,SAAO;AACT;AA5DgB;",
6
6
  "names": []
7
7
  }
@@ -1,10 +1,15 @@
1
+ /**
2
+ * @fileoverview Safe interval utility with automatic cleanup support.
3
+ * Provides AbortController integration for cancellable intervals.
4
+ */
1
5
  /**
2
6
  * Safe interval with automatic cleanup support and optional AbortController.
7
+ * Must be called within a component and cleaned up on unmount.
3
8
  *
4
- * @param {Function} fn - Function to run at each interval.
5
- * @param {number} ms - Interval duration in milliseconds.
6
- * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the interval.
7
- * @returns {Function} Cleanup function to stop the interval.
9
+ * @param {() => void} fn - Function to run at each interval
10
+ * @param {number} ms - Interval duration in milliseconds
11
+ * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the interval
12
+ * @returns {() => void} Cleanup function to stop the interval
8
13
  *
9
14
  * @example
10
15
  * const cancel = nixInterval(() => console.log('tick'), 1000);
@@ -15,5 +20,7 @@
15
20
  * const controller = new AbortController();
16
21
  * nixInterval(() => console.log('tick'), 1000, controller.signal);
17
22
  * controller.abort(); // automatically clears interval
23
+ *
24
+ * @throws {TypeError} If fn is not a function or ms is not a non-negative number
18
25
  */
19
- export function nixInterval(fn: Function, ms: number, signal?: AbortSignal): Function;
26
+ export function nixInterval(fn: () => void, ms: number, signal?: AbortSignal): () => void;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../hooks/nixInterval.js"],
4
- "sourcesContent": ["/**\r\n * Safe interval with automatic cleanup support and optional AbortController.\r\n *\r\n * @param {Function} fn - Function to run at each interval.\r\n * @param {number} ms - Interval duration in milliseconds.\r\n * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the interval.\r\n * @returns {Function} Cleanup function to stop the interval.\r\n *\r\n * @example\r\n * const cancel = nixInterval(() => console.log('tick'), 1000);\r\n * // stop interval\r\n * cancel();\r\n *\r\n * @example\r\n * const controller = new AbortController();\r\n * nixInterval(() => console.log('tick'), 1000, controller.signal);\r\n * controller.abort(); // automatically clears interval\r\n */\r\nexport function nixInterval(fn, ms, signal) {\r\n if (typeof fn !== \"function\") {\r\n throw new TypeError(\"nixInterval: first argument must be a function\");\r\n }\r\n if (typeof ms !== \"number\" || ms < 0) {\r\n throw new TypeError(\"nixInterval: second argument must be a non-negative number\");\r\n }\r\n\r\n const id = setInterval(fn, ms);\r\n\r\n const cancel = () => clearInterval(id);\r\n\r\n if (signal) {\r\n if (signal.aborted) {\r\n cancel();\r\n } else {\r\n const listener = () => {\r\n cancel();\r\n signal.removeEventListener(\"abort\", listener);\r\n };\r\n signal.addEventListener(\"abort\", listener);\r\n }\r\n }\r\n\r\n return cancel;\r\n}\r\n"],
5
- "mappings": ";;AAkBO,SAAS,YAAY,IAAI,IAAI,QAAQ;AAC1C,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAM,IAAI,UAAU,gDAAgD;AAAA,EACtE;AACA,MAAI,OAAO,OAAO,YAAY,KAAK,GAAG;AACpC,UAAM,IAAI,UAAU,4DAA4D;AAAA,EAClF;AAEA,QAAM,KAAK,YAAY,IAAI,EAAE;AAE7B,QAAM,SAAS,6BAAM,cAAc,EAAE,GAAtB;AAEf,MAAI,QAAQ;AACV,QAAI,OAAO,SAAS;AAClB,aAAO;AAAA,IACT,OAAO;AACL,YAAM,WAAW,6BAAM;AACrB,eAAO;AACP,eAAO,oBAAoB,SAAS,QAAQ;AAAA,MAC9C,GAHiB;AAIjB,aAAO,iBAAiB,SAAS,QAAQ;AAAA,IAC3C;AAAA,EACF;AAEA,SAAO;AACT;AAzBgB;",
4
+ "sourcesContent": ["/**\r\n * @fileoverview Safe interval utility with automatic cleanup support.\r\n * Provides AbortController integration for cancellable intervals.\r\n */\r\n\r\n/**\r\n * Safe interval with automatic cleanup support and optional AbortController.\r\n * Must be called within a component and cleaned up on unmount.\r\n *\r\n * @param {() => void} fn - Function to run at each interval\r\n * @param {number} ms - Interval duration in milliseconds\r\n * @param {AbortSignal} [signal] - Optional AbortSignal to cancel the interval\r\n * @returns {() => void} Cleanup function to stop the interval\r\n *\r\n * @example\r\n * const cancel = nixInterval(() => console.log('tick'), 1000);\r\n * // stop interval\r\n * cancel();\r\n *\r\n * @example\r\n * const controller = new AbortController();\r\n * nixInterval(() => console.log('tick'), 1000, controller.signal);\r\n * controller.abort(); // automatically clears interval\r\n * \r\n * @throws {TypeError} If fn is not a function or ms is not a non-negative number\r\n */\r\nexport function nixInterval(fn, ms, signal) {\r\n if (typeof fn !== \"function\") {\r\n throw new TypeError(\"nixInterval: first argument must be a function\");\r\n }\r\n if (typeof ms !== \"number\" || ms < 0) {\r\n throw new TypeError(\"nixInterval: second argument must be a non-negative number\");\r\n }\r\n\r\n const id = setInterval(fn, ms);\r\n\r\n const cancel = () => clearInterval(id);\r\n\r\n if (signal) {\r\n if (signal.aborted) {\r\n cancel();\r\n } else {\r\n const listener = () => {\r\n cancel();\r\n signal.removeEventListener(\"abort\", listener);\r\n };\r\n signal.addEventListener(\"abort\", listener);\r\n }\r\n }\r\n\r\n return cancel;\r\n}\r\n"],
5
+ "mappings": ";;AA0BO,SAAS,YAAY,IAAI,IAAI,QAAQ;AAC1C,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAM,IAAI,UAAU,gDAAgD;AAAA,EACtE;AACA,MAAI,OAAO,OAAO,YAAY,KAAK,GAAG;AACpC,UAAM,IAAI,UAAU,4DAA4D;AAAA,EAClF;AAEA,QAAM,KAAK,YAAY,IAAI,EAAE;AAE7B,QAAM,SAAS,6BAAM,cAAc,EAAE,GAAtB;AAEf,MAAI,QAAQ;AACV,QAAI,OAAO,SAAS;AAClB,aAAO;AAAA,IACT,OAAO;AACL,YAAM,WAAW,6BAAM;AACrB,eAAO;AACP,eAAO,oBAAoB,SAAS,QAAQ;AAAA,MAC9C,GAHiB;AAIjB,aAAO,iBAAiB,SAAS,QAAQ;AAAA,IAC3C;AAAA,EACF;AAEA,SAAO;AACT;AAzBgB;",
6
6
  "names": []
7
7
  }