crosshook 1.0.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,255 @@
1
+ # crosshook
2
+
3
+ > Tiny hook-first event bridge for decoupled React component
4
+ > communication.
5
+
6
+ `crosshook` lets React components communicate without prop drilling,
7
+ context nesting, or heavyweight state managers.
8
+
9
+ Perfect for UI orchestration like modals, toasts, loaders, and
10
+ notifications.
11
+
12
+ ------------------------------------------------------------------------
13
+
14
+ ## ✨ Features
15
+
16
+ ✅ Hook-first API\
17
+ ✅ No providers required\
18
+ ✅ Tiny footprint\
19
+ ✅ Lifecycle-safe (auto cleanup)\
20
+ ✅ Strict Mode friendly\
21
+ ✅ TypeScript ready
22
+
23
+ ------------------------------------------------------------------------
24
+
25
+ ## 📦 Installation
26
+
27
+ ``` bash
28
+ npm install crosshook
29
+ ```
30
+
31
+ or
32
+
33
+ ``` bash
34
+ yarn add crosshook
35
+ ```
36
+
37
+ ------------------------------------------------------------------------
38
+
39
+ ## 🚀 Quick Start
40
+
41
+ ### ✅ Register a Listener
42
+
43
+ ``` tsx
44
+ import { useRegister } from "crosshook";
45
+
46
+ function Toast() {
47
+ useRegister("toast.show", (payload: { message: string }) => {
48
+ console.log(payload.message);
49
+ });
50
+
51
+ return null;
52
+ }
53
+ ```
54
+
55
+ ------------------------------------------------------------------------
56
+
57
+ ### ✅ Call an Event
58
+
59
+ ``` tsx
60
+ import { useBridge } from "crosshook";
61
+
62
+ function SaveButton() {
63
+ const { call } = useBridge();
64
+
65
+ return (
66
+ <button
67
+ onClick={() =>
68
+ call("toast.show", { message: "Saved successfully!" })
69
+ }
70
+ >
71
+ Save
72
+ </button>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ------------------------------------------------------------------------
78
+
79
+ ## 🎯 Common Use Cases
80
+
81
+ ### 🔔 Toast Notifications
82
+
83
+ ``` tsx
84
+ useRegister("toast.show", ({ message }) => {
85
+ setToast(message);
86
+ });
87
+
88
+ call("toast.show", { message: "Profile updated!" });
89
+ ```
90
+
91
+ ------------------------------------------------------------------------
92
+
93
+ ### 🪟 Modal Control
94
+
95
+ ``` tsx
96
+ useRegister("modal.open", () => setOpen(true));
97
+ useRegister("modal.close", () => setOpen(false));
98
+
99
+ call("modal.open");
100
+ ```
101
+
102
+ ------------------------------------------------------------------------
103
+
104
+ ### ⏳ Global Loader
105
+
106
+ ``` tsx
107
+ useRegister("loader.show", () => setLoading(true));
108
+ useRegister("loader.hide", () => setLoading(false));
109
+
110
+ call("loader.show");
111
+ call("loader.hide");
112
+ ```
113
+
114
+ ------------------------------------------------------------------------
115
+
116
+ ## 🧠 API
117
+
118
+ ### `useBridge()`
119
+
120
+ Returns bridge actions.
121
+
122
+ ``` ts
123
+ const { call } = useBridge();
124
+ ```
125
+
126
+ #### `call(eventName, payload?)`
127
+
128
+ Triggers all listeners registered for the event.
129
+
130
+ ------------------------------------------------------------------------
131
+
132
+ ### `useRegister(eventName, handler)`
133
+
134
+ Registers a listener.
135
+
136
+ ``` ts
137
+ useRegister("event.name", (payload) => {
138
+ ...
139
+ });
140
+ ```
141
+
142
+ ✔ Automatically cleaned up on unmount\
143
+ ✔ Safe in React Strict Mode
144
+
145
+ ------------------------------------------------------------------------
146
+
147
+ ### `call(eventName, payload?)`
148
+
149
+ Non-hook version (can be used outside React).
150
+
151
+ ``` ts
152
+ import { call } from "crosshook";
153
+
154
+ call("toast.show", { message: "Hello!" });
155
+ ```
156
+
157
+ ------------------------------------------------------------------------
158
+
159
+ ### `register(eventName, handler)`
160
+
161
+ Manual registration.
162
+
163
+ ``` ts
164
+ import { register } from "crosshook";
165
+
166
+ const unregister = register("event", handler);
167
+
168
+ // later
169
+ unregister();
170
+ ```
171
+
172
+ ------------------------------------------------------------------------
173
+
174
+ ## ⚠️ Best Practices
175
+
176
+ ### ✅ Good For
177
+
178
+ ✔ UI interactions\
179
+ ✔ Modals\
180
+ ✔ Toasts\
181
+ ✔ Notifications\
182
+ ✔ Cross-tree communication
183
+
184
+ ------------------------------------------------------------------------
185
+
186
+ ### ❌ Avoid Using For
187
+
188
+ ✖ Business logic orchestration\
189
+ ✖ App state management\
190
+ ✖ Server/cache state\
191
+ ✖ Data persistence
192
+
193
+ Use React state / Zustand / Redux instead.
194
+
195
+ ------------------------------------------------------------------------
196
+
197
+ ## 🔷 TypeScript Usage (Optional)
198
+
199
+ You can strongly type events:
200
+
201
+ ``` ts
202
+ type Events = {
203
+ "toast.show": { message: string };
204
+ "modal.open": void;
205
+ };
206
+ ```
207
+
208
+ (Advanced typed helpers coming soon)
209
+
210
+ ------------------------------------------------------------------------
211
+
212
+ ## 🧹 Cleanup Behavior
213
+
214
+ Listeners automatically unsubscribe when components unmount.
215
+
216
+ No memory leaks. No manual cleanup required.
217
+
218
+ ------------------------------------------------------------------------
219
+
220
+ ## 🛠 Example Pattern
221
+
222
+ ``` tsx
223
+ // Listener
224
+ useRegister("user.select", (user) => {
225
+ setUser(user);
226
+ });
227
+
228
+ // Caller
229
+ call("user.select", { id: 1, name: "Manu" });
230
+ ```
231
+
232
+ ------------------------------------------------------------------------
233
+
234
+ ## 📏 Bundle Size
235
+
236
+ Designed to be extremely lightweight.
237
+
238
+ ------------------------------------------------------------------------
239
+
240
+ ## 📜 License
241
+
242
+ MIT
243
+
244
+ ------------------------------------------------------------------------
245
+
246
+ ## ❤️ Why crosshook?
247
+
248
+ Because not every interaction needs:
249
+
250
+ ❌ Prop drilling\
251
+ ❌ Deep context trees\
252
+ ❌ Heavy state libraries
253
+
254
+ Sometimes you just need a tiny event bridge.
255
+
@@ -0,0 +1,10 @@
1
+ type Handler<T = any> = (payload: T) => void;
2
+ declare function register<T = any>(event: string, handler: Handler<T>): () => void;
3
+ declare function call<T = any>(event: string, payload?: T): void;
4
+
5
+ declare function useRegister<T = any>(event: string, handler: Handler<T>): void;
6
+ declare function useBridge(): {
7
+ call: typeof call;
8
+ };
9
+
10
+ export { type Handler, call, register, useBridge, useRegister };
@@ -0,0 +1,10 @@
1
+ type Handler<T = any> = (payload: T) => void;
2
+ declare function register<T = any>(event: string, handler: Handler<T>): () => void;
3
+ declare function call<T = any>(event: string, payload?: T): void;
4
+
5
+ declare function useRegister<T = any>(event: string, handler: Handler<T>): void;
6
+ declare function useBridge(): {
7
+ call: typeof call;
8
+ };
9
+
10
+ export { type Handler, call, register, useBridge, useRegister };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var l=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var x=(r,e)=>{for(var t in e)l(r,t,{get:e[t],enumerable:!0})},y=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of p(e))!u.call(r,o)&&o!==t&&l(r,o,{get:()=>e[o],enumerable:!(s=f(e,o))||s.enumerable});return r};var H=r=>y(l({},"__esModule",{value:!0}),r);var T={};x(T,{call:()=>a,register:()=>i,useBridge:()=>c,useRegister:()=>g});module.exports=H(T);var d=require("react");var n=new Map;function i(r,e){return n.has(r)||n.set(r,new Set),n.get(r).add(e),()=>{n.get(r)?.delete(e),n.get(r)?.size===0&&n.delete(r)}}function a(r,e){let t=n.get(r);if(!t){process.env.NODE_ENV!=="production"&&console.warn(`[Bridge] No listeners for event "${r}"`);return}t.forEach(s=>{try{s(e)}catch(o){console.error(`[Bridge] Error in handler for "${r}"`,o)}})}function g(r,e){(0,d.useEffect)(()=>i(r,e),[r,e])}function c(){return{call:a}}0&&(module.exports={call,register,useBridge,useRegister});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/hooks.ts","../src/bridge.ts"],"sourcesContent":["export { useBridge, useRegister } from \"./hooks\";\nexport { call, register } from \"./bridge\";\nexport type { Handler } from \"./bridge\";","import { useEffect } from \"react\";\nimport { register, call, Handler } from \"./bridge\";\n\nexport function useRegister<T = any>(\n event: string,\n handler: Handler<T>\n) {\n useEffect(() => {\n const unregister = register(event, handler);\n return unregister;\n }, [event, handler]);\n}\n\nexport function useBridge() {\n return { call };\n}","export type Handler<T = any> = (payload: T) => void;\n\nconst listeners = new Map<string, Set<Handler>>();\n\nexport function register<T = any>(\n event: string,\n handler: Handler<T>\n) {\n if (!listeners.has(event)) {\n listeners.set(event, new Set());\n }\n\n listeners.get(event)!.add(handler as Handler);\n\n return () => {\n listeners.get(event)?.delete(handler as Handler);\n\n if (listeners.get(event)?.size === 0) {\n listeners.delete(event);\n }\n };\n}\n\nexport function call<T = any>(event: string, payload?: T) {\n const handlers = listeners.get(event);\n\n if (!handlers) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[Bridge] No listeners for event \"${event}\"`\n );\n }\n return;\n }\n\n handlers.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(\n `[Bridge] Error in handler for \"${event}\"`,\n err\n );\n }\n });\n}"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,UAAAE,EAAA,aAAAC,EAAA,cAAAC,EAAA,gBAAAC,IAAA,eAAAC,EAAAN,GCAA,IAAAO,EAA0B,iBCE1B,IAAMC,EAAY,IAAI,IAEf,SAASC,EACZC,EACAC,EACF,CACE,OAAKH,EAAU,IAAIE,CAAK,GACpBF,EAAU,IAAIE,EAAO,IAAI,GAAK,EAGlCF,EAAU,IAAIE,CAAK,EAAG,IAAIC,CAAkB,EAErC,IAAM,CACTH,EAAU,IAAIE,CAAK,GAAG,OAAOC,CAAkB,EAE3CH,EAAU,IAAIE,CAAK,GAAG,OAAS,GAC/BF,EAAU,OAAOE,CAAK,CAE9B,CACJ,CAEO,SAASE,EAAcF,EAAeG,EAAa,CACtD,IAAMC,EAAWN,EAAU,IAAIE,CAAK,EAEpC,GAAI,CAACI,EAAU,CACP,QAAQ,IAAI,WAAa,cACzB,QAAQ,KACJ,oCAAoCJ,CAAK,GAC7C,EAEJ,MACJ,CAEAI,EAAS,QAASH,GAAY,CAC1B,GAAI,CACAA,EAAQE,CAAO,CACnB,OAASE,EAAK,CACV,QAAQ,MACJ,kCAAkCL,CAAK,IACvCK,CACJ,CACJ,CACJ,CAAC,CACL,CD1CO,SAASC,EACZC,EACAC,EACF,IACE,aAAU,IACaC,EAASF,EAAOC,CAAO,EAE3C,CAACD,EAAOC,CAAO,CAAC,CACvB,CAEO,SAASE,GAAY,CACxB,MAAO,CAAE,KAAAC,CAAK,CAClB","names":["index_exports","__export","call","register","useBridge","useRegister","__toCommonJS","import_react","listeners","register","event","handler","call","payload","handlers","err","useRegister","event","handler","register","useBridge","call"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{useEffect as l}from"react";var t=new Map;function n(r,e){return t.has(r)||t.set(r,new Set),t.get(r).add(e),()=>{t.get(r)?.delete(e),t.get(r)?.size===0&&t.delete(r)}}function s(r,e){let o=t.get(r);if(!o){process.env.NODE_ENV!=="production"&&console.warn(`[Bridge] No listeners for event "${r}"`);return}o.forEach(i=>{try{i(e)}catch(a){console.error(`[Bridge] Error in handler for "${r}"`,a)}})}function d(r,e){l(()=>n(r,e),[r,e])}function g(){return{call:s}}export{s as call,n as register,g as useBridge,d as useRegister};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks.ts","../src/bridge.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { register, call, Handler } from \"./bridge\";\n\nexport function useRegister<T = any>(\n event: string,\n handler: Handler<T>\n) {\n useEffect(() => {\n const unregister = register(event, handler);\n return unregister;\n }, [event, handler]);\n}\n\nexport function useBridge() {\n return { call };\n}","export type Handler<T = any> = (payload: T) => void;\n\nconst listeners = new Map<string, Set<Handler>>();\n\nexport function register<T = any>(\n event: string,\n handler: Handler<T>\n) {\n if (!listeners.has(event)) {\n listeners.set(event, new Set());\n }\n\n listeners.get(event)!.add(handler as Handler);\n\n return () => {\n listeners.get(event)?.delete(handler as Handler);\n\n if (listeners.get(event)?.size === 0) {\n listeners.delete(event);\n }\n };\n}\n\nexport function call<T = any>(event: string, payload?: T) {\n const handlers = listeners.get(event);\n\n if (!handlers) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[Bridge] No listeners for event \"${event}\"`\n );\n }\n return;\n }\n\n handlers.forEach((handler) => {\n try {\n handler(payload);\n } catch (err) {\n console.error(\n `[Bridge] Error in handler for \"${event}\"`,\n err\n );\n }\n });\n}"],"mappings":"AAAA,OAAS,aAAAA,MAAiB,QCE1B,IAAMC,EAAY,IAAI,IAEf,SAASC,EACZC,EACAC,EACF,CACE,OAAKH,EAAU,IAAIE,CAAK,GACpBF,EAAU,IAAIE,EAAO,IAAI,GAAK,EAGlCF,EAAU,IAAIE,CAAK,EAAG,IAAIC,CAAkB,EAErC,IAAM,CACTH,EAAU,IAAIE,CAAK,GAAG,OAAOC,CAAkB,EAE3CH,EAAU,IAAIE,CAAK,GAAG,OAAS,GAC/BF,EAAU,OAAOE,CAAK,CAE9B,CACJ,CAEO,SAASE,EAAcF,EAAeG,EAAa,CACtD,IAAMC,EAAWN,EAAU,IAAIE,CAAK,EAEpC,GAAI,CAACI,EAAU,CACP,QAAQ,IAAI,WAAa,cACzB,QAAQ,KACJ,oCAAoCJ,CAAK,GAC7C,EAEJ,MACJ,CAEAI,EAAS,QAASH,GAAY,CAC1B,GAAI,CACAA,EAAQE,CAAO,CACnB,OAASE,EAAK,CACV,QAAQ,MACJ,kCAAkCL,CAAK,IACvCK,CACJ,CACJ,CACJ,CAAC,CACL,CD1CO,SAASC,EACZC,EACAC,EACF,CACEC,EAAU,IACaC,EAASH,EAAOC,CAAO,EAE3C,CAACD,EAAOC,CAAO,CAAC,CACvB,CAEO,SAASG,GAAY,CACxB,MAAO,CAAE,KAAAC,CAAK,CAClB","names":["useEffect","listeners","register","event","handler","call","payload","handlers","err","useRegister","event","handler","useEffect","register","useBridge","call"]}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "crosshook",
3
+ "version": "1.0.0",
4
+ "description": "Tiny hook-based event bridge for React",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsup",
13
+ "dev": "tsup --watch"
14
+ },
15
+ "peerDependencies": {
16
+ "react": ">=17"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^25.3.0",
20
+ "@types/react": "^19.2.14",
21
+ "tsup": "^8.0.0",
22
+ "typescript": "^5.0.0"
23
+ }
24
+ }