@quazardous/quarkernel-react 2.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.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # @quazardous/quarkernel-react
2
+
3
+ React 18+ bindings for QuarKernel - Context provider and hooks with auto-cleanup.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @quazardous/quarkernel @quazardous/quarkernel-react
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **KernelProvider**: Context provider for dependency injection
14
+ - **useKernel()**: Access kernel anywhere in tree
15
+ - **useOn()**: Register listener with auto-cleanup on unmount
16
+ - **useEventState()**: Reactive state from events
17
+ - **Type-safe**: Full TypeScript support
18
+ - **SSR-safe**: Warns on server-side access
19
+
20
+ ## Usage
21
+
22
+ ### Basic Setup
23
+
24
+ ```tsx
25
+ import { createKernel } from '@quazardous/quarkernel';
26
+ import { KernelProvider } from '@quazardous/quarkernel-react';
27
+
28
+ const qk = createKernel();
29
+
30
+ function App() {
31
+ return (
32
+ <KernelProvider kernel={kernel}>
33
+ <MyComponent />
34
+ </KernelProvider>
35
+ );
36
+ }
37
+ ```
38
+
39
+ ### useKernel()
40
+
41
+ Access kernel instance from any component:
42
+
43
+ ```tsx
44
+ import { useKernel } from '@quazardous/quarkernel-react';
45
+
46
+ function MyComponent() {
47
+ const qk = useKernel();
48
+
49
+ const handleClick = () => {
50
+ qk.emit('button:clicked', { timestamp: Date.now() });
51
+ };
52
+
53
+ return <button onClick={handleClick}>Click me</button>;
54
+ }
55
+ ```
56
+
57
+ ### useOn()
58
+
59
+ Register listener with automatic cleanup on unmount:
60
+
61
+ ```tsx
62
+ import { useOn } from '@quazardous/quarkernel-react';
63
+
64
+ function Notifications() {
65
+ useOn('notification:new', (event) => {
66
+ console.log('New notification:', event.data);
67
+ });
68
+
69
+ // Listener auto-removed when component unmounts
70
+ return <div>Listening for notifications...</div>;
71
+ }
72
+ ```
73
+
74
+ ### useEventState()
75
+
76
+ Reactive state derived from events:
77
+
78
+ ```tsx
79
+ import { useEventState } from '@quazardous/quarkernel-react';
80
+
81
+ function Counter() {
82
+ const count = useEventState('counter:updated', 0, (event) => event.data.value);
83
+
84
+ return <div>Count: {count}</div>;
85
+ }
86
+ ```
87
+
88
+ ### Wildcard Patterns
89
+
90
+ ```tsx
91
+ import { useOn } from '@quazardous/quarkernel-react';
92
+
93
+ function UserTracker() {
94
+ useOn('user:*', (event) => {
95
+ console.log('User event:', event.name, event.data);
96
+ });
97
+
98
+ return null;
99
+ }
100
+ ```
101
+
102
+ ## API
103
+
104
+ ### `<KernelProvider kernel={kernel}>`
105
+
106
+ Context provider for kernel instance.
107
+
108
+ **Props:**
109
+ - `kernel`: Kernel instance (required)
110
+ - `children`: React children
111
+
112
+ ### `useKernel()`
113
+
114
+ Hook to access kernel from context.
115
+
116
+ **Returns:** Kernel instance
117
+
118
+ **Throws:** `KernelProviderError` if used outside provider
119
+
120
+ ### `useOn(event, handler, options?)`
121
+
122
+ Register event listener with auto-cleanup.
123
+
124
+ **Parameters:**
125
+ - `event`: Event name or wildcard pattern
126
+ - `handler`: `(event, ctx) => void | Promise<void>`
127
+ - `options`: Listener options (id, after, priority)
128
+
129
+ **Returns:** Unsubscribe function
130
+
131
+ ### `useEventState(event, initial, selector?)`
132
+
133
+ Reactive state from events.
134
+
135
+ **Parameters:**
136
+ - `event`: Event name to listen for
137
+ - `initial`: Initial state value
138
+ - `selector`: Optional `(event) => T` to extract value
139
+
140
+ **Returns:** Current state value
141
+
142
+ ## License
143
+
144
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/KernelProvider.tsx
7
+ var KernelContext = react.createContext(null);
8
+ function KernelProvider({
9
+ kernel,
10
+ children
11
+ }) {
12
+ return /* @__PURE__ */ jsxRuntime.jsx(KernelContext.Provider, { value: kernel, children });
13
+ }
14
+ var KernelProviderError = class extends Error {
15
+ constructor() {
16
+ super(
17
+ "useKernel must be used within a KernelProvider. Wrap your component tree with <KernelProvider kernel={kernel}>."
18
+ );
19
+ this.name = "KernelProviderError";
20
+ }
21
+ };
22
+ function isSSR() {
23
+ return typeof window === "undefined";
24
+ }
25
+ function useKernel() {
26
+ if (isSSR()) {
27
+ if (typeof console !== "undefined" && console.warn) {
28
+ console.warn(
29
+ "[QuarKernel] useKernel called during server-side rendering. Event listeners will not work during SSR. Consider using useEffect to register listeners on the client side only."
30
+ );
31
+ }
32
+ }
33
+ const kernel = react.useContext(KernelContext);
34
+ if (!kernel) {
35
+ throw new KernelProviderError();
36
+ }
37
+ return kernel;
38
+ }
39
+ function useOn(eventName, handler, options) {
40
+ const kernel = useKernel();
41
+ react.useEffect(() => {
42
+ const off = kernel.on(eventName, handler, options);
43
+ if (options?.signal) {
44
+ const abortHandler = () => {
45
+ off();
46
+ };
47
+ options.signal.addEventListener("abort", abortHandler, { once: true });
48
+ return () => {
49
+ off();
50
+ if (!options.signal.aborted) {
51
+ options.signal.removeEventListener("abort", abortHandler);
52
+ }
53
+ };
54
+ }
55
+ return off;
56
+ }, [kernel, eventName, handler, options]);
57
+ }
58
+ function useEventState(eventName, initialValue, selectorOrOptions) {
59
+ const kernel = useKernel();
60
+ const [state, setState] = react.useState(initialValue);
61
+ const { selector, options } = react.useMemo(() => {
62
+ if (typeof selectorOrOptions === "function") {
63
+ return { selector: selectorOrOptions, options: void 0 };
64
+ }
65
+ if (selectorOrOptions) {
66
+ const { selector: sel, ...opts } = selectorOrOptions;
67
+ return { selector: sel, options: Object.keys(opts).length > 0 ? opts : void 0 };
68
+ }
69
+ return { selector: void 0, options: void 0 };
70
+ }, [selectorOrOptions]);
71
+ const selectorFn = selector || ((event) => event.data);
72
+ react.useEffect(() => {
73
+ const off = kernel.on(
74
+ eventName,
75
+ (event) => {
76
+ setState(selectorFn(event));
77
+ },
78
+ options
79
+ );
80
+ if (options?.signal) {
81
+ const abortHandler = () => {
82
+ off();
83
+ };
84
+ options.signal.addEventListener("abort", abortHandler, { once: true });
85
+ return () => {
86
+ off();
87
+ if (!options.signal.aborted) {
88
+ options.signal.removeEventListener("abort", abortHandler);
89
+ }
90
+ };
91
+ }
92
+ return off;
93
+ }, [kernel, eventName, selector, options]);
94
+ return state;
95
+ }
96
+
97
+ // src/index.ts
98
+ var VERSION = "2.1.0";
99
+
100
+ exports.KernelProvider = KernelProvider;
101
+ exports.KernelProviderError = KernelProviderError;
102
+ exports.VERSION = VERSION;
103
+ exports.useEventState = useEventState;
104
+ exports.useKernel = useKernel;
105
+ exports.useOn = useOn;
106
+ //# sourceMappingURL=index.cjs.map
107
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/KernelProvider.tsx","../src/useKernel.ts","../src/useOn.ts","../src/useEventState.ts","../src/index.ts"],"names":["createContext","useContext","useEffect","useState","useMemo"],"mappings":";;;;;;AAcO,IAAM,aAAA,GAAgBA,oBAAkC,IAAI,CAAA;AA6B5D,SAAS,cAAA,CAAmD;AAAA,EACjE,MAAA;AAAA,EACA;AACF,CAAA,EAAgC;AAC9B,EAAA,sCACG,aAAA,CAAc,QAAA,EAAd,EAAuB,KAAA,EAAO,QAC5B,QAAA,EACH,CAAA;AAEJ;ACtCO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAC7C,WAAA,GAAc;AACZ,IAAA,KAAA;AAAA,MACE;AAAA,KAEF;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAMA,SAAS,KAAA,GAAiB;AACxB,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA;AAC3B;AAuBO,SAAS,SAAA,GAAgE;AAC9E,EAAA,IAAI,OAAM,EAAG;AACX,IAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,IAAA,EAAM;AAClD,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,OAGF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAASC,iBAAW,aAAa,CAAA;AAEvC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,mBAAA,EAAoB;AAAA,EAChC;AAEA,EAAA,OAAO,MAAA;AACT;ACXO,SAAS,KAAA,CACd,SAAA,EACA,OAAA,EACA,OAAA,EACM;AACN,EAAA,MAAM,SAAS,SAAA,EAAkB;AAEjC,EAAAC,eAAA,CAAU,MAAM;AAEd,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,EAAA,CAAG,SAAA,EAAW,SAAS,OAAO,CAAA;AAGjD,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,MAAM,eAAe,MAAM;AACzB,QAAA,GAAA,EAAI;AAAA,MACN,CAAA;AAEA,MAAA,OAAA,CAAQ,OAAO,gBAAA,CAAiB,OAAA,EAAS,cAAc,EAAE,IAAA,EAAM,MAAM,CAAA;AAGrE,MAAA,OAAO,MAAM;AACX,QAAA,GAAA,EAAI;AACJ,QAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,CAAQ,OAAA,EAAS;AAC5B,UAAA,OAAA,CAAQ,MAAA,CAAQ,mBAAA,CAAoB,OAAA,EAAS,YAAY,CAAA;AAAA,QAC3D;AAAA,MACF,CAAA;AAAA,IACF;AAGA,IAAA,OAAO,GAAA;AAAA,EACT,GAAG,CAAC,MAAA,EAAQ,SAAA,EAAW,OAAA,EAAS,OAAO,CAAC,CAAA;AAC1C;ACDO,SAAS,aAAA,CAKd,SAAA,EACA,YAAA,EACA,iBAAA,EACG;AACH,EAAA,MAAM,SAAS,SAAA,EAAkB;AACjC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIC,eAAY,YAAY,CAAA;AAGlD,EAAA,MAAM,EAAE,QAAA,EAAU,OAAA,EAAQ,GAAIC,cAAQ,MAAM;AAC1C,IAAA,IAAI,OAAO,sBAAsB,UAAA,EAAY;AAC3C,MAAA,OAAO,EAAE,QAAA,EAAU,iBAAA,EAAmB,OAAA,EAAS,MAAA,EAAU;AAAA,IAC3D;AACA,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,MAAM,EAAE,QAAA,EAAU,GAAA,EAAK,GAAG,MAAK,GAAI,iBAAA;AACnC,MAAA,OAAO,EAAE,QAAA,EAAU,GAAA,EAAK,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,IAAA,GAAO,MAAA,EAAU;AAAA,IACnF;AACA,IAAA,OAAO,EAAE,QAAA,EAAU,MAAA,EAAW,OAAA,EAAS,MAAA,EAAU;AAAA,EACnD,CAAA,EAAG,CAAC,iBAAiB,CAAC,CAAA;AAGtB,EAAA,MAAM,UAAA,GAAa,QAAA,KAAa,CAAC,KAAA,KAAwB,KAAA,CAAM,IAAA,CAAA;AAE/D,EAAAF,gBAAU,MAAM;AAEd,IAAA,MAAM,MAAM,MAAA,CAAO,EAAA;AAAA,MACjB,SAAA;AAAA,MACA,CAAC,KAAA,KAAmC;AAClC,QAAA,QAAA,CAAS,UAAA,CAAW,KAAK,CAAC,CAAA;AAAA,MAC5B,CAAA;AAAA,MACA;AAAA,KACF;AAGA,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,MAAM,eAAe,MAAM;AACzB,QAAA,GAAA,EAAI;AAAA,MACN,CAAA;AAEA,MAAA,OAAA,CAAQ,OAAO,gBAAA,CAAiB,OAAA,EAAS,cAAc,EAAE,IAAA,EAAM,MAAM,CAAA;AAGrE,MAAA,OAAO,MAAM;AACX,QAAA,GAAA,EAAI;AACJ,QAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,CAAQ,OAAA,EAAS;AAC5B,UAAA,OAAA,CAAQ,MAAA,CAAQ,mBAAA,CAAoB,OAAA,EAAS,YAAY,CAAA;AAAA,QAC3D;AAAA,MACF,CAAA;AAAA,IACF;AAGA,IAAA,OAAO,GAAA;AAAA,EACT,GAAG,CAAC,MAAA,EAAQ,SAAA,EAAW,QAAA,EAAU,OAAO,CAAC,CAAA;AAEzC,EAAA,OAAO,KAAA;AACT;;;ACnHO,IAAM,OAAA,GAAU","file":"index.cjs","sourcesContent":["/**\n * KernelProvider - React Context Provider for QuarKernel\n *\n * Provides kernel instance to child components via React Context.\n * Use useKernel() hook to access the kernel in components.\n */\n\nimport { createContext, type ReactNode } from 'react';\nimport type { Kernel, EventMap } from '@quazardous/quarkernel';\n\n/**\n * React Context for kernel instance\n * @internal\n */\nexport const KernelContext = createContext<Kernel<any> | null>(null);\n\n/**\n * Props for KernelProvider component\n */\nexport interface KernelProviderProps<Events extends EventMap = EventMap> {\n /** Kernel instance to provide to children */\n kernel: Kernel<Events>;\n\n /** Child components that can access the kernel */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes kernel available to child components\n *\n * @example\n * ```tsx\n * const kernel = createKernel();\n *\n * function App() {\n * return (\n * <KernelProvider kernel={kernel}>\n * <MyComponent />\n * </KernelProvider>\n * );\n * }\n * ```\n */\nexport function KernelProvider<Events extends EventMap = EventMap>({\n kernel,\n children,\n}: KernelProviderProps<Events>) {\n return (\n <KernelContext.Provider value={kernel}>\n {children}\n </KernelContext.Provider>\n );\n}\n","/**\n * useKernel - React hook to access kernel from context\n *\n * Must be used inside a KernelProvider component.\n * Includes SSR safety checks.\n */\n\nimport { useContext } from 'react';\nimport type { Kernel, EventMap } from '@quazardous/quarkernel';\nimport { KernelContext } from './KernelProvider.js';\n\n/**\n * Error thrown when useKernel is called outside KernelProvider\n */\nexport class KernelProviderError extends Error {\n constructor() {\n super(\n 'useKernel must be used within a KernelProvider. ' +\n 'Wrap your component tree with <KernelProvider kernel={kernel}>.'\n );\n this.name = 'KernelProviderError';\n }\n}\n\n/**\n * Check if code is running during server-side rendering\n * @internal\n */\nfunction isSSR(): boolean {\n return typeof window === 'undefined';\n}\n\n/**\n * React hook to access the kernel instance from context\n *\n * @throws {KernelProviderError} If called outside KernelProvider\n * @returns The kernel instance\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const kernel = useKernel();\n *\n * useEffect(() => {\n * return kernel.on('user:login', (event) => {\n * console.log('User logged in:', event.data);\n * });\n * }, [kernel]);\n *\n * return <div>...</div>;\n * }\n * ```\n */\nexport function useKernel<Events extends EventMap = EventMap>(): Kernel<Events> {\n if (isSSR()) {\n if (typeof console !== 'undefined' && console.warn) {\n console.warn(\n '[QuarKernel] useKernel called during server-side rendering. ' +\n 'Event listeners will not work during SSR. ' +\n 'Consider using useEffect to register listeners on the client side only.'\n );\n }\n }\n\n const kernel = useContext(KernelContext);\n\n if (!kernel) {\n throw new KernelProviderError();\n }\n\n return kernel as Kernel<Events>;\n}\n","/**\n * useOn - React hook for event listeners with automatic cleanup\n *\n * Registers an event listener on the kernel and automatically removes it\n * when the component unmounts or dependencies change.\n */\n\nimport { useEffect } from 'react';\nimport type { EventMap, ListenerFunction, ListenerOptions } from '@quazardous/quarkernel';\nimport { useKernel } from './useKernel.js';\n\n/**\n * Hook to register an event listener with automatic cleanup\n *\n * The listener is registered when the component mounts and automatically\n * removed when the component unmounts or when dependencies change.\n *\n * @param eventName - Event name to listen to\n * @param handler - Listener function to execute when event fires\n * @param options - Optional listener options (priority, after, signal, etc.)\n *\n * @example\n * ```tsx\n * function UserStatus() {\n * useOn('user:login', (event, context) => {\n * console.log('User logged in:', event.data.userId);\n * });\n *\n * return <div>User Status</div>;\n * }\n * ```\n *\n * @example With options\n * ```tsx\n * function PriorityListener() {\n * useOn('app:init', handler, {\n * priority: 10,\n * after: 'core-init'\n * });\n *\n * return <div>App</div>;\n * }\n * ```\n *\n * @example With AbortSignal\n * ```tsx\n * function ConditionalListener() {\n * const [enabled, setEnabled] = useState(true);\n * const controller = useMemo(() => new AbortController(), []);\n *\n * useOn('data:update', handler, {\n * signal: controller.signal\n * });\n *\n * const disable = () => controller.abort();\n *\n * return <button onClick={disable}>Disable</button>;\n * }\n * ```\n */\nexport function useOn<Events extends EventMap = EventMap, K extends keyof Events = keyof Events>(\n eventName: K,\n handler: ListenerFunction<Events[K]>,\n options?: ListenerOptions\n): void {\n const kernel = useKernel<Events>();\n\n useEffect(() => {\n // Register listener and get cleanup function\n const off = kernel.on(eventName, handler, options);\n\n // If AbortSignal provided, also listen for abort\n if (options?.signal) {\n const abortHandler = () => {\n off();\n };\n\n options.signal.addEventListener('abort', abortHandler, { once: true });\n\n // Return cleanup that removes both listener and abort handler\n return () => {\n off();\n if (!options.signal!.aborted) {\n options.signal!.removeEventListener('abort', abortHandler);\n }\n };\n }\n\n // Return cleanup function\n return off;\n }, [kernel, eventName, handler, options]);\n}\n","/**\n * useEventState - React hook that maintains state synchronized with events\n *\n * Creates a state value that updates whenever a specific event is emitted.\n * Automatically cleans up the listener when the component unmounts.\n */\n\nimport { useState, useEffect, useMemo } from 'react';\nimport type { EventMap, ListenerOptions, IKernelEvent } from '@quazardous/quarkernel';\nimport { useKernel } from './useKernel.js';\n\n/**\n * Selector function to extract value from event\n */\nexport type EventStateSelector<T = any> = (event: IKernelEvent) => T;\n\n/**\n * Options for useEventState hook\n */\nexport interface UseEventStateOptions<T = any> extends ListenerOptions {\n /**\n * Selector function to extract value from event\n * Default: (event) => event.data\n */\n selector?: EventStateSelector<T>;\n}\n\n/**\n * Hook to maintain state that updates on events\n *\n * The state value is updated whenever the specified event fires.\n * The listener is automatically cleaned up when the component unmounts.\n *\n * @param eventName - Event name to listen to\n * @param initialValue - Initial state value\n * @param selectorOrOptions - Optional selector function or options object\n * @returns Current state value\n *\n * @example\n * ```tsx\n * function UserInfo() {\n * const user = useEventState<Events, 'user:login'>(\n * 'user:login',\n * null\n * );\n *\n * return <div>User: {user?.userId}</div>;\n * }\n * ```\n *\n * @example With selector function\n * ```tsx\n * function Counter() {\n * const count = useEventState('counter:updated', 0, (event) => event.data.value);\n *\n * return <div>Count: {count}</div>;\n * }\n * ```\n *\n * @example With options object\n * ```tsx\n * function UserName() {\n * const name = useEventState('user:login', 'Guest', {\n * selector: (event) => event.data.name,\n * priority: 10\n * });\n *\n * return <div>Hello, {name}</div>;\n * }\n * ```\n *\n * @example With AbortSignal\n * ```tsx\n * function ConditionalState() {\n * const controller = useMemo(() => new AbortController(), []);\n * const value = useEventState('data:update', null, {\n * signal: controller.signal\n * });\n *\n * const stop = () => controller.abort();\n *\n * return (\n * <div>\n * Value: {value}\n * <button onClick={stop}>Stop Updates</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useEventState<\n Events extends EventMap = EventMap,\n K extends keyof Events = keyof Events,\n T = Events[K]\n>(\n eventName: K,\n initialValue: T,\n selectorOrOptions?: EventStateSelector<T> | UseEventStateOptions<T>\n): T {\n const kernel = useKernel<Events>();\n const [state, setState] = useState<T>(initialValue);\n\n // Normalize options: function becomes selector, object used as-is\n const { selector, options } = useMemo(() => {\n if (typeof selectorOrOptions === 'function') {\n return { selector: selectorOrOptions, options: undefined };\n }\n if (selectorOrOptions) {\n const { selector: sel, ...opts } = selectorOrOptions;\n return { selector: sel, options: Object.keys(opts).length > 0 ? opts : undefined };\n }\n return { selector: undefined, options: undefined };\n }, [selectorOrOptions]);\n\n // Default selector returns event.data\n const selectorFn = selector || ((event: IKernelEvent) => event.data as T);\n\n useEffect(() => {\n // Register listener that updates state\n const off = kernel.on(\n eventName,\n (event: IKernelEvent<Events[K]>) => {\n setState(selectorFn(event));\n },\n options\n );\n\n // If AbortSignal provided, also listen for abort\n if (options?.signal) {\n const abortHandler = () => {\n off();\n };\n\n options.signal.addEventListener('abort', abortHandler, { once: true });\n\n // Return cleanup that removes both listener and abort handler\n return () => {\n off();\n if (!options.signal!.aborted) {\n options.signal!.removeEventListener('abort', abortHandler);\n }\n };\n }\n\n // Return cleanup function\n return off;\n }, [kernel, eventName, selector, options]);\n\n return state;\n}\n","/**\n * @quazardous/quarkernel-react - React bindings for QuarKernel\n *\n * Provides Context provider and hooks for using QuarKernel in React applications.\n *\n * @example\n * ```tsx\n * import { createKernel } from '@quazardous/quarkernel';\n * import { KernelProvider, useKernel } from '@quazardous/quarkernel-react';\n *\n * const kernel = createKernel();\n *\n * function App() {\n * return (\n * <KernelProvider kernel={kernel}>\n * <MyComponent />\n * </KernelProvider>\n * );\n * }\n *\n * function MyComponent() {\n * const kernel = useKernel();\n *\n * useEffect(() => {\n * return kernel.on('event', (event) => {\n * console.log(event.data);\n * });\n * }, [kernel]);\n *\n * return <div>...</div>;\n * }\n * ```\n */\n\nexport const VERSION = '2.1.0';\n\n// Components\nexport { KernelProvider, type KernelProviderProps } from './KernelProvider.js';\n\n// Hooks\nexport { useKernel, KernelProviderError } from './useKernel.js';\nexport { useOn } from './useOn.js';\nexport { useEventState, type EventStateSelector, type UseEventStateOptions } from './useEventState.js';\n"]}
@@ -0,0 +1,247 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { EventMap, Kernel, ListenerFunction, ListenerOptions, IKernelEvent } from '@quazardous/quarkernel';
4
+
5
+ /**
6
+ * Props for KernelProvider component
7
+ */
8
+ interface KernelProviderProps<Events extends EventMap = EventMap> {
9
+ /** Kernel instance to provide to children */
10
+ kernel: Kernel<Events>;
11
+ /** Child components that can access the kernel */
12
+ children: ReactNode;
13
+ }
14
+ /**
15
+ * Provider component that makes kernel available to child components
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const kernel = createKernel();
20
+ *
21
+ * function App() {
22
+ * return (
23
+ * <KernelProvider kernel={kernel}>
24
+ * <MyComponent />
25
+ * </KernelProvider>
26
+ * );
27
+ * }
28
+ * ```
29
+ */
30
+ declare function KernelProvider<Events extends EventMap = EventMap>({ kernel, children, }: KernelProviderProps<Events>): react_jsx_runtime.JSX.Element;
31
+
32
+ /**
33
+ * useKernel - React hook to access kernel from context
34
+ *
35
+ * Must be used inside a KernelProvider component.
36
+ * Includes SSR safety checks.
37
+ */
38
+
39
+ /**
40
+ * Error thrown when useKernel is called outside KernelProvider
41
+ */
42
+ declare class KernelProviderError extends Error {
43
+ constructor();
44
+ }
45
+ /**
46
+ * React hook to access the kernel instance from context
47
+ *
48
+ * @throws {KernelProviderError} If called outside KernelProvider
49
+ * @returns The kernel instance
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * function MyComponent() {
54
+ * const kernel = useKernel();
55
+ *
56
+ * useEffect(() => {
57
+ * return kernel.on('user:login', (event) => {
58
+ * console.log('User logged in:', event.data);
59
+ * });
60
+ * }, [kernel]);
61
+ *
62
+ * return <div>...</div>;
63
+ * }
64
+ * ```
65
+ */
66
+ declare function useKernel<Events extends EventMap = EventMap>(): Kernel<Events>;
67
+
68
+ /**
69
+ * useOn - React hook for event listeners with automatic cleanup
70
+ *
71
+ * Registers an event listener on the kernel and automatically removes it
72
+ * when the component unmounts or dependencies change.
73
+ */
74
+
75
+ /**
76
+ * Hook to register an event listener with automatic cleanup
77
+ *
78
+ * The listener is registered when the component mounts and automatically
79
+ * removed when the component unmounts or when dependencies change.
80
+ *
81
+ * @param eventName - Event name to listen to
82
+ * @param handler - Listener function to execute when event fires
83
+ * @param options - Optional listener options (priority, after, signal, etc.)
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * function UserStatus() {
88
+ * useOn('user:login', (event, context) => {
89
+ * console.log('User logged in:', event.data.userId);
90
+ * });
91
+ *
92
+ * return <div>User Status</div>;
93
+ * }
94
+ * ```
95
+ *
96
+ * @example With options
97
+ * ```tsx
98
+ * function PriorityListener() {
99
+ * useOn('app:init', handler, {
100
+ * priority: 10,
101
+ * after: 'core-init'
102
+ * });
103
+ *
104
+ * return <div>App</div>;
105
+ * }
106
+ * ```
107
+ *
108
+ * @example With AbortSignal
109
+ * ```tsx
110
+ * function ConditionalListener() {
111
+ * const [enabled, setEnabled] = useState(true);
112
+ * const controller = useMemo(() => new AbortController(), []);
113
+ *
114
+ * useOn('data:update', handler, {
115
+ * signal: controller.signal
116
+ * });
117
+ *
118
+ * const disable = () => controller.abort();
119
+ *
120
+ * return <button onClick={disable}>Disable</button>;
121
+ * }
122
+ * ```
123
+ */
124
+ declare function useOn<Events extends EventMap = EventMap, K extends keyof Events = keyof Events>(eventName: K, handler: ListenerFunction<Events[K]>, options?: ListenerOptions): void;
125
+
126
+ /**
127
+ * useEventState - React hook that maintains state synchronized with events
128
+ *
129
+ * Creates a state value that updates whenever a specific event is emitted.
130
+ * Automatically cleans up the listener when the component unmounts.
131
+ */
132
+
133
+ /**
134
+ * Selector function to extract value from event
135
+ */
136
+ type EventStateSelector<T = any> = (event: IKernelEvent) => T;
137
+ /**
138
+ * Options for useEventState hook
139
+ */
140
+ interface UseEventStateOptions<T = any> extends ListenerOptions {
141
+ /**
142
+ * Selector function to extract value from event
143
+ * Default: (event) => event.data
144
+ */
145
+ selector?: EventStateSelector<T>;
146
+ }
147
+ /**
148
+ * Hook to maintain state that updates on events
149
+ *
150
+ * The state value is updated whenever the specified event fires.
151
+ * The listener is automatically cleaned up when the component unmounts.
152
+ *
153
+ * @param eventName - Event name to listen to
154
+ * @param initialValue - Initial state value
155
+ * @param selectorOrOptions - Optional selector function or options object
156
+ * @returns Current state value
157
+ *
158
+ * @example
159
+ * ```tsx
160
+ * function UserInfo() {
161
+ * const user = useEventState<Events, 'user:login'>(
162
+ * 'user:login',
163
+ * null
164
+ * );
165
+ *
166
+ * return <div>User: {user?.userId}</div>;
167
+ * }
168
+ * ```
169
+ *
170
+ * @example With selector function
171
+ * ```tsx
172
+ * function Counter() {
173
+ * const count = useEventState('counter:updated', 0, (event) => event.data.value);
174
+ *
175
+ * return <div>Count: {count}</div>;
176
+ * }
177
+ * ```
178
+ *
179
+ * @example With options object
180
+ * ```tsx
181
+ * function UserName() {
182
+ * const name = useEventState('user:login', 'Guest', {
183
+ * selector: (event) => event.data.name,
184
+ * priority: 10
185
+ * });
186
+ *
187
+ * return <div>Hello, {name}</div>;
188
+ * }
189
+ * ```
190
+ *
191
+ * @example With AbortSignal
192
+ * ```tsx
193
+ * function ConditionalState() {
194
+ * const controller = useMemo(() => new AbortController(), []);
195
+ * const value = useEventState('data:update', null, {
196
+ * signal: controller.signal
197
+ * });
198
+ *
199
+ * const stop = () => controller.abort();
200
+ *
201
+ * return (
202
+ * <div>
203
+ * Value: {value}
204
+ * <button onClick={stop}>Stop Updates</button>
205
+ * </div>
206
+ * );
207
+ * }
208
+ * ```
209
+ */
210
+ declare function useEventState<Events extends EventMap = EventMap, K extends keyof Events = keyof Events, T = Events[K]>(eventName: K, initialValue: T, selectorOrOptions?: EventStateSelector<T> | UseEventStateOptions<T>): T;
211
+
212
+ /**
213
+ * @quazardous/quarkernel-react - React bindings for QuarKernel
214
+ *
215
+ * Provides Context provider and hooks for using QuarKernel in React applications.
216
+ *
217
+ * @example
218
+ * ```tsx
219
+ * import { createKernel } from '@quazardous/quarkernel';
220
+ * import { KernelProvider, useKernel } from '@quazardous/quarkernel-react';
221
+ *
222
+ * const kernel = createKernel();
223
+ *
224
+ * function App() {
225
+ * return (
226
+ * <KernelProvider kernel={kernel}>
227
+ * <MyComponent />
228
+ * </KernelProvider>
229
+ * );
230
+ * }
231
+ *
232
+ * function MyComponent() {
233
+ * const kernel = useKernel();
234
+ *
235
+ * useEffect(() => {
236
+ * return kernel.on('event', (event) => {
237
+ * console.log(event.data);
238
+ * });
239
+ * }, [kernel]);
240
+ *
241
+ * return <div>...</div>;
242
+ * }
243
+ * ```
244
+ */
245
+ declare const VERSION = "2.1.0";
246
+
247
+ export { type EventStateSelector, KernelProvider, KernelProviderError, type KernelProviderProps, type UseEventStateOptions, VERSION, useEventState, useKernel, useOn };
@@ -0,0 +1,247 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { EventMap, Kernel, ListenerFunction, ListenerOptions, IKernelEvent } from '@quazardous/quarkernel';
4
+
5
+ /**
6
+ * Props for KernelProvider component
7
+ */
8
+ interface KernelProviderProps<Events extends EventMap = EventMap> {
9
+ /** Kernel instance to provide to children */
10
+ kernel: Kernel<Events>;
11
+ /** Child components that can access the kernel */
12
+ children: ReactNode;
13
+ }
14
+ /**
15
+ * Provider component that makes kernel available to child components
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const kernel = createKernel();
20
+ *
21
+ * function App() {
22
+ * return (
23
+ * <KernelProvider kernel={kernel}>
24
+ * <MyComponent />
25
+ * </KernelProvider>
26
+ * );
27
+ * }
28
+ * ```
29
+ */
30
+ declare function KernelProvider<Events extends EventMap = EventMap>({ kernel, children, }: KernelProviderProps<Events>): react_jsx_runtime.JSX.Element;
31
+
32
+ /**
33
+ * useKernel - React hook to access kernel from context
34
+ *
35
+ * Must be used inside a KernelProvider component.
36
+ * Includes SSR safety checks.
37
+ */
38
+
39
+ /**
40
+ * Error thrown when useKernel is called outside KernelProvider
41
+ */
42
+ declare class KernelProviderError extends Error {
43
+ constructor();
44
+ }
45
+ /**
46
+ * React hook to access the kernel instance from context
47
+ *
48
+ * @throws {KernelProviderError} If called outside KernelProvider
49
+ * @returns The kernel instance
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * function MyComponent() {
54
+ * const kernel = useKernel();
55
+ *
56
+ * useEffect(() => {
57
+ * return kernel.on('user:login', (event) => {
58
+ * console.log('User logged in:', event.data);
59
+ * });
60
+ * }, [kernel]);
61
+ *
62
+ * return <div>...</div>;
63
+ * }
64
+ * ```
65
+ */
66
+ declare function useKernel<Events extends EventMap = EventMap>(): Kernel<Events>;
67
+
68
+ /**
69
+ * useOn - React hook for event listeners with automatic cleanup
70
+ *
71
+ * Registers an event listener on the kernel and automatically removes it
72
+ * when the component unmounts or dependencies change.
73
+ */
74
+
75
+ /**
76
+ * Hook to register an event listener with automatic cleanup
77
+ *
78
+ * The listener is registered when the component mounts and automatically
79
+ * removed when the component unmounts or when dependencies change.
80
+ *
81
+ * @param eventName - Event name to listen to
82
+ * @param handler - Listener function to execute when event fires
83
+ * @param options - Optional listener options (priority, after, signal, etc.)
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * function UserStatus() {
88
+ * useOn('user:login', (event, context) => {
89
+ * console.log('User logged in:', event.data.userId);
90
+ * });
91
+ *
92
+ * return <div>User Status</div>;
93
+ * }
94
+ * ```
95
+ *
96
+ * @example With options
97
+ * ```tsx
98
+ * function PriorityListener() {
99
+ * useOn('app:init', handler, {
100
+ * priority: 10,
101
+ * after: 'core-init'
102
+ * });
103
+ *
104
+ * return <div>App</div>;
105
+ * }
106
+ * ```
107
+ *
108
+ * @example With AbortSignal
109
+ * ```tsx
110
+ * function ConditionalListener() {
111
+ * const [enabled, setEnabled] = useState(true);
112
+ * const controller = useMemo(() => new AbortController(), []);
113
+ *
114
+ * useOn('data:update', handler, {
115
+ * signal: controller.signal
116
+ * });
117
+ *
118
+ * const disable = () => controller.abort();
119
+ *
120
+ * return <button onClick={disable}>Disable</button>;
121
+ * }
122
+ * ```
123
+ */
124
+ declare function useOn<Events extends EventMap = EventMap, K extends keyof Events = keyof Events>(eventName: K, handler: ListenerFunction<Events[K]>, options?: ListenerOptions): void;
125
+
126
+ /**
127
+ * useEventState - React hook that maintains state synchronized with events
128
+ *
129
+ * Creates a state value that updates whenever a specific event is emitted.
130
+ * Automatically cleans up the listener when the component unmounts.
131
+ */
132
+
133
+ /**
134
+ * Selector function to extract value from event
135
+ */
136
+ type EventStateSelector<T = any> = (event: IKernelEvent) => T;
137
+ /**
138
+ * Options for useEventState hook
139
+ */
140
+ interface UseEventStateOptions<T = any> extends ListenerOptions {
141
+ /**
142
+ * Selector function to extract value from event
143
+ * Default: (event) => event.data
144
+ */
145
+ selector?: EventStateSelector<T>;
146
+ }
147
+ /**
148
+ * Hook to maintain state that updates on events
149
+ *
150
+ * The state value is updated whenever the specified event fires.
151
+ * The listener is automatically cleaned up when the component unmounts.
152
+ *
153
+ * @param eventName - Event name to listen to
154
+ * @param initialValue - Initial state value
155
+ * @param selectorOrOptions - Optional selector function or options object
156
+ * @returns Current state value
157
+ *
158
+ * @example
159
+ * ```tsx
160
+ * function UserInfo() {
161
+ * const user = useEventState<Events, 'user:login'>(
162
+ * 'user:login',
163
+ * null
164
+ * );
165
+ *
166
+ * return <div>User: {user?.userId}</div>;
167
+ * }
168
+ * ```
169
+ *
170
+ * @example With selector function
171
+ * ```tsx
172
+ * function Counter() {
173
+ * const count = useEventState('counter:updated', 0, (event) => event.data.value);
174
+ *
175
+ * return <div>Count: {count}</div>;
176
+ * }
177
+ * ```
178
+ *
179
+ * @example With options object
180
+ * ```tsx
181
+ * function UserName() {
182
+ * const name = useEventState('user:login', 'Guest', {
183
+ * selector: (event) => event.data.name,
184
+ * priority: 10
185
+ * });
186
+ *
187
+ * return <div>Hello, {name}</div>;
188
+ * }
189
+ * ```
190
+ *
191
+ * @example With AbortSignal
192
+ * ```tsx
193
+ * function ConditionalState() {
194
+ * const controller = useMemo(() => new AbortController(), []);
195
+ * const value = useEventState('data:update', null, {
196
+ * signal: controller.signal
197
+ * });
198
+ *
199
+ * const stop = () => controller.abort();
200
+ *
201
+ * return (
202
+ * <div>
203
+ * Value: {value}
204
+ * <button onClick={stop}>Stop Updates</button>
205
+ * </div>
206
+ * );
207
+ * }
208
+ * ```
209
+ */
210
+ declare function useEventState<Events extends EventMap = EventMap, K extends keyof Events = keyof Events, T = Events[K]>(eventName: K, initialValue: T, selectorOrOptions?: EventStateSelector<T> | UseEventStateOptions<T>): T;
211
+
212
+ /**
213
+ * @quazardous/quarkernel-react - React bindings for QuarKernel
214
+ *
215
+ * Provides Context provider and hooks for using QuarKernel in React applications.
216
+ *
217
+ * @example
218
+ * ```tsx
219
+ * import { createKernel } from '@quazardous/quarkernel';
220
+ * import { KernelProvider, useKernel } from '@quazardous/quarkernel-react';
221
+ *
222
+ * const kernel = createKernel();
223
+ *
224
+ * function App() {
225
+ * return (
226
+ * <KernelProvider kernel={kernel}>
227
+ * <MyComponent />
228
+ * </KernelProvider>
229
+ * );
230
+ * }
231
+ *
232
+ * function MyComponent() {
233
+ * const kernel = useKernel();
234
+ *
235
+ * useEffect(() => {
236
+ * return kernel.on('event', (event) => {
237
+ * console.log(event.data);
238
+ * });
239
+ * }, [kernel]);
240
+ *
241
+ * return <div>...</div>;
242
+ * }
243
+ * ```
244
+ */
245
+ declare const VERSION = "2.1.0";
246
+
247
+ export { type EventStateSelector, KernelProvider, KernelProviderError, type KernelProviderProps, type UseEventStateOptions, VERSION, useEventState, useKernel, useOn };
package/dist/index.js ADDED
@@ -0,0 +1,100 @@
1
+ import { createContext, useContext, useEffect, useState, useMemo } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/KernelProvider.tsx
5
+ var KernelContext = createContext(null);
6
+ function KernelProvider({
7
+ kernel,
8
+ children
9
+ }) {
10
+ return /* @__PURE__ */ jsx(KernelContext.Provider, { value: kernel, children });
11
+ }
12
+ var KernelProviderError = class extends Error {
13
+ constructor() {
14
+ super(
15
+ "useKernel must be used within a KernelProvider. Wrap your component tree with <KernelProvider kernel={kernel}>."
16
+ );
17
+ this.name = "KernelProviderError";
18
+ }
19
+ };
20
+ function isSSR() {
21
+ return typeof window === "undefined";
22
+ }
23
+ function useKernel() {
24
+ if (isSSR()) {
25
+ if (typeof console !== "undefined" && console.warn) {
26
+ console.warn(
27
+ "[QuarKernel] useKernel called during server-side rendering. Event listeners will not work during SSR. Consider using useEffect to register listeners on the client side only."
28
+ );
29
+ }
30
+ }
31
+ const kernel = useContext(KernelContext);
32
+ if (!kernel) {
33
+ throw new KernelProviderError();
34
+ }
35
+ return kernel;
36
+ }
37
+ function useOn(eventName, handler, options) {
38
+ const kernel = useKernel();
39
+ useEffect(() => {
40
+ const off = kernel.on(eventName, handler, options);
41
+ if (options?.signal) {
42
+ const abortHandler = () => {
43
+ off();
44
+ };
45
+ options.signal.addEventListener("abort", abortHandler, { once: true });
46
+ return () => {
47
+ off();
48
+ if (!options.signal.aborted) {
49
+ options.signal.removeEventListener("abort", abortHandler);
50
+ }
51
+ };
52
+ }
53
+ return off;
54
+ }, [kernel, eventName, handler, options]);
55
+ }
56
+ function useEventState(eventName, initialValue, selectorOrOptions) {
57
+ const kernel = useKernel();
58
+ const [state, setState] = useState(initialValue);
59
+ const { selector, options } = useMemo(() => {
60
+ if (typeof selectorOrOptions === "function") {
61
+ return { selector: selectorOrOptions, options: void 0 };
62
+ }
63
+ if (selectorOrOptions) {
64
+ const { selector: sel, ...opts } = selectorOrOptions;
65
+ return { selector: sel, options: Object.keys(opts).length > 0 ? opts : void 0 };
66
+ }
67
+ return { selector: void 0, options: void 0 };
68
+ }, [selectorOrOptions]);
69
+ const selectorFn = selector || ((event) => event.data);
70
+ useEffect(() => {
71
+ const off = kernel.on(
72
+ eventName,
73
+ (event) => {
74
+ setState(selectorFn(event));
75
+ },
76
+ options
77
+ );
78
+ if (options?.signal) {
79
+ const abortHandler = () => {
80
+ off();
81
+ };
82
+ options.signal.addEventListener("abort", abortHandler, { once: true });
83
+ return () => {
84
+ off();
85
+ if (!options.signal.aborted) {
86
+ options.signal.removeEventListener("abort", abortHandler);
87
+ }
88
+ };
89
+ }
90
+ return off;
91
+ }, [kernel, eventName, selector, options]);
92
+ return state;
93
+ }
94
+
95
+ // src/index.ts
96
+ var VERSION = "2.1.0";
97
+
98
+ export { KernelProvider, KernelProviderError, VERSION, useEventState, useKernel, useOn };
99
+ //# sourceMappingURL=index.js.map
100
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/KernelProvider.tsx","../src/useKernel.ts","../src/useOn.ts","../src/useEventState.ts","../src/index.ts"],"names":["useEffect"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * KernelProvider - React Context Provider for QuarKernel\n *\n * Provides kernel instance to child components via React Context.\n * Use useKernel() hook to access the kernel in components.\n */\n\nimport { createContext, type ReactNode } from 'react';\nimport type { Kernel, EventMap } from '@quazardous/quarkernel';\n\n/**\n * React Context for kernel instance\n * @internal\n */\nexport const KernelContext = createContext<Kernel<any> | null>(null);\n\n/**\n * Props for KernelProvider component\n */\nexport interface KernelProviderProps<Events extends EventMap = EventMap> {\n /** Kernel instance to provide to children */\n kernel: Kernel<Events>;\n\n /** Child components that can access the kernel */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes kernel available to child components\n *\n * @example\n * ```tsx\n * const kernel = createKernel();\n *\n * function App() {\n * return (\n * <KernelProvider kernel={kernel}>\n * <MyComponent />\n * </KernelProvider>\n * );\n * }\n * ```\n */\nexport function KernelProvider<Events extends EventMap = EventMap>({\n kernel,\n children,\n}: KernelProviderProps<Events>) {\n return (\n <KernelContext.Provider value={kernel}>\n {children}\n </KernelContext.Provider>\n );\n}\n","/**\n * useKernel - React hook to access kernel from context\n *\n * Must be used inside a KernelProvider component.\n * Includes SSR safety checks.\n */\n\nimport { useContext } from 'react';\nimport type { Kernel, EventMap } from '@quazardous/quarkernel';\nimport { KernelContext } from './KernelProvider.js';\n\n/**\n * Error thrown when useKernel is called outside KernelProvider\n */\nexport class KernelProviderError extends Error {\n constructor() {\n super(\n 'useKernel must be used within a KernelProvider. ' +\n 'Wrap your component tree with <KernelProvider kernel={kernel}>.'\n );\n this.name = 'KernelProviderError';\n }\n}\n\n/**\n * Check if code is running during server-side rendering\n * @internal\n */\nfunction isSSR(): boolean {\n return typeof window === 'undefined';\n}\n\n/**\n * React hook to access the kernel instance from context\n *\n * @throws {KernelProviderError} If called outside KernelProvider\n * @returns The kernel instance\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const kernel = useKernel();\n *\n * useEffect(() => {\n * return kernel.on('user:login', (event) => {\n * console.log('User logged in:', event.data);\n * });\n * }, [kernel]);\n *\n * return <div>...</div>;\n * }\n * ```\n */\nexport function useKernel<Events extends EventMap = EventMap>(): Kernel<Events> {\n if (isSSR()) {\n if (typeof console !== 'undefined' && console.warn) {\n console.warn(\n '[QuarKernel] useKernel called during server-side rendering. ' +\n 'Event listeners will not work during SSR. ' +\n 'Consider using useEffect to register listeners on the client side only.'\n );\n }\n }\n\n const kernel = useContext(KernelContext);\n\n if (!kernel) {\n throw new KernelProviderError();\n }\n\n return kernel as Kernel<Events>;\n}\n","/**\n * useOn - React hook for event listeners with automatic cleanup\n *\n * Registers an event listener on the kernel and automatically removes it\n * when the component unmounts or dependencies change.\n */\n\nimport { useEffect } from 'react';\nimport type { EventMap, ListenerFunction, ListenerOptions } from '@quazardous/quarkernel';\nimport { useKernel } from './useKernel.js';\n\n/**\n * Hook to register an event listener with automatic cleanup\n *\n * The listener is registered when the component mounts and automatically\n * removed when the component unmounts or when dependencies change.\n *\n * @param eventName - Event name to listen to\n * @param handler - Listener function to execute when event fires\n * @param options - Optional listener options (priority, after, signal, etc.)\n *\n * @example\n * ```tsx\n * function UserStatus() {\n * useOn('user:login', (event, context) => {\n * console.log('User logged in:', event.data.userId);\n * });\n *\n * return <div>User Status</div>;\n * }\n * ```\n *\n * @example With options\n * ```tsx\n * function PriorityListener() {\n * useOn('app:init', handler, {\n * priority: 10,\n * after: 'core-init'\n * });\n *\n * return <div>App</div>;\n * }\n * ```\n *\n * @example With AbortSignal\n * ```tsx\n * function ConditionalListener() {\n * const [enabled, setEnabled] = useState(true);\n * const controller = useMemo(() => new AbortController(), []);\n *\n * useOn('data:update', handler, {\n * signal: controller.signal\n * });\n *\n * const disable = () => controller.abort();\n *\n * return <button onClick={disable}>Disable</button>;\n * }\n * ```\n */\nexport function useOn<Events extends EventMap = EventMap, K extends keyof Events = keyof Events>(\n eventName: K,\n handler: ListenerFunction<Events[K]>,\n options?: ListenerOptions\n): void {\n const kernel = useKernel<Events>();\n\n useEffect(() => {\n // Register listener and get cleanup function\n const off = kernel.on(eventName, handler, options);\n\n // If AbortSignal provided, also listen for abort\n if (options?.signal) {\n const abortHandler = () => {\n off();\n };\n\n options.signal.addEventListener('abort', abortHandler, { once: true });\n\n // Return cleanup that removes both listener and abort handler\n return () => {\n off();\n if (!options.signal!.aborted) {\n options.signal!.removeEventListener('abort', abortHandler);\n }\n };\n }\n\n // Return cleanup function\n return off;\n }, [kernel, eventName, handler, options]);\n}\n","/**\n * useEventState - React hook that maintains state synchronized with events\n *\n * Creates a state value that updates whenever a specific event is emitted.\n * Automatically cleans up the listener when the component unmounts.\n */\n\nimport { useState, useEffect, useMemo } from 'react';\nimport type { EventMap, ListenerOptions, IKernelEvent } from '@quazardous/quarkernel';\nimport { useKernel } from './useKernel.js';\n\n/**\n * Selector function to extract value from event\n */\nexport type EventStateSelector<T = any> = (event: IKernelEvent) => T;\n\n/**\n * Options for useEventState hook\n */\nexport interface UseEventStateOptions<T = any> extends ListenerOptions {\n /**\n * Selector function to extract value from event\n * Default: (event) => event.data\n */\n selector?: EventStateSelector<T>;\n}\n\n/**\n * Hook to maintain state that updates on events\n *\n * The state value is updated whenever the specified event fires.\n * The listener is automatically cleaned up when the component unmounts.\n *\n * @param eventName - Event name to listen to\n * @param initialValue - Initial state value\n * @param selectorOrOptions - Optional selector function or options object\n * @returns Current state value\n *\n * @example\n * ```tsx\n * function UserInfo() {\n * const user = useEventState<Events, 'user:login'>(\n * 'user:login',\n * null\n * );\n *\n * return <div>User: {user?.userId}</div>;\n * }\n * ```\n *\n * @example With selector function\n * ```tsx\n * function Counter() {\n * const count = useEventState('counter:updated', 0, (event) => event.data.value);\n *\n * return <div>Count: {count}</div>;\n * }\n * ```\n *\n * @example With options object\n * ```tsx\n * function UserName() {\n * const name = useEventState('user:login', 'Guest', {\n * selector: (event) => event.data.name,\n * priority: 10\n * });\n *\n * return <div>Hello, {name}</div>;\n * }\n * ```\n *\n * @example With AbortSignal\n * ```tsx\n * function ConditionalState() {\n * const controller = useMemo(() => new AbortController(), []);\n * const value = useEventState('data:update', null, {\n * signal: controller.signal\n * });\n *\n * const stop = () => controller.abort();\n *\n * return (\n * <div>\n * Value: {value}\n * <button onClick={stop}>Stop Updates</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useEventState<\n Events extends EventMap = EventMap,\n K extends keyof Events = keyof Events,\n T = Events[K]\n>(\n eventName: K,\n initialValue: T,\n selectorOrOptions?: EventStateSelector<T> | UseEventStateOptions<T>\n): T {\n const kernel = useKernel<Events>();\n const [state, setState] = useState<T>(initialValue);\n\n // Normalize options: function becomes selector, object used as-is\n const { selector, options } = useMemo(() => {\n if (typeof selectorOrOptions === 'function') {\n return { selector: selectorOrOptions, options: undefined };\n }\n if (selectorOrOptions) {\n const { selector: sel, ...opts } = selectorOrOptions;\n return { selector: sel, options: Object.keys(opts).length > 0 ? opts : undefined };\n }\n return { selector: undefined, options: undefined };\n }, [selectorOrOptions]);\n\n // Default selector returns event.data\n const selectorFn = selector || ((event: IKernelEvent) => event.data as T);\n\n useEffect(() => {\n // Register listener that updates state\n const off = kernel.on(\n eventName,\n (event: IKernelEvent<Events[K]>) => {\n setState(selectorFn(event));\n },\n options\n );\n\n // If AbortSignal provided, also listen for abort\n if (options?.signal) {\n const abortHandler = () => {\n off();\n };\n\n options.signal.addEventListener('abort', abortHandler, { once: true });\n\n // Return cleanup that removes both listener and abort handler\n return () => {\n off();\n if (!options.signal!.aborted) {\n options.signal!.removeEventListener('abort', abortHandler);\n }\n };\n }\n\n // Return cleanup function\n return off;\n }, [kernel, eventName, selector, options]);\n\n return state;\n}\n","/**\n * @quazardous/quarkernel-react - React bindings for QuarKernel\n *\n * Provides Context provider and hooks for using QuarKernel in React applications.\n *\n * @example\n * ```tsx\n * import { createKernel } from '@quazardous/quarkernel';\n * import { KernelProvider, useKernel } from '@quazardous/quarkernel-react';\n *\n * const kernel = createKernel();\n *\n * function App() {\n * return (\n * <KernelProvider kernel={kernel}>\n * <MyComponent />\n * </KernelProvider>\n * );\n * }\n *\n * function MyComponent() {\n * const kernel = useKernel();\n *\n * useEffect(() => {\n * return kernel.on('event', (event) => {\n * console.log(event.data);\n * });\n * }, [kernel]);\n *\n * return <div>...</div>;\n * }\n * ```\n */\n\nexport const VERSION = '2.1.0';\n\n// Components\nexport { KernelProvider, type KernelProviderProps } from './KernelProvider.js';\n\n// Hooks\nexport { useKernel, KernelProviderError } from './useKernel.js';\nexport { useOn } from './useOn.js';\nexport { useEventState, type EventStateSelector, type UseEventStateOptions } from './useEventState.js';\n"]}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@quazardous/quarkernel-react",
3
+ "version": "2.1.0",
4
+ "type": "module",
5
+ "description": "React bindings for QuarKernel - Context provider and hooks",
6
+ "author": "quazardous <berliozdavid@gmail.com>",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/quazardous/quarkernel.git",
11
+ "directory": "packages/react"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/quazardous/quarkernel/issues"
15
+ },
16
+ "homepage": "https://github.com/quazardous/quarkernel#readme",
17
+ "keywords": [
18
+ "react",
19
+ "hooks",
20
+ "context",
21
+ "quarkernel",
22
+ "event",
23
+ "kernel"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "require": "./dist/index.cjs"
30
+ }
31
+ },
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "sideEffects": false,
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "build:watch": "tsup --watch",
47
+ "test": "vitest run",
48
+ "test:watch": "vitest",
49
+ "clean": "rm -rf dist"
50
+ },
51
+ "peerDependencies": {
52
+ "@quazardous/quarkernel": "^2.1.0",
53
+ "react": "^18.0.0 || ^19.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@quazardous/quarkernel": "file:../quarkernel",
57
+ "@testing-library/react": "^14.0.0",
58
+ "@types/react": "^18.2.0",
59
+ "jsdom": "^23.0.0",
60
+ "react": "^18.2.0",
61
+ "react-dom": "^18.2.0",
62
+ "tsup": "^8.0.0",
63
+ "typescript": "^5.3.0",
64
+ "vitest": "^1.0.0"
65
+ }
66
+ }