@swoff/cli 0.4.1 → 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/lib/commands/generate-guide.js +15 -15
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/init.js +52 -6
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/config/validator.js +1 -1
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js +2 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +2 -1
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/context.js +4 -1
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +28 -22
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +1 -5
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-precache.js +8 -8
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js +2 -5
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +14 -3
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/shared/config-types.js +1 -0
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/detect-framework.js +16 -0
- package/dist/lib/utils/detect-framework.js.map +1 -1
- package/dist/runtime/client-injector-bundle.js +2 -2
- package/dist/runtime/client-injector-bundle.js.map +1 -1
- package/dist/runtime/client-injector.js +3 -2
- package/dist/runtime/client-injector.js.map +1 -1
- package/dist/runtime/gql-wrapper.js +3 -1
- package/dist/runtime/gql-wrapper.js.map +1 -1
- package/dist/runtime/swoff-api-bundle.js +2 -1
- package/dist/runtime/swoff-api-bundle.js.map +1 -1
- package/package.json +1 -1
- package/templates/react/{useOfflineAnalytics.jsx → useSwoffAnalytics.jsx} +3 -3
- package/templates/react/{useOfflineAnalytics.tsx → useSwoffAnalytics.tsx} +3 -3
- package/templates/react/{useAuth.jsx → useSwoffAuth.jsx} +2 -2
- package/templates/react/{useAuth.tsx → useSwoffAuth.tsx} +2 -2
- package/templates/react/{useCachedFetch.jsx → useSwoffFetch.jsx} +6 -6
- package/templates/react/{useCachedFetch.tsx → useSwoffFetch.tsx} +7 -7
- package/templates/react/{useMutation.jsx → useSwoffMutation.jsx} +3 -3
- package/templates/react/{useMutation.tsx → useSwoffMutation.tsx} +3 -3
- package/templates/react/{useMutationState.jsx → useSwoffMutationState.jsx} +1 -1
- package/templates/react/{useMutationState.tsx → useSwoffMutationState.tsx} +2 -2
- package/templates/react/{useNetworkStatus.jsx → useSwoffNetwork.jsx} +2 -2
- package/templates/react/{useNetworkStatus.tsx → useSwoffNetwork.tsx} +2 -2
- package/templates/react/{usePrecacheProgress.jsx → useSwoffPrecache.jsx} +4 -7
- package/templates/react/{usePrecacheProgress.tsx → useSwoffPrecache.tsx} +4 -7
- package/templates/react/{usePrefetch.jsx → useSwoffPrefetch.jsx} +2 -2
- package/templates/react/{usePrefetch.tsx → useSwoffPrefetch.tsx} +2 -2
- package/templates/react/{usePushSubscription.jsx → useSwoffPush.jsx} +1 -1
- package/templates/react/{usePushSubscription.tsx → useSwoffPush.tsx} +1 -1
- package/templates/react/{usePwaInstall.jsx → useSwoffPwa.jsx} +1 -1
- package/templates/react/{usePwaInstall.tsx → useSwoffPwa.tsx} +1 -1
- package/templates/react/{useMutationQueue.jsx → useSwoffQueue.jsx} +2 -2
- package/templates/react/{useMutationQueue.tsx → useSwoffQueue.tsx} +2 -2
- package/templates/react/{useStorageEstimate.jsx → useSwoffStorage.jsx} +2 -2
- package/templates/react/{useStorageEstimate.tsx → useSwoffStorage.tsx} +2 -2
- package/templates/react/{useBackgroundSync.jsx → useSwoffSync.jsx} +1 -1
- package/templates/react/{useBackgroundSync.tsx → useSwoffSync.tsx} +1 -1
- package/templates/svelte/useSwoffAnalytics.js +31 -0
- package/templates/svelte/useSwoffAnalytics.ts +37 -0
- package/templates/svelte/useSwoffAuth.js +95 -0
- package/templates/svelte/useSwoffAuth.ts +96 -0
- package/templates/svelte/useSwoffFetch.js +97 -0
- package/templates/svelte/useSwoffFetch.ts +112 -0
- package/templates/svelte/useSwoffMutation.js +110 -0
- package/templates/svelte/useSwoffMutation.ts +141 -0
- package/templates/svelte/useSwoffMutationState.js +27 -0
- package/templates/svelte/useSwoffMutationState.ts +28 -0
- package/templates/svelte/useSwoffNetwork.js +75 -0
- package/templates/svelte/useSwoffNetwork.ts +75 -0
- package/templates/svelte/useSwoffPrecache.js +19 -0
- package/templates/svelte/useSwoffPrecache.ts +19 -0
- package/templates/svelte/useSwoffPrefetch.js +19 -0
- package/templates/svelte/useSwoffPrefetch.ts +20 -0
- package/templates/svelte/useSwoffPush.js +82 -0
- package/templates/svelte/useSwoffPush.ts +82 -0
- package/templates/svelte/useSwoffPwa.js +27 -0
- package/templates/svelte/useSwoffPwa.ts +27 -0
- package/templates/svelte/useSwoffQueue.js +57 -0
- package/templates/svelte/useSwoffQueue.ts +67 -0
- package/templates/svelte/useSwoffReset.js +21 -0
- package/templates/svelte/useSwoffReset.ts +32 -0
- package/templates/svelte/useSwoffStorage.js +47 -0
- package/templates/svelte/useSwoffStorage.ts +47 -0
- package/templates/svelte/useSwoffSync.js +31 -0
- package/templates/svelte/useSwoffSync.ts +31 -0
- package/templates/vue/useSwoffAnalytics.js +46 -0
- package/templates/vue/useSwoffAnalytics.ts +54 -0
- package/templates/vue/useSwoffAuth.js +106 -0
- package/templates/vue/useSwoffAuth.ts +118 -0
- package/templates/vue/useSwoffFetch.js +127 -0
- package/templates/vue/useSwoffFetch.ts +143 -0
- package/templates/vue/useSwoffMutation.js +124 -0
- package/templates/vue/useSwoffMutation.ts +155 -0
- package/templates/vue/useSwoffMutationState.js +35 -0
- package/templates/vue/useSwoffMutationState.ts +36 -0
- package/templates/vue/useSwoffNetwork.js +86 -0
- package/templates/vue/useSwoffNetwork.ts +89 -0
- package/templates/vue/useSwoffPrecache.js +19 -0
- package/templates/vue/useSwoffPrecache.ts +19 -0
- package/templates/vue/useSwoffPrefetch.js +33 -0
- package/templates/vue/useSwoffPrefetch.ts +41 -0
- package/templates/vue/useSwoffPush.js +81 -0
- package/templates/vue/useSwoffPush.ts +81 -0
- package/templates/vue/useSwoffPwa.js +27 -0
- package/templates/vue/useSwoffPwa.ts +27 -0
- package/templates/vue/useSwoffQueue.js +70 -0
- package/templates/vue/useSwoffQueue.ts +82 -0
- package/templates/vue/useSwoffReset.js +32 -0
- package/templates/vue/useSwoffReset.ts +39 -0
- package/templates/vue/useSwoffStorage.js +56 -0
- package/templates/vue/useSwoffStorage.ts +57 -0
- package/templates/vue/useSwoffSync.js +30 -0
- package/templates/vue/useSwoffSync.ts +30 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { promptInstall } from "../pwa/prompt.js";
|
|
4
|
+
|
|
5
|
+
export function useSwoffPwa() {
|
|
6
|
+
const canInstall = writable(false);
|
|
7
|
+
|
|
8
|
+
function onReady() { canInstall.set(true); }
|
|
9
|
+
function onDone() { canInstall.set(false); }
|
|
10
|
+
|
|
11
|
+
onMount(() => {
|
|
12
|
+
window.addEventListener("pwa-installable", onReady);
|
|
13
|
+
window.addEventListener("pwa-installed", onDone);
|
|
14
|
+
window.addEventListener("pwa-dismissed", onDone);
|
|
15
|
+
return () => {
|
|
16
|
+
window.removeEventListener("pwa-installable", onReady);
|
|
17
|
+
window.removeEventListener("pwa-installed", onDone);
|
|
18
|
+
window.removeEventListener("pwa-dismissed", onDone);
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
async function install() {
|
|
23
|
+
await promptInstall();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { canInstall, install };
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { promptInstall } from "../pwa/prompt";
|
|
4
|
+
|
|
5
|
+
export function useSwoffPwa() {
|
|
6
|
+
const canInstall = writable(false);
|
|
7
|
+
|
|
8
|
+
function onReady() { canInstall.set(true); }
|
|
9
|
+
function onDone() { canInstall.set(false); }
|
|
10
|
+
|
|
11
|
+
onMount(() => {
|
|
12
|
+
window.addEventListener("pwa-installable", onReady);
|
|
13
|
+
window.addEventListener("pwa-installed", onDone);
|
|
14
|
+
window.addEventListener("pwa-dismissed", onDone);
|
|
15
|
+
return () => {
|
|
16
|
+
window.removeEventListener("pwa-installable", onReady);
|
|
17
|
+
window.removeEventListener("pwa-installed", onDone);
|
|
18
|
+
window.removeEventListener("pwa-dismissed", onDone);
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
async function install() {
|
|
23
|
+
await promptInstall();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { canInstall, install };
|
|
27
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
getPendingCount,
|
|
5
|
+
getQueueItems,
|
|
6
|
+
processMutationQueue,
|
|
7
|
+
} from "../mutation/queue.js";
|
|
8
|
+
|
|
9
|
+
export function useSwoffQueue() {
|
|
10
|
+
const pending = writable(0);
|
|
11
|
+
const items = writable([]);
|
|
12
|
+
const lastSync = writable(null);
|
|
13
|
+
const isProcessing = writable(false);
|
|
14
|
+
|
|
15
|
+
async function refresh() {
|
|
16
|
+
const [count, queueItems] = await Promise.all([
|
|
17
|
+
getPendingCount(),
|
|
18
|
+
getQueueItems(),
|
|
19
|
+
]);
|
|
20
|
+
pending.set(count);
|
|
21
|
+
items.set(queueItems);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
onMount(() => {
|
|
25
|
+
queueMicrotask(() => refresh());
|
|
26
|
+
|
|
27
|
+
function onSync(e) {
|
|
28
|
+
isProcessing.set(false);
|
|
29
|
+
lastSync.set({ succeeded: e.detail.succeeded, failed: e.detail.failed });
|
|
30
|
+
refresh();
|
|
31
|
+
}
|
|
32
|
+
function onChange() { refresh(); }
|
|
33
|
+
function onProgress() { isProcessing.set(true); }
|
|
34
|
+
|
|
35
|
+
window.addEventListener("mutation-sync-complete", onSync);
|
|
36
|
+
window.addEventListener("mutation-queue-changed", onChange);
|
|
37
|
+
window.addEventListener("mutation-sync-progress", onProgress);
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
window.removeEventListener("mutation-sync-complete", onSync);
|
|
41
|
+
window.removeEventListener("mutation-queue-changed", onChange);
|
|
42
|
+
window.removeEventListener("mutation-sync-progress", onProgress);
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
async function retryAll() {
|
|
47
|
+
isProcessing.set(true);
|
|
48
|
+
try {
|
|
49
|
+
await processMutationQueue();
|
|
50
|
+
} finally {
|
|
51
|
+
await refresh();
|
|
52
|
+
isProcessing.set(false);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { pending, items, lastSync, isProcessing, retryAll };
|
|
57
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
getPendingCount,
|
|
5
|
+
getQueueItems,
|
|
6
|
+
processMutationQueue,
|
|
7
|
+
} from "../mutation/queue";
|
|
8
|
+
import type { MutationQueueItem } from "../swoff.d";
|
|
9
|
+
|
|
10
|
+
export interface MutationQueueState {
|
|
11
|
+
pending: number;
|
|
12
|
+
items: MutationQueueItem[];
|
|
13
|
+
lastSync: { succeeded: number; failed: number } | null;
|
|
14
|
+
isProcessing: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useSwoffQueue(): MutationQueueState & {
|
|
18
|
+
retryAll: () => Promise<void>;
|
|
19
|
+
} {
|
|
20
|
+
const pending = writable(0);
|
|
21
|
+
const items = writable<MutationQueueItem[]>([]);
|
|
22
|
+
const lastSync = writable<{ succeeded: number; failed: number } | null>(null);
|
|
23
|
+
const isProcessing = writable(false);
|
|
24
|
+
|
|
25
|
+
async function refresh() {
|
|
26
|
+
const [count, queueItems] = await Promise.all([
|
|
27
|
+
getPendingCount(),
|
|
28
|
+
getQueueItems(),
|
|
29
|
+
]);
|
|
30
|
+
pending.set(count);
|
|
31
|
+
items.set(queueItems);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
onMount(() => {
|
|
35
|
+
queueMicrotask(() => refresh());
|
|
36
|
+
|
|
37
|
+
function onSync(e: CustomEvent) {
|
|
38
|
+
isProcessing.set(false);
|
|
39
|
+
lastSync.set({ succeeded: e.detail.succeeded, failed: e.detail.failed });
|
|
40
|
+
refresh();
|
|
41
|
+
}
|
|
42
|
+
function onChange() { refresh(); }
|
|
43
|
+
function onProgress() { isProcessing.set(true); }
|
|
44
|
+
|
|
45
|
+
window.addEventListener("mutation-sync-complete", onSync);
|
|
46
|
+
window.addEventListener("mutation-queue-changed", onChange);
|
|
47
|
+
window.addEventListener("mutation-sync-progress", onProgress);
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
window.removeEventListener("mutation-sync-complete", onSync);
|
|
51
|
+
window.removeEventListener("mutation-queue-changed", onChange);
|
|
52
|
+
window.removeEventListener("mutation-sync-progress", onProgress);
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
async function retryAll() {
|
|
57
|
+
isProcessing.set(true);
|
|
58
|
+
try {
|
|
59
|
+
await processMutationQueue();
|
|
60
|
+
} finally {
|
|
61
|
+
await refresh();
|
|
62
|
+
isProcessing.set(false);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { pending, items, lastSync, isProcessing, retryAll };
|
|
67
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { resetSwoff } from "../reset.js";
|
|
3
|
+
|
|
4
|
+
export function useSwoffReset() {
|
|
5
|
+
const isResetting = writable(false);
|
|
6
|
+
const error = writable(null);
|
|
7
|
+
|
|
8
|
+
async function reset(options) {
|
|
9
|
+
isResetting.set(true);
|
|
10
|
+
error.set(null);
|
|
11
|
+
try {
|
|
12
|
+
await resetSwoff(options);
|
|
13
|
+
} catch (e) {
|
|
14
|
+
error.set(e instanceof Error ? e : new Error(String(e)));
|
|
15
|
+
} finally {
|
|
16
|
+
isResetting.set(false);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { reset, isResetting, error };
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
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
|
+
export function useSwoffReset(): UseSwoffResetResult {
|
|
12
|
+
const isResetting = writable(false);
|
|
13
|
+
const error = writable<Error | null>(null);
|
|
14
|
+
|
|
15
|
+
async function reset(options?: ResetSwoffOptions) {
|
|
16
|
+
isResetting.set(true);
|
|
17
|
+
error.set(null);
|
|
18
|
+
try {
|
|
19
|
+
await resetSwoff(options);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
error.set(e instanceof Error ? e : new Error(String(e)));
|
|
22
|
+
} finally {
|
|
23
|
+
isResetting.set(false);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
reset,
|
|
29
|
+
isResetting: isResetting,
|
|
30
|
+
error: error,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { getStorageEstimate, formatBytes } from "../storage.js";
|
|
4
|
+
|
|
5
|
+
export function useSwoffStorage(autoRefresh = true) {
|
|
6
|
+
const usage = writable(0);
|
|
7
|
+
const quota = writable(0);
|
|
8
|
+
const percentUsed = writable(0);
|
|
9
|
+
const formattedUsage = writable("0 B");
|
|
10
|
+
const formattedQuota = writable("0 B");
|
|
11
|
+
const loading = writable(true);
|
|
12
|
+
const error = writable(null);
|
|
13
|
+
|
|
14
|
+
async function check() {
|
|
15
|
+
try {
|
|
16
|
+
const result = await getStorageEstimate();
|
|
17
|
+
usage.set(result.usage);
|
|
18
|
+
quota.set(result.quota);
|
|
19
|
+
percentUsed.set(result.percentUsed);
|
|
20
|
+
formattedUsage.set(formatBytes(result.usage));
|
|
21
|
+
formattedQuota.set(formatBytes(result.quota));
|
|
22
|
+
loading.set(false);
|
|
23
|
+
error.set(null);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
loading.set(false);
|
|
26
|
+
error.set(String(err));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onMount(() => {
|
|
31
|
+
check();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (autoRefresh) {
|
|
35
|
+
onMount(() => {
|
|
36
|
+
function onVisible() {
|
|
37
|
+
if (document.visibilityState === "visible") check();
|
|
38
|
+
}
|
|
39
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
40
|
+
return () => {
|
|
41
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { usage, quota, percentUsed, formattedUsage, formattedQuota, loading, error };
|
|
47
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { getStorageEstimate, formatBytes } from "../storage";
|
|
4
|
+
|
|
5
|
+
export function useSwoffStorage(autoRefresh = true) {
|
|
6
|
+
const usage = writable(0);
|
|
7
|
+
const quota = writable(0);
|
|
8
|
+
const percentUsed = writable(0);
|
|
9
|
+
const formattedUsage = writable("0 B");
|
|
10
|
+
const formattedQuota = writable("0 B");
|
|
11
|
+
const loading = writable(true);
|
|
12
|
+
const error = writable<string | null>(null);
|
|
13
|
+
|
|
14
|
+
async function check() {
|
|
15
|
+
try {
|
|
16
|
+
const result = await getStorageEstimate();
|
|
17
|
+
usage.set(result.usage);
|
|
18
|
+
quota.set(result.quota);
|
|
19
|
+
percentUsed.set(result.percentUsed);
|
|
20
|
+
formattedUsage.set(formatBytes(result.usage));
|
|
21
|
+
formattedQuota.set(formatBytes(result.quota));
|
|
22
|
+
loading.set(false);
|
|
23
|
+
error.set(null);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
loading.set(false);
|
|
26
|
+
error.set(String(err));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onMount(() => {
|
|
31
|
+
check();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (autoRefresh) {
|
|
35
|
+
onMount(() => {
|
|
36
|
+
function onVisible() {
|
|
37
|
+
if (document.visibilityState === "visible") check();
|
|
38
|
+
}
|
|
39
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
40
|
+
return () => {
|
|
41
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { usage, quota, percentUsed, formattedUsage, formattedQuota, loading, error };
|
|
47
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { retrySync } from "../mutation/sync.js";
|
|
4
|
+
|
|
5
|
+
export function useSwoffSync() {
|
|
6
|
+
const supported = writable(
|
|
7
|
+
typeof window !== "undefined" && typeof navigator !== "undefined"
|
|
8
|
+
? "serviceWorker" in navigator && "SyncManager" in window
|
|
9
|
+
: false,
|
|
10
|
+
);
|
|
11
|
+
const registered = writable(false);
|
|
12
|
+
const lastSync = writable(null);
|
|
13
|
+
|
|
14
|
+
onMount(() => {
|
|
15
|
+
function onSyncComplete(e) {
|
|
16
|
+
registered.set(true);
|
|
17
|
+
lastSync.set({ succeeded: e.detail.succeeded, failed: e.detail.failed });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
window.addEventListener("background-sync-complete", onSyncComplete);
|
|
21
|
+
return () => {
|
|
22
|
+
window.removeEventListener("background-sync-complete", onSyncComplete);
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
async function triggerSync() {
|
|
27
|
+
await retrySync();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { supported, registered, lastSync, triggerSync };
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { retrySync } from "../mutation/sync";
|
|
4
|
+
|
|
5
|
+
export function useSwoffSync() {
|
|
6
|
+
const supported = writable(
|
|
7
|
+
typeof window !== "undefined" && typeof navigator !== "undefined"
|
|
8
|
+
? "serviceWorker" in navigator && "SyncManager" in window
|
|
9
|
+
: false,
|
|
10
|
+
);
|
|
11
|
+
const registered = writable(false);
|
|
12
|
+
const lastSync = writable<{ succeeded: number; failed: number } | null>(null);
|
|
13
|
+
|
|
14
|
+
onMount(() => {
|
|
15
|
+
function onSyncComplete(e: CustomEvent) {
|
|
16
|
+
registered.set(true);
|
|
17
|
+
lastSync.set({ succeeded: e.detail.succeeded, failed: e.detail.failed });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
window.addEventListener("background-sync-complete", onSyncComplete);
|
|
21
|
+
return () => {
|
|
22
|
+
window.removeEventListener("background-sync-complete", onSyncComplete);
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
async function triggerSync() {
|
|
27
|
+
await retrySync();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { supported, registered, lastSync, triggerSync };
|
|
31
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tracks offline navigation fallback events dispatched by the Service Worker.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const { lastEvent, events } = useSwoffAnalytics();
|
|
8
|
+
*
|
|
9
|
+
* // Render an offline indicator when a fallback was served:
|
|
10
|
+
* <div v-if="lastEvent">
|
|
11
|
+
* Offline: served {{ lastEvent.fallbackLevel }} for {{ lastEvent.route }}
|
|
12
|
+
* </div>
|
|
13
|
+
*
|
|
14
|
+
* // Or pass a callback:
|
|
15
|
+
* useSwoffAnalytics((event) => console.log("Fallback:", event));
|
|
16
|
+
*/
|
|
17
|
+
export function useSwoffAnalytics(callback) {
|
|
18
|
+
const events = ref([]);
|
|
19
|
+
const lastEvent = ref(null);
|
|
20
|
+
|
|
21
|
+
function handler(e) {
|
|
22
|
+
if (e.data?.type === "OFFLINE_FALLBACK_ACTIVATED" && e.data?.detail) {
|
|
23
|
+
const detail = e.data.detail;
|
|
24
|
+
events.value = [...events.value, detail];
|
|
25
|
+
lastEvent.value = detail;
|
|
26
|
+
callback?.(detail);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onMounted(() => {
|
|
31
|
+
if (typeof navigator === "undefined" || !navigator.serviceWorker) return;
|
|
32
|
+
navigator.serviceWorker.addEventListener("message", handler);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
onUnmounted(() => {
|
|
36
|
+
if (typeof navigator === "undefined" || !navigator.serviceWorker) return;
|
|
37
|
+
navigator.serviceWorker.removeEventListener("message", handler);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function clear() {
|
|
41
|
+
events.value = [];
|
|
42
|
+
lastEvent.value = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { lastEvent, events, clear };
|
|
46
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
2
|
+
|
|
3
|
+
export interface OfflineFallbackEvent {
|
|
4
|
+
route: string;
|
|
5
|
+
fallbackLevel: "route-fallback" | "offline-page" | "spa-shell" | "inline-503";
|
|
6
|
+
timestamp: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks offline navigation fallback events dispatched by the Service Worker.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const { lastEvent, events } = useSwoffAnalytics();
|
|
14
|
+
*
|
|
15
|
+
* // Render an offline indicator when a fallback was served:
|
|
16
|
+
* <div v-if="lastEvent">
|
|
17
|
+
* Offline: served {{ lastEvent.fallbackLevel }} for {{ lastEvent.route }}
|
|
18
|
+
* </div>
|
|
19
|
+
*
|
|
20
|
+
* // Or pass a callback:
|
|
21
|
+
* useSwoffAnalytics((event) => console.log("Fallback:", event));
|
|
22
|
+
*
|
|
23
|
+
* @returns {{ lastEvent: Ref<OfflineFallbackEvent | null>, events: Ref<OfflineFallbackEvent[]>, clear: () => void }}
|
|
24
|
+
*/
|
|
25
|
+
export function useSwoffAnalytics(callback?: (event: OfflineFallbackEvent) => void) {
|
|
26
|
+
const events = ref<OfflineFallbackEvent[]>([]);
|
|
27
|
+
const lastEvent = ref<OfflineFallbackEvent | null>(null);
|
|
28
|
+
|
|
29
|
+
function handler(e: MessageEvent) {
|
|
30
|
+
if (e.data?.type === "OFFLINE_FALLBACK_ACTIVATED" && e.data?.detail) {
|
|
31
|
+
const detail = e.data.detail as OfflineFallbackEvent;
|
|
32
|
+
events.value = [...events.value, detail];
|
|
33
|
+
lastEvent.value = detail;
|
|
34
|
+
callback?.(detail);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
if (typeof navigator === "undefined" || !navigator.serviceWorker) return;
|
|
40
|
+
navigator.serviceWorker.addEventListener("message", handler);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
onUnmounted(() => {
|
|
44
|
+
if (typeof navigator === "undefined" || !navigator.serviceWorker) return;
|
|
45
|
+
navigator.serviceWorker.removeEventListener("message", handler);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function clear() {
|
|
49
|
+
events.value = [];
|
|
50
|
+
lastEvent.value = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { lastEvent, events, clear };
|
|
54
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
2
|
+
import { getAuthState } from "../auth/state.js";
|
|
3
|
+
import { setAuth, clearAuth, ensureValidAuth } from "../auth/store.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reactive auth state with actions: setAuth, clearAuth, ensureValid.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const { authenticated, auth, online, isLoading, error, setAuth, clearAuth, ensureValid } = useSwoffAuth();
|
|
10
|
+
*
|
|
11
|
+
* // Login:
|
|
12
|
+
* const res = await fetch("/api/login", { method: "POST" });
|
|
13
|
+
* const data = await res.json();
|
|
14
|
+
* await setAuth({ token: data.token, user: data.user, expiresAt: data.expiresAt });
|
|
15
|
+
*
|
|
16
|
+
* // Logout:
|
|
17
|
+
* await clearAuth();
|
|
18
|
+
*/
|
|
19
|
+
export function useSwoffAuth() {
|
|
20
|
+
const authenticated = ref(false);
|
|
21
|
+
const auth = ref(null);
|
|
22
|
+
const online = ref(typeof navigator !== "undefined" ? navigator.onLine : true);
|
|
23
|
+
const isLoading = ref(false);
|
|
24
|
+
const error = ref(null);
|
|
25
|
+
|
|
26
|
+
async function refreshState() {
|
|
27
|
+
try {
|
|
28
|
+
const authState = await getAuthState();
|
|
29
|
+
authenticated.value = authState.authenticated;
|
|
30
|
+
auth.value = authState.auth;
|
|
31
|
+
error.value = null;
|
|
32
|
+
} catch {
|
|
33
|
+
// ignore — auth not initialized
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onMounted(() => {
|
|
38
|
+
refreshState();
|
|
39
|
+
|
|
40
|
+
function onOnline() { online.value = true; }
|
|
41
|
+
function onOffline() { online.value = false; }
|
|
42
|
+
function onAuthChange() { refreshState(); }
|
|
43
|
+
|
|
44
|
+
window.addEventListener("online", onOnline);
|
|
45
|
+
window.addEventListener("offline", onOffline);
|
|
46
|
+
window.addEventListener("sw-auth-state-change", onAuthChange);
|
|
47
|
+
|
|
48
|
+
onUnmounted(() => {
|
|
49
|
+
window.removeEventListener("online", onOnline);
|
|
50
|
+
window.removeEventListener("offline", onOffline);
|
|
51
|
+
window.removeEventListener("sw-auth-state-change", onAuthChange);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
async function doSetAuth(authData) {
|
|
56
|
+
isLoading.value = true;
|
|
57
|
+
error.value = null;
|
|
58
|
+
try {
|
|
59
|
+
await setAuth(authData);
|
|
60
|
+
await refreshState();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
63
|
+
} finally {
|
|
64
|
+
isLoading.value = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function doClearAuth() {
|
|
69
|
+
isLoading.value = true;
|
|
70
|
+
error.value = null;
|
|
71
|
+
try {
|
|
72
|
+
await clearAuth();
|
|
73
|
+
await refreshState();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
76
|
+
} finally {
|
|
77
|
+
isLoading.value = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function doEnsureValid() {
|
|
82
|
+
isLoading.value = true;
|
|
83
|
+
error.value = null;
|
|
84
|
+
try {
|
|
85
|
+
const result = await ensureValidAuth();
|
|
86
|
+
await refreshState();
|
|
87
|
+
return result;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
error.value = err instanceof Error ? err : new Error(String(err));
|
|
90
|
+
return null;
|
|
91
|
+
} finally {
|
|
92
|
+
isLoading.value = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
authenticated,
|
|
98
|
+
auth,
|
|
99
|
+
online,
|
|
100
|
+
isLoading,
|
|
101
|
+
error,
|
|
102
|
+
setAuth: doSetAuth,
|
|
103
|
+
clearAuth: doClearAuth,
|
|
104
|
+
ensureValid: doEnsureValid,
|
|
105
|
+
};
|
|
106
|
+
}
|