@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 +144 -0
- package/dist/index.cjs +107 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +247 -0
- package/dist/index.d.ts +247 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
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":"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","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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|