@object-ui/react 0.5.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/dist/LazyPluginLoader.d.ts +44 -0
  4. package/dist/LazyPluginLoader.js +92 -3
  5. package/dist/SchemaRenderer.d.ts +20 -1
  6. package/dist/SchemaRenderer.js +76 -13
  7. package/dist/components/form/FieldFactory.js +44 -40
  8. package/dist/components/form/FormRenderer.js +45 -3
  9. package/dist/context/ActionContext.d.ts +76 -0
  10. package/dist/context/ActionContext.js +139 -0
  11. package/dist/context/DndContext.d.ts +135 -0
  12. package/dist/context/DndContext.js +120 -0
  13. package/dist/context/NotificationContext.d.ts +113 -0
  14. package/dist/context/NotificationContext.js +130 -0
  15. package/dist/context/ThemeContext.d.ts +94 -0
  16. package/dist/context/ThemeContext.js +215 -0
  17. package/dist/context/index.d.ts +4 -0
  18. package/dist/context/index.js +4 -0
  19. package/dist/hooks/index.d.ts +22 -0
  20. package/dist/hooks/index.js +22 -0
  21. package/dist/hooks/useActionRunner.d.ts +21 -7
  22. package/dist/hooks/useActionRunner.js +27 -7
  23. package/dist/hooks/useAnimation.d.ts +46 -0
  24. package/dist/hooks/useAnimation.js +59 -0
  25. package/dist/hooks/useClientNotifications.d.ts +53 -0
  26. package/dist/hooks/useClientNotifications.js +190 -0
  27. package/dist/hooks/useColumnSummary.d.ts +38 -0
  28. package/dist/hooks/useColumnSummary.js +54 -0
  29. package/dist/hooks/useCrudShortcuts.d.ts +39 -0
  30. package/dist/hooks/useCrudShortcuts.js +96 -0
  31. package/dist/hooks/useDensityMode.d.ts +48 -0
  32. package/dist/hooks/useDensityMode.js +60 -0
  33. package/dist/hooks/useDiscovery.d.ts +78 -0
  34. package/dist/hooks/useDiscovery.js +96 -0
  35. package/dist/hooks/useDynamicApp.d.ts +70 -0
  36. package/dist/hooks/useDynamicApp.js +92 -0
  37. package/dist/hooks/useETagCache.d.ts +75 -0
  38. package/dist/hooks/useETagCache.js +306 -0
  39. package/dist/hooks/useFocusManagement.d.ts +89 -0
  40. package/dist/hooks/useFocusManagement.js +188 -0
  41. package/dist/hooks/useFocusTrap.d.ts +41 -0
  42. package/dist/hooks/useFocusTrap.js +103 -0
  43. package/dist/hooks/useKeyboardShortcuts.d.ts +49 -0
  44. package/dist/hooks/useKeyboardShortcuts.js +86 -0
  45. package/dist/hooks/useNavigationOverlay.d.ts +78 -0
  46. package/dist/hooks/useNavigationOverlay.js +112 -0
  47. package/dist/hooks/useOffline.d.ts +96 -0
  48. package/dist/hooks/useOffline.js +185 -0
  49. package/dist/hooks/usePageTransition.d.ts +60 -0
  50. package/dist/hooks/usePageTransition.js +111 -0
  51. package/dist/hooks/usePageVariables.d.ts +67 -0
  52. package/dist/hooks/usePageVariables.js +109 -0
  53. package/dist/hooks/usePerformance.d.ts +87 -0
  54. package/dist/hooks/usePerformance.js +165 -0
  55. package/dist/hooks/usePerformanceBudget.d.ts +78 -0
  56. package/dist/hooks/usePerformanceBudget.js +209 -0
  57. package/dist/hooks/useReducedMotion.d.ts +28 -0
  58. package/dist/hooks/useReducedMotion.js +48 -0
  59. package/dist/hooks/useSchemaPersistence.d.ts +87 -0
  60. package/dist/hooks/useSchemaPersistence.js +173 -0
  61. package/dist/hooks/useViewData.d.ts +73 -0
  62. package/dist/hooks/useViewData.js +135 -0
  63. package/dist/hooks/useViewSharing.d.ts +57 -0
  64. package/dist/hooks/useViewSharing.js +74 -0
  65. package/dist/hooks/useViewTransition.d.ts +54 -0
  66. package/dist/hooks/useViewTransition.js +83 -0
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +2 -0
  69. package/dist/utils/i18n.d.ts +10 -0
  70. package/dist/utils/i18n.js +12 -0
  71. package/package.json +7 -5
  72. package/src/LazyPluginLoader.tsx +129 -7
  73. package/src/SchemaRenderer.tsx +122 -13
  74. package/src/__tests__/LazyPluginLoader.test.tsx +97 -1
  75. package/src/__tests__/SchemaRenderer.aria.test.tsx +118 -0
  76. package/src/__tests__/SchemaRenderer.errorBoundary.test.tsx +114 -0
  77. package/src/__tests__/SchemaRendererProvider.smoke.test.tsx +132 -0
  78. package/src/__tests__/ThemeProvider.test.tsx +451 -0
  79. package/src/__tests__/useNavigationOverlay.test.ts +416 -0
  80. package/src/__tests__/useOffline.test.ts +198 -0
  81. package/src/__tests__/usePageTransition.test.ts +214 -0
  82. package/src/__tests__/usePageVariables.test.tsx +334 -0
  83. package/src/__tests__/usePerformance.test.ts +137 -0
  84. package/src/__tests__/useViewData.test.tsx +422 -0
  85. package/src/__tests__/view-switching.test.tsx +456 -0
  86. package/src/components/form/FieldFactory.tsx +51 -46
  87. package/src/components/form/FormRenderer.test.tsx +145 -0
  88. package/src/components/form/FormRenderer.tsx +52 -3
  89. package/src/context/ActionContext.tsx +213 -0
  90. package/src/context/DndContext.tsx +260 -0
  91. package/src/context/NotificationContext.tsx +280 -0
  92. package/src/context/ThemeContext.tsx +325 -0
  93. package/src/context/__tests__/ActionContext.test.tsx +174 -0
  94. package/src/context/__tests__/DndContext.test.tsx +150 -0
  95. package/src/context/__tests__/NotificationContext.test.tsx +176 -0
  96. package/src/context/index.ts +4 -0
  97. package/src/hooks/__tests__/useActionRunner.test.ts +153 -0
  98. package/src/hooks/__tests__/useAnimation.test.ts +82 -0
  99. package/src/hooks/__tests__/useColumnSummary.test.ts +93 -0
  100. package/src/hooks/__tests__/useDensityMode.test.ts +81 -0
  101. package/src/hooks/__tests__/useDiscovery.test.tsx +149 -0
  102. package/src/hooks/__tests__/useDynamicApp.test.ts +161 -0
  103. package/src/hooks/__tests__/useETagCache.test.ts +153 -0
  104. package/src/hooks/__tests__/useExpression.test.ts +86 -0
  105. package/src/hooks/__tests__/useFocusTrap.test.ts +47 -0
  106. package/src/hooks/__tests__/useKeyboardShortcuts.test.ts +144 -0
  107. package/src/hooks/__tests__/usePerformanceBudget.test.ts +157 -0
  108. package/src/hooks/__tests__/useSchemaPersistence.test.ts +220 -0
  109. package/src/hooks/__tests__/useViewSharing.test.ts +121 -0
  110. package/src/hooks/__tests__/useViewTransition.test.ts +61 -0
  111. package/src/hooks/index.ts +22 -1
  112. package/src/hooks/useActionRunner.ts +49 -11
  113. package/src/hooks/useAnimation.ts +117 -0
  114. package/src/hooks/useClientNotifications.ts +251 -0
  115. package/src/hooks/useColumnSummary.ts +87 -0
  116. package/src/hooks/useCrudShortcuts.ts +126 -0
  117. package/src/hooks/useDensityMode.ts +102 -0
  118. package/src/hooks/useDiscovery.ts +141 -0
  119. package/src/hooks/useDynamicApp.ts +142 -0
  120. package/src/hooks/useETagCache.ts +404 -0
  121. package/src/hooks/useFocusManagement.ts +248 -0
  122. package/src/hooks/useFocusTrap.ts +138 -0
  123. package/src/hooks/useKeyboardShortcuts.ts +119 -0
  124. package/src/hooks/useNavigationOverlay.ts +180 -0
  125. package/src/hooks/useOffline.ts +301 -0
  126. package/src/hooks/usePageTransition.ts +187 -0
  127. package/src/hooks/usePageVariables.tsx +157 -0
  128. package/src/hooks/usePerformance.ts +246 -0
  129. package/src/hooks/usePerformanceBudget.ts +313 -0
  130. package/src/hooks/useReducedMotion.ts +53 -0
  131. package/src/hooks/useSchemaPersistence.ts +222 -0
  132. package/src/hooks/useViewData.ts +201 -0
  133. package/src/hooks/useViewSharing.ts +140 -0
  134. package/src/hooks/useViewTransition.ts +144 -0
  135. package/src/index.ts +21 -0
  136. package/src/utils/i18n.ts +10 -0
  137. package/tsconfig.json +1 -0
  138. package/tsconfig.tsbuildinfo +1 -1
  139. package/dist/__tests__/LazyPluginLoader.test.d.ts +0 -8
  140. package/dist/__tests__/LazyPluginLoader.test.js +0 -49
  141. package/dist/components/form/FormRenderer.test.d.ts +0 -8
  142. package/dist/components/form/FormRenderer.test.js +0 -534
  143. package/dist/index.test.d.ts +0 -8
  144. package/dist/index.test.js +0 -13
@@ -0,0 +1,78 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * Discovery service information structure.
10
+ * Represents server capabilities and service status.
11
+ */
12
+ export interface DiscoveryInfo {
13
+ /** Server name and version */
14
+ name?: string;
15
+ version?: string;
16
+ /** Service availability status */
17
+ services?: {
18
+ /** Authentication service status */
19
+ auth?: {
20
+ enabled: boolean;
21
+ status?: 'available' | 'unavailable';
22
+ message?: string;
23
+ };
24
+ /** Data access service status */
25
+ data?: {
26
+ enabled: boolean;
27
+ status?: 'available' | 'unavailable';
28
+ };
29
+ /** Metadata service status */
30
+ metadata?: {
31
+ enabled: boolean;
32
+ status?: 'available' | 'unavailable';
33
+ };
34
+ [key: string]: any;
35
+ };
36
+ /** API capabilities */
37
+ capabilities?: string[];
38
+ /** Additional discovery metadata */
39
+ [key: string]: any;
40
+ }
41
+ /**
42
+ * Hook to access discovery information from the ObjectStack server.
43
+ *
44
+ * This hook retrieves server capabilities and service status, which can be used
45
+ * to conditionally enable/disable features based on server configuration.
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * function App() {
50
+ * const { discovery, isLoading } = useDiscovery();
51
+ *
52
+ * if (isLoading) {
53
+ * return <LoadingScreen />;
54
+ * }
55
+ *
56
+ * // Check if auth is enabled on the server
57
+ * const authEnabled = discovery?.services?.auth?.enabled ?? true;
58
+ *
59
+ * return (
60
+ * <div>
61
+ * {authEnabled ? <AuthProvider>...</AuthProvider> : <App />}
62
+ * </div>
63
+ * );
64
+ * }
65
+ * ```
66
+ *
67
+ * @returns Discovery information and loading state
68
+ */
69
+ export declare function useDiscovery(): {
70
+ discovery: DiscoveryInfo | null;
71
+ isLoading: boolean;
72
+ error: Error | null;
73
+ /**
74
+ * Check if authentication is enabled on the server.
75
+ * Defaults to true if discovery data is not available.
76
+ */
77
+ isAuthEnabled: boolean;
78
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ import { useState, useEffect, useContext } from 'react';
9
+ import { SchemaRendererContext } from '../context/SchemaRendererContext';
10
+ /**
11
+ * Hook to access discovery information from the ObjectStack server.
12
+ *
13
+ * This hook retrieves server capabilities and service status, which can be used
14
+ * to conditionally enable/disable features based on server configuration.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * function App() {
19
+ * const { discovery, isLoading } = useDiscovery();
20
+ *
21
+ * if (isLoading) {
22
+ * return <LoadingScreen />;
23
+ * }
24
+ *
25
+ * // Check if auth is enabled on the server
26
+ * const authEnabled = discovery?.services?.auth?.enabled ?? true;
27
+ *
28
+ * return (
29
+ * <div>
30
+ * {authEnabled ? <AuthProvider>...</AuthProvider> : <App />}
31
+ * </div>
32
+ * );
33
+ * }
34
+ * ```
35
+ *
36
+ * @returns Discovery information and loading state
37
+ */
38
+ export function useDiscovery() {
39
+ const context = useContext(SchemaRendererContext);
40
+ const dataSource = context?.dataSource;
41
+ const [discovery, setDiscovery] = useState(null);
42
+ const [isLoading, setIsLoading] = useState(true);
43
+ const [error, setError] = useState(null);
44
+ useEffect(() => {
45
+ let cancelled = false;
46
+ async function fetchDiscovery() {
47
+ if (!dataSource) {
48
+ setIsLoading(false);
49
+ return;
50
+ }
51
+ try {
52
+ setIsLoading(true);
53
+ // Check if dataSource has getDiscovery method
54
+ if (typeof dataSource.getDiscovery === 'function') {
55
+ const discoveryData = await dataSource.getDiscovery();
56
+ if (!cancelled) {
57
+ setDiscovery(discoveryData);
58
+ setError(null);
59
+ }
60
+ }
61
+ else {
62
+ // DataSource doesn't support discovery
63
+ if (!cancelled) {
64
+ setDiscovery(null);
65
+ setError(null);
66
+ }
67
+ }
68
+ }
69
+ catch (err) {
70
+ if (!cancelled) {
71
+ setError(err instanceof Error ? err : new Error('Failed to fetch discovery'));
72
+ setDiscovery(null);
73
+ }
74
+ }
75
+ finally {
76
+ if (!cancelled) {
77
+ setIsLoading(false);
78
+ }
79
+ }
80
+ }
81
+ fetchDiscovery();
82
+ return () => {
83
+ cancelled = true;
84
+ };
85
+ }, [dataSource]);
86
+ return {
87
+ discovery,
88
+ isLoading,
89
+ error,
90
+ /**
91
+ * Check if authentication is enabled on the server.
92
+ * Defaults to true if discovery data is not available.
93
+ */
94
+ isAuthEnabled: discovery?.services?.auth?.enabled ?? true,
95
+ };
96
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * Options for dynamic app configuration loading.
10
+ */
11
+ export interface DynamicAppOptions<TAppConfig = unknown> {
12
+ /** Application identifier to load */
13
+ appId: string;
14
+ /**
15
+ * Static (fallback) configuration used when the server does not
16
+ * provide an app definition or is unreachable.
17
+ */
18
+ staticConfig?: TAppConfig;
19
+ /**
20
+ * Data source adapter with `getApp` and `invalidateCache` methods.
21
+ * Typically an ObjectStackAdapter instance.
22
+ */
23
+ adapter?: {
24
+ getApp: (appId: string) => Promise<unknown | null>;
25
+ invalidateCache?: (key?: string) => void;
26
+ getObjectSchema?: (objectName: string) => Promise<unknown>;
27
+ };
28
+ /** Whether to attempt loading from the server (default: true) */
29
+ enabled?: boolean;
30
+ }
31
+ /**
32
+ * Result returned by useDynamicApp.
33
+ */
34
+ export interface DynamicAppResult<TAppConfig = unknown> {
35
+ /** The resolved app configuration (server or static fallback) */
36
+ config: TAppConfig | null;
37
+ /** Whether the configuration is currently loading */
38
+ isLoading: boolean;
39
+ /** Error from the most recent load attempt */
40
+ error: Error | null;
41
+ /** Whether the config was loaded from the server (vs static fallback) */
42
+ isServerConfig: boolean;
43
+ /** Re-fetch the app configuration from the server, invalidating cache */
44
+ refresh: () => Promise<void>;
45
+ }
46
+ /**
47
+ * React hook for dynamic app configuration loading.
48
+ *
49
+ * Fetches app configuration from the server via `adapter.getApp(appId)`,
50
+ * falling back to `staticConfig` when the server is unavailable.
51
+ * Supports cache-based hot-reload via the `refresh()` callback.
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * import { useDynamicApp } from '@object-ui/react';
56
+ * import staticAppConfig from '../config/app.json';
57
+ *
58
+ * function App() {
59
+ * const { config, isLoading, refresh } = useDynamicApp({
60
+ * appId: 'crm',
61
+ * staticConfig: staticAppConfig,
62
+ * adapter: dataSource,
63
+ * });
64
+ *
65
+ * if (isLoading) return <LoadingScreen />;
66
+ * return <Console config={config} onRefresh={refresh} />;
67
+ * }
68
+ * ```
69
+ */
70
+ export declare function useDynamicApp<TAppConfig = unknown>(options: DynamicAppOptions<TAppConfig>): DynamicAppResult<TAppConfig>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ import { useState, useEffect, useCallback, useRef } from 'react';
9
+ /**
10
+ * React hook for dynamic app configuration loading.
11
+ *
12
+ * Fetches app configuration from the server via `adapter.getApp(appId)`,
13
+ * falling back to `staticConfig` when the server is unavailable.
14
+ * Supports cache-based hot-reload via the `refresh()` callback.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * import { useDynamicApp } from '@object-ui/react';
19
+ * import staticAppConfig from '../config/app.json';
20
+ *
21
+ * function App() {
22
+ * const { config, isLoading, refresh } = useDynamicApp({
23
+ * appId: 'crm',
24
+ * staticConfig: staticAppConfig,
25
+ * adapter: dataSource,
26
+ * });
27
+ *
28
+ * if (isLoading) return <LoadingScreen />;
29
+ * return <Console config={config} onRefresh={refresh} />;
30
+ * }
31
+ * ```
32
+ */
33
+ export function useDynamicApp(options) {
34
+ const { appId, staticConfig, adapter, enabled = true } = options;
35
+ const [config, setConfig] = useState(staticConfig ?? null);
36
+ const [isLoading, setIsLoading] = useState(enabled && !!adapter);
37
+ const [error, setError] = useState(null);
38
+ const [isServerConfig, setIsServerConfig] = useState(false);
39
+ // Ref to track unmount / stale requests
40
+ const mountedRef = useRef(true);
41
+ useEffect(() => {
42
+ mountedRef.current = true;
43
+ return () => { mountedRef.current = false; };
44
+ }, []);
45
+ const loadConfig = useCallback(async () => {
46
+ if (!adapter || !enabled) {
47
+ setConfig(staticConfig ?? null);
48
+ setIsServerConfig(false);
49
+ setIsLoading(false);
50
+ return;
51
+ }
52
+ setIsLoading(true);
53
+ setError(null);
54
+ try {
55
+ const serverConfig = await adapter.getApp(appId);
56
+ if (!mountedRef.current)
57
+ return;
58
+ if (serverConfig) {
59
+ setConfig(serverConfig);
60
+ setIsServerConfig(true);
61
+ }
62
+ else {
63
+ // Server returned null — fall back to static config
64
+ setConfig(staticConfig ?? null);
65
+ setIsServerConfig(false);
66
+ }
67
+ }
68
+ catch (err) {
69
+ if (!mountedRef.current)
70
+ return;
71
+ setError(err instanceof Error ? err : new Error(String(err)));
72
+ // Fall back to static config on error
73
+ setConfig(staticConfig ?? null);
74
+ setIsServerConfig(false);
75
+ }
76
+ finally {
77
+ if (mountedRef.current) {
78
+ setIsLoading(false);
79
+ }
80
+ }
81
+ }, [adapter, appId, staticConfig, enabled]);
82
+ // Load on mount and when appId changes
83
+ useEffect(() => {
84
+ loadConfig();
85
+ }, [loadConfig]);
86
+ const refresh = useCallback(async () => {
87
+ // Invalidate cache before re-fetching
88
+ adapter?.invalidateCache?.(`app:${appId}`);
89
+ await loadConfig();
90
+ }, [adapter, appId, loadConfig]);
91
+ return { config, isLoading, error, isServerConfig, refresh };
92
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /** Configuration for ETag-based HTTP caching. */
9
+ export interface ETagCacheConfig {
10
+ /** Enable ETag caching (default: true) */
11
+ enabled?: boolean;
12
+ /** Storage backend for ETags ('memory' | 'localStorage') */
13
+ storage?: 'memory' | 'localStorage';
14
+ /** Storage key prefix (default: 'oui-etag') */
15
+ storagePrefix?: string;
16
+ /** Max cache entries (default: 1000) */
17
+ maxEntries?: number;
18
+ /** Cache TTL in ms (default: 3600000 = 1 hour) */
19
+ ttl?: number;
20
+ /** Whether to register a Service Worker for caching (default: false) */
21
+ useServiceWorker?: boolean;
22
+ /** Service Worker script URL */
23
+ serviceWorkerUrl?: string;
24
+ }
25
+ /** A single cache entry with its associated ETag and metadata. */
26
+ export interface CacheEntry<T = unknown> {
27
+ data: T;
28
+ etag: string;
29
+ url: string;
30
+ timestamp: number;
31
+ lastModified?: string;
32
+ }
33
+ /** Result returned by the useETagCache hook. */
34
+ export interface ETagCacheResult {
35
+ /** Fetch data with ETag support — sends If-None-Match header, returns cached data on 304. */
36
+ fetchWithETag: <T>(url: string, options?: RequestInit) => Promise<{
37
+ data: T;
38
+ fromCache: boolean;
39
+ etag?: string;
40
+ }>;
41
+ /** Invalidate cache for a specific URL. */
42
+ invalidate: (url: string) => void;
43
+ /** Invalidate all cache entries matching a pattern. */
44
+ invalidatePattern: (pattern: string | RegExp) => void;
45
+ /** Clear entire cache. */
46
+ clearCache: () => void;
47
+ /** Number of cached entries. */
48
+ cacheSize: number;
49
+ /** Whether Service Worker is registered and active. */
50
+ serviceWorkerActive: boolean;
51
+ /** Register/update the Service Worker. */
52
+ registerServiceWorker: () => Promise<boolean>;
53
+ }
54
+ /**
55
+ * Hook for ETag-based HTTP caching with optional Service Worker support.
56
+ * Integrates with @objectstack/client ETag caching for efficient data fetching.
57
+ * When the server responds with 304 Not Modified, cached data is reused.
58
+ *
59
+ * @example
60
+ * ```tsx
61
+ * function UserProfile({ userId }: { userId: string }) {
62
+ * const cache = useETagCache({ storage: 'localStorage', ttl: 60_000 });
63
+ *
64
+ * useEffect(() => {
65
+ * cache.fetchWithETag<User>(`/api/users/${userId}`).then(({ data, fromCache }) => {
66
+ * console.log(fromCache ? 'Served from cache' : 'Fresh data');
67
+ * setUser(data);
68
+ * });
69
+ * }, [userId]);
70
+ *
71
+ * return <div>Cache entries: {cache.cacheSize}</div>;
72
+ * }
73
+ * ```
74
+ */
75
+ export declare function useETagCache(userConfig?: ETagCacheConfig): ETagCacheResult;