@swoff/cli 0.3.8 → 0.3.9

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 (112) hide show
  1. package/README.md +47 -33
  2. package/dist/__tests__/assemble-sw.test.js +8 -38
  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 +32 -15
  7. package/dist/__tests__/config-types.test.js.map +1 -1
  8. package/dist/__tests__/file-generators.test.js +2 -2
  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 +12 -12
  15. package/dist/__tests__/loader.test.js.map +1 -1
  16. package/dist/__tests__/validator.test.js +144 -97
  17. package/dist/__tests__/validator.test.js.map +1 -1
  18. package/dist/index.js +11 -10
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/cli/help.js +0 -5
  21. package/dist/lib/cli/help.js.map +1 -1
  22. package/dist/lib/commands/add.js +50 -32
  23. package/dist/lib/commands/add.js.map +1 -1
  24. package/dist/lib/commands/clean.js +1 -15
  25. package/dist/lib/commands/clean.js.map +1 -1
  26. package/dist/lib/commands/generate-guide.js +2 -2
  27. package/dist/lib/commands/generate-guide.js.map +1 -1
  28. package/dist/lib/commands/generate.js +7 -21
  29. package/dist/lib/commands/generate.js.map +1 -1
  30. package/dist/lib/commands/info.js +8 -6
  31. package/dist/lib/commands/info.js.map +1 -1
  32. package/dist/lib/commands/init.js +2 -10
  33. package/dist/lib/commands/init.js.map +1 -1
  34. package/dist/lib/commands/validate.js +3 -3
  35. package/dist/lib/commands/validate.js.map +1 -1
  36. package/dist/lib/config/loader.js +3 -6
  37. package/dist/lib/config/loader.js.map +1 -1
  38. package/dist/lib/config/validator.js +157 -88
  39. package/dist/lib/config/validator.js.map +1 -1
  40. package/dist/lib/generators/file-generators/cache.js +2 -10
  41. package/dist/lib/generators/file-generators/cache.js.map +1 -1
  42. package/dist/lib/generators/file-generators/client-injector.js +50 -11
  43. package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
  44. package/dist/lib/generators/file-generators/fetch-wrapper.js +92 -64
  45. package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
  46. package/dist/lib/generators/file-generators/generate-hooks.js +1 -1
  47. package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
  48. package/dist/lib/generators/file-generators/gql-wrapper.js +1 -14
  49. package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
  50. package/dist/lib/generators/file-generators/guide-generator.js +6 -640
  51. package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
  52. package/dist/lib/generators/file-generators/invalidation-tags.js +206 -19
  53. package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
  54. package/dist/lib/generators/file-generators/manifest.js +2 -2
  55. package/dist/lib/generators/file-generators/manifest.js.map +1 -1
  56. package/dist/lib/generators/file-generators/mutation-queue.js +29 -9
  57. package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
  58. package/dist/lib/generators/file-generators/push.js +6 -7
  59. package/dist/lib/generators/file-generators/push.js.map +1 -1
  60. package/dist/lib/generators/file-generators/quick-readme.js +8 -125
  61. package/dist/lib/generators/file-generators/quick-readme.js.map +1 -1
  62. package/dist/lib/generators/file-generators/server-push.js +11 -1
  63. package/dist/lib/generators/file-generators/server-push.js.map +1 -1
  64. package/dist/lib/generators/file-generators/sw-generator-build.js +8 -9
  65. package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
  66. package/dist/lib/generators/file-generators/sw-injector.js +2 -1
  67. package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
  68. package/dist/lib/generators/file-generators/type-definitions.js +0 -3
  69. package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
  70. package/dist/lib/generators/sw-generator.js +8 -18
  71. package/dist/lib/generators/sw-generator.js.map +1 -1
  72. package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
  73. package/dist/lib/generators/sw-sections/assemble-sw.js +14 -11
  74. package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
  75. package/dist/lib/generators/sw-sections/background-sync-handler.js +59 -17
  76. package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
  77. package/dist/lib/generators/sw-sections/config-header.js +2 -2
  78. package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
  79. package/dist/lib/generators/sw-sections/fetch-handler.js +437 -136
  80. package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
  81. package/dist/lib/generators/sw-sections/message-handler.js +55 -15
  82. package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
  83. package/dist/lib/generators/sw-sections/sw-push.js +67 -0
  84. package/dist/lib/generators/sw-sections/sw-push.js.map +1 -0
  85. package/dist/lib/generators/sw-sections/tag-management.js +60 -20
  86. package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
  87. package/dist/lib/generators/swoff-files-generator.js +5 -15
  88. package/dist/lib/generators/swoff-files-generator.js.map +1 -1
  89. package/dist/lib/shared/config-types.js +121 -71
  90. package/dist/lib/shared/config-types.js.map +1 -1
  91. package/dist/lib/shared/glob-matcher.js +77 -0
  92. package/dist/lib/shared/glob-matcher.js.map +1 -0
  93. package/dist/lib/utils/tty-status.js +21 -0
  94. package/dist/lib/utils/tty-status.js.map +1 -0
  95. package/docs/API.md +565 -0
  96. package/docs/ARCHITECTURE.md +374 -0
  97. package/docs/CLI.md +148 -0
  98. package/docs/COMPARISON.md +192 -0
  99. package/docs/CONFIG.md +277 -0
  100. package/docs/ECOSYSTEM.md +33 -0
  101. package/package.json +2 -1
  102. package/templates/hooks/useAuth.jsx +2 -2
  103. package/templates/hooks/useAuth.tsx +2 -2
  104. package/templates/hooks/useCachedFetch.jsx +3 -28
  105. package/templates/hooks/useCachedFetch.tsx +2 -33
  106. package/templates/hooks/useMutation.tsx +13 -1
  107. package/templates/hooks/useMutationQueue.jsx +2 -1
  108. package/templates/hooks/useMutationQueue.tsx +2 -1
  109. package/templates/hooks/useMutationState.jsx +8 -22
  110. package/templates/hooks/useMutationState.tsx +8 -24
  111. package/templates/hooks/usePushSubscription.jsx +3 -3
  112. package/templates/hooks/usePushSubscription.tsx +3 -3
@@ -2,7 +2,6 @@ import {
2
2
  useState,
3
3
  useEffect,
4
4
  useCallback,
5
- useRef,
6
5
  startTransition,
7
6
  } from "react";
8
7
  import { fetchWithCache } from "../fetch-wrapper.ts";
@@ -12,9 +11,6 @@ import type { FetchWithCacheOptions } from "../fetch-wrapper.ts";
12
11
  export function useCachedFetch<T>(
13
12
  url: string | null,
14
13
  options: FetchWithCacheOptions & {
15
- refetchOnWindowFocus?: boolean;
16
- refetchOnReconnect?: boolean;
17
- refetchInterval?: number;
18
14
  enabled?: boolean;
19
15
  } = {},
20
16
  ) {
@@ -22,10 +18,6 @@ export function useCachedFetch<T>(
22
18
  const [error, setError] = useState<Error | null>(null);
23
19
  const [loading, setLoading] = useState(true);
24
20
  const [refetchCount, setRefetchCount] = useState(0);
25
- const optionsRef = useRef(options);
26
- useEffect(() => {
27
- optionsRef.current = options;
28
- }, [options]);
29
21
 
30
22
  const refetch = useCallback(() => setRefetchCount((c) => c + 1), []);
31
23
 
@@ -33,7 +25,7 @@ export function useCachedFetch<T>(
33
25
 
34
26
  useEffect(() => {
35
27
  if (!isEnabled) {
36
- setLoading(false);
28
+ startTransition(() => setLoading(false));
37
29
  return;
38
30
  }
39
31
  let cancelled = false;
@@ -43,7 +35,7 @@ export function useCachedFetch<T>(
43
35
  const doFetch = async () => {
44
36
  try {
45
37
  const { response } = await fetchWithCache(url, {
46
- ...optionsRef.current,
38
+ ...options,
47
39
  signal: controller.signal,
48
40
  });
49
41
  if (cancelled) return;
@@ -85,28 +77,5 @@ export function useCachedFetch<T>(
85
77
  return () => window.removeEventListener("cache-invalidated", onInvalidated);
86
78
  }, [url, isEnabled]);
87
79
 
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
80
  return { data, error, loading, refetch };
112
81
  }
@@ -52,7 +52,19 @@ export function useMutation<TData = unknown>(
52
52
  });
53
53
 
54
54
  try {
55
- const { response } = await fetchWithCache<TData>(url, fetchOptions);
55
+ const { response, queued } = await fetchWithCache<TData>(url, fetchOptions);
56
+ if (queued) {
57
+ resolveMutation(mutationId, null);
58
+ setState({
59
+ data: null,
60
+ error: null,
61
+ isLoading: false,
62
+ isError: false,
63
+ isSuccess: false,
64
+ });
65
+ optionsRef.current.onSettled?.();
66
+ return null;
67
+ }
56
68
  const data: TData = await response.json();
57
69
  resolveMutation(mutationId, data);
58
70
  setState({
@@ -17,7 +17,8 @@ export function useMutationQueue() {
17
17
  }, []);
18
18
 
19
19
  useEffect(() => {
20
- refresh();
20
+ // Defer initial data load to avoid cascading render warning
21
+ queueMicrotask(() => refresh());
21
22
 
22
23
  const onSync = (e) => {
23
24
  refresh();
@@ -24,7 +24,8 @@ export function useMutationQueue() {
24
24
  }, []);
25
25
 
26
26
  useEffect(() => {
27
- refresh();
27
+ // Defer initial data load to avoid cascading render warning
28
+ queueMicrotask(() => refresh());
28
29
 
29
30
  const onSync = (e: CustomEvent) => {
30
31
  refresh();
@@ -1,27 +1,13 @@
1
- import { useState, useEffect } from "react";
1
+ import { useSyncExternalStore } from "react";
2
2
  import { getMutationState, onMutationStateChange } from "../mutation-state.js";
3
3
 
4
+ function subscribeToMutations(cb) {
5
+ return onMutationStateChange(() => cb());
6
+ }
7
+
4
8
  export function useMutationState(id) {
5
- const [state, setState] = useState(() =>
6
- id ? getMutationState(id) ?? null : null,
9
+ return useSyncExternalStore(
10
+ subscribeToMutations,
11
+ () => (id ? getMutationState(id) ?? null : null),
7
12
  );
8
-
9
- useEffect(() => {
10
- if (!id) {
11
- setState(null);
12
- return;
13
- }
14
-
15
- setState(getMutationState(id) ?? null);
16
-
17
- const unsub = onMutationStateChange((updated) => {
18
- if (updated.id === id) {
19
- setState(updated);
20
- }
21
- });
22
-
23
- return unsub;
24
- }, [id]);
25
-
26
- return state;
27
13
  }
@@ -1,10 +1,14 @@
1
- import { useState, useEffect } from "react";
1
+ import { useSyncExternalStore } from "react";
2
2
  import {
3
3
  getMutationState,
4
4
  onMutationStateChange,
5
5
  } from "../mutation-state.ts";
6
6
  import type { MutationState } from "../mutation-state.ts";
7
7
 
8
+ function subscribeToMutations(cb: () => void) {
9
+ return onMutationStateChange(() => cb());
10
+ }
11
+
8
12
  /**
9
13
  * Hook that subscribes to a specific mutation's state changes.
10
14
  * Useful for showing per-mutation loading spinners, error states, etc.
@@ -14,28 +18,8 @@ import type { MutationState } from "../mutation-state.ts";
14
18
  * if (mutation?.status === "error") { ... }
15
19
  */
16
20
  export function useMutationState(id: string | null): MutationState | null {
17
- const [state, setState] = useState<MutationState | null>(() =>
18
- id ? getMutationState(id) ?? null : null,
21
+ return useSyncExternalStore(
22
+ subscribeToMutations,
23
+ () => (id ? getMutationState(id) ?? null : null),
19
24
  );
20
-
21
- useEffect(() => {
22
- if (!id) {
23
- setState(null);
24
- return;
25
- }
26
-
27
- // Initial state
28
- setState(getMutationState(id) ?? null);
29
-
30
- // Subscribe to changes
31
- const unsub = onMutationStateChange((updated) => {
32
- if (updated.id === id) {
33
- setState(updated);
34
- }
35
- });
36
-
37
- return unsub;
38
- }, [id]);
39
-
40
- return state;
41
25
  }
@@ -6,7 +6,7 @@ import {
6
6
  getPushSubscription,
7
7
  } from "../push.js";
8
8
 
9
- export function usePushSubscription(vapidPublicKey) {
9
+ export function usePushSubscription() {
10
10
  const [state, setState] = useState({
11
11
  subscribed: false,
12
12
  subscription: null,
@@ -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();
@@ -6,7 +6,7 @@ import {
6
6
  getPushSubscription,
7
7
  } from "../push.ts";
8
8
 
9
- export function usePushSubscription(vapidPublicKey: string) {
9
+ export function usePushSubscription() {
10
10
  const [state, setState] = useState({
11
11
  subscribed: false,
12
12
  subscription: null as PushSubscription | null,
@@ -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();