@felixfern/tunnel-fn 2.0.0 → 2.0.2

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/dist/index.cjs CHANGED
@@ -41,7 +41,9 @@ function createTunnel() {
41
41
  const TunnelContext = (0, import_react.createContext)(null);
42
42
  function TunnelProvider({ children }) {
43
43
  const storeRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
44
- const listenersRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
44
+ const listenersRef = (0, import_react.useRef)(
45
+ /* @__PURE__ */ new Map()
46
+ );
45
47
  const isRenderPhaseRef = (0, import_react.useRef)(false);
46
48
  const registrationCountRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
47
49
  const churnWarnedRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
@@ -57,7 +59,7 @@ function createTunnel() {
57
59
  if (process.env.NODE_ENV !== "production") {
58
60
  if (storeRef.current.has(key)) {
59
61
  console.warn(
60
- `[tunnel-fn] Duplicate registration for key "${String(key)}". Another component already registered this key. Only the last registration will be active \u2014 this is likely a bug.`
62
+ `[@felixfern/tunnel-fn] Duplicate registration for key "${String(key)}". Another component already registered this key. Only the last registration will be active \u2014 this is likely a bug.`
61
63
  );
62
64
  }
63
65
  const now = Date.now();
@@ -68,7 +70,7 @@ function createTunnel() {
68
70
  if (recent.length > 3 && !churnWarnedRef.current.has(key)) {
69
71
  churnWarnedRef.current.add(key);
70
72
  console.warn(
71
- `[tunnel-fn] Function for key "${String(key)}" is being re-registered rapidly (${recent.length} times in <1s). This usually means the function passed to useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:
73
+ `[@felixfern/tunnel-fn] Function for key "${String(key)}" is being re-registered rapidly (${recent.length} times in <1s). This usually means the function passed to useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:
72
74
 
73
75
  const handler = useCallback((...) => { ... }, [deps]);
74
76
  useTunnelFunction("${String(key)}", handler);`
@@ -89,7 +91,7 @@ function createTunnel() {
89
91
  if (process.env.NODE_ENV !== "production") {
90
92
  if (isRenderPhaseRef.current) {
91
93
  console.warn(
92
- `[tunnel-fn] call("${String(key)}") was invoked during the render phase. Tunnel functions should only be called from event handlers, useEffect, or callbacks \u2014 never directly during render. This can cause unpredictable behavior.`
94
+ `[@felixfern/tunnel-fn] call("${String(key)}") was invoked during the render phase. Tunnel functions should only be called from event handlers, useEffect, or callbacks \u2014 never directly during render. This can cause unpredictable behavior.`
93
95
  );
94
96
  }
95
97
  }
@@ -97,7 +99,7 @@ function createTunnel() {
97
99
  if (!fn) {
98
100
  if (process.env.NODE_ENV !== "production") {
99
101
  console.warn(
100
- `[tunnel-fn] No function registered for key "${String(key)}". Make sure a component with useTunnelFunction("${String(key)}", ...) is mounted.`
102
+ `[@felixfern/tunnel-fn] No function registered for key "${String(key)}". Make sure a component with useTunnelFunction("${String(key)}", ...) is mounted.`
101
103
  );
102
104
  }
103
105
  return void 0;
@@ -155,7 +157,7 @@ function createTunnel() {
155
157
  const store = (0, import_react.useContext)(TunnelContext);
156
158
  if (!store) {
157
159
  throw new Error(
158
- "[tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. Wrap your component tree with the TunnelProvider from createTunnel()."
160
+ "[@felixfern/tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. Wrap your component tree with the TunnelProvider from createTunnel()."
159
161
  );
160
162
  }
161
163
  return store;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/tunnel.tsx"],"sourcesContent":["export { createTunnel } from \"./tunnel\";\n","import * as React from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\ttype ReactNode,\n} from \"react\";\n\ntype TunnelFunctionMap = Record<string, (...args: never[]) => unknown>;\n\ntype Awaited<T> = T extends Promise<infer U> ? U : T;\n\ntype TunnelStore<T extends TunnelFunctionMap> = {\n\tregister<K extends keyof T>(key: K, fn: T[K]): void;\n\tunregister<K extends keyof T>(key: K): void;\n\tcall<K extends keyof T>(\n\t\tkey: K,\n\t\t...args: Parameters<T[K]>\n\t): ReturnType<T[K]> | undefined;\n\tcallAsync<K extends keyof T>(\n\t\tkey: K,\n\t\t...args: Parameters<T[K]>\n\t): Promise<Awaited<ReturnType<T[K]>> | undefined>;\n\thas<K extends keyof T>(key: K): boolean;\n\tonReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void): () => void;\n};\n\n/**\n * Creates a type-safe tunnel for passing functions across components via context.\n *\n * @example\n * ```tsx\n * const { TunnelProvider, useTunnel, useTunnelFunction } = createTunnel<{\n * onSave: (data: string) => void;\n * getCount: () => number;\n * }>();\n * ```\n */\nexport function createTunnel<T extends TunnelFunctionMap>() {\n\tconst TunnelContext = createContext<TunnelStore<T> | null>(null);\n\n\tfunction TunnelProvider({ children }: { children: ReactNode }) {\n\t\tconst storeRef = useRef<Map<keyof T, T[keyof T]>>(new Map());\n\t\tconst listenersRef = useRef<Map<keyof T, Set<(fn: T[keyof T]) => void>>>(new Map());\n\n\t\tconst isRenderPhaseRef = useRef(false);\n\t\tconst registrationCountRef = useRef<Map<keyof T, number[]>>(new Map());\n\t\tconst churnWarnedRef = useRef<Set<keyof T>>(new Set());\n\n\t\t// Track render phase: set true synchronously during render, false in effect\n\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\tisRenderPhaseRef.current = true;\n\t\t}\n\n\t\tuseLayoutEffect(() => {\n\t\t\tisRenderPhaseRef.current = false;\n\t\t});\n\n\t\tconst store = useRef<TunnelStore<T>>({\n\t\t\tregister<K extends keyof T>(key: K, fn: T[K]) {\n\t\t\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\t\t\t// Warn: duplicate key registration\n\t\t\t\t\tif (storeRef.current.has(key)) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] Duplicate registration for key \"${String(key)}\". ` +\n\t\t\t\t\t\t\t\t`Another component already registered this key. ` +\n\t\t\t\t\t\t\t\t`Only the last registration will be active — this is likely a bug.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Track: registration churn (unstable fn reference)\n\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\tconst timestamps = registrationCountRef.current.get(key) ?? [];\n\t\t\t\t\ttimestamps.push(now);\n\t\t\t\t\t// Keep only timestamps from the last second\n\t\t\t\t\tconst recent = timestamps.filter((t) => now - t < 1000);\n\t\t\t\t\tregistrationCountRef.current.set(key, recent);\n\n\t\t\t\t\tif (recent.length > 3 && !churnWarnedRef.current.has(key)) {\n\t\t\t\t\t\tchurnWarnedRef.current.add(key);\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] Function for key \"${String(key)}\" is being re-registered rapidly ` +\n\t\t\t\t\t\t\t\t`(${recent.length} times in <1s). This usually means the function passed to ` +\n\t\t\t\t\t\t\t\t`useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:\\n\\n` +\n\t\t\t\t\t\t\t\t` const handler = useCallback((...) => { ... }, [deps]);\\n` +\n\t\t\t\t\t\t\t\t` useTunnelFunction(\"${String(key)}\", handler);`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tstoreRef.current.set(key, fn);\n\n\t\t\t\tconst listeners = listenersRef.current.get(key);\n\t\t\t\tif (listeners) {\n\t\t\t\t\tlisteners.forEach((cb) => cb(fn as T[keyof T]));\n\t\t\t\t\tlistenersRef.current.delete(key);\n\t\t\t\t}\n\t\t\t},\n\t\t\tunregister<K extends keyof T>(key: K) {\n\t\t\t\tstoreRef.current.delete(key);\n\t\t\t},\n\t\t\tcall<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n\t\t\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\t\t\t// Warn: call() during render phase\n\t\t\t\t\tif (isRenderPhaseRef.current) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] call(\"${String(key)}\") was invoked during the render phase. ` +\n\t\t\t\t\t\t\t\t`Tunnel functions should only be called from event handlers, useEffect, or callbacks — ` +\n\t\t\t\t\t\t\t\t`never directly during render. This can cause unpredictable behavior.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst fn = storeRef.current.get(key);\n\t\t\t\tif (!fn) {\n\t\t\t\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] No function registered for key \"${String(key)}\". ` +\n\t\t\t\t\t\t\t\t`Make sure a component with useTunnelFunction(\"${String(key)}\", ...) is mounted.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn undefined as ReturnType<T[K]> | undefined;\n\t\t\t\t}\n\t\t\t\treturn (fn as T[K])(...args) as ReturnType<T[K]>;\n\t\t\t},\n\t\t\thas<K extends keyof T>(key: K) {\n\t\t\t\treturn storeRef.current.has(key);\n\t\t\t},\n\t\t\tcallAsync<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n\t\t\t\tconst fn = storeRef.current.get(key);\n\t\t\t\tif (fn) {\n\t\t\t\t\treturn Promise.resolve((fn as T[K])(...args)).then(\n\t\t\t\t\t\t(v) => v as Awaited<ReturnType<T[K]>>\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn new Promise<Awaited<ReturnType<T[K]>> | undefined>((resolve) => {\n\t\t\t\t\tconst listener = (registeredFn: T[keyof T]) => {\n\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\tPromise.resolve((registeredFn as T[K])(...args)).then(\n\t\t\t\t\t\t\t\t(v) => v as Awaited<ReturnType<T[K]>>\n\t\t\t\t\t\t\t) as unknown as Awaited<ReturnType<T[K]>>\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\t\t\t\t\tlet set = listenersRef.current.get(key);\n\t\t\t\t\tif (!set) {\n\t\t\t\t\t\tset = new Set();\n\t\t\t\t\t\tlistenersRef.current.set(key, set);\n\t\t\t\t\t}\n\t\t\t\t\tset.add(listener);\n\t\t\t\t});\n\t\t\t},\n\t\t\tonReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void) {\n\t\t\t\tconst existing = storeRef.current.get(key);\n\t\t\t\tif (existing) {\n\t\t\t\t\tcallback(existing as T[K]);\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tconst listener = (fn: T[keyof T]) => callback(fn as T[K]);\n\t\t\t\tlet set = listenersRef.current.get(key);\n\t\t\t\tif (!set) {\n\t\t\t\t\tset = new Set();\n\t\t\t\t\tlistenersRef.current.set(key, set);\n\t\t\t\t}\n\t\t\t\tset.add(listener);\n\t\t\t\treturn () => {\n\t\t\t\t\tset!.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t}).current;\n\n\t\treturn (\n\t\t\t<TunnelContext.Provider value={store}>\n\t\t\t\t{children}\n\t\t\t</TunnelContext.Provider>\n\t\t);\n\t}\n\n\tfunction useTunnelStore(): TunnelStore<T> {\n\t\tconst store = useContext(TunnelContext);\n\t\tif (!store) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. \" +\n\t\t\t\t\t\"Wrap your component tree with the TunnelProvider from createTunnel().\"\n\t\t\t);\n\t\t}\n\t\treturn store;\n\t}\n\n\tfunction useTunnel() {\n\t\tconst store = useTunnelStore();\n\n\t\tconst call = useCallback(\n\t\t\t<K extends keyof T>(key: K, ...args: Parameters<T[K]>): ReturnType<T[K]> | undefined => {\n\t\t\t\treturn store.call(key, ...args);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\tconst callAsync = useCallback(\n\t\t\t<K extends keyof T>(key: K, ...args: Parameters<T[K]>): Promise<Awaited<ReturnType<T[K]>> | undefined> => {\n\t\t\t\treturn store.callAsync(key, ...args);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\tconst has = useCallback(\n\t\t\t<K extends keyof T>(key: K): boolean => {\n\t\t\t\treturn store.has(key);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\tconst onReady = useCallback(\n\t\t\t<K extends keyof T>(key: K, callback: (fn: T[K]) => void): (() => void) => {\n\t\t\t\treturn store.onReady(key, callback);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\treturn { call, callAsync, has, onReady } as const;\n\t}\n\n\tfunction useTunnelFunction<K extends keyof T>(key: K, fn: T[K]): T[K] {\n\t\tconst store = useTunnelStore();\n\n\t\tuseEffect(() => {\n\t\t\tstore.register(key, fn);\n\t\t\treturn () => {\n\t\t\t\tstore.unregister(key);\n\t\t\t};\n\t\t}, [store, key, fn]);\n\n\t\treturn fn;\n\t}\n\n\treturn { TunnelProvider, useTunnel, useTunnelFunction } as const;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,mBAQO;AAgCA,SAAS,eAA4C;AAC3D,QAAM,oBAAgB,4BAAqC,IAAI;AAE/D,WAAS,eAAe,EAAE,SAAS,GAA4B;AAC9D,UAAM,eAAW,qBAAiC,oBAAI,IAAI,CAAC;AAC3D,UAAM,mBAAe,qBAAoD,oBAAI,IAAI,CAAC;AAElF,UAAM,uBAAmB,qBAAO,KAAK;AACrC,UAAM,2BAAuB,qBAA+B,oBAAI,IAAI,CAAC;AACrE,UAAM,qBAAiB,qBAAqB,oBAAI,IAAI,CAAC;AAGrD,QAAI,QAAQ,IAAI,aAAa,cAAc;AAC1C,uBAAiB,UAAU;AAAA,IAC5B;AAEA,sCAAgB,MAAM;AACrB,uBAAiB,UAAU;AAAA,IAC5B,CAAC;AAED,UAAM,YAAQ,qBAAuB;AAAA,MACpC,SAA4B,KAAQ,IAAU;AA9DjD;AA+DI,YAAI,QAAQ,IAAI,aAAa,cAAc;AAE1C,cAAI,SAAS,QAAQ,IAAI,GAAG,GAAG;AAC9B,oBAAQ;AAAA,cACP,+CAA+C,OAAO,GAAG,CAAC;AAAA,YAG3D;AAAA,UACD;AAGA,gBAAM,MAAM,KAAK,IAAI;AACrB,gBAAM,cAAa,0BAAqB,QAAQ,IAAI,GAAG,MAApC,YAAyC,CAAC;AAC7D,qBAAW,KAAK,GAAG;AAEnB,gBAAM,SAAS,WAAW,OAAO,CAAC,MAAM,MAAM,IAAI,GAAI;AACtD,+BAAqB,QAAQ,IAAI,KAAK,MAAM;AAE5C,cAAI,OAAO,SAAS,KAAK,CAAC,eAAe,QAAQ,IAAI,GAAG,GAAG;AAC1D,2BAAe,QAAQ,IAAI,GAAG;AAC9B,oBAAQ;AAAA,cACP,iCAAiC,OAAO,GAAG,CAAC,qCACvC,OAAO,MAAM;AAAA;AAAA;AAAA,uBAGO,OAAO,GAAG,CAAC;AAAA,YACrC;AAAA,UACD;AAAA,QACD;AAEA,iBAAS,QAAQ,IAAI,KAAK,EAAE;AAE5B,cAAM,YAAY,aAAa,QAAQ,IAAI,GAAG;AAC9C,YAAI,WAAW;AACd,oBAAU,QAAQ,CAAC,OAAO,GAAG,EAAgB,CAAC;AAC9C,uBAAa,QAAQ,OAAO,GAAG;AAAA,QAChC;AAAA,MACD;AAAA,MACA,WAA8B,KAAQ;AACrC,iBAAS,QAAQ,OAAO,GAAG;AAAA,MAC5B;AAAA,MACA,KAAwB,QAAW,MAAwB;AAC1D,YAAI,QAAQ,IAAI,aAAa,cAAc;AAE1C,cAAI,iBAAiB,SAAS;AAC7B,oBAAQ;AAAA,cACP,qBAAqB,OAAO,GAAG,CAAC;AAAA,YAGjC;AAAA,UACD;AAAA,QACD;AAEA,cAAM,KAAK,SAAS,QAAQ,IAAI,GAAG;AACnC,YAAI,CAAC,IAAI;AACR,cAAI,QAAQ,IAAI,aAAa,cAAc;AAC1C,oBAAQ;AAAA,cACP,+CAA+C,OAAO,GAAG,CAAC,oDACR,OAAO,GAAG,CAAC;AAAA,YAC9D;AAAA,UACD;AACA,iBAAO;AAAA,QACR;AACA,eAAQ,GAAY,GAAG,IAAI;AAAA,MAC5B;AAAA,MACA,IAAuB,KAAQ;AAC9B,eAAO,SAAS,QAAQ,IAAI,GAAG;AAAA,MAChC;AAAA,MACA,UAA6B,QAAW,MAAwB;AAC/D,cAAM,KAAK,SAAS,QAAQ,IAAI,GAAG;AACnC,YAAI,IAAI;AACP,iBAAO,QAAQ,QAAS,GAAY,GAAG,IAAI,CAAC,EAAE;AAAA,YAC7C,CAAC,MAAM;AAAA,UACR;AAAA,QACD;AACA,eAAO,IAAI,QAA+C,CAAC,YAAY;AACtE,gBAAM,WAAW,CAAC,iBAA6B;AAC9C;AAAA,cACC,QAAQ,QAAS,aAAsB,GAAG,IAAI,CAAC,EAAE;AAAA,gBAChD,CAAC,MAAM;AAAA,cACR;AAAA,YACD;AAAA,UACD;AACA,cAAI,MAAM,aAAa,QAAQ,IAAI,GAAG;AACtC,cAAI,CAAC,KAAK;AACT,kBAAM,oBAAI,IAAI;AACd,yBAAa,QAAQ,IAAI,KAAK,GAAG;AAAA,UAClC;AACA,cAAI,IAAI,QAAQ;AAAA,QACjB,CAAC;AAAA,MACF;AAAA,MACA,QAA2B,KAAQ,UAA8B;AAChE,cAAM,WAAW,SAAS,QAAQ,IAAI,GAAG;AACzC,YAAI,UAAU;AACb,mBAAS,QAAgB;AACzB,iBAAO,MAAM;AAAA,UAAC;AAAA,QACf;AACA,cAAM,WAAW,CAAC,OAAmB,SAAS,EAAU;AACxD,YAAI,MAAM,aAAa,QAAQ,IAAI,GAAG;AACtC,YAAI,CAAC,KAAK;AACT,gBAAM,oBAAI,IAAI;AACd,uBAAa,QAAQ,IAAI,KAAK,GAAG;AAAA,QAClC;AACA,YAAI,IAAI,QAAQ;AAChB,eAAO,MAAM;AACZ,cAAK,OAAO,QAAQ;AAAA,QACrB;AAAA,MACD;AAAA,IACD,CAAC,EAAE;AAEH,WACC,oCAAC,cAAc,UAAd,EAAuB,OAAO,SAC7B,QACF;AAAA,EAEF;AAEA,WAAS,iBAAiC;AACzC,UAAM,YAAQ,yBAAW,aAAa;AACtC,QAAI,CAAC,OAAO;AACX,YAAM,IAAI;AAAA,QACT;AAAA,MAED;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAEA,WAAS,YAAY;AACpB,UAAM,QAAQ,eAAe;AAE7B,UAAM,WAAO;AAAA,MACZ,CAAoB,QAAW,SAAyD;AACvF,eAAO,MAAM,KAAK,KAAK,GAAG,IAAI;AAAA,MAC/B;AAAA,MACA,CAAC,KAAK;AAAA,IACP;AAEA,UAAM,gBAAY;AAAA,MACjB,CAAoB,QAAW,SAA2E;AACzG,eAAO,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,MACpC;AAAA,MACA,CAAC,KAAK;AAAA,IACP;AAEA,UAAM,UAAM;AAAA,MACX,CAAoB,QAAoB;AACvC,eAAO,MAAM,IAAI,GAAG;AAAA,MACrB;AAAA,MACA,CAAC,KAAK;AAAA,IACP;AAEA,UAAM,cAAU;AAAA,MACf,CAAoB,KAAQ,aAA+C;AAC1E,eAAO,MAAM,QAAQ,KAAK,QAAQ;AAAA,MACnC;AAAA,MACA,CAAC,KAAK;AAAA,IACP;AAEA,WAAO,EAAE,MAAM,WAAW,KAAK,QAAQ;AAAA,EACxC;AAEA,WAAS,kBAAqC,KAAQ,IAAgB;AACrE,UAAM,QAAQ,eAAe;AAE7B,gCAAU,MAAM;AACf,YAAM,SAAS,KAAK,EAAE;AACtB,aAAO,MAAM;AACZ,cAAM,WAAW,GAAG;AAAA,MACrB;AAAA,IACD,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC;AAEnB,WAAO;AAAA,EACR;AAEA,SAAO,EAAE,gBAAgB,WAAW,kBAAkB;AACvD;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/tunnel.tsx"],"sourcesContent":["export { createTunnel } from \"./tunnel\";\n","import * as React from \"react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n type ReactNode,\n} from \"react\";\n\ntype TunnelFunctionMap = Record<string, (...args: never[]) => unknown>;\n\ntype Awaited<T> = T extends Promise<infer U> ? U : T;\n\ntype TunnelStore<T extends TunnelFunctionMap> = {\n register<K extends keyof T>(key: K, fn: T[K]): void;\n unregister<K extends keyof T>(key: K): void;\n call<K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): ReturnType<T[K]> | undefined;\n callAsync<K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): Promise<Awaited<ReturnType<T[K]>> | undefined>;\n has<K extends keyof T>(key: K): boolean;\n onReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void): () => void;\n};\n\n/**\n * Creates a type-safe tunnel for passing functions across components via context.\n *\n * @example\n * ```tsx\n * const { TunnelProvider, useTunnel, useTunnelFunction } = createTunnel<{\n * onSave: (data: string) => void;\n * getCount: () => number;\n * }>();\n * ```\n */\nexport function createTunnel<T extends TunnelFunctionMap>() {\n const TunnelContext = createContext<TunnelStore<T> | null>(null);\n\n function TunnelProvider({ children }: { children: ReactNode }) {\n const storeRef = useRef<Map<keyof T, T[keyof T]>>(new Map());\n const listenersRef = useRef<Map<keyof T, Set<(fn: T[keyof T]) => void>>>(\n new Map(),\n );\n\n const isRenderPhaseRef = useRef(false);\n const registrationCountRef = useRef<Map<keyof T, number[]>>(new Map());\n const churnWarnedRef = useRef<Set<keyof T>>(new Set());\n\n if (process.env.NODE_ENV !== \"production\") {\n isRenderPhaseRef.current = true;\n }\n\n useLayoutEffect(() => {\n isRenderPhaseRef.current = false;\n });\n\n const store = useRef<TunnelStore<T>>({\n register<K extends keyof T>(key: K, fn: T[K]) {\n if (process.env.NODE_ENV !== \"production\") {\n if (storeRef.current.has(key)) {\n console.warn(\n `[@felixfern/tunnel-fn] Duplicate registration for key \"${String(key)}\". ` +\n `Another component already registered this key. ` +\n `Only the last registration will be active — this is likely a bug.`,\n );\n }\n\n const now = Date.now();\n const timestamps = registrationCountRef.current.get(key) ?? [];\n timestamps.push(now);\n\n const recent = timestamps.filter((t) => now - t < 1000);\n registrationCountRef.current.set(key, recent);\n\n if (recent.length > 3 && !churnWarnedRef.current.has(key)) {\n churnWarnedRef.current.add(key);\n console.warn(\n `[@felixfern/tunnel-fn] Function for key \"${String(key)}\" is being re-registered rapidly ` +\n `(${recent.length} times in <1s). This usually means the function passed to ` +\n `useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:\\n\\n` +\n ` const handler = useCallback((...) => { ... }, [deps]);\\n` +\n ` useTunnelFunction(\"${String(key)}\", handler);`,\n );\n }\n }\n\n storeRef.current.set(key, fn);\n\n const listeners = listenersRef.current.get(key);\n if (listeners) {\n listeners.forEach((cb) => cb(fn as T[keyof T]));\n listenersRef.current.delete(key);\n }\n },\n unregister<K extends keyof T>(key: K) {\n storeRef.current.delete(key);\n },\n call<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n if (process.env.NODE_ENV !== \"production\") {\n if (isRenderPhaseRef.current) {\n console.warn(\n `[@felixfern/tunnel-fn] call(\"${String(key)}\") was invoked during the render phase. ` +\n `Tunnel functions should only be called from event handlers, useEffect, or callbacks — ` +\n `never directly during render. This can cause unpredictable behavior.`,\n );\n }\n }\n\n const fn = storeRef.current.get(key);\n if (!fn) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[@felixfern/tunnel-fn] No function registered for key \"${String(key)}\". ` +\n `Make sure a component with useTunnelFunction(\"${String(key)}\", ...) is mounted.`,\n );\n }\n return undefined as ReturnType<T[K]> | undefined;\n }\n return (fn as T[K])(...args) as ReturnType<T[K]>;\n },\n has<K extends keyof T>(key: K) {\n return storeRef.current.has(key);\n },\n callAsync<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n const fn = storeRef.current.get(key);\n if (fn) {\n return Promise.resolve((fn as T[K])(...args)).then(\n (v) => v as Awaited<ReturnType<T[K]>>,\n );\n }\n return new Promise<Awaited<ReturnType<T[K]>> | undefined>((resolve) => {\n const listener = (registeredFn: T[keyof T]) => {\n resolve(\n Promise.resolve((registeredFn as T[K])(...args)).then(\n (v) => v as Awaited<ReturnType<T[K]>>,\n ) as unknown as Awaited<ReturnType<T[K]>>,\n );\n };\n let set = listenersRef.current.get(key);\n if (!set) {\n set = new Set();\n listenersRef.current.set(key, set);\n }\n set.add(listener);\n });\n },\n onReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void) {\n const existing = storeRef.current.get(key);\n if (existing) {\n callback(existing as T[K]);\n return () => {};\n }\n const listener = (fn: T[keyof T]) => callback(fn as T[K]);\n let set = listenersRef.current.get(key);\n if (!set) {\n set = new Set();\n listenersRef.current.set(key, set);\n }\n set.add(listener);\n return () => {\n set!.delete(listener);\n };\n },\n }).current;\n\n return (\n <TunnelContext.Provider value={store}>{children}</TunnelContext.Provider>\n );\n }\n\n function useTunnelStore(): TunnelStore<T> {\n const store = useContext(TunnelContext);\n if (!store) {\n throw new Error(\n \"[@felixfern/tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. \" +\n \"Wrap your component tree with the TunnelProvider from createTunnel().\",\n );\n }\n return store;\n }\n\n function useTunnel() {\n const store = useTunnelStore();\n\n const call = useCallback(\n <K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): ReturnType<T[K]> | undefined => {\n return store.call(key, ...args);\n },\n [store],\n );\n\n const callAsync = useCallback(\n <K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): Promise<Awaited<ReturnType<T[K]>> | undefined> => {\n return store.callAsync(key, ...args);\n },\n [store],\n );\n\n const has = useCallback(\n <K extends keyof T>(key: K): boolean => {\n return store.has(key);\n },\n [store],\n );\n\n const onReady = useCallback(\n <K extends keyof T>(\n key: K,\n callback: (fn: T[K]) => void,\n ): (() => void) => {\n return store.onReady(key, callback);\n },\n [store],\n );\n\n return { call, callAsync, has, onReady } as const;\n }\n\n function useTunnelFunction<K extends keyof T>(key: K, fn: T[K]): T[K] {\n const store = useTunnelStore();\n\n useEffect(() => {\n store.register(key, fn);\n return () => {\n store.unregister(key);\n };\n }, [store, key, fn]);\n\n return fn;\n }\n\n return { TunnelProvider, useTunnel, useTunnelFunction } as const;\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -12,7 +12,9 @@ function createTunnel() {
12
12
  const TunnelContext = createContext(null);
13
13
  function TunnelProvider({ children }) {
14
14
  const storeRef = useRef(/* @__PURE__ */ new Map());
15
- const listenersRef = useRef(/* @__PURE__ */ new Map());
15
+ const listenersRef = useRef(
16
+ /* @__PURE__ */ new Map()
17
+ );
16
18
  const isRenderPhaseRef = useRef(false);
17
19
  const registrationCountRef = useRef(/* @__PURE__ */ new Map());
18
20
  const churnWarnedRef = useRef(/* @__PURE__ */ new Set());
@@ -28,7 +30,7 @@ function createTunnel() {
28
30
  if (process.env.NODE_ENV !== "production") {
29
31
  if (storeRef.current.has(key)) {
30
32
  console.warn(
31
- `[tunnel-fn] Duplicate registration for key "${String(key)}". Another component already registered this key. Only the last registration will be active \u2014 this is likely a bug.`
33
+ `[@felixfern/tunnel-fn] Duplicate registration for key "${String(key)}". Another component already registered this key. Only the last registration will be active \u2014 this is likely a bug.`
32
34
  );
33
35
  }
34
36
  const now = Date.now();
@@ -39,7 +41,7 @@ function createTunnel() {
39
41
  if (recent.length > 3 && !churnWarnedRef.current.has(key)) {
40
42
  churnWarnedRef.current.add(key);
41
43
  console.warn(
42
- `[tunnel-fn] Function for key "${String(key)}" is being re-registered rapidly (${recent.length} times in <1s). This usually means the function passed to useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:
44
+ `[@felixfern/tunnel-fn] Function for key "${String(key)}" is being re-registered rapidly (${recent.length} times in <1s). This usually means the function passed to useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:
43
45
 
44
46
  const handler = useCallback((...) => { ... }, [deps]);
45
47
  useTunnelFunction("${String(key)}", handler);`
@@ -60,7 +62,7 @@ function createTunnel() {
60
62
  if (process.env.NODE_ENV !== "production") {
61
63
  if (isRenderPhaseRef.current) {
62
64
  console.warn(
63
- `[tunnel-fn] call("${String(key)}") was invoked during the render phase. Tunnel functions should only be called from event handlers, useEffect, or callbacks \u2014 never directly during render. This can cause unpredictable behavior.`
65
+ `[@felixfern/tunnel-fn] call("${String(key)}") was invoked during the render phase. Tunnel functions should only be called from event handlers, useEffect, or callbacks \u2014 never directly during render. This can cause unpredictable behavior.`
64
66
  );
65
67
  }
66
68
  }
@@ -68,7 +70,7 @@ function createTunnel() {
68
70
  if (!fn) {
69
71
  if (process.env.NODE_ENV !== "production") {
70
72
  console.warn(
71
- `[tunnel-fn] No function registered for key "${String(key)}". Make sure a component with useTunnelFunction("${String(key)}", ...) is mounted.`
73
+ `[@felixfern/tunnel-fn] No function registered for key "${String(key)}". Make sure a component with useTunnelFunction("${String(key)}", ...) is mounted.`
72
74
  );
73
75
  }
74
76
  return void 0;
@@ -126,7 +128,7 @@ function createTunnel() {
126
128
  const store = useContext(TunnelContext);
127
129
  if (!store) {
128
130
  throw new Error(
129
- "[tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. Wrap your component tree with the TunnelProvider from createTunnel()."
131
+ "[@felixfern/tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. Wrap your component tree with the TunnelProvider from createTunnel()."
130
132
  );
131
133
  }
132
134
  return store;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tunnel.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\ttype ReactNode,\n} from \"react\";\n\ntype TunnelFunctionMap = Record<string, (...args: never[]) => unknown>;\n\ntype Awaited<T> = T extends Promise<infer U> ? U : T;\n\ntype TunnelStore<T extends TunnelFunctionMap> = {\n\tregister<K extends keyof T>(key: K, fn: T[K]): void;\n\tunregister<K extends keyof T>(key: K): void;\n\tcall<K extends keyof T>(\n\t\tkey: K,\n\t\t...args: Parameters<T[K]>\n\t): ReturnType<T[K]> | undefined;\n\tcallAsync<K extends keyof T>(\n\t\tkey: K,\n\t\t...args: Parameters<T[K]>\n\t): Promise<Awaited<ReturnType<T[K]>> | undefined>;\n\thas<K extends keyof T>(key: K): boolean;\n\tonReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void): () => void;\n};\n\n/**\n * Creates a type-safe tunnel for passing functions across components via context.\n *\n * @example\n * ```tsx\n * const { TunnelProvider, useTunnel, useTunnelFunction } = createTunnel<{\n * onSave: (data: string) => void;\n * getCount: () => number;\n * }>();\n * ```\n */\nexport function createTunnel<T extends TunnelFunctionMap>() {\n\tconst TunnelContext = createContext<TunnelStore<T> | null>(null);\n\n\tfunction TunnelProvider({ children }: { children: ReactNode }) {\n\t\tconst storeRef = useRef<Map<keyof T, T[keyof T]>>(new Map());\n\t\tconst listenersRef = useRef<Map<keyof T, Set<(fn: T[keyof T]) => void>>>(new Map());\n\n\t\tconst isRenderPhaseRef = useRef(false);\n\t\tconst registrationCountRef = useRef<Map<keyof T, number[]>>(new Map());\n\t\tconst churnWarnedRef = useRef<Set<keyof T>>(new Set());\n\n\t\t// Track render phase: set true synchronously during render, false in effect\n\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\tisRenderPhaseRef.current = true;\n\t\t}\n\n\t\tuseLayoutEffect(() => {\n\t\t\tisRenderPhaseRef.current = false;\n\t\t});\n\n\t\tconst store = useRef<TunnelStore<T>>({\n\t\t\tregister<K extends keyof T>(key: K, fn: T[K]) {\n\t\t\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\t\t\t// Warn: duplicate key registration\n\t\t\t\t\tif (storeRef.current.has(key)) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] Duplicate registration for key \"${String(key)}\". ` +\n\t\t\t\t\t\t\t\t`Another component already registered this key. ` +\n\t\t\t\t\t\t\t\t`Only the last registration will be active — this is likely a bug.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Track: registration churn (unstable fn reference)\n\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\tconst timestamps = registrationCountRef.current.get(key) ?? [];\n\t\t\t\t\ttimestamps.push(now);\n\t\t\t\t\t// Keep only timestamps from the last second\n\t\t\t\t\tconst recent = timestamps.filter((t) => now - t < 1000);\n\t\t\t\t\tregistrationCountRef.current.set(key, recent);\n\n\t\t\t\t\tif (recent.length > 3 && !churnWarnedRef.current.has(key)) {\n\t\t\t\t\t\tchurnWarnedRef.current.add(key);\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] Function for key \"${String(key)}\" is being re-registered rapidly ` +\n\t\t\t\t\t\t\t\t`(${recent.length} times in <1s). This usually means the function passed to ` +\n\t\t\t\t\t\t\t\t`useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:\\n\\n` +\n\t\t\t\t\t\t\t\t` const handler = useCallback((...) => { ... }, [deps]);\\n` +\n\t\t\t\t\t\t\t\t` useTunnelFunction(\"${String(key)}\", handler);`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tstoreRef.current.set(key, fn);\n\n\t\t\t\tconst listeners = listenersRef.current.get(key);\n\t\t\t\tif (listeners) {\n\t\t\t\t\tlisteners.forEach((cb) => cb(fn as T[keyof T]));\n\t\t\t\t\tlistenersRef.current.delete(key);\n\t\t\t\t}\n\t\t\t},\n\t\t\tunregister<K extends keyof T>(key: K) {\n\t\t\t\tstoreRef.current.delete(key);\n\t\t\t},\n\t\t\tcall<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n\t\t\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\t\t\t// Warn: call() during render phase\n\t\t\t\t\tif (isRenderPhaseRef.current) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] call(\"${String(key)}\") was invoked during the render phase. ` +\n\t\t\t\t\t\t\t\t`Tunnel functions should only be called from event handlers, useEffect, or callbacks — ` +\n\t\t\t\t\t\t\t\t`never directly during render. This can cause unpredictable behavior.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst fn = storeRef.current.get(key);\n\t\t\t\tif (!fn) {\n\t\t\t\t\tif (process.env.NODE_ENV !== \"production\") {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[tunnel-fn] No function registered for key \"${String(key)}\". ` +\n\t\t\t\t\t\t\t\t`Make sure a component with useTunnelFunction(\"${String(key)}\", ...) is mounted.`\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn undefined as ReturnType<T[K]> | undefined;\n\t\t\t\t}\n\t\t\t\treturn (fn as T[K])(...args) as ReturnType<T[K]>;\n\t\t\t},\n\t\t\thas<K extends keyof T>(key: K) {\n\t\t\t\treturn storeRef.current.has(key);\n\t\t\t},\n\t\t\tcallAsync<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n\t\t\t\tconst fn = storeRef.current.get(key);\n\t\t\t\tif (fn) {\n\t\t\t\t\treturn Promise.resolve((fn as T[K])(...args)).then(\n\t\t\t\t\t\t(v) => v as Awaited<ReturnType<T[K]>>\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn new Promise<Awaited<ReturnType<T[K]>> | undefined>((resolve) => {\n\t\t\t\t\tconst listener = (registeredFn: T[keyof T]) => {\n\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\tPromise.resolve((registeredFn as T[K])(...args)).then(\n\t\t\t\t\t\t\t\t(v) => v as Awaited<ReturnType<T[K]>>\n\t\t\t\t\t\t\t) as unknown as Awaited<ReturnType<T[K]>>\n\t\t\t\t\t\t);\n\t\t\t\t\t};\n\t\t\t\t\tlet set = listenersRef.current.get(key);\n\t\t\t\t\tif (!set) {\n\t\t\t\t\t\tset = new Set();\n\t\t\t\t\t\tlistenersRef.current.set(key, set);\n\t\t\t\t\t}\n\t\t\t\t\tset.add(listener);\n\t\t\t\t});\n\t\t\t},\n\t\t\tonReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void) {\n\t\t\t\tconst existing = storeRef.current.get(key);\n\t\t\t\tif (existing) {\n\t\t\t\t\tcallback(existing as T[K]);\n\t\t\t\t\treturn () => {};\n\t\t\t\t}\n\t\t\t\tconst listener = (fn: T[keyof T]) => callback(fn as T[K]);\n\t\t\t\tlet set = listenersRef.current.get(key);\n\t\t\t\tif (!set) {\n\t\t\t\t\tset = new Set();\n\t\t\t\t\tlistenersRef.current.set(key, set);\n\t\t\t\t}\n\t\t\t\tset.add(listener);\n\t\t\t\treturn () => {\n\t\t\t\t\tset!.delete(listener);\n\t\t\t\t};\n\t\t\t},\n\t\t}).current;\n\n\t\treturn (\n\t\t\t<TunnelContext.Provider value={store}>\n\t\t\t\t{children}\n\t\t\t</TunnelContext.Provider>\n\t\t);\n\t}\n\n\tfunction useTunnelStore(): TunnelStore<T> {\n\t\tconst store = useContext(TunnelContext);\n\t\tif (!store) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. \" +\n\t\t\t\t\t\"Wrap your component tree with the TunnelProvider from createTunnel().\"\n\t\t\t);\n\t\t}\n\t\treturn store;\n\t}\n\n\tfunction useTunnel() {\n\t\tconst store = useTunnelStore();\n\n\t\tconst call = useCallback(\n\t\t\t<K extends keyof T>(key: K, ...args: Parameters<T[K]>): ReturnType<T[K]> | undefined => {\n\t\t\t\treturn store.call(key, ...args);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\tconst callAsync = useCallback(\n\t\t\t<K extends keyof T>(key: K, ...args: Parameters<T[K]>): Promise<Awaited<ReturnType<T[K]>> | undefined> => {\n\t\t\t\treturn store.callAsync(key, ...args);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\tconst has = useCallback(\n\t\t\t<K extends keyof T>(key: K): boolean => {\n\t\t\t\treturn store.has(key);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\tconst onReady = useCallback(\n\t\t\t<K extends keyof T>(key: K, callback: (fn: T[K]) => void): (() => void) => {\n\t\t\t\treturn store.onReady(key, callback);\n\t\t\t},\n\t\t\t[store]\n\t\t);\n\n\t\treturn { call, callAsync, has, onReady } as const;\n\t}\n\n\tfunction useTunnelFunction<K extends keyof T>(key: K, fn: T[K]): T[K] {\n\t\tconst store = useTunnelStore();\n\n\t\tuseEffect(() => {\n\t\t\tstore.register(key, fn);\n\t\t\treturn () => {\n\t\t\t\tstore.unregister(key);\n\t\t\t};\n\t\t}, [store, key, fn]);\n\n\t\treturn fn;\n\t}\n\n\treturn { TunnelProvider, useTunnel, useTunnelFunction } as const;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/tunnel.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n type ReactNode,\n} from \"react\";\n\ntype TunnelFunctionMap = Record<string, (...args: never[]) => unknown>;\n\ntype Awaited<T> = T extends Promise<infer U> ? U : T;\n\ntype TunnelStore<T extends TunnelFunctionMap> = {\n register<K extends keyof T>(key: K, fn: T[K]): void;\n unregister<K extends keyof T>(key: K): void;\n call<K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): ReturnType<T[K]> | undefined;\n callAsync<K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): Promise<Awaited<ReturnType<T[K]>> | undefined>;\n has<K extends keyof T>(key: K): boolean;\n onReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void): () => void;\n};\n\n/**\n * Creates a type-safe tunnel for passing functions across components via context.\n *\n * @example\n * ```tsx\n * const { TunnelProvider, useTunnel, useTunnelFunction } = createTunnel<{\n * onSave: (data: string) => void;\n * getCount: () => number;\n * }>();\n * ```\n */\nexport function createTunnel<T extends TunnelFunctionMap>() {\n const TunnelContext = createContext<TunnelStore<T> | null>(null);\n\n function TunnelProvider({ children }: { children: ReactNode }) {\n const storeRef = useRef<Map<keyof T, T[keyof T]>>(new Map());\n const listenersRef = useRef<Map<keyof T, Set<(fn: T[keyof T]) => void>>>(\n new Map(),\n );\n\n const isRenderPhaseRef = useRef(false);\n const registrationCountRef = useRef<Map<keyof T, number[]>>(new Map());\n const churnWarnedRef = useRef<Set<keyof T>>(new Set());\n\n if (process.env.NODE_ENV !== \"production\") {\n isRenderPhaseRef.current = true;\n }\n\n useLayoutEffect(() => {\n isRenderPhaseRef.current = false;\n });\n\n const store = useRef<TunnelStore<T>>({\n register<K extends keyof T>(key: K, fn: T[K]) {\n if (process.env.NODE_ENV !== \"production\") {\n if (storeRef.current.has(key)) {\n console.warn(\n `[@felixfern/tunnel-fn] Duplicate registration for key \"${String(key)}\". ` +\n `Another component already registered this key. ` +\n `Only the last registration will be active — this is likely a bug.`,\n );\n }\n\n const now = Date.now();\n const timestamps = registrationCountRef.current.get(key) ?? [];\n timestamps.push(now);\n\n const recent = timestamps.filter((t) => now - t < 1000);\n registrationCountRef.current.set(key, recent);\n\n if (recent.length > 3 && !churnWarnedRef.current.has(key)) {\n churnWarnedRef.current.add(key);\n console.warn(\n `[@felixfern/tunnel-fn] Function for key \"${String(key)}\" is being re-registered rapidly ` +\n `(${recent.length} times in <1s). This usually means the function passed to ` +\n `useTunnelFunction is recreated every render. Wrap it in useCallback() to fix this:\\n\\n` +\n ` const handler = useCallback((...) => { ... }, [deps]);\\n` +\n ` useTunnelFunction(\"${String(key)}\", handler);`,\n );\n }\n }\n\n storeRef.current.set(key, fn);\n\n const listeners = listenersRef.current.get(key);\n if (listeners) {\n listeners.forEach((cb) => cb(fn as T[keyof T]));\n listenersRef.current.delete(key);\n }\n },\n unregister<K extends keyof T>(key: K) {\n storeRef.current.delete(key);\n },\n call<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n if (process.env.NODE_ENV !== \"production\") {\n if (isRenderPhaseRef.current) {\n console.warn(\n `[@felixfern/tunnel-fn] call(\"${String(key)}\") was invoked during the render phase. ` +\n `Tunnel functions should only be called from event handlers, useEffect, or callbacks — ` +\n `never directly during render. This can cause unpredictable behavior.`,\n );\n }\n }\n\n const fn = storeRef.current.get(key);\n if (!fn) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[@felixfern/tunnel-fn] No function registered for key \"${String(key)}\". ` +\n `Make sure a component with useTunnelFunction(\"${String(key)}\", ...) is mounted.`,\n );\n }\n return undefined as ReturnType<T[K]> | undefined;\n }\n return (fn as T[K])(...args) as ReturnType<T[K]>;\n },\n has<K extends keyof T>(key: K) {\n return storeRef.current.has(key);\n },\n callAsync<K extends keyof T>(key: K, ...args: Parameters<T[K]>) {\n const fn = storeRef.current.get(key);\n if (fn) {\n return Promise.resolve((fn as T[K])(...args)).then(\n (v) => v as Awaited<ReturnType<T[K]>>,\n );\n }\n return new Promise<Awaited<ReturnType<T[K]>> | undefined>((resolve) => {\n const listener = (registeredFn: T[keyof T]) => {\n resolve(\n Promise.resolve((registeredFn as T[K])(...args)).then(\n (v) => v as Awaited<ReturnType<T[K]>>,\n ) as unknown as Awaited<ReturnType<T[K]>>,\n );\n };\n let set = listenersRef.current.get(key);\n if (!set) {\n set = new Set();\n listenersRef.current.set(key, set);\n }\n set.add(listener);\n });\n },\n onReady<K extends keyof T>(key: K, callback: (fn: T[K]) => void) {\n const existing = storeRef.current.get(key);\n if (existing) {\n callback(existing as T[K]);\n return () => {};\n }\n const listener = (fn: T[keyof T]) => callback(fn as T[K]);\n let set = listenersRef.current.get(key);\n if (!set) {\n set = new Set();\n listenersRef.current.set(key, set);\n }\n set.add(listener);\n return () => {\n set!.delete(listener);\n };\n },\n }).current;\n\n return (\n <TunnelContext.Provider value={store}>{children}</TunnelContext.Provider>\n );\n }\n\n function useTunnelStore(): TunnelStore<T> {\n const store = useContext(TunnelContext);\n if (!store) {\n throw new Error(\n \"[@felixfern/tunnel-fn] useTunnel/useTunnelFunction must be used within a <TunnelProvider>. \" +\n \"Wrap your component tree with the TunnelProvider from createTunnel().\",\n );\n }\n return store;\n }\n\n function useTunnel() {\n const store = useTunnelStore();\n\n const call = useCallback(\n <K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): ReturnType<T[K]> | undefined => {\n return store.call(key, ...args);\n },\n [store],\n );\n\n const callAsync = useCallback(\n <K extends keyof T>(\n key: K,\n ...args: Parameters<T[K]>\n ): Promise<Awaited<ReturnType<T[K]>> | undefined> => {\n return store.callAsync(key, ...args);\n },\n [store],\n );\n\n const has = useCallback(\n <K extends keyof T>(key: K): boolean => {\n return store.has(key);\n },\n [store],\n );\n\n const onReady = useCallback(\n <K extends keyof T>(\n key: K,\n callback: (fn: T[K]) => void,\n ): (() => void) => {\n return store.onReady(key, callback);\n },\n [store],\n );\n\n return { call, callAsync, has, onReady } as const;\n }\n\n function useTunnelFunction<K extends keyof T>(key: K, fn: T[K]): T[K] {\n const store = useTunnelStore();\n\n useEffect(() => {\n store.register(key, fn);\n return () => {\n store.unregister(key);\n };\n }, [store, key, fn]);\n\n return fn;\n }\n\n return { TunnelProvider, useTunnel, useTunnelFunction } as const;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felixfern/tunnel-fn",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Type-safe function tunneling across React components via context",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",