heliumts 0.4.2 → 0.4.4
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/client/Router.d.ts.map +1 -1
- package/dist/client/Router.js +76 -10
- package/dist/client/Router.js.map +1 -1
- package/dist/client/cache.d.ts +17 -0
- package/dist/client/cache.d.ts.map +1 -1
- package/dist/client/cache.js +58 -2
- package/dist/client/cache.js.map +1 -1
- package/dist/client/types.d.ts +10 -2
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js.map +1 -1
- package/dist/client/useCall.d.ts.map +1 -1
- package/dist/client/useCall.js +16 -8
- package/dist/client/useCall.js.map +1 -1
- package/dist/client/useFetch.d.ts.map +1 -1
- package/dist/client/useFetch.js +223 -114
- package/dist/client/useFetch.js.map +1 -1
- package/dist/vite/heliumPlugin.d.ts.map +1 -1
- package/dist/vite/heliumPlugin.js +54 -8
- package/dist/vite/heliumPlugin.js.map +1 -1
- package/dist/vite/scanner.d.ts.map +1 -1
- package/dist/vite/scanner.js +24 -3
- package/dist/vite/scanner.js.map +1 -1
- package/dist/vite/virtualServerModule.d.ts.map +1 -1
- package/dist/vite/virtualServerModule.js +21 -4
- package/dist/vite/virtualServerModule.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../src/client/Router.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAuD,MAAM,OAAO,CAAC;AAM5E,KAAK,WAAW,GAAG,YAAY,GAAG,mBAAmB,CAAC;AACtD,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../src/client/Router.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAuD,MAAM,OAAO,CAAC;AAM5E,KAAK,WAAW,GAAG,YAAY,GAAG,mBAAmB,CAAC;AACtD,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,KAAK,IAAI,CAAC;AAgKhG,kDAAkD;AAClD,MAAM,WAAW,uBAAuB;IACpC,qDAAqD;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAGD,KAAK,aAAa,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,YAAY,EAAE,eAAe,CAAC;IAC9B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACnE,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,KAAK,MAAM,IAAI,CAAC;IAChE,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,8GAA8G;IAC9G,SAAS,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,aAAa,qCAAkD,CAAC;AAE7E;;;;;;;;GAQG;AACH,wBAAgB,SAAS,kBAmCxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAe,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,QAyBlF;AA2CD,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,iBAAiB,CAC3C,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qDAAqD;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CACJ,CAAC;AA+BF;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,2CA6CpC;AAGD;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IAC9F,SAAS,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACrC,SAAS,EAAE,UAAU,CAAC;CACzB,CAAC;AAGF,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;CAAE,2CAoFlF"}
|
package/dist/client/Router.js
CHANGED
|
@@ -66,8 +66,42 @@ function matchRoute(path, routes) {
|
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
68
|
// Location store for useSyncExternalStore
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// Preserve across HMR by attaching to window in dev mode
|
|
70
|
+
let currentLocation;
|
|
71
|
+
let locationListeners;
|
|
72
|
+
if (typeof window !== "undefined" && import.meta.env?.DEV) {
|
|
73
|
+
const globalWindow = window;
|
|
74
|
+
if (!globalWindow.__heliumCurrentLocation) {
|
|
75
|
+
globalWindow.__heliumCurrentLocation = getLocation();
|
|
76
|
+
}
|
|
77
|
+
if (!globalWindow.__heliumLocationListeners) {
|
|
78
|
+
globalWindow.__heliumLocationListeners = new Set();
|
|
79
|
+
}
|
|
80
|
+
currentLocation = globalWindow.__heliumCurrentLocation;
|
|
81
|
+
locationListeners = globalWindow.__heliumLocationListeners;
|
|
82
|
+
}
|
|
83
|
+
else if (typeof window !== "undefined") {
|
|
84
|
+
currentLocation = getLocation();
|
|
85
|
+
locationListeners = new Set();
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
currentLocation = { path: "/", searchParams: new URLSearchParams(), isNavigating: false };
|
|
89
|
+
locationListeners = new Set();
|
|
90
|
+
}
|
|
91
|
+
// Helper to re-extract params from path using stored routes during HMR
|
|
92
|
+
function extractParamsFromPath(path) {
|
|
93
|
+
if (typeof window !== "undefined" && import.meta.env?.DEV) {
|
|
94
|
+
const globalWindow = window;
|
|
95
|
+
const routes = globalWindow.__heliumGlobalRoutes;
|
|
96
|
+
if (routes && routes.length > 0) {
|
|
97
|
+
const match = matchRoute(path, routes);
|
|
98
|
+
if (match) {
|
|
99
|
+
return match.params;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
71
105
|
function subscribeToLocation(callback) {
|
|
72
106
|
locationListeners.add(callback);
|
|
73
107
|
return () => locationListeners.delete(callback);
|
|
@@ -79,7 +113,12 @@ function getServerSnapshot() {
|
|
|
79
113
|
return { path: "/", searchParams: new URLSearchParams(), isNavigating: false };
|
|
80
114
|
}
|
|
81
115
|
function updateLocation(isNavigating = false) {
|
|
82
|
-
|
|
116
|
+
const newLocation = { ...getLocation(), isNavigating };
|
|
117
|
+
currentLocation = newLocation;
|
|
118
|
+
// Update the global reference in dev mode
|
|
119
|
+
if (typeof window !== "undefined" && import.meta.env?.DEV) {
|
|
120
|
+
window.__heliumCurrentLocation = newLocation;
|
|
121
|
+
}
|
|
83
122
|
locationListeners.forEach((listener) => listener());
|
|
84
123
|
}
|
|
85
124
|
// Set up global listeners once
|
|
@@ -108,10 +147,12 @@ export function useRouter() {
|
|
|
108
147
|
if (!ctx) {
|
|
109
148
|
// During HMR in development, context might be temporarily unavailable
|
|
110
149
|
// Provide a temporary fallback to prevent white screen of death
|
|
150
|
+
// Re-extract params from current path using stored routes
|
|
111
151
|
if (typeof window !== "undefined" && import.meta.env?.DEV) {
|
|
152
|
+
const currentPath = window.location.pathname;
|
|
112
153
|
return {
|
|
113
|
-
path:
|
|
114
|
-
params:
|
|
154
|
+
path: currentPath,
|
|
155
|
+
params: extractParamsFromPath(currentPath),
|
|
115
156
|
searchParams: new URLSearchParams(window.location.search),
|
|
116
157
|
push: (href, options) => {
|
|
117
158
|
window.history.pushState({}, "", href);
|
|
@@ -218,7 +259,18 @@ function isExternalUrl(href) {
|
|
|
218
259
|
}
|
|
219
260
|
}
|
|
220
261
|
// Store routes globally for prefetching (set by AppRouter)
|
|
221
|
-
|
|
262
|
+
// Preserve across HMR in dev mode
|
|
263
|
+
let globalRoutes;
|
|
264
|
+
if (typeof window !== "undefined" && import.meta.env?.DEV) {
|
|
265
|
+
const globalWindow = window;
|
|
266
|
+
if (!globalWindow.__heliumGlobalRoutes) {
|
|
267
|
+
globalWindow.__heliumGlobalRoutes = [];
|
|
268
|
+
}
|
|
269
|
+
globalRoutes = globalWindow.__heliumGlobalRoutes;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
globalRoutes = [];
|
|
273
|
+
}
|
|
222
274
|
/**
|
|
223
275
|
* Client-side navigation link.
|
|
224
276
|
*
|
|
@@ -275,17 +327,31 @@ export function AppRouter({ AppShell }) {
|
|
|
275
327
|
}
|
|
276
328
|
const result = buildRoutes();
|
|
277
329
|
// Store routes globally for Link prefetching
|
|
278
|
-
|
|
330
|
+
// In dev mode, also update the global reference to survive HMR
|
|
331
|
+
if (import.meta.env?.DEV) {
|
|
332
|
+
const globalWindow = window;
|
|
333
|
+
globalWindow.__heliumGlobalRoutes = result.routes;
|
|
334
|
+
// Update the module-level reference as well
|
|
335
|
+
globalRoutes.length = 0;
|
|
336
|
+
globalRoutes.push(...result.routes);
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
globalRoutes = result.routes;
|
|
340
|
+
}
|
|
279
341
|
return result;
|
|
280
342
|
}, []);
|
|
281
343
|
// Use useSyncExternalStore to subscribe to location changes
|
|
282
344
|
// This ensures synchronous updates when location changes
|
|
283
345
|
const state = useSyncExternalStore(subscribeToLocation, getLocationSnapshot, getServerSnapshot);
|
|
284
346
|
const match = useMemo(() => matchRoute(state.path, routes), [state.path, routes]);
|
|
347
|
+
// Use matched params, or fall back to re-extracting from path using global routes during HMR
|
|
348
|
+
const currentParams = match?.params ?? (import.meta.env?.DEV ? extractParamsFromPath(state.path) : {});
|
|
349
|
+
// In dev mode, always read fresh searchParams from URL to handle HMR edge cases
|
|
350
|
+
const currentSearchParams = import.meta.env?.DEV ? new URLSearchParams(window.location.search) : state.searchParams;
|
|
285
351
|
const routerValue = {
|
|
286
352
|
path: state.path,
|
|
287
|
-
params:
|
|
288
|
-
searchParams:
|
|
353
|
+
params: currentParams,
|
|
354
|
+
searchParams: currentSearchParams,
|
|
289
355
|
push: (href, options) => {
|
|
290
356
|
// Wrap navigation in startTransition for smoother updates
|
|
291
357
|
startTransition(() => {
|
|
@@ -310,7 +376,7 @@ export function AppRouter({ AppShell }) {
|
|
|
310
376
|
const Page = match.route.Component;
|
|
311
377
|
const pageProps = {
|
|
312
378
|
params: match.params,
|
|
313
|
-
searchParams:
|
|
379
|
+
searchParams: currentSearchParams,
|
|
314
380
|
};
|
|
315
381
|
// Create a wrapped component that includes all layouts
|
|
316
382
|
const WrappedPage = () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Router.js","sourceRoot":"","sources":["../../src/client/Router.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from \"react\";\nimport React, { useMemo, useSyncExternalStore, useTransition } from \"react\";\n\nimport type { RouteEntry } from \"./routerManifest.js\";\nimport { buildRoutes } from \"./routerManifest.js\";\n\n// Event emitter for router events\ntype RouterEvent = \"navigation\" | \"before-navigation\";\ntype EventListener = (event: { from: string; to: string; preventDefault?: () => void }) => void;\n\nclass RouterEventEmitter {\n private listeners: Map<RouterEvent, Set<EventListener>> = new Map();\n\n on(event: RouterEvent, listener: EventListener): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n\n // Return unsubscribe function\n return () => {\n this.listeners.get(event)?.delete(listener);\n };\n }\n\n emit(event: RouterEvent, data: { from: string; to: string }): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return true;\n }\n\n let prevented = false;\n const preventDefault = () => {\n prevented = true;\n };\n\n const eventData = event === \"before-navigation\" ? { ...data, preventDefault } : data;\n\n eventListeners.forEach((listener) => {\n listener(eventData);\n });\n\n return !prevented;\n }\n\n // Clear all listeners - useful for HMR\n clear() {\n this.listeners.clear();\n }\n}\n\n// Use a singleton that survives HMR by attaching to window in dev mode\nlet routerEventEmitter: RouterEventEmitter;\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n // In dev mode, reuse the same emitter instance across HMR\n const globalWindow = window as typeof window & { __heliumRouterEmitter?: RouterEventEmitter };\n if (!globalWindow.__heliumRouterEmitter) {\n globalWindow.__heliumRouterEmitter = new RouterEventEmitter();\n }\n routerEventEmitter = globalWindow.__heliumRouterEmitter;\n} else {\n routerEventEmitter = new RouterEventEmitter();\n}\n\ntype RouterState = {\n path: string;\n searchParams: URLSearchParams;\n isNavigating: boolean;\n};\n\nfunction getLocation(): RouterState {\n const { pathname, search } = window.location;\n return {\n path: pathname,\n searchParams: new URLSearchParams(search),\n isNavigating: false,\n };\n}\n\nfunction matchRoute(path: string, routes: RouteEntry[]) {\n for (const r of routes) {\n const m = r.matcher(path);\n if (m) {\n return { params: m.params, route: r };\n }\n }\n return null;\n}\n\n// Location store for useSyncExternalStore\nlet currentLocation = typeof window !== \"undefined\" ? getLocation() : { path: \"/\", searchParams: new URLSearchParams(), isNavigating: false };\nconst locationListeners = new Set<() => void>();\n\nfunction subscribeToLocation(callback: () => void) {\n locationListeners.add(callback);\n return () => locationListeners.delete(callback);\n}\n\nfunction getLocationSnapshot() {\n return currentLocation;\n}\n\nfunction getServerSnapshot() {\n return { path: \"/\", searchParams: new URLSearchParams(), isNavigating: false };\n}\n\nfunction updateLocation(isNavigating = false) {\n currentLocation = { ...getLocation(), isNavigating };\n locationListeners.forEach((listener) => listener());\n}\n\n// Set up global listeners once\nif (typeof window !== \"undefined\") {\n // Only set up once, survives HMR\n const globalWindow = window as typeof window & { __heliumLocationListenerSetup?: boolean };\n if (!globalWindow.__heliumLocationListenerSetup) {\n globalWindow.__heliumLocationListenerSetup = true;\n\n window.addEventListener(\"popstate\", () => updateLocation(false));\n\n // Also listen to navigation events from the emitter\n routerEventEmitter.on(\"navigation\", () => updateLocation(false));\n }\n}\n\n/** Options for push/replace navigation methods */\nexport interface RouterNavigationOptions {\n /** Scroll to top after navigation (default: true) */\n scrollToTop?: boolean;\n}\n\n// Context for useRouter hook\ntype RouterContext = {\n path: string;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n push: (href: string, options?: RouterNavigationOptions) => void;\n replace: (href: string, options?: RouterNavigationOptions) => void;\n on: (event: RouterEvent, listener: EventListener) => () => void;\n status: 200 | 404;\n isNavigating: boolean;\n /** Indicates content is stale (old content shown while new content renders) - React 18+ concurrent feature */\n isPending: boolean;\n};\n\nexport const RouterContext = React.createContext<RouterContext | null>(null);\n\n/**\n * Access router context inside a component tree managed by <AppRouter />.\n *\n * Provides current path, route params, URL search params and navigation helpers\n * (\\`push\\`, \\`replace\\`) as well as an \\`on\\` method to subscribe to navigation events.\n * The \\`isNavigating\\` property indicates when a navigation is in progress.\n * The \\`isPending\\` property indicates when content is stale (React concurrent features).\n * Throws when used outside of an <AppRouter /> provider.\n */\nexport function useRouter() {\n const ctx = React.useContext(RouterContext);\n if (!ctx) {\n // During HMR in development, context might be temporarily unavailable\n // Provide a temporary fallback to prevent white screen of death\n if (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n return {\n path: window.location.pathname,\n params: {},\n searchParams: new URLSearchParams(window.location.search),\n push: (href: string, options?: RouterNavigationOptions) => {\n window.history.pushState({}, \"\", href);\n if (options?.scrollToTop !== false) {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n },\n replace: (href: string, options?: RouterNavigationOptions) => {\n window.history.replaceState({}, \"\", href);\n if (options?.scrollToTop !== false) {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n },\n on: () => () => {},\n status: 200 as const,\n isNavigating: false,\n isPending: false,\n };\n }\n throw new Error(\"useRouter must be used inside <AppRouter>\");\n }\n return ctx;\n}\n\n/**\n * Redirect component for declarative navigation.\n * Use this instead of calling router.push() during render.\n *\n * @example\n * \\`\\`\\`tsx\n * export default function Docs() {\n * return <Redirect to=\"/docs/getting-started\" />;\n * }\n * \\`\\`\\`\n */\nexport function Redirect({ to, replace = false }: { to: string; replace?: boolean }) {\n const hasRedirected = React.useRef(false);\n\n // Use useLayoutEffect to redirect before paint\n React.useLayoutEffect(() => {\n const targetPath = to.split(\"?\")[0];\n if (!hasRedirected.current && window.location.pathname !== targetPath) {\n hasRedirected.current = true;\n\n // Perform navigation\n if (replace) {\n window.history.replaceState(null, \"\", to);\n } else {\n window.history.pushState(null, \"\", to);\n }\n\n // Emit navigation event to update router state\n routerEventEmitter.emit(\"navigation\", {\n from: window.location.pathname,\n to: targetPath,\n });\n }\n }, [to, replace]);\n\n return null;\n}\n\n/** Options for navigation */\ninterface NavigateOptions {\n replace?: boolean;\n scrollToTop?: boolean;\n}\n\n// Navigation helper\nfunction navigate(href: string, options: NavigateOptions = {}) {\n const { replace = false, scrollToTop = true } = options;\n const from = window.location.pathname;\n const to = href.split(\"?\")[0]; // Extract pathname from href\n\n // Emit before-navigation event (can be prevented)\n const canNavigate = routerEventEmitter.emit(\"before-navigation\", { from, to });\n if (!canNavigate) {\n return; // Navigation was prevented\n }\n\n // Set navigating state to true before navigation\n currentLocation = { ...currentLocation, isNavigating: true };\n locationListeners.forEach((listener) => listener());\n\n if (replace) {\n window.history.replaceState(null, \"\", href);\n } else {\n window.history.pushState(null, \"\", href);\n }\n\n // Update location and clear navigating state after navigation\n // Use requestAnimationFrame to allow the new page to start rendering\n requestAnimationFrame(() => {\n updateLocation(false);\n // Scroll to top if enabled\n if (scrollToTop) {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n }\n // Emit navigation event after navigation completes\n routerEventEmitter.emit(\"navigation\", { from, to });\n });\n}\n\nexport type LinkProps = React.PropsWithChildren<\n Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, \"href\"> & {\n href: string;\n replace?: boolean;\n /** Disable prefetching on hover (default: false - prefetch is enabled) */\n prefetch?: boolean;\n /** Scroll to top after navigation (default: true) */\n scrollToTop?: boolean;\n }\n>;\n\n/**\n * Check if a URL is external (different origin).\n */\nfunction isExternalUrl(href: string): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n try {\n const url = new URL(href, window.location.origin);\n return url.origin !== window.location.origin;\n } catch {\n return false;\n }\n}\n\n// Store routes globally for prefetching (set by AppRouter)\nlet globalRoutes: RouteEntry[] = [];\n\n/**\n * Client-side navigation link.\n *\n * Intercepts left-clicks and uses the router's navigation helpers for SPA\n * navigation. Keeps normal anchor behaviour when modifier keys are used\n * or when the link is external.\n *\n * Automatically prefetches page chunks on hover for faster navigation.\n */\nexport function Link(props: LinkProps) {\n const { prefetch = true, scrollToTop = true } = props;\n\n const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n if (\n e.defaultPrevented ||\n e.button !== 0 || // only left click\n e.metaKey ||\n e.ctrlKey ||\n e.shiftKey ||\n e.altKey ||\n isExternalUrl(props.href) // let browser handle external links\n ) {\n return;\n }\n e.preventDefault();\n navigate(props.href, { replace: props.replace, scrollToTop });\n props.onClick?.(e);\n };\n\n const onMouseEnter = async (e: React.MouseEvent<HTMLAnchorElement>) => {\n // Prefetch the route on hover if enabled and not external (lazy-load prefetch logic)\n if (prefetch && !isExternalUrl(props.href) && globalRoutes.length > 0) {\n const { prefetchRoute } = await import(\"./prefetch.js\");\n prefetchRoute(props.href, globalRoutes);\n }\n props.onMouseEnter?.(e);\n };\n\n const onFocus = async (e: React.FocusEvent<HTMLAnchorElement>) => {\n // Also prefetch on focus (keyboard navigation)\n if (prefetch && !isExternalUrl(props.href) && globalRoutes.length > 0) {\n const { prefetchRoute } = await import(\"./prefetch.js\");\n prefetchRoute(props.href, globalRoutes);\n }\n props.onFocus?.(e);\n };\n\n const { children, href, className, prefetch: _prefetch, scrollToTop: _scrollToTop, ...safeProps } = props;\n\n return (\n <a href={href} onClick={onClick} onMouseEnter={onMouseEnter} onFocus={onFocus} className={className} {...safeProps}>\n {children}\n </a>\n );\n}\n\n// AppShell props type\n/**\n * Props passed to an optional \\`AppShell\\` wrapper component used by <AppRouter />.\n *\n * \\`Component\\` — the page component to render. \\`pageProps\\` — props provided to the page.\n */\nexport type AppShellProps<TPageProps extends Record<string, unknown> = Record<string, unknown>> = {\n Component: ComponentType<TPageProps>;\n pageProps: TPageProps;\n};\n\n// Main router component\nexport function AppRouter({ AppShell }: { AppShell?: ComponentType<AppShellProps> }) {\n // useTransition for concurrent rendering - allows React to keep UI responsive during navigation\n const [isPending, startTransition] = useTransition();\n\n // Build routes once on mount (client-side only)\n const { routes, NotFound } = useMemo(() => {\n if (typeof window === \"undefined\") {\n return { routes: [], NotFound: undefined };\n }\n const result = buildRoutes();\n // Store routes globally for Link prefetching\n globalRoutes = result.routes;\n return result;\n }, []);\n\n // Use useSyncExternalStore to subscribe to location changes\n // This ensures synchronous updates when location changes\n const state = useSyncExternalStore(subscribeToLocation, getLocationSnapshot, getServerSnapshot);\n\n const match = useMemo(() => matchRoute(state.path, routes), [state.path, routes]);\n\n const routerValue: RouterContext = {\n path: state.path,\n params: match?.params ?? {},\n searchParams: state.searchParams,\n push: (href, options) => {\n // Wrap navigation in startTransition for smoother updates\n startTransition(() => {\n navigate(href, { scrollToTop: options?.scrollToTop });\n });\n },\n replace: (href, options) => {\n startTransition(() => {\n navigate(href, { replace: true, scrollToTop: options?.scrollToTop });\n });\n },\n on: (event, listener) => routerEventEmitter.on(event, listener),\n status: match ? 200 : 404,\n isNavigating: state.isNavigating,\n isPending,\n };\n\n if (!match) {\n const NotFoundComp = NotFound ?? (() => <div>Not found</div>);\n const content = <NotFoundComp />;\n\n return <RouterContext.Provider value={routerValue}>{AppShell ? <AppShell Component={NotFoundComp} pageProps={{}} /> : content}</RouterContext.Provider>;\n }\n\n const Page = match.route.Component as ComponentType<{ params: Record<string, string | string[]>; searchParams: URLSearchParams }>;\n const pageProps = {\n params: match.params,\n searchParams: state.searchParams,\n };\n\n // Create a wrapped component that includes all layouts\n const WrappedPage = () => {\n let content = <Page {...pageProps} />;\n // Wrap page with layouts (from outer to inner)\n for (let i = match.route.layouts.length - 1; i >= 0; i--) {\n const Layout = match.route.layouts[i];\n content = <Layout>{content}</Layout>;\n }\n return content;\n };\n\n const finalContent = AppShell ? <AppShell Component={WrappedPage} pageProps={{}} /> : <WrappedPage />;\n\n return <RouterContext.Provider value={routerValue}>{finalContent}</RouterContext.Provider>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Router.js","sourceRoot":"","sources":["../../src/client/Router.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentType } from \"react\";\nimport React, { useMemo, useSyncExternalStore, useTransition } from \"react\";\n\nimport type { RouteEntry } from \"./routerManifest.js\";\nimport { buildRoutes } from \"./routerManifest.js\";\n\n// Event emitter for router events\ntype RouterEvent = \"navigation\" | \"before-navigation\";\ntype EventListener = (event: { from: string; to: string; preventDefault?: () => void }) => void;\n\nclass RouterEventEmitter {\n private listeners: Map<RouterEvent, Set<EventListener>> = new Map();\n\n on(event: RouterEvent, listener: EventListener): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener);\n\n // Return unsubscribe function\n return () => {\n this.listeners.get(event)?.delete(listener);\n };\n }\n\n emit(event: RouterEvent, data: { from: string; to: string }): boolean {\n const eventListeners = this.listeners.get(event);\n if (!eventListeners || eventListeners.size === 0) {\n return true;\n }\n\n let prevented = false;\n const preventDefault = () => {\n prevented = true;\n };\n\n const eventData = event === \"before-navigation\" ? { ...data, preventDefault } : data;\n\n eventListeners.forEach((listener) => {\n listener(eventData);\n });\n\n return !prevented;\n }\n\n // Clear all listeners - useful for HMR\n clear() {\n this.listeners.clear();\n }\n}\n\n// Use a singleton that survives HMR by attaching to window in dev mode\nlet routerEventEmitter: RouterEventEmitter;\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n // In dev mode, reuse the same emitter instance across HMR\n const globalWindow = window as typeof window & { __heliumRouterEmitter?: RouterEventEmitter };\n if (!globalWindow.__heliumRouterEmitter) {\n globalWindow.__heliumRouterEmitter = new RouterEventEmitter();\n }\n routerEventEmitter = globalWindow.__heliumRouterEmitter;\n} else {\n routerEventEmitter = new RouterEventEmitter();\n}\n\ntype RouterState = {\n path: string;\n searchParams: URLSearchParams;\n isNavigating: boolean;\n};\n\nfunction getLocation(): RouterState {\n const { pathname, search } = window.location;\n return {\n path: pathname,\n searchParams: new URLSearchParams(search),\n isNavigating: false,\n };\n}\n\nfunction matchRoute(path: string, routes: RouteEntry[]) {\n for (const r of routes) {\n const m = r.matcher(path);\n if (m) {\n return { params: m.params, route: r };\n }\n }\n return null;\n}\n\n// Location store for useSyncExternalStore\n// Preserve across HMR by attaching to window in dev mode\nlet currentLocation: RouterState;\nlet locationListeners: Set<() => void>;\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const globalWindow = window as typeof window & {\n __heliumCurrentLocation?: RouterState;\n __heliumLocationListeners?: Set<() => void>;\n };\n if (!globalWindow.__heliumCurrentLocation) {\n globalWindow.__heliumCurrentLocation = getLocation();\n }\n if (!globalWindow.__heliumLocationListeners) {\n globalWindow.__heliumLocationListeners = new Set();\n }\n currentLocation = globalWindow.__heliumCurrentLocation;\n locationListeners = globalWindow.__heliumLocationListeners;\n} else if (typeof window !== \"undefined\") {\n currentLocation = getLocation();\n locationListeners = new Set();\n} else {\n currentLocation = { path: \"/\", searchParams: new URLSearchParams(), isNavigating: false };\n locationListeners = new Set();\n}\n\n// Helper to re-extract params from path using stored routes during HMR\nfunction extractParamsFromPath(path: string): Record<string, string | string[]> {\n if (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const globalWindow = window as typeof window & { __heliumGlobalRoutes?: RouteEntry[] };\n const routes = globalWindow.__heliumGlobalRoutes;\n if (routes && routes.length > 0) {\n const match = matchRoute(path, routes);\n if (match) {\n return match.params;\n }\n }\n }\n return {};\n}\n\nfunction subscribeToLocation(callback: () => void) {\n locationListeners.add(callback);\n return () => locationListeners.delete(callback);\n}\n\nfunction getLocationSnapshot() {\n return currentLocation;\n}\n\nfunction getServerSnapshot() {\n return { path: \"/\", searchParams: new URLSearchParams(), isNavigating: false };\n}\n\nfunction updateLocation(isNavigating = false) {\n const newLocation = { ...getLocation(), isNavigating };\n currentLocation = newLocation;\n // Update the global reference in dev mode\n if (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n (window as typeof window & { __heliumCurrentLocation?: RouterState }).__heliumCurrentLocation = newLocation;\n }\n locationListeners.forEach((listener) => listener());\n}\n\n// Set up global listeners once\nif (typeof window !== \"undefined\") {\n // Only set up once, survives HMR\n const globalWindow = window as typeof window & { __heliumLocationListenerSetup?: boolean };\n if (!globalWindow.__heliumLocationListenerSetup) {\n globalWindow.__heliumLocationListenerSetup = true;\n\n window.addEventListener(\"popstate\", () => updateLocation(false));\n\n // Also listen to navigation events from the emitter\n routerEventEmitter.on(\"navigation\", () => updateLocation(false));\n }\n}\n\n/** Options for push/replace navigation methods */\nexport interface RouterNavigationOptions {\n /** Scroll to top after navigation (default: true) */\n scrollToTop?: boolean;\n}\n\n// Context for useRouter hook\ntype RouterContext = {\n path: string;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n push: (href: string, options?: RouterNavigationOptions) => void;\n replace: (href: string, options?: RouterNavigationOptions) => void;\n on: (event: RouterEvent, listener: EventListener) => () => void;\n status: 200 | 404;\n isNavigating: boolean;\n /** Indicates content is stale (old content shown while new content renders) - React 18+ concurrent feature */\n isPending: boolean;\n};\n\nexport const RouterContext = React.createContext<RouterContext | null>(null);\n\n/**\n * Access router context inside a component tree managed by <AppRouter />.\n *\n * Provides current path, route params, URL search params and navigation helpers\n * (\\`push\\`, \\`replace\\`) as well as an \\`on\\` method to subscribe to navigation events.\n * The \\`isNavigating\\` property indicates when a navigation is in progress.\n * The \\`isPending\\` property indicates when content is stale (React concurrent features).\n * Throws when used outside of an <AppRouter /> provider.\n */\nexport function useRouter() {\n const ctx = React.useContext(RouterContext);\n if (!ctx) {\n // During HMR in development, context might be temporarily unavailable\n // Provide a temporary fallback to prevent white screen of death\n // Re-extract params from current path using stored routes\n if (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const currentPath = window.location.pathname;\n return {\n path: currentPath,\n params: extractParamsFromPath(currentPath),\n searchParams: new URLSearchParams(window.location.search),\n push: (href: string, options?: RouterNavigationOptions) => {\n window.history.pushState({}, \"\", href);\n if (options?.scrollToTop !== false) {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n },\n replace: (href: string, options?: RouterNavigationOptions) => {\n window.history.replaceState({}, \"\", href);\n if (options?.scrollToTop !== false) {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n },\n on: () => () => {},\n status: 200 as const,\n isNavigating: false,\n isPending: false,\n };\n }\n throw new Error(\"useRouter must be used inside <AppRouter>\");\n }\n return ctx;\n}\n\n/**\n * Redirect component for declarative navigation.\n * Use this instead of calling router.push() during render.\n *\n * @example\n * \\`\\`\\`tsx\n * export default function Docs() {\n * return <Redirect to=\"/docs/getting-started\" />;\n * }\n * \\`\\`\\`\n */\nexport function Redirect({ to, replace = false }: { to: string; replace?: boolean }) {\n const hasRedirected = React.useRef(false);\n\n // Use useLayoutEffect to redirect before paint\n React.useLayoutEffect(() => {\n const targetPath = to.split(\"?\")[0];\n if (!hasRedirected.current && window.location.pathname !== targetPath) {\n hasRedirected.current = true;\n\n // Perform navigation\n if (replace) {\n window.history.replaceState(null, \"\", to);\n } else {\n window.history.pushState(null, \"\", to);\n }\n\n // Emit navigation event to update router state\n routerEventEmitter.emit(\"navigation\", {\n from: window.location.pathname,\n to: targetPath,\n });\n }\n }, [to, replace]);\n\n return null;\n}\n\n/** Options for navigation */\ninterface NavigateOptions {\n replace?: boolean;\n scrollToTop?: boolean;\n}\n\n// Navigation helper\nfunction navigate(href: string, options: NavigateOptions = {}) {\n const { replace = false, scrollToTop = true } = options;\n const from = window.location.pathname;\n const to = href.split(\"?\")[0]; // Extract pathname from href\n\n // Emit before-navigation event (can be prevented)\n const canNavigate = routerEventEmitter.emit(\"before-navigation\", { from, to });\n if (!canNavigate) {\n return; // Navigation was prevented\n }\n\n // Set navigating state to true before navigation\n currentLocation = { ...currentLocation, isNavigating: true };\n locationListeners.forEach((listener) => listener());\n\n if (replace) {\n window.history.replaceState(null, \"\", href);\n } else {\n window.history.pushState(null, \"\", href);\n }\n\n // Update location and clear navigating state after navigation\n // Use requestAnimationFrame to allow the new page to start rendering\n requestAnimationFrame(() => {\n updateLocation(false);\n // Scroll to top if enabled\n if (scrollToTop) {\n window.scrollTo({ top: 0, left: 0, behavior: \"smooth\" });\n }\n // Emit navigation event after navigation completes\n routerEventEmitter.emit(\"navigation\", { from, to });\n });\n}\n\nexport type LinkProps = React.PropsWithChildren<\n Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, \"href\"> & {\n href: string;\n replace?: boolean;\n /** Disable prefetching on hover (default: false - prefetch is enabled) */\n prefetch?: boolean;\n /** Scroll to top after navigation (default: true) */\n scrollToTop?: boolean;\n }\n>;\n\n/**\n * Check if a URL is external (different origin).\n */\nfunction isExternalUrl(href: string): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n try {\n const url = new URL(href, window.location.origin);\n return url.origin !== window.location.origin;\n } catch {\n return false;\n }\n}\n\n// Store routes globally for prefetching (set by AppRouter)\n// Preserve across HMR in dev mode\nlet globalRoutes: RouteEntry[];\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const globalWindow = window as typeof window & { __heliumGlobalRoutes?: RouteEntry[] };\n if (!globalWindow.__heliumGlobalRoutes) {\n globalWindow.__heliumGlobalRoutes = [];\n }\n globalRoutes = globalWindow.__heliumGlobalRoutes;\n} else {\n globalRoutes = [];\n}\n\n/**\n * Client-side navigation link.\n *\n * Intercepts left-clicks and uses the router's navigation helpers for SPA\n * navigation. Keeps normal anchor behaviour when modifier keys are used\n * or when the link is external.\n *\n * Automatically prefetches page chunks on hover for faster navigation.\n */\nexport function Link(props: LinkProps) {\n const { prefetch = true, scrollToTop = true } = props;\n\n const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n if (\n e.defaultPrevented ||\n e.button !== 0 || // only left click\n e.metaKey ||\n e.ctrlKey ||\n e.shiftKey ||\n e.altKey ||\n isExternalUrl(props.href) // let browser handle external links\n ) {\n return;\n }\n e.preventDefault();\n navigate(props.href, { replace: props.replace, scrollToTop });\n props.onClick?.(e);\n };\n\n const onMouseEnter = async (e: React.MouseEvent<HTMLAnchorElement>) => {\n // Prefetch the route on hover if enabled and not external (lazy-load prefetch logic)\n if (prefetch && !isExternalUrl(props.href) && globalRoutes.length > 0) {\n const { prefetchRoute } = await import(\"./prefetch.js\");\n prefetchRoute(props.href, globalRoutes);\n }\n props.onMouseEnter?.(e);\n };\n\n const onFocus = async (e: React.FocusEvent<HTMLAnchorElement>) => {\n // Also prefetch on focus (keyboard navigation)\n if (prefetch && !isExternalUrl(props.href) && globalRoutes.length > 0) {\n const { prefetchRoute } = await import(\"./prefetch.js\");\n prefetchRoute(props.href, globalRoutes);\n }\n props.onFocus?.(e);\n };\n\n const { children, href, className, prefetch: _prefetch, scrollToTop: _scrollToTop, ...safeProps } = props;\n\n return (\n <a href={href} onClick={onClick} onMouseEnter={onMouseEnter} onFocus={onFocus} className={className} {...safeProps}>\n {children}\n </a>\n );\n}\n\n// AppShell props type\n/**\n * Props passed to an optional \\`AppShell\\` wrapper component used by <AppRouter />.\n *\n * \\`Component\\` — the page component to render. \\`pageProps\\` — props provided to the page.\n */\nexport type AppShellProps<TPageProps extends Record<string, unknown> = Record<string, unknown>> = {\n Component: ComponentType<TPageProps>;\n pageProps: TPageProps;\n};\n\n// Main router component\nexport function AppRouter({ AppShell }: { AppShell?: ComponentType<AppShellProps> }) {\n // useTransition for concurrent rendering - allows React to keep UI responsive during navigation\n const [isPending, startTransition] = useTransition();\n\n // Build routes once on mount (client-side only)\n const { routes, NotFound } = useMemo(() => {\n if (typeof window === \"undefined\") {\n return { routes: [], NotFound: undefined };\n }\n const result = buildRoutes();\n // Store routes globally for Link prefetching\n // In dev mode, also update the global reference to survive HMR\n if (import.meta.env?.DEV) {\n const globalWindow = window as typeof window & { __heliumGlobalRoutes?: RouteEntry[] };\n globalWindow.__heliumGlobalRoutes = result.routes;\n // Update the module-level reference as well\n globalRoutes.length = 0;\n globalRoutes.push(...result.routes);\n } else {\n globalRoutes = result.routes;\n }\n return result;\n }, []);\n\n // Use useSyncExternalStore to subscribe to location changes\n // This ensures synchronous updates when location changes\n const state = useSyncExternalStore(subscribeToLocation, getLocationSnapshot, getServerSnapshot);\n\n const match = useMemo(() => matchRoute(state.path, routes), [state.path, routes]);\n\n // Use matched params, or fall back to re-extracting from path using global routes during HMR\n const currentParams = match?.params ?? (import.meta.env?.DEV ? extractParamsFromPath(state.path) : {});\n\n // In dev mode, always read fresh searchParams from URL to handle HMR edge cases\n const currentSearchParams = import.meta.env?.DEV ? new URLSearchParams(window.location.search) : state.searchParams;\n\n const routerValue: RouterContext = {\n path: state.path,\n params: currentParams,\n searchParams: currentSearchParams,\n push: (href, options) => {\n // Wrap navigation in startTransition for smoother updates\n startTransition(() => {\n navigate(href, { scrollToTop: options?.scrollToTop });\n });\n },\n replace: (href, options) => {\n startTransition(() => {\n navigate(href, { replace: true, scrollToTop: options?.scrollToTop });\n });\n },\n on: (event, listener) => routerEventEmitter.on(event, listener),\n status: match ? 200 : 404,\n isNavigating: state.isNavigating,\n isPending,\n };\n\n if (!match) {\n const NotFoundComp = NotFound ?? (() => <div>Not found</div>);\n const content = <NotFoundComp />;\n\n return <RouterContext.Provider value={routerValue}>{AppShell ? <AppShell Component={NotFoundComp} pageProps={{}} /> : content}</RouterContext.Provider>;\n }\n\n const Page = match.route.Component as ComponentType<{ params: Record<string, string | string[]>; searchParams: URLSearchParams }>;\n const pageProps = {\n params: match.params,\n searchParams: currentSearchParams,\n };\n\n // Create a wrapped component that includes all layouts\n const WrappedPage = () => {\n let content = <Page {...pageProps} />;\n // Wrap page with layouts (from outer to inner)\n for (let i = match.route.layouts.length - 1; i >= 0; i--) {\n const Layout = match.route.layouts[i];\n content = <Layout>{content}</Layout>;\n }\n return content;\n };\n\n const finalContent = AppShell ? <AppShell Component={WrappedPage} pageProps={{}} /> : <WrappedPage />;\n\n return <RouterContext.Provider value={routerValue}>{finalContent}</RouterContext.Provider>;\n}\n"]}
|
package/dist/client/cache.d.ts
CHANGED
|
@@ -5,4 +5,21 @@ export declare function has(key: string): boolean;
|
|
|
5
5
|
export declare function invalidateByMethod(methodId: string): void;
|
|
6
6
|
export declare function subscribeInvalidations(listener: (methodId: string) => void): () => void;
|
|
7
7
|
export declare function invalidateAll(): void;
|
|
8
|
+
/**
|
|
9
|
+
* Check if a fetch is currently pending for a given cache key.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isPending(key: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Get an existing pending fetch promise, or undefined if none.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPendingFetch<T>(key: string): Promise<T> | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Register a pending fetch. Returns the promise.
|
|
18
|
+
* If a fetch for this key is already pending, returns the existing promise.
|
|
19
|
+
*/
|
|
20
|
+
export declare function setPendingFetch<T>(key: string, promise: Promise<T>): Promise<T>;
|
|
21
|
+
/**
|
|
22
|
+
* Clear a pending fetch (if you need to cancel/reset).
|
|
23
|
+
*/
|
|
24
|
+
export declare function clearPendingFetch(key: string): void;
|
|
8
25
|
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"AAqCA,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAEhE;AASD,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAYjD;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,QAM5D;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAYxC;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,QAalD;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,cAK1E;AAED,wBAAgB,aAAa,SAkB5B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAEtE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAW/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEnD"}
|
package/dist/client/cache.js
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Preserve cache across HMR by attaching to window in dev mode
|
|
2
|
+
let store;
|
|
3
|
+
let listeners;
|
|
4
|
+
let pendingFetches;
|
|
5
|
+
if (typeof window !== "undefined" && import.meta.env?.DEV) {
|
|
6
|
+
const globalWindow = window;
|
|
7
|
+
if (!globalWindow.__heliumCacheStore) {
|
|
8
|
+
globalWindow.__heliumCacheStore = new Map();
|
|
9
|
+
}
|
|
10
|
+
if (!globalWindow.__heliumCacheListeners) {
|
|
11
|
+
globalWindow.__heliumCacheListeners = new Set();
|
|
12
|
+
}
|
|
13
|
+
if (!globalWindow.__heliumPendingFetches) {
|
|
14
|
+
globalWindow.__heliumPendingFetches = new Map();
|
|
15
|
+
}
|
|
16
|
+
store = globalWindow.__heliumCacheStore;
|
|
17
|
+
listeners = globalWindow.__heliumCacheListeners;
|
|
18
|
+
pendingFetches = globalWindow.__heliumPendingFetches;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
store = new Map();
|
|
22
|
+
listeners = new Set();
|
|
23
|
+
pendingFetches = new Map();
|
|
24
|
+
}
|
|
3
25
|
const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
4
26
|
export function cacheKey(methodId, args) {
|
|
5
27
|
return JSON.stringify([methodId, args ?? null]);
|
|
@@ -79,4 +101,38 @@ export function invalidateAll() {
|
|
|
79
101
|
}
|
|
80
102
|
}
|
|
81
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if a fetch is currently pending for a given cache key.
|
|
106
|
+
*/
|
|
107
|
+
export function isPending(key) {
|
|
108
|
+
return pendingFetches.has(key);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get an existing pending fetch promise, or undefined if none.
|
|
112
|
+
*/
|
|
113
|
+
export function getPendingFetch(key) {
|
|
114
|
+
return pendingFetches.get(key);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Register a pending fetch. Returns the promise.
|
|
118
|
+
* If a fetch for this key is already pending, returns the existing promise.
|
|
119
|
+
*/
|
|
120
|
+
export function setPendingFetch(key, promise) {
|
|
121
|
+
const existing = pendingFetches.get(key);
|
|
122
|
+
if (existing) {
|
|
123
|
+
return existing;
|
|
124
|
+
}
|
|
125
|
+
pendingFetches.set(key, promise);
|
|
126
|
+
// Clean up when done
|
|
127
|
+
promise.finally(() => {
|
|
128
|
+
pendingFetches.delete(key);
|
|
129
|
+
});
|
|
130
|
+
return promise;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Clear a pending fetch (if you need to cancel/reset).
|
|
134
|
+
*/
|
|
135
|
+
export function clearPendingFetch(key) {
|
|
136
|
+
pendingFetches.delete(key);
|
|
137
|
+
}
|
|
82
138
|
//# sourceMappingURL=cache.js.map
|
package/dist/client/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"AAMA,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"AAMA,+DAA+D;AAC/D,IAAI,KAA8B,CAAC;AACnC,IAAI,SAA0C,CAAC;AAC/C,IAAI,cAA6C,CAAC;AAElD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;IACxD,MAAM,YAAY,GAAG,MAIpB,CAAC;IACF,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,CAAC;QACnC,YAAY,CAAC,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAC;IAChD,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,sBAAsB,EAAE,CAAC;QACvC,YAAY,CAAC,sBAAsB,GAAG,IAAI,GAAG,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,sBAAsB,EAAE,CAAC;QACvC,YAAY,CAAC,sBAAsB,GAAG,IAAI,GAAG,EAAE,CAAC;IACpD,CAAC;IACD,KAAK,GAAG,YAAY,CAAC,kBAAkB,CAAC;IACxC,SAAS,GAAG,YAAY,CAAC,sBAAsB,CAAC;IAChD,cAAc,GAAG,YAAY,CAAC,sBAAsB,CAAC;AACzD,CAAC;KAAM,CAAC;IACJ,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;IAClB,SAAS,GAAG,IAAI,GAAG,EAAE,CAAC;IACtB,cAAc,GAAG,IAAI,GAAG,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAE/C,MAAM,UAAU,QAAQ,CAAC,QAAgB,EAAE,IAAa;IACpD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,SAAS,CAAC,KAAiB;IAChC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,GAAG,CAAI,GAAW;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,OAAO,KAAK,CAAC,KAAsB,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,KAAc,EAAE,GAAY;IACzD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;QACX,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,GAAG,EAAE,GAAG,IAAI,WAAW;KAC1B,CAAC,CAAC;AACP,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAW;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IAC/C,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,QAAQ,GAAG,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClB,WAAW,GAAG,IAAI,CAAC;QACvB,CAAC;IACL,CAAC;IACD,IAAI,WAAW,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;QAChC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC/B,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAoC;IACvE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,OAAO,GAAG,EAAE;QACR,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,aAAa;IACzB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7C,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,wBAAwB;QAC5B,CAAC;IACL,CAAC;IACD,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC/B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC/B,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACL,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACjC,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAI,GAAW;IAC1C,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAA2B,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAI,GAAW,EAAE,OAAmB;IAC/D,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,QAAQ,EAAE,CAAC;QACX,OAAO,QAAsB,CAAC;IAClC,CAAC;IACD,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACjC,qBAAqB;IACrB,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE;QACjB,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IACzC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["interface CacheEntry {\n value: unknown;\n timestamp: number;\n ttl?: number; // TTL in milliseconds\n}\n\n// Preserve cache across HMR by attaching to window in dev mode\nlet store: Map<string, CacheEntry>;\nlet listeners: Set<(methodId: string) => void>;\nlet pendingFetches: Map<string, Promise<unknown>>;\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const globalWindow = window as typeof window & {\n __heliumCacheStore?: Map<string, CacheEntry>;\n __heliumCacheListeners?: Set<(methodId: string) => void>;\n __heliumPendingFetches?: Map<string, Promise<unknown>>;\n };\n if (!globalWindow.__heliumCacheStore) {\n globalWindow.__heliumCacheStore = new Map();\n }\n if (!globalWindow.__heliumCacheListeners) {\n globalWindow.__heliumCacheListeners = new Set();\n }\n if (!globalWindow.__heliumPendingFetches) {\n globalWindow.__heliumPendingFetches = new Map();\n }\n store = globalWindow.__heliumCacheStore;\n listeners = globalWindow.__heliumCacheListeners;\n pendingFetches = globalWindow.__heliumPendingFetches;\n} else {\n store = new Map();\n listeners = new Set();\n pendingFetches = new Map();\n}\n\nconst DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport function cacheKey(methodId: string, args: unknown): string {\n return JSON.stringify([methodId, args ?? null]);\n}\n\nfunction isExpired(entry: CacheEntry): boolean {\n if (!entry.ttl) {\n return false;\n }\n return Date.now() - entry.timestamp > entry.ttl;\n}\n\nexport function get<T>(key: string): T | undefined {\n const entry = store.get(key);\n if (!entry) {\n return undefined;\n }\n\n if (isExpired(entry)) {\n store.delete(key);\n return undefined;\n }\n\n return entry.value as T | undefined;\n}\n\nexport function set(key: string, value: unknown, ttl?: number) {\n store.set(key, {\n value,\n timestamp: Date.now(),\n ttl: ttl ?? DEFAULT_TTL,\n });\n}\n\nexport function has(key: string): boolean {\n const entry = store.get(key);\n if (!entry) {\n return false;\n }\n\n if (isExpired(entry)) {\n store.delete(key);\n return false;\n }\n\n return true;\n}\n\nexport function invalidateByMethod(methodId: string) {\n let invalidated = false;\n for (const key of store.keys()) {\n if (key.startsWith(`[\"${methodId}\"`)) {\n store.delete(key);\n invalidated = true;\n }\n }\n if (invalidated || listeners.size) {\n for (const listener of listeners) {\n listener(methodId);\n }\n }\n}\n\nexport function subscribeInvalidations(listener: (methodId: string) => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function invalidateAll() {\n const methodIds = new Set<string>();\n for (const key of store.keys()) {\n try {\n const parsed = JSON.parse(key);\n if (Array.isArray(parsed) && parsed.length > 0) {\n methodIds.add(parsed[0]);\n }\n } catch {\n // Ignore malformed keys\n }\n }\n store.clear();\n for (const methodId of methodIds) {\n for (const listener of listeners) {\n listener(methodId);\n }\n }\n}\n\n/**\n * Check if a fetch is currently pending for a given cache key.\n */\nexport function isPending(key: string): boolean {\n return pendingFetches.has(key);\n}\n\n/**\n * Get an existing pending fetch promise, or undefined if none.\n */\nexport function getPendingFetch<T>(key: string): Promise<T> | undefined {\n return pendingFetches.get(key) as Promise<T> | undefined;\n}\n\n/**\n * Register a pending fetch. Returns the promise.\n * If a fetch for this key is already pending, returns the existing promise.\n */\nexport function setPendingFetch<T>(key: string, promise: Promise<T>): Promise<T> {\n const existing = pendingFetches.get(key);\n if (existing) {\n return existing as Promise<T>;\n }\n pendingFetches.set(key, promise);\n // Clean up when done\n promise.finally(() => {\n pendingFetches.delete(key);\n });\n return promise;\n}\n\n/**\n * Clear a pending fetch (if you need to cancel/reset).\n */\nexport function clearPendingFetch(key: string): void {\n pendingFetches.delete(key);\n}\n"]}
|
package/dist/client/types.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a server method stub that can be passed to client hooks like useFetch/useCall.
|
|
3
|
+
* This type is compatible with HeliumMethodDef so that methods defined with defineMethod
|
|
4
|
+
* can be passed directly to client hooks with proper type inference.
|
|
5
|
+
*
|
|
6
|
+
* Note: Using method shorthand syntax for `handler` makes it bivariant,
|
|
7
|
+
* allowing HeliumMethodDef (with HeliumContext) to be assignable to MethodStub.
|
|
8
|
+
*/
|
|
1
9
|
export type MethodStub<TArgs = unknown, TResult = unknown> = {
|
|
10
|
+
__kind: "method";
|
|
2
11
|
__id: string;
|
|
3
|
-
|
|
4
|
-
__result?: TResult;
|
|
12
|
+
handler(args: TArgs, ctx: unknown): Promise<TResult> | TResult;
|
|
5
13
|
};
|
|
6
14
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,KAAK,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,CAAC,KAAK,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI;IACzD,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAClE,CAAC"}
|
package/dist/client/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"","sourcesContent":["
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Represents a server method stub that can be passed to client hooks like useFetch/useCall.\n * This type is compatible with HeliumMethodDef so that methods defined with defineMethod\n * can be passed directly to client hooks with proper type inference.\n *\n * Note: Using method shorthand syntax for `handler` makes it bivariant,\n * allowing HeliumMethodDef (with HeliumContext) to be assignable to MethodStub.\n */\nexport type MethodStub<TArgs = unknown, TResult = unknown> = {\n __kind: \"method\";\n __id: string;\n handler(args: TArgs, ctx: unknown): Promise<TResult> | TResult;\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCall.d.ts","sourceRoot":"","sources":["../../src/client/useCall.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;GAMG;AACH,KAAK,cAAc,GAAG;IAClB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,GAAE,cAAmB;;
|
|
1
|
+
{"version":3,"file":"useCall.d.ts","sourceRoot":"","sources":["../../src/client/useCall.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;GAMG;AACH,KAAK,cAAc,GAAG;IAClB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,OAAO,GAAE,cAAmB;;iBAe9D,KAAK,KAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;;;;EAqB7E"}
|
package/dist/client/useCall.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
2
|
import { invalidateByMethod } from "./cache.js";
|
|
3
3
|
import { rpcCall } from "./rpcClient.js";
|
|
4
4
|
/**
|
|
@@ -15,26 +15,34 @@ export function useCall(method, options = {}) {
|
|
|
15
15
|
const [isCalling, setCalling] = useState(false);
|
|
16
16
|
const [error, setError] = useState(null);
|
|
17
17
|
const [stats, setStats] = useState(null);
|
|
18
|
-
|
|
18
|
+
// Use refs to store latest values without causing callback recreation
|
|
19
|
+
const methodIdRef = useRef(method.__id);
|
|
20
|
+
const optionsRef = useRef(options);
|
|
21
|
+
// Update refs on each render
|
|
22
|
+
methodIdRef.current = method.__id;
|
|
23
|
+
optionsRef.current = options;
|
|
24
|
+
// Memoized call function - stable reference across renders
|
|
25
|
+
const call = useCallback(async (args) => {
|
|
19
26
|
setCalling(true);
|
|
20
27
|
setError(null);
|
|
21
28
|
try {
|
|
22
|
-
const result = await rpcCall(
|
|
29
|
+
const result = await rpcCall(methodIdRef.current, args);
|
|
23
30
|
setData(result.data);
|
|
24
31
|
setStats(result.stats);
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
optionsRef.current.invalidate?.forEach((m) => invalidateByMethod(m.__id));
|
|
33
|
+
optionsRef.current.onSuccess?.(result.data);
|
|
27
34
|
return result.data;
|
|
28
35
|
}
|
|
29
36
|
catch (err) {
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
const errorObj = err;
|
|
38
|
+
setError(errorObj.error ?? "Unknown error");
|
|
39
|
+
setStats(errorObj.stats ?? null);
|
|
32
40
|
return undefined;
|
|
33
41
|
}
|
|
34
42
|
finally {
|
|
35
43
|
setCalling(false);
|
|
36
44
|
}
|
|
37
|
-
}
|
|
45
|
+
}, []); // Empty deps - uses refs for latest values
|
|
38
46
|
return { data, call, isCalling, error, stats };
|
|
39
47
|
}
|
|
40
48
|
//# sourceMappingURL=useCall.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCall.js","sourceRoot":"","sources":["../../src/client/useCall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useCall.js","sourceRoot":"","sources":["../../src/client/useCall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAezC;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAiB,MAAkC,EAAE,UAA0B,EAAE;IACpG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAsB,SAAS,CAAC,CAAC;IACjE,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAC;IAE1D,sEAAsE;IACtE,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAEnC,6BAA6B;IAC7B,WAAW,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;IAClC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,2DAA2D;IAC3D,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,IAAW,EAAgC,EAAE;QACzE,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAiB,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrB,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvB,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAC1E,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC,IAAI,CAAC;QACvB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,GAA2C,CAAC;YAC7D,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,eAAe,CAAC,CAAC;YAC5C,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;YACjC,OAAO,SAAS,CAAC;QACrB,CAAC;gBAAS,CAAC;YACP,UAAU,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,2CAA2C;IAEnD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACnD,CAAC","sourcesContent":["import { useCallback, useRef, useState } from \"react\";\n\nimport type { RpcStats } from \"../runtime/protocol.js\";\nimport { invalidateByMethod } from \"./cache.js\";\nimport { rpcCall } from \"./rpcClient.js\";\nimport type { MethodStub } from \"./types.js\";\n\n/**\n * Options passed to `useCall`.\n *\n * - invalidate: array of MethodStubs whose cache entries will be invalidated\n * when this call completes successfully (useful to refresh related reads).\n * - onSuccess: optional callback that receives the result on success.\n */\ntype UseCallOptions = {\n invalidate?: MethodStub[];\n onSuccess?: (result: unknown) => void;\n};\n\n/**\n * React hook for imperative RPC calls (commonly used for mutations).\n *\n * @template TArgs - argument type for the method\n * @template TResult - expected result type\n * @param method - MethodStub identifying the server method to call\n * @param options - UseCallOptions to control invalidation / callbacks\n * @returns { data, call, isCalling, error, stats } where `call(args)` triggers the RPC and returns the result\n */\nexport function useCall<TArgs, TResult>(method: MethodStub<TArgs, TResult>, options: UseCallOptions = {}) {\n const [data, setData] = useState<TResult | undefined>(undefined);\n const [isCalling, setCalling] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [stats, setStats] = useState<RpcStats | null>(null);\n\n // Use refs to store latest values without causing callback recreation\n const methodIdRef = useRef(method.__id);\n const optionsRef = useRef(options);\n\n // Update refs on each render\n methodIdRef.current = method.__id;\n optionsRef.current = options;\n\n // Memoized call function - stable reference across renders\n const call = useCallback(async (args: TArgs): Promise<TResult | undefined> => {\n setCalling(true);\n setError(null);\n try {\n const result = await rpcCall<TResult, TArgs>(methodIdRef.current, args);\n setData(result.data);\n setStats(result.stats);\n optionsRef.current.invalidate?.forEach((m) => invalidateByMethod(m.__id));\n optionsRef.current.onSuccess?.(result.data);\n return result.data;\n } catch (err: unknown) {\n const errorObj = err as { error?: string; stats?: RpcStats };\n setError(errorObj.error ?? \"Unknown error\");\n setStats(errorObj.stats ?? null);\n return undefined;\n } finally {\n setCalling(false);\n }\n }, []); // Empty deps - uses refs for latest values\n\n return { data, call, isCalling, error, stats };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFetch.d.ts","sourceRoot":"","sources":["../../src/client/useFetch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"useFetch.d.ts","sourceRoot":"","sources":["../../src/client/useFetch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAsED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,eAAe;;;;;2BA0OvE,OAAO;EAGnD"}
|