@swoff/cli 0.3.8 → 0.3.10

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 (228) hide show
  1. package/README.md +47 -33
  2. package/dist/__tests__/assemble-sw.test.js +14 -53
  3. package/dist/__tests__/assemble-sw.test.js.map +1 -1
  4. package/dist/__tests__/cli-integration.test.js +119 -11
  5. package/dist/__tests__/cli-integration.test.js.map +1 -1
  6. package/dist/__tests__/config-types.test.js +44 -17
  7. package/dist/__tests__/config-types.test.js.map +1 -1
  8. package/dist/__tests__/file-generators.test.js +3 -3
  9. package/dist/__tests__/file-generators.test.js.map +1 -1
  10. package/dist/__tests__/glob-matcher.test.js +86 -0
  11. package/dist/__tests__/glob-matcher.test.js.map +1 -0
  12. package/dist/__tests__/invalidation-tags.test.js +207 -23
  13. package/dist/__tests__/invalidation-tags.test.js.map +1 -1
  14. package/dist/__tests__/loader.test.js +16 -12
  15. package/dist/__tests__/loader.test.js.map +1 -1
  16. package/dist/__tests__/validator.test.js +140 -100
  17. package/dist/__tests__/validator.test.js.map +1 -1
  18. package/dist/index.js +39 -34
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/cli/help.js +12 -12
  21. package/dist/lib/cli/help.js.map +1 -1
  22. package/dist/lib/commands/add.js +84 -36
  23. package/dist/lib/commands/add.js.map +1 -1
  24. package/dist/lib/commands/assets.js +37 -0
  25. package/dist/lib/commands/assets.js.map +1 -0
  26. package/dist/lib/commands/clean.js +32 -30
  27. package/dist/lib/commands/clean.js.map +1 -1
  28. package/dist/lib/commands/generate-assets.js +44 -0
  29. package/dist/lib/commands/generate-assets.js.map +1 -0
  30. package/dist/lib/commands/generate-guide.js +22 -29
  31. package/dist/lib/commands/generate-guide.js.map +1 -1
  32. package/dist/lib/commands/generate.js +17 -24
  33. package/dist/lib/commands/generate.js.map +1 -1
  34. package/dist/lib/commands/info.js +41 -48
  35. package/dist/lib/commands/info.js.map +1 -1
  36. package/dist/lib/commands/init.js +27 -13
  37. package/dist/lib/commands/init.js.map +1 -1
  38. package/dist/lib/commands/validate.js +4 -4
  39. package/dist/lib/commands/validate.js.map +1 -1
  40. package/dist/lib/config/loader.js +25 -23
  41. package/dist/lib/config/loader.js.map +1 -1
  42. package/dist/lib/config/validator.js +169 -91
  43. package/dist/lib/config/validator.js.map +1 -1
  44. package/dist/lib/generators/asset-generator/dependency-resolver.js +76 -0
  45. package/dist/lib/generators/asset-generator/dependency-resolver.js.map +1 -0
  46. package/dist/lib/generators/asset-generator/generate.js +111 -0
  47. package/dist/lib/generators/asset-generator/generate.js.map +1 -0
  48. package/dist/lib/generators/asset-generator/guide.js +43 -0
  49. package/dist/lib/generators/asset-generator/guide.js.map +1 -0
  50. package/dist/lib/generators/asset-generator/html-patcher.js +65 -0
  51. package/dist/lib/generators/asset-generator/html-patcher.js.map +1 -0
  52. package/dist/lib/generators/asset-generator/ico-encoder.js +29 -0
  53. package/dist/lib/generators/asset-generator/ico-encoder.js.map +1 -0
  54. package/dist/lib/generators/asset-generator/manifest-patcher.js +23 -0
  55. package/dist/lib/generators/asset-generator/manifest-patcher.js.map +1 -0
  56. package/dist/lib/generators/asset-generator/maskable.js +15 -0
  57. package/dist/lib/generators/asset-generator/maskable.js.map +1 -0
  58. package/dist/lib/generators/asset-generator/rasterize.js +32 -0
  59. package/dist/lib/generators/asset-generator/rasterize.js.map +1 -0
  60. package/dist/lib/generators/asset-generator/sizes.js +25 -0
  61. package/dist/lib/generators/asset-generator/sizes.js.map +1 -0
  62. package/dist/lib/generators/file-generators/api-config.js +6 -0
  63. package/dist/lib/generators/file-generators/api-config.js.map +1 -0
  64. package/dist/lib/generators/file-generators/auth-state.js +2 -34
  65. package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
  66. package/dist/lib/generators/file-generators/auth-store.js +2 -250
  67. package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
  68. package/dist/lib/generators/file-generators/auth-user.js +3 -81
  69. package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
  70. package/dist/lib/generators/file-generators/background-sync.js +2 -57
  71. package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
  72. package/dist/lib/generators/file-generators/cache.js +2 -38
  73. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  74. package/dist/lib/generators/file-generators/client-injector.js +2 -91
  75. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  76. package/dist/lib/generators/file-generators/fetch-state.js +6 -0
  77. package/dist/lib/generators/file-generators/fetch-state.js.map +1 -0
  78. package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -267
  79. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  80. package/dist/lib/generators/file-generators/generate-framework-adapters.js +56 -0
  81. package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -0
  82. package/dist/lib/generators/file-generators/generate-hooks.js +4 -3
  83. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  84. package/dist/lib/generators/file-generators/gql-wrapper.js +3 -145
  85. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  86. package/dist/lib/generators/file-generators/guide-generator.js +24 -630
  87. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  88. package/dist/lib/generators/file-generators/invalidation-tags.js +18 -86
  89. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  90. package/dist/lib/generators/file-generators/manifest.js +15 -5
  91. package/dist/lib/generators/file-generators/manifest.js.map +1 -1
  92. package/dist/lib/generators/file-generators/mutation-queue.js +3 -285
  93. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  94. package/dist/lib/generators/file-generators/mutation-state.js +2 -117
  95. package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
  96. package/dist/lib/generators/file-generators/push.js +6 -139
  97. package/dist/lib/generators/file-generators/push.js.map +1 -1
  98. package/dist/lib/generators/file-generators/pwa-install.js +11 -84
  99. package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
  100. package/dist/lib/generators/file-generators/quick-readme.js +8 -125
  101. package/dist/lib/generators/file-generators/quick-readme.js.map +1 -1
  102. package/dist/lib/generators/file-generators/reset.js +6 -0
  103. package/dist/lib/generators/file-generators/reset.js.map +1 -0
  104. package/dist/lib/generators/file-generators/server-push.js +3 -124
  105. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  106. package/dist/lib/generators/file-generators/sw-generator-build.js +8 -9
  107. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  108. package/dist/lib/generators/file-generators/sw-injector.js +10 -230
  109. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  110. package/dist/lib/generators/file-generators/type-definitions.js +2 -114
  111. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  112. package/dist/lib/generators/sw-generator.js +10 -20
  113. package/dist/lib/generators/sw-generator.js.map +1 -1
  114. package/dist/lib/generators/sw-sections/activate-handler.js +25 -3
  115. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  116. package/dist/lib/generators/sw-sections/assemble-sw.js +20 -16
  117. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  118. package/dist/lib/generators/sw-sections/background-sync-handler.js +68 -19
  119. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  120. package/dist/lib/generators/sw-sections/config-header.js +2 -2
  121. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  122. package/dist/lib/generators/sw-sections/fetch-handler.js +506 -144
  123. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  124. package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +1 -0
  125. package/dist/lib/generators/sw-sections/message-handler.js +55 -15
  126. package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
  127. package/dist/lib/generators/sw-sections/server-push-handler.js +12 -0
  128. package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
  129. package/dist/lib/generators/sw-sections/sw-push.js +67 -0
  130. package/dist/lib/generators/sw-sections/sw-push.js.map +1 -0
  131. package/dist/lib/generators/sw-sections/tag-management.js +65 -23
  132. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  133. package/dist/lib/generators/swoff-files-generator.js +66 -45
  134. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  135. package/dist/lib/shared/config-types.js +182 -71
  136. package/dist/lib/shared/config-types.js.map +1 -1
  137. package/dist/lib/shared/glob-matcher.js +77 -0
  138. package/dist/lib/shared/glob-matcher.js.map +1 -0
  139. package/dist/lib/utils/tty-status.js +21 -0
  140. package/dist/lib/utils/tty-status.js.map +1 -0
  141. package/dist/runtime/api-config.js +18 -0
  142. package/dist/runtime/api-config.js.map +1 -0
  143. package/dist/runtime/auth-state.js +35 -0
  144. package/dist/runtime/auth-state.js.map +1 -0
  145. package/dist/runtime/auth-store.js +273 -0
  146. package/dist/runtime/auth-store.js.map +1 -0
  147. package/dist/runtime/auth-user.js +168 -0
  148. package/dist/runtime/auth-user.js.map +1 -0
  149. package/dist/runtime/background-sync.js +59 -0
  150. package/dist/runtime/background-sync.js.map +1 -0
  151. package/dist/runtime/cache.js +30 -0
  152. package/dist/runtime/cache.js.map +1 -0
  153. package/dist/runtime/client-injector.js +136 -0
  154. package/dist/runtime/client-injector.js.map +1 -0
  155. package/dist/runtime/fetch-state.js +42 -0
  156. package/dist/runtime/fetch-state.js.map +1 -0
  157. package/dist/runtime/fetch-wrapper.js +355 -0
  158. package/dist/runtime/fetch-wrapper.js.map +1 -0
  159. package/dist/runtime/gql-wrapper.js +135 -0
  160. package/dist/runtime/gql-wrapper.js.map +1 -0
  161. package/dist/runtime/invalidation-tags.js +260 -0
  162. package/dist/runtime/invalidation-tags.js.map +1 -0
  163. package/dist/runtime/mutation-queue.js +304 -0
  164. package/dist/runtime/mutation-queue.js.map +1 -0
  165. package/dist/runtime/mutation-state.js +117 -0
  166. package/dist/runtime/mutation-state.js.map +1 -0
  167. package/dist/runtime/push.js +138 -0
  168. package/dist/runtime/push.js.map +1 -0
  169. package/dist/runtime/pwa-index.js +11 -0
  170. package/dist/runtime/pwa-index.js.map +1 -0
  171. package/dist/runtime/pwa-injector.js +44 -0
  172. package/dist/runtime/pwa-injector.js.map +1 -0
  173. package/dist/runtime/pwa-install.js +83 -0
  174. package/dist/runtime/pwa-install.js.map +1 -0
  175. package/dist/runtime/pwa-prompt.js +54 -0
  176. package/dist/runtime/pwa-prompt.js.map +1 -0
  177. package/dist/runtime/reset.js +148 -0
  178. package/dist/runtime/reset.js.map +1 -0
  179. package/dist/runtime/server-push.js +148 -0
  180. package/dist/runtime/server-push.js.map +1 -0
  181. package/dist/runtime/sw-injector.js +236 -0
  182. package/dist/runtime/sw-injector.js.map +1 -0
  183. package/dist/runtime/type-definitions.js +138 -0
  184. package/dist/runtime/type-definitions.js.map +1 -0
  185. package/dist/runtime/utils.js +27 -0
  186. package/dist/runtime/utils.js.map +1 -0
  187. package/package.json +3 -1
  188. package/templates/react/useAuth.jsx +106 -0
  189. package/templates/react/useAuth.tsx +118 -0
  190. package/templates/{hooks → react}/useBackgroundSync.jsx +6 -4
  191. package/templates/{hooks → react}/useBackgroundSync.tsx +6 -4
  192. package/templates/{hooks → react}/useCacheInvalidation.jsx +2 -2
  193. package/templates/react/useCacheInvalidation.tsx +52 -0
  194. package/templates/react/useCachedFetch.jsx +140 -0
  195. package/templates/react/useCachedFetch.tsx +160 -0
  196. package/templates/react/useIsFetching.jsx +32 -0
  197. package/templates/react/useIsFetching.tsx +32 -0
  198. package/templates/react/useMutation.jsx +141 -0
  199. package/templates/react/useMutation.tsx +180 -0
  200. package/templates/react/useMutationQueue.jsx +69 -0
  201. package/templates/react/useMutationQueue.tsx +83 -0
  202. package/templates/react/useMutationState.jsx +13 -0
  203. package/templates/react/useMutationState.tsx +25 -0
  204. package/templates/react/useNetworkStatus.jsx +61 -0
  205. package/templates/react/useNetworkStatus.tsx +76 -0
  206. package/templates/react/usePrefetch.jsx +32 -0
  207. package/templates/react/usePrefetch.tsx +42 -0
  208. package/templates/{hooks → react}/usePushSubscription.jsx +7 -7
  209. package/templates/{hooks → react}/usePushSubscription.tsx +7 -7
  210. package/templates/{hooks → react}/useSWUpdate.jsx +3 -3
  211. package/templates/{hooks → react}/useSWUpdate.tsx +4 -4
  212. package/templates/react/useSwoffReset.jsx +32 -0
  213. package/templates/react/useSwoffReset.tsx +39 -0
  214. package/templates/hooks/useAuth.jsx +0 -30
  215. package/templates/hooks/useAuth.tsx +0 -30
  216. package/templates/hooks/useCacheInvalidation.tsx +0 -19
  217. package/templates/hooks/useCachedFetch.jsx +0 -94
  218. package/templates/hooks/useCachedFetch.tsx +0 -112
  219. package/templates/hooks/useMutation.jsx +0 -76
  220. package/templates/hooks/useMutation.tsx +0 -97
  221. package/templates/hooks/useMutationQueue.jsx +0 -42
  222. package/templates/hooks/useMutationQueue.tsx +0 -49
  223. package/templates/hooks/useMutationState.jsx +0 -27
  224. package/templates/hooks/useMutationState.tsx +0 -41
  225. package/templates/hooks/useNetworkStatus.jsx +0 -19
  226. package/templates/hooks/useNetworkStatus.tsx +0 -19
  227. package/templates/hooks/usePrefetch.jsx +0 -10
  228. package/templates/hooks/usePrefetch.tsx +0 -25
@@ -0,0 +1,42 @@
1
+ import { useCallback, useState } from "react";
2
+ import { prefetchCache } from "../fetch/core.ts";
3
+ import type { FetchWithCacheOptions } from "../fetch/core.ts";
4
+
5
+ /**
6
+ * Hook that returns a stable prefetch callback, observable prefetch list, and clear function.
7
+ *
8
+ * Usage:
9
+ * const { prefetch, prefetchList, clear } = usePrefetch();
10
+ *
11
+ * // Prefetch when user hovers a link:
12
+ * <Link onMouseEnter={() => prefetch("/api/todos")} to="/todos" />
13
+ *
14
+ * // Show how many URLs are prefetched:
15
+ * <span>{prefetchList.length} routes cached</span>
16
+ *
17
+ * // Clear prefetched data:
18
+ * <button onClick={clear}>Clear prefetch</button>
19
+ *
20
+ * @returns {{ prefetch: (url: string, options?: FetchWithCacheOptions) => void, prefetchList: string[], clear: () => void }}
21
+ */
22
+ export function usePrefetch(): {
23
+ prefetch: (url: string, options?: FetchWithCacheOptions) => void;
24
+ prefetchList: string[];
25
+ clear: () => void;
26
+ } {
27
+ const [prefetchList, setPrefetchList] = useState<string[]>([]);
28
+
29
+ const prefetch = useCallback(
30
+ (url: string, options?: FetchWithCacheOptions) => {
31
+ prefetchCache(url, options);
32
+ setPrefetchList((list) => list.includes(url) ? list : [...list, url]);
33
+ },
34
+ [],
35
+ );
36
+
37
+ const clear = useCallback(() => {
38
+ setPrefetchList([]);
39
+ }, []);
40
+
41
+ return { prefetch, prefetchList, clear };
42
+ }
@@ -4,15 +4,15 @@ import {
4
4
  unsubscribeFromPush,
5
5
  isSubscribed,
6
6
  getPushSubscription,
7
- } from "../push.js";
7
+ } from "../realtime/notifications.js";
8
8
 
9
- export function usePushSubscription(vapidPublicKey) {
10
- const [state, setState] = useState({
9
+ export function usePushSubscription() {
10
+ const [state, setState] = useState(() => ({
11
11
  subscribed: false,
12
12
  subscription: null,
13
- permission: Notification.permission,
13
+ permission: typeof Notification !== "undefined" ? Notification.permission : "denied",
14
14
  loading: true,
15
- });
15
+ }));
16
16
 
17
17
  useEffect(() => {
18
18
  let cancelled = false;
@@ -55,9 +55,9 @@ export function usePushSubscription(vapidPublicKey) {
55
55
  }, []);
56
56
 
57
57
  const subscribe = useCallback(async () => {
58
- const sub = await subscribeToPush(vapidPublicKey);
58
+ const sub = await subscribeToPush();
59
59
  return sub !== null;
60
- }, [vapidPublicKey]);
60
+ }, []);
61
61
 
62
62
  const unsubscribe = useCallback(async () => {
63
63
  await unsubscribeFromPush();
@@ -4,15 +4,15 @@ import {
4
4
  unsubscribeFromPush,
5
5
  isSubscribed,
6
6
  getPushSubscription,
7
- } from "../push.ts";
7
+ } from "../realtime/notifications.ts";
8
8
 
9
- export function usePushSubscription(vapidPublicKey: string) {
10
- const [state, setState] = useState({
9
+ export function usePushSubscription() {
10
+ const [state, setState] = useState(() => ({
11
11
  subscribed: false,
12
12
  subscription: null as PushSubscription | null,
13
- permission: Notification.permission,
13
+ permission: typeof Notification !== "undefined" ? Notification.permission : "denied",
14
14
  loading: true,
15
- });
15
+ }));
16
16
 
17
17
  useEffect(() => {
18
18
  let cancelled = false;
@@ -55,9 +55,9 @@ export function usePushSubscription(vapidPublicKey: string) {
55
55
  }, []);
56
56
 
57
57
  const subscribe = useCallback(async () => {
58
- const sub = await subscribeToPush(vapidPublicKey);
58
+ const sub = await subscribeToPush();
59
59
  return sub !== null;
60
- }, [vapidPublicKey]);
60
+ }, []);
61
61
 
62
62
  const unsubscribe = useCallback(async () => {
63
63
  await unsubscribeFromPush();
@@ -2,14 +2,14 @@ import { useState, useEffect, useCallback } from "react";
2
2
  import { handleUpdateApproved } from "../sw/injector.js";
3
3
 
4
4
  export function useSWUpdate() {
5
- const [state, setState] = useState({
5
+ const [state, setState] = useState(() => ({
6
6
  updateStatus: "idle",
7
- currentVersion: window.currentSWVersion || null,
7
+ currentVersion: typeof window !== "undefined" ? window.currentSWVersion || null : null,
8
8
  availableVersion: null,
9
9
  progress: 0,
10
10
  forceUpdate: false,
11
11
  error: null,
12
- });
12
+ }));
13
13
 
14
14
  useEffect(() => {
15
15
  if (sessionStorage.getItem("sw-dismissed-update") === "true") return;
@@ -1,15 +1,15 @@
1
1
  import { useState, useEffect, useCallback } from "react";
2
- import { handleUpdateApproved } from "../sw/injector.ts";
2
+ import { handleUpdateApproved } from "../sw/injector";
3
3
 
4
4
  export function useSWUpdate() {
5
- const [state, setState] = useState({
5
+ const [state, setState] = useState(() => ({
6
6
  updateStatus: "idle" as "idle" | "available" | "downloading" | "ready",
7
- currentVersion: (window.currentSWVersion as string | undefined) || null,
7
+ currentVersion: typeof window !== "undefined" ? (window.currentSWVersion as string | undefined) || null : null,
8
8
  availableVersion: null as string | null,
9
9
  progress: 0 as number,
10
10
  forceUpdate: false,
11
11
  error: null as string | null,
12
- });
12
+ }));
13
13
 
14
14
  useEffect(() => {
15
15
  if (sessionStorage.getItem("sw-dismissed-update") === "true") return;
@@ -0,0 +1,32 @@
1
+ import { useState, useCallback } from "react";
2
+ import { resetSwoff } from "../reset";
3
+
4
+ /**
5
+ * Hook exposing swoff's nuclear reset with reactive state.
6
+ *
7
+ * Usage:
8
+ * const { reset, isResetting, error } = useSwoffReset();
9
+ *
10
+ * <button onClick={() => reset()} disabled={isResetting}>
11
+ * {isResetting ? "Resetting..." : "Reset Swoff"}
12
+ * </button>
13
+ * {error && <p style={{ color: "red" }}>{error.message}</p>}
14
+ */
15
+ export function useSwoffReset() {
16
+ const [isResetting, setIsResetting] = useState(false);
17
+ const [error, setError] = useState(null);
18
+
19
+ const reset = useCallback(async (options) => {
20
+ setIsResetting(true);
21
+ setError(null);
22
+ try {
23
+ await resetSwoff(options);
24
+ } catch (e) {
25
+ setError(e instanceof Error ? e : new Error(String(e)));
26
+ } finally {
27
+ setIsResetting(false);
28
+ }
29
+ }, []);
30
+
31
+ return { reset, isResetting, error };
32
+ }
@@ -0,0 +1,39 @@
1
+ import { useState, useCallback } from "react";
2
+ import { resetSwoff } from "../reset";
3
+ import type { ResetSwoffOptions } from "../reset";
4
+
5
+ interface UseSwoffResetResult {
6
+ reset: (options?: ResetSwoffOptions) => Promise<void>;
7
+ isResetting: boolean;
8
+ error: Error | null;
9
+ }
10
+
11
+ /**
12
+ * Hook exposing swoff's nuclear reset with reactive state.
13
+ *
14
+ * Usage:
15
+ * const { reset, isResetting, error } = useSwoffReset();
16
+ *
17
+ * <button onClick={() => reset()} disabled={isResetting}>
18
+ * {isResetting ? "Resetting..." : "Reset Swoff"}
19
+ * </button>
20
+ * {error && <p style={{ color: "red" }}>{error.message}</p>}
21
+ */
22
+ export function useSwoffReset(): UseSwoffResetResult {
23
+ const [isResetting, setIsResetting] = useState(false);
24
+ const [error, setError] = useState<Error | null>(null);
25
+
26
+ const reset = useCallback(async (options?: ResetSwoffOptions) => {
27
+ setIsResetting(true);
28
+ setError(null);
29
+ try {
30
+ await resetSwoff(options);
31
+ } catch (e) {
32
+ setError(e instanceof Error ? e : new Error(String(e)));
33
+ } finally {
34
+ setIsResetting(false);
35
+ }
36
+ }, []);
37
+
38
+ return { reset, isResetting, error };
39
+ }
@@ -1,30 +0,0 @@
1
- import { useState, useEffect } from "react";
2
- import { getAuthState } from "../auth/state.js";
3
-
4
- export function useAuth() {
5
- const [state, setState] = useState({
6
- authenticated: false,
7
- user: null,
8
- online: navigator.onLine,
9
- });
10
-
11
- useEffect(() => {
12
- getAuthState().then(setState);
13
-
14
- const onOnline = () => setState((s) => ({ ...s, online: true }));
15
- const onOffline = () => setState((s) => ({ ...s, online: false }));
16
- const onAuthChange = () => getAuthState().then(setState);
17
-
18
- window.addEventListener("online", onOnline);
19
- window.addEventListener("offline", onOffline);
20
- window.addEventListener("sw-auth-state-change", onAuthChange);
21
-
22
- return () => {
23
- window.removeEventListener("online", onOnline);
24
- window.removeEventListener("offline", onOffline);
25
- window.removeEventListener("sw-auth-state-change", onAuthChange);
26
- };
27
- }, []);
28
-
29
- return state;
30
- }
@@ -1,30 +0,0 @@
1
- import { useState, useEffect } from "react";
2
- import { getAuthState } from "../auth/state.ts";
3
-
4
- export function useAuth() {
5
- const [state, setState] = useState({
6
- authenticated: false,
7
- user: null as Record<string, unknown> | null,
8
- online: navigator.onLine,
9
- });
10
-
11
- useEffect(() => {
12
- getAuthState().then(setState);
13
-
14
- const onOnline = () => setState((s) => ({ ...s, online: true }));
15
- const onOffline = () => setState((s) => ({ ...s, online: false }));
16
- const onAuthChange = () => getAuthState().then(setState);
17
-
18
- window.addEventListener("online", onOnline);
19
- window.addEventListener("offline", onOffline);
20
- window.addEventListener("sw-auth-state-change", onAuthChange);
21
-
22
- return () => {
23
- window.removeEventListener("online", onOnline);
24
- window.removeEventListener("offline", onOffline);
25
- window.removeEventListener("sw-auth-state-change", onAuthChange);
26
- };
27
- }, []);
28
-
29
- return state;
30
- }
@@ -1,19 +0,0 @@
1
- import { useCallback } from "react";
2
- import { invalidateByTag, invalidateByTags } from "../cache.ts";
3
- import { invalidateUrl } from "../invalidation-tags.ts";
4
-
5
- export function useCacheInvalidation() {
6
- const invalidateTag = useCallback(async (tag: string) => {
7
- await invalidateByTag(tag);
8
- }, []);
9
-
10
- const invalidateTags = useCallback(async (tags: string[]) => {
11
- await invalidateByTags(tags);
12
- }, []);
13
-
14
- const invalidateCacheUrl = useCallback(async (url: string) => {
15
- await invalidateUrl(url);
16
- }, []);
17
-
18
- return { invalidateByTag: invalidateTag, invalidateByTags: invalidateTags, invalidateUrl: invalidateCacheUrl };
19
- }
@@ -1,94 +0,0 @@
1
- import { useState, useEffect, useCallback, useRef, startTransition } from "react";
2
- import { fetchWithCache } from "../fetch-wrapper.js";
3
- import { generateTags } from "../invalidation-tags.js";
4
-
5
- export function useCachedFetch(url, options = {}) {
6
- const [data, setData] = useState(null);
7
- const [error, setError] = useState(null);
8
- const [loading, setLoading] = useState(true);
9
- const [refetchCount, setRefetchCount] = useState(0);
10
- const optionsRef = useRef(options);
11
- useEffect(() => { optionsRef.current = options; }, [options]);
12
-
13
- const refetch = useCallback(() => setRefetchCount((c) => c + 1), []);
14
-
15
- const isEnabled = options.enabled !== false && url != null;
16
-
17
- useEffect(() => {
18
- if (!isEnabled) {
19
- setLoading(false);
20
- return;
21
- }
22
- let cancelled = false;
23
- const controller = new AbortController();
24
- startTransition(() => setLoading(true));
25
-
26
- const doFetch = async () => {
27
- try {
28
- const { response } = await fetchWithCache(url, {
29
- ...optionsRef.current,
30
- signal: controller.signal,
31
- });
32
- if (cancelled) return;
33
- if (response) {
34
- setData(await response.json());
35
- } else {
36
- setData(null);
37
- }
38
- if (!cancelled) setError(null);
39
- } catch (err) {
40
- if (!cancelled && err instanceof DOMException && err.name === "AbortError") return;
41
- if (!cancelled) setError(err instanceof Error ? err : new Error(String(err)));
42
- } finally {
43
- if (!cancelled) setLoading(false);
44
- }
45
- };
46
-
47
- doFetch();
48
- return () => {
49
- cancelled = true;
50
- controller.abort();
51
- };
52
- }, [url, refetchCount, isEnabled]);
53
-
54
- // Auto-refetch on cache invalidation
55
- useEffect(() => {
56
- if (!isEnabled) return;
57
- const onInvalidated = (e) => {
58
- const detail = e.detail;
59
- const tags = detail?.tags;
60
- if (!tags || !Array.isArray(tags)) return;
61
- const urlTags = generateTags(url);
62
- if (tags.some((t) => urlTags.includes(t))) {
63
- setRefetchCount((c) => c + 1);
64
- }
65
- };
66
- window.addEventListener("cache-invalidated", onInvalidated);
67
- return () => window.removeEventListener("cache-invalidated", onInvalidated);
68
- }, [url, isEnabled]);
69
-
70
- // Auto-refetch on window focus
71
- useEffect(() => {
72
- if (!options.refetchOnWindowFocus || !isEnabled) return;
73
- const onFocus = () => refetch();
74
- window.addEventListener("focus", onFocus);
75
- return () => window.removeEventListener("focus", onFocus);
76
- }, [options.refetchOnWindowFocus, refetch, isEnabled]);
77
-
78
- // Auto-refetch on reconnect
79
- useEffect(() => {
80
- if (!options.refetchOnReconnect || !isEnabled) return;
81
- const onOnline = () => refetch();
82
- window.addEventListener("online", onOnline);
83
- return () => window.removeEventListener("online", onOnline);
84
- }, [options.refetchOnReconnect, refetch, isEnabled]);
85
-
86
- // Auto-refetch on interval
87
- useEffect(() => {
88
- if (!options.refetchInterval || options.refetchInterval <= 0 || !isEnabled) return;
89
- const id = setInterval(refetch, options.refetchInterval * 1000);
90
- return () => clearInterval(id);
91
- }, [options.refetchInterval, refetch, isEnabled]);
92
-
93
- return { data, error, loading, refetch };
94
- }
@@ -1,112 +0,0 @@
1
- import {
2
- useState,
3
- useEffect,
4
- useCallback,
5
- useRef,
6
- startTransition,
7
- } from "react";
8
- import { fetchWithCache } from "../fetch-wrapper.ts";
9
- import { generateTags } from "../invalidation-tags.ts";
10
- import type { FetchWithCacheOptions } from "../fetch-wrapper.ts";
11
-
12
- export function useCachedFetch<T>(
13
- url: string | null,
14
- options: FetchWithCacheOptions & {
15
- refetchOnWindowFocus?: boolean;
16
- refetchOnReconnect?: boolean;
17
- refetchInterval?: number;
18
- enabled?: boolean;
19
- } = {},
20
- ) {
21
- const [data, setData] = useState<T | null>(null);
22
- const [error, setError] = useState<Error | null>(null);
23
- const [loading, setLoading] = useState(true);
24
- const [refetchCount, setRefetchCount] = useState(0);
25
- const optionsRef = useRef(options);
26
- useEffect(() => {
27
- optionsRef.current = options;
28
- }, [options]);
29
-
30
- const refetch = useCallback(() => setRefetchCount((c) => c + 1), []);
31
-
32
- const isEnabled = options.enabled !== false && url != null;
33
-
34
- useEffect(() => {
35
- if (!isEnabled) {
36
- setLoading(false);
37
- return;
38
- }
39
- let cancelled = false;
40
- const controller = new AbortController();
41
- startTransition(() => setLoading(true));
42
-
43
- const doFetch = async () => {
44
- try {
45
- const { response } = await fetchWithCache(url, {
46
- ...optionsRef.current,
47
- signal: controller.signal,
48
- });
49
- if (cancelled) return;
50
- if (response) {
51
- setData(await response.json());
52
- } else {
53
- setData(null);
54
- }
55
- if (!cancelled) setError(null);
56
- } catch (err) {
57
- if (!cancelled && err instanceof DOMException && err.name === "AbortError") return;
58
- if (!cancelled)
59
- setError(err instanceof Error ? err : new Error(String(err)));
60
- } finally {
61
- if (!cancelled) setLoading(false);
62
- }
63
- };
64
-
65
- doFetch();
66
- return () => {
67
- cancelled = true;
68
- controller.abort();
69
- };
70
- }, [url, refetchCount, isEnabled]);
71
-
72
- // Auto-refetch on cache invalidation
73
- useEffect(() => {
74
- if (!isEnabled) return;
75
- const onInvalidated = (e: Event) => {
76
- const detail = (e as CustomEvent).detail;
77
- const tags = detail?.tags;
78
- if (!tags || !Array.isArray(tags)) return;
79
- const urlTags = generateTags(url!);
80
- if (tags.some((t: string) => urlTags.includes(t))) {
81
- setRefetchCount((c) => c + 1);
82
- }
83
- };
84
- window.addEventListener("cache-invalidated", onInvalidated);
85
- return () => window.removeEventListener("cache-invalidated", onInvalidated);
86
- }, [url, isEnabled]);
87
-
88
- // Auto-refetch on window focus
89
- useEffect(() => {
90
- if (!options.refetchOnWindowFocus || !isEnabled) return;
91
- const onFocus = () => refetch();
92
- window.addEventListener("focus", onFocus);
93
- return () => window.removeEventListener("focus", onFocus);
94
- }, [options.refetchOnWindowFocus, refetch, isEnabled]);
95
-
96
- // Auto-refetch on reconnect
97
- useEffect(() => {
98
- if (!options.refetchOnReconnect || !isEnabled) return;
99
- const onOnline = () => refetch();
100
- window.addEventListener("online", onOnline);
101
- return () => window.removeEventListener("online", onOnline);
102
- }, [options.refetchOnReconnect, refetch, isEnabled]);
103
-
104
- // Auto-refetch on interval
105
- useEffect(() => {
106
- if (!options.refetchInterval || options.refetchInterval <= 0 || !isEnabled) return;
107
- const id = setInterval(refetch, options.refetchInterval * 1000);
108
- return () => clearInterval(id);
109
- }, [options.refetchInterval, refetch, isEnabled]);
110
-
111
- return { data, error, loading, refetch };
112
- }
@@ -1,76 +0,0 @@
1
- import { useEffect, useState, useCallback, useRef } from "react";
2
- import { fetchWithCache } from "../fetch-wrapper.js";
3
- import {
4
- trackMutation,
5
- resolveMutation,
6
- rejectMutation,
7
- } from "../mutation-state.js";
8
-
9
- export function useMutation(options = {}) {
10
- const [state, setState] = useState({
11
- data: null,
12
- error: null,
13
- isLoading: false,
14
- isError: false,
15
- isSuccess: false,
16
- });
17
-
18
- const optionsRef = useRef(options);
19
-
20
- useEffect(() => {
21
- optionsRef.current = options;
22
- }, [options]);
23
-
24
- const mutate = useCallback(async (url, fetchOptions = {}) => {
25
- const mutationId = "mut-" + crypto.randomUUID();
26
- trackMutation(mutationId, "pending");
27
- setState({
28
- data: null,
29
- error: null,
30
- isLoading: true,
31
- isError: false,
32
- isSuccess: false,
33
- });
34
-
35
- try {
36
- const { response } = await fetchWithCache(url, fetchOptions);
37
- const data = await response.json();
38
- resolveMutation(mutationId, data);
39
- setState({
40
- data,
41
- error: null,
42
- isLoading: false,
43
- isError: false,
44
- isSuccess: true,
45
- });
46
- optionsRef.current.onSuccess?.(data);
47
- optionsRef.current.onSettled?.();
48
- return data;
49
- } catch (err) {
50
- const error = err instanceof Error ? err : new Error(String(err));
51
- rejectMutation(mutationId, error);
52
- setState({
53
- data: null,
54
- error,
55
- isLoading: false,
56
- isError: true,
57
- isSuccess: false,
58
- });
59
- optionsRef.current.onError?.(error);
60
- optionsRef.current.onSettled?.();
61
- return null;
62
- }
63
- }, []);
64
-
65
- const reset = useCallback(() => {
66
- setState({
67
- data: null,
68
- error: null,
69
- isLoading: false,
70
- isError: false,
71
- isSuccess: false,
72
- });
73
- }, []);
74
-
75
- return { ...state, mutate, reset };
76
- }
@@ -1,97 +0,0 @@
1
- import { useEffect, useState, useCallback, useRef } from "react";
2
- import { fetchWithCache } from "../fetch-wrapper.ts";
3
- import {
4
- trackMutation,
5
- resolveMutation,
6
- rejectMutation,
7
- } from "../mutation-state.ts";
8
- import type { FetchWithCacheOptions } from "../fetch-wrapper.ts";
9
-
10
- export interface UseMutationOptions<TData> {
11
- onSuccess?: (data: TData) => void;
12
- onError?: (error: Error) => void;
13
- onSettled?: () => void;
14
- }
15
-
16
- export function useMutation<TData = unknown>(
17
- options: UseMutationOptions<TData> = {},
18
- ) {
19
- const [state, setState] = useState<{
20
- data: TData | null;
21
- error: Error | null;
22
- isLoading: boolean;
23
- isError: boolean;
24
- isSuccess: boolean;
25
- }>({
26
- data: null,
27
- error: null,
28
- isLoading: false,
29
- isError: false,
30
- isSuccess: false,
31
- });
32
-
33
- const optionsRef = useRef(options);
34
-
35
- useEffect(() => {
36
- optionsRef.current = options;
37
- }, [options]);
38
-
39
- const mutate = useCallback(
40
- async (
41
- url: string,
42
- fetchOptions: FetchWithCacheOptions = {},
43
- ): Promise<TData | null> => {
44
- const mutationId = "mut-" + crypto.randomUUID();
45
- trackMutation(mutationId, "pending");
46
- setState({
47
- data: null,
48
- error: null,
49
- isLoading: true,
50
- isError: false,
51
- isSuccess: false,
52
- });
53
-
54
- try {
55
- const { response } = await fetchWithCache<TData>(url, fetchOptions);
56
- const data: TData = await response.json();
57
- resolveMutation(mutationId, data);
58
- setState({
59
- data,
60
- error: null,
61
- isLoading: false,
62
- isError: false,
63
- isSuccess: true,
64
- });
65
- optionsRef.current.onSuccess?.(data);
66
- optionsRef.current.onSettled?.();
67
- return data;
68
- } catch (err) {
69
- const error = err instanceof Error ? err : new Error(String(err));
70
- rejectMutation(mutationId, error);
71
- setState({
72
- data: null,
73
- error,
74
- isLoading: false,
75
- isError: true,
76
- isSuccess: false,
77
- });
78
- optionsRef.current.onError?.(error);
79
- optionsRef.current.onSettled?.();
80
- return null;
81
- }
82
- },
83
- [],
84
- );
85
-
86
- const reset = useCallback(() => {
87
- setState({
88
- data: null,
89
- error: null,
90
- isLoading: false,
91
- isError: false,
92
- isSuccess: false,
93
- });
94
- }, []);
95
-
96
- return { ...state, mutate, reset };
97
- }