@swoff/cli 0.3.13 → 0.3.14
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/README.md +28 -32
- package/dist/index.js +2 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +3 -8
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +29 -87
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +16 -30
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +31 -6
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/init.js +188 -11
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +2 -4
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/loader.js +34 -25
- package/dist/lib/config/loader.js.map +1 -1
- package/dist/lib/config/validator.js +202 -96
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/api-config.js +1 -1
- package/dist/lib/generators/file-generators/api-config.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-adapter.js +13 -0
- package/dist/lib/generators/file-generators/auth-adapter.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-check.js +40 -0
- package/dist/lib/generators/file-generators/auth-check.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-store.js +2 -5
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +1 -1
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +2 -1
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js +15 -0
- package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -0
- 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/connectivity-manager.js +12 -0
- package/dist/lib/generators/file-generators/connectivity-manager.js.map +1 -0
- package/dist/lib/generators/file-generators/connectivity.js +12 -0
- package/dist/lib/generators/file-generators/connectivity.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -0
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +29 -20
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
- package/dist/lib/generators/file-generators/gql-wrapper.js +2 -2
- package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +51 -1
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
- package/dist/lib/generators/file-generators/invalidation-tags.js +2 -3
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +2 -2
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
- package/dist/lib/generators/file-generators/notification-generator.js +6 -0
- package/dist/lib/generators/file-generators/notification-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/open-db.js +6 -0
- package/dist/lib/generators/file-generators/open-db.js.map +1 -0
- package/dist/lib/generators/file-generators/push.js +1 -2
- package/dist/lib/generators/file-generators/push.js.map +1 -1
- package/dist/lib/generators/file-generators/pwa-install.js +2 -6
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/server-push.js +1 -1
- package/dist/lib/generators/file-generators/server-push.js.map +1 -1
- package/dist/lib/generators/file-generators/storage-generator.js +6 -0
- package/dist/lib/generators/file-generators/storage-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/storage-notify.js +6 -0
- package/dist/lib/generators/file-generators/storage-notify.js.map +1 -0
- package/dist/lib/generators/file-generators/storage.js +6 -0
- package/dist/lib/generators/file-generators/storage.js.map +1 -0
- package/dist/lib/generators/file-generators/sw-generator-build.js +96 -45
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +0 -4
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +8 -9
- package/dist/lib/generators/file-generators/sw-template.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-version-gen.js +32 -0
- package/dist/lib/generators/file-generators/sw-version-gen.js.map +1 -0
- package/dist/lib/generators/file-generators/swoff-api-bundle.js +51 -0
- package/dist/lib/generators/file-generators/swoff-api-bundle.js.map +1 -0
- package/dist/lib/generators/sw-build-utils.js +69 -0
- package/dist/lib/generators/sw-build-utils.js.map +1 -0
- package/dist/lib/generators/sw-generator.js +23 -34
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +74 -23
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +15 -94
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-sync-handler.js +74 -72
- package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js +102 -0
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -0
- package/dist/lib/generators/sw-sections/config-header.js +2 -3
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/default-template.js +59 -6
- package/dist/lib/generators/sw-sections/default-template.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +621 -495
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/install-handler.js +40 -23
- package/dist/lib/generators/sw-sections/install-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/message-handler.js +25 -8
- package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/server-push-handler.js +10 -14
- package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/shared.js +68 -0
- package/dist/lib/generators/sw-sections/shared.js.map +1 -0
- package/dist/lib/generators/sw-sections/sw-push.js +2 -6
- package/dist/lib/generators/sw-sections/sw-push.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +55 -86
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +183 -59
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +67 -63
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/shared/feature-registry.js +198 -0
- package/dist/lib/shared/feature-registry.js.map +1 -0
- package/dist/lib/utils/detect-framework.js +52 -9
- package/dist/lib/utils/detect-framework.js.map +1 -1
- package/dist/runtime/api-config.js +2 -2
- package/dist/runtime/api-config.js.map +1 -1
- package/dist/runtime/auth-adapter.js +179 -0
- package/dist/runtime/auth-adapter.js.map +1 -0
- package/dist/runtime/auth-state.js +16 -6
- package/dist/runtime/auth-state.js.map +1 -1
- package/dist/runtime/auth-store.js +219 -213
- package/dist/runtime/auth-store.js.map +1 -1
- package/dist/runtime/auth-user.js +5 -19
- package/dist/runtime/auth-user.js.map +1 -1
- package/dist/runtime/background-sync.js +5 -2
- package/dist/runtime/background-sync.js.map +1 -1
- package/dist/runtime/cache.js +91 -3
- package/dist/runtime/cache.js.map +1 -1
- package/dist/runtime/client-injector-bundle.js +314 -0
- package/dist/runtime/client-injector-bundle.js.map +1 -0
- package/dist/runtime/client-injector.js +173 -30
- package/dist/runtime/client-injector.js.map +1 -1
- package/dist/runtime/connectivity-manager.js +83 -0
- package/dist/runtime/connectivity-manager.js.map +1 -0
- package/dist/runtime/connectivity.js +83 -0
- package/dist/runtime/connectivity.js.map +1 -0
- package/dist/runtime/fetch-wrapper.js +26 -59
- package/dist/runtime/fetch-wrapper.js.map +1 -1
- package/dist/runtime/gql-wrapper.js +15 -9
- package/dist/runtime/gql-wrapper.js.map +1 -1
- package/dist/runtime/invalidation-tags.js +12 -89
- package/dist/runtime/invalidation-tags.js.map +1 -1
- package/dist/runtime/mutation-queue.js +83 -31
- package/dist/runtime/mutation-queue.js.map +1 -1
- package/dist/runtime/mutation-state.js +3 -3
- package/dist/runtime/mutation-state.js.map +1 -1
- package/dist/runtime/notification.js +65 -0
- package/dist/runtime/notification.js.map +1 -0
- package/dist/runtime/open-db.js +41 -0
- package/dist/runtime/open-db.js.map +1 -0
- package/dist/runtime/push.js +15 -27
- package/dist/runtime/push.js.map +1 -1
- package/dist/runtime/pwa-injector.js +3 -32
- package/dist/runtime/pwa-injector.js.map +1 -1
- package/dist/runtime/pwa-prompt.js +35 -3
- package/dist/runtime/pwa-prompt.js.map +1 -1
- package/dist/runtime/reset.js +39 -42
- package/dist/runtime/reset.js.map +1 -1
- package/dist/runtime/server-push.js +23 -13
- package/dist/runtime/server-push.js.map +1 -1
- package/dist/runtime/storage.js +32 -0
- package/dist/runtime/storage.js.map +1 -0
- package/dist/runtime/sw-injector.js +19 -178
- package/dist/runtime/sw-injector.js.map +1 -1
- package/dist/runtime/sw-version.js +13 -0
- package/dist/runtime/sw-version.js.map +1 -0
- package/dist/runtime/swoff-api-bundle.js +1491 -0
- package/dist/runtime/swoff-api-bundle.js.map +1 -0
- package/dist/runtime/type-definitions.js +8 -7
- package/dist/runtime/type-definitions.js.map +1 -1
- package/dist/runtime/utils.js +4 -0
- package/dist/runtime/utils.js.map +1 -1
- package/dist/swoff-nkdhhmw8. +18 -0
- package/dist/swoff.js +5 -0
- package/package.json +1 -1
- package/templates/react/useAuth.jsx +7 -4
- package/templates/react/useAuth.tsx +14 -14
- package/templates/react/useBackgroundSync.jsx +5 -4
- package/templates/react/useBackgroundSync.tsx +5 -4
- package/templates/react/useCachedFetch.jsx +8 -0
- package/templates/react/useCachedFetch.tsx +8 -0
- package/templates/react/useMutation.jsx +60 -56
- package/templates/react/useMutation.tsx +65 -64
- package/templates/react/useMutationQueue.jsx +5 -1
- package/templates/react/useMutationQueue.tsx +8 -2
- package/templates/react/useMutationState.jsx +3 -4
- package/templates/react/useMutationState.tsx +4 -8
- package/templates/react/useNetworkStatus.jsx +57 -17
- package/templates/react/useNetworkStatus.tsx +42 -20
- package/templates/react/useOfflineAnalytics.jsx +42 -0
- package/templates/react/useOfflineAnalytics.tsx +50 -0
- package/templates/react/usePwaInstall.jsx +26 -0
- package/templates/react/usePwaInstall.tsx +26 -0
- package/templates/react/useSWUpdate.jsx +7 -52
- package/templates/react/useSWUpdate.tsx +12 -51
- package/templates/react/useStorageEstimate.jsx +56 -0
- package/templates/react/useStorageEstimate.tsx +67 -0
- package/dist/__tests__/assemble-sw.test.js +0 -145
- package/dist/__tests__/assemble-sw.test.js.map +0 -1
- package/dist/__tests__/cli-integration.test.js +0 -231
- package/dist/__tests__/cli-integration.test.js.map +0 -1
- package/dist/__tests__/config-types.test.js +0 -124
- package/dist/__tests__/config-types.test.js.map +0 -1
- package/dist/__tests__/detect-language.test.js +0 -51
- package/dist/__tests__/detect-language.test.js.map +0 -1
- package/dist/__tests__/file-generators.test.js +0 -228
- package/dist/__tests__/file-generators.test.js.map +0 -1
- package/dist/__tests__/glob-matcher.test.js +0 -86
- package/dist/__tests__/glob-matcher.test.js.map +0 -1
- package/dist/__tests__/invalidation-tags.test.js +0 -283
- package/dist/__tests__/invalidation-tags.test.js.map +0 -1
- package/dist/__tests__/loader.test.js +0 -77
- package/dist/__tests__/loader.test.js.map +0 -1
- package/dist/__tests__/validator.test.js +0 -340
- package/dist/__tests__/validator.test.js.map +0 -1
- package/templates/react/useCacheInvalidation.jsx +0 -19
- package/templates/react/useCacheInvalidation.tsx +0 -52
- package/templates/react/useIsFetching.jsx +0 -32
- package/templates/react/useIsFetching.tsx +0 -32
|
@@ -1,37 +1,79 @@
|
|
|
1
|
-
export function generateClientInjectorCode(ctx, pwaEnabled, mutationQueueEnabled, serverPushEnabled) {
|
|
1
|
+
export function generateClientInjectorCode(ctx, pwaEnabled, mutationQueueEnabled, serverPushEnabled, navMode, authEnabled, connectivityEnabled, tagInvalidationEnabled) {
|
|
2
2
|
const { ext, ts } = ctx;
|
|
3
|
+
const ssrNav = navMode === "ssr" && tagInvalidationEnabled;
|
|
3
4
|
const pwaImport = pwaEnabled
|
|
4
|
-
? `import { setupPwaInstall } from "./pwa/
|
|
5
|
+
? `import { setupPwaInstall } from "./pwa/prompt.${ext}";
|
|
5
6
|
`
|
|
6
7
|
: "";
|
|
7
|
-
const pwaCall = pwaEnabled
|
|
8
|
+
const pwaCall = pwaEnabled
|
|
9
|
+
? `if (typeof window !== "undefined" && typeof document !== "undefined") { setupPwaInstall(); }\n`
|
|
10
|
+
: "";
|
|
8
11
|
const pushImport = serverPushEnabled
|
|
9
|
-
? `import { startPushEvents } from "./
|
|
12
|
+
? `import { startPushEvents } from "./server-push/client.${ext}";
|
|
10
13
|
`
|
|
11
14
|
: "";
|
|
12
|
-
const pushCall = serverPushEnabled
|
|
15
|
+
const pushCall = serverPushEnabled
|
|
16
|
+
? `if (typeof navigator !== "undefined" && "serviceWorker" in navigator) { startPushEvents(); }\n`
|
|
17
|
+
: "";
|
|
13
18
|
const mutationImport = mutationQueueEnabled
|
|
14
|
-
? `import { processMutationQueue } from "./
|
|
19
|
+
? `import { processMutationQueue, clearQueue } from "./mutation/queue.${ext}";
|
|
15
20
|
`
|
|
16
21
|
: "";
|
|
17
|
-
const
|
|
18
|
-
? `
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
const authImport = authEnabled
|
|
23
|
+
? `import { ensureValidAuth, clearMemoryAuth } from "./auth/store.${ext}";
|
|
24
|
+
`
|
|
25
|
+
: "";
|
|
26
|
+
const storageImport = connectivityEnabled
|
|
27
|
+
? `import { getStorageEstimate, formatBytes } from "./storage.${ext}";
|
|
28
|
+
`
|
|
29
|
+
: "";
|
|
30
|
+
const connectivityImport = connectivityEnabled
|
|
31
|
+
? `import {
|
|
32
|
+
dispatchState,
|
|
33
|
+
startHeartbeat,
|
|
34
|
+
stopHeartbeat,
|
|
35
|
+
verifyAndNotify,
|
|
36
|
+
} from './connectivity.${ext}'
|
|
21
37
|
`
|
|
22
38
|
: "";
|
|
23
|
-
const
|
|
24
|
-
|
|
39
|
+
const onlineListener = connectivityEnabled
|
|
40
|
+
? `
|
|
41
|
+
// --- Online Listener ---
|
|
25
42
|
// When connectivity returns, the SW checks stale cache entries and refetches them.
|
|
26
|
-
if (typeof window !==
|
|
27
|
-
window.addEventListener(
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
if (typeof window !== 'undefined') {
|
|
44
|
+
window.addEventListener('online', async () => {
|
|
45
|
+
const isOnline = await verifyAndNotify()
|
|
46
|
+
startHeartbeat()
|
|
47
|
+
if (isOnline) {
|
|
48
|
+
${mutationQueueEnabled ? `processMutationQueue()` : ""}
|
|
30
49
|
}
|
|
31
|
-
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
window.addEventListener('offline', () => {
|
|
53
|
+
stopHeartbeat()
|
|
54
|
+
dispatchState(false)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
document.addEventListener('visibilitychange', () => {
|
|
58
|
+
if (!document.hidden) {
|
|
59
|
+
verifyAndNotify()
|
|
60
|
+
startHeartbeat()
|
|
61
|
+
} else {
|
|
62
|
+
stopHeartbeat()
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
if (navigator.onLine) {
|
|
67
|
+
verifyAndNotify()
|
|
68
|
+
startHeartbeat()
|
|
69
|
+
} else {
|
|
70
|
+
queueMicrotask(() => dispatchState(false))
|
|
71
|
+
}
|
|
32
72
|
}
|
|
33
|
-
|
|
34
|
-
|
|
73
|
+
`
|
|
74
|
+
: "";
|
|
75
|
+
const focusListener = tagInvalidationEnabled
|
|
76
|
+
? `
|
|
35
77
|
// --- Focus Listener for Reactive Strategy ---
|
|
36
78
|
// Notifies the SW when the tab gains focus so it can refresh stale reactive entries.
|
|
37
79
|
// Uses visibilitychange only (covers tab switch, window refocus, alt-tab) — single source avoids duplicate FOCUS messages.
|
|
@@ -42,8 +84,10 @@ if (typeof document !== "undefined") {
|
|
|
42
84
|
}
|
|
43
85
|
});
|
|
44
86
|
}
|
|
45
|
-
|
|
46
|
-
|
|
87
|
+
`
|
|
88
|
+
: "";
|
|
89
|
+
const invalidationHandler = tagInvalidationEnabled
|
|
90
|
+
? `
|
|
47
91
|
if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
|
|
48
92
|
window.dispatchEvent(
|
|
49
93
|
new CustomEvent("cache-invalidated", {
|
|
@@ -51,9 +95,53 @@ if (typeof document !== "undefined") {
|
|
|
51
95
|
})
|
|
52
96
|
);
|
|
53
97
|
}
|
|
54
|
-
|
|
98
|
+
`
|
|
99
|
+
: "";
|
|
55
100
|
const swImport = `import { initServiceWorker as swInit } from "./sw/injector.${ext}";
|
|
56
101
|
`;
|
|
102
|
+
const autoPrefetchImport = ssrNav
|
|
103
|
+
? `import { prefetchCache } from "./fetch/core.${ext}";
|
|
104
|
+
`
|
|
105
|
+
: "";
|
|
106
|
+
const autoPrefetchCode = ssrNav
|
|
107
|
+
? `
|
|
108
|
+
// --- Auto-prefetch HTML on client-side navigation (SSR mode) ---
|
|
109
|
+
// Intercepts history.pushState/replaceState to warm the SW cache with HTML
|
|
110
|
+
// for routes the user navigates to via client-side routing.
|
|
111
|
+
if (typeof history !== "undefined") {
|
|
112
|
+
const origPushState = history.pushState.bind(history);
|
|
113
|
+
history.pushState = function (data, unused, url) {
|
|
114
|
+
origPushState(data, unused, url);
|
|
115
|
+
if (typeof url === "string" && url.startsWith("/")) {
|
|
116
|
+
prefetchCache(url);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const origReplaceState = history.replaceState.bind(history);
|
|
120
|
+
history.replaceState = function (data, unused, url) {
|
|
121
|
+
origReplaceState(data, unused, url);
|
|
122
|
+
if (typeof url === "string" && url.startsWith("/")) {
|
|
123
|
+
prefetchCache(url);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
`
|
|
128
|
+
: "";
|
|
129
|
+
const storageInit = connectivityEnabled
|
|
130
|
+
? `
|
|
131
|
+
const storage = await getStorageEstimate();
|
|
132
|
+
if (storage.percentUsed > 80) {
|
|
133
|
+
window.dispatchEvent(
|
|
134
|
+
new CustomEvent("swoff:notification", {
|
|
135
|
+
detail: {
|
|
136
|
+
level: "warn",
|
|
137
|
+
code: "STORAGE_QUOTA_HIGH",
|
|
138
|
+
message: \`Storage at \${storage.percentUsed}% capacity (\${formatBytes(storage.usage)} / \${formatBytes(storage.quota)})\`,
|
|
139
|
+
},
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
`
|
|
144
|
+
: "";
|
|
57
145
|
return `/**
|
|
58
146
|
* Swoff Client Injector
|
|
59
147
|
* Orchestrates SW registration, PWA install, and cross-tab sync.
|
|
@@ -68,7 +156,9 @@ if (typeof document !== "undefined") {
|
|
|
68
156
|
* sw-ready - SW active and controlling page
|
|
69
157
|
* sw-error - SW registration failed
|
|
70
158
|
* cache-invalidated - Cache entries cleared on SW confirmation (detail: { tags })
|
|
71
|
-
* swoff:cache-updated
|
|
159
|
+
* swoff:cache-updated - Background refresh completed (detail: { url })
|
|
160
|
+
* swoff:offline-fallback - Offline fallback page served (detail: { route, fallbackLevel, timestamp })
|
|
161
|
+
* swoff:notification - SW or storage notification (detail: { level, code, message })
|
|
72
162
|
* mutation-sync-complete - Queued mutations synced (detail: { succeeded, failed })
|
|
73
163
|
* mutation-sync-progress - Batch progress during sync (detail: { succeeded, failed, total, current })
|
|
74
164
|
* mutation-queue-changed - Queue modified
|
|
@@ -78,11 +168,9 @@ if (typeof document !== "undefined") {
|
|
|
78
168
|
* pwa-installed - User accepted install (detail: { outcome: 'accepted' })
|
|
79
169
|
* sw-auth-unauthorized - 401 response received
|
|
80
170
|
* sw-auth-state-change - Login or logout (detail: { authenticated: boolean })
|
|
81
|
-
* sw-update-available - New version ready (detail: { version })
|
|
82
|
-
* sw-version-detected - Version info available
|
|
83
171
|
*/
|
|
84
|
-
${pwaImport}${mutationImport}${swImport}${pushImport}
|
|
85
|
-
${pwaCall}${
|
|
172
|
+
${pwaImport}${mutationImport}${authImport}${swImport}${storageImport}${connectivityImport}${pushImport}${autoPrefetchImport}
|
|
173
|
+
${pwaCall}${pushCall}${onlineListener}${focusListener}${autoPrefetchCode}
|
|
86
174
|
// --- SW Message Listener ---
|
|
87
175
|
if (typeof window !== "undefined" && "serviceWorker" in navigator) {
|
|
88
176
|
navigator.serviceWorker.addEventListener("message", (event) => {
|
|
@@ -93,6 +181,20 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
|
|
|
93
181
|
})
|
|
94
182
|
);
|
|
95
183
|
}
|
|
184
|
+
if (event.data.type === "NAV_RETRY_SUCCESS") {
|
|
185
|
+
window.dispatchEvent(
|
|
186
|
+
new CustomEvent("swoff:navigation-online", {
|
|
187
|
+
detail: { url: event.data.url },
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (event.data.type === "OFFLINE_FALLBACK_ACTIVATED") {
|
|
192
|
+
window.dispatchEvent(
|
|
193
|
+
new CustomEvent("swoff:offline-fallback", {
|
|
194
|
+
detail: event.data.detail,
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}
|
|
96
198
|
if (event.data.type === "SW_PROGRESS") {
|
|
97
199
|
const { percent, downloaded, total } = event.data;
|
|
98
200
|
window.dispatchEvent(
|
|
@@ -101,6 +203,17 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
|
|
|
101
203
|
})
|
|
102
204
|
);
|
|
103
205
|
}
|
|
206
|
+
if (event.data.type === "SW_NOTIFICATION") {
|
|
207
|
+
window.dispatchEvent(
|
|
208
|
+
new CustomEvent("swoff:notification", {
|
|
209
|
+
detail: {
|
|
210
|
+
level: event.data.level,
|
|
211
|
+
code: event.data.code,
|
|
212
|
+
message: event.data.message,
|
|
213
|
+
},
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
}
|
|
104
217
|
if (event.data.type === "BACKGROUND_SYNC_PROGRESS") {
|
|
105
218
|
window.dispatchEvent(
|
|
106
219
|
new CustomEvent("mutation-sync-progress", {
|
|
@@ -122,14 +235,44 @@ if (typeof window !== "undefined" && "serviceWorker" in navigator) {
|
|
|
122
235
|
}
|
|
123
236
|
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
124
237
|
}
|
|
125
|
-
if (event.data.type === "MUTATION_STORED") {
|
|
126
|
-
|
|
238
|
+
if (event.data.type === "MUTATION_STORED" && typeof processMutationQueue !== "undefined") {
|
|
239
|
+
processMutationQueue();
|
|
240
|
+
}
|
|
241
|
+
if (event.data.type === "AUTH_CLEARED") {
|
|
242
|
+
// Another tab cleared auth — clear memory only (IndexedDB + caches already cleaned by initiator)
|
|
243
|
+
if (typeof clearMemoryAuth !== "undefined") {
|
|
244
|
+
clearMemoryAuth();
|
|
245
|
+
}
|
|
246
|
+
window.dispatchEvent(new CustomEvent("sw-auth-state-change", { detail: { type: "clear" } }));
|
|
247
|
+
}
|
|
248
|
+
if (event.data.type === "AUTH_FAILURE") {
|
|
249
|
+
// SW detected 401 during background refetch — check if session is still valid
|
|
250
|
+
(async () => {
|
|
251
|
+
if (typeof ensureValidAuth === "undefined") return;
|
|
252
|
+
const refreshed = await ensureValidAuth();
|
|
253
|
+
if (!refreshed) {
|
|
254
|
+
// Session expired — clear queue and runtime caches
|
|
255
|
+
if (typeof clearQueue !== "undefined") {
|
|
256
|
+
await clearQueue();
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
for (const name of ["swoff-runtime", "swoff-runtime-html"]) {
|
|
260
|
+
const cache = await caches.open(name);
|
|
261
|
+
const keys = await cache.keys();
|
|
262
|
+
await Promise.all(keys.map((k) => cache.delete(k)));
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
// Handle cache deletion errors
|
|
266
|
+
}
|
|
267
|
+
window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
|
|
268
|
+
}
|
|
269
|
+
})();
|
|
127
270
|
}${invalidationHandler} });
|
|
128
271
|
}
|
|
129
272
|
|
|
130
273
|
/** Initialize SW registration and all client-side features (PWA install, mutation queue, cross-tab sync). Call once at app startup. */
|
|
131
274
|
export async function initServiceWorker()${ts ? ": Promise<void>" : " "}{
|
|
132
|
-
await swInit()
|
|
275
|
+
await swInit();${storageInit}
|
|
133
276
|
}
|
|
134
277
|
`;
|
|
135
278
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../src/runtime/client-injector.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,0BAA0B,CACxC,GAAmB,EACnB,UAAmB,EACnB,oBAA6B,EAC7B,iBAA0B;
|
|
1
|
+
{"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../src/runtime/client-injector.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,0BAA0B,CACxC,GAAmB,EACnB,UAAmB,EACnB,oBAA6B,EAC7B,iBAA0B,EAC1B,OAAgB,EAChB,WAAqB,EACrB,mBAA6B,EAC7B,sBAAgC;IAEhC,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACxB,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,IAAI,sBAAsB,CAAC;IAE3D,MAAM,SAAS,GAAG,UAAU;QAC1B,CAAC,CAAC,iDAAiD,GAAG;CACzD;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,UAAU;QACxB,CAAC,CAAC,gGAAgG;QAClG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GAAG,iBAAiB;QAClC,CAAC,CAAC,yDAAyD,GAAG;CACjE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,iBAAiB;QAChC,CAAC,CAAC,gGAAgG;QAClG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG,oBAAoB;QACzC,CAAC,CAAC,sEAAsE,GAAG;CAC9E;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GAAG,WAAW;QAC5B,CAAC,CAAC,kEAAkE,GAAG;CAC1E;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,aAAa,GAAG,mBAAmB;QACvC,CAAC,CAAC,8DAA8D,GAAG;CACtE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,kBAAkB,GAAG,mBAAmB;QAC5C,CAAC,CAAC;;;;;yBAKmB,GAAG;CAC3B;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG,mBAAmB;QACxC,CAAC,CAAC;;;;;;;;QAQE,oBAAoB,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;CAyB3D;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,aAAa,GAAG,sBAAsB;QAC1C,CAAC,CAAC;;;;;;;;;;;CAWL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,mBAAmB,GAAG,sBAAsB;QAChD,CAAC,CAAC;;;;;;;;CAQL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,8DAA8D,GAAG;CACnF,CAAC;IAEA,MAAM,kBAAkB,GAAG,MAAM;QAC/B,CAAC,CAAC,+CAA+C,GAAG;CACvD;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,gBAAgB,GAAG,MAAM;QAC7B,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;CAoBL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,WAAW,GAAG,mBAAmB;QACrC,CAAC,CAAC;;;;;;;;;;;;;CAaL;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;kEAMyD,GAAG;;;;;;;;;;;;;;;;;;;;;EAqBnE,SAAS,GAAG,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,aAAa,GAAG,kBAAkB,GAAG,UAAU,GAAG,kBAAkB;EACzH,OAAO,GAAG,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiGjE,mBAAmB;;;;2CAIiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG;mBACpD,WAAW;;CAE7B,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { T } from "./utils.js";
|
|
2
|
+
export function generateConnectivityManagerCode(ctx) {
|
|
3
|
+
const { ts } = ctx;
|
|
4
|
+
return `/**
|
|
5
|
+
**/
|
|
6
|
+
export const CONNECTIVITY_EVENT = 'app-connectivity-change'
|
|
7
|
+
|
|
8
|
+
let heartbeatIntervalId${T(ts, "ReturnType<typeof setInterval> | null")} = null
|
|
9
|
+
const HEARTBEAT_DELAY = 30000
|
|
10
|
+
|
|
11
|
+
let _currentOnlineStatus = typeof navigator !== 'undefined' ? navigator.onLine : true
|
|
12
|
+
|
|
13
|
+
export function getCurrentOnlineStatus(): boolean {
|
|
14
|
+
return _currentOnlineStatus
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createTimeoutSignal(ms) {
|
|
18
|
+
if (typeof AbortSignal.timeout === "function") return AbortSignal.timeout(ms);
|
|
19
|
+
var ctrl = new AbortController();
|
|
20
|
+
setTimeout(function() { ctrl.abort(); }, ms);
|
|
21
|
+
return ctrl.signal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function verifyAndNotify() {
|
|
25
|
+
if (typeof window === 'undefined') return false
|
|
26
|
+
|
|
27
|
+
if (!navigator.onLine) {
|
|
28
|
+
dispatchState(false)
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
await fetch(\`/\${Date.now()}?hb=1\`, {
|
|
34
|
+
method: 'HEAD',
|
|
35
|
+
cache: 'no-cache',
|
|
36
|
+
signal: createTimeoutSignal(5000),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (navigator.serviceWorker.controller) {
|
|
40
|
+
navigator.serviceWorker.controller.postMessage({ type: 'ONLINE' })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
dispatchState(true)
|
|
44
|
+
return true
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (navigator.serviceWorker.controller) {
|
|
47
|
+
navigator.serviceWorker.controller.postMessage({ type: 'OFFLINE' })
|
|
48
|
+
}
|
|
49
|
+
dispatchState(false)
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function dispatchState(isTrulyOnline: boolean) {
|
|
55
|
+
_currentOnlineStatus = isTrulyOnline
|
|
56
|
+
const event = new CustomEvent(CONNECTIVITY_EVENT, {
|
|
57
|
+
detail: { online: isTrulyOnline },
|
|
58
|
+
})
|
|
59
|
+
window.dispatchEvent(event)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function startHeartbeat() {
|
|
63
|
+
if (heartbeatIntervalId) return
|
|
64
|
+
heartbeatIntervalId = setInterval(async () => {
|
|
65
|
+
if (document.hidden) return
|
|
66
|
+
await verifyAndNotify()
|
|
67
|
+
}, HEARTBEAT_DELAY)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function stopHeartbeat() {
|
|
71
|
+
if (!heartbeatIntervalId) return
|
|
72
|
+
clearInterval(heartbeatIntervalId)
|
|
73
|
+
heartbeatIntervalId = null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function forceRetry() {
|
|
77
|
+
stopHeartbeat()
|
|
78
|
+
await verifyAndNotify()
|
|
79
|
+
startHeartbeat()
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=connectivity-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectivity-manager.js","sourceRoot":"","sources":["../../src/runtime/connectivity-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,+BAA+B,CAAC,GAAmB;IACjE,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACnB,OAAO;;;;yBAIgB,CAAC,CAAC,EAAE,EAAE,uCAAuC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyEtE,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { T } from "./utils.js";
|
|
2
|
+
export function generateConnectivityCode(ctx) {
|
|
3
|
+
const { ts } = ctx;
|
|
4
|
+
return `/**
|
|
5
|
+
**/
|
|
6
|
+
export const CONNECTIVITY_EVENT = 'app-connectivity-change'
|
|
7
|
+
|
|
8
|
+
let heartbeatIntervalId${T(ts, "ReturnType<typeof setInterval> | null")} = null
|
|
9
|
+
const HEARTBEAT_DELAY = 30000
|
|
10
|
+
|
|
11
|
+
let _currentOnlineStatus = typeof navigator !== 'undefined' ? navigator.onLine : true
|
|
12
|
+
|
|
13
|
+
export function getCurrentOnlineStatus(): boolean {
|
|
14
|
+
return _currentOnlineStatus
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createTimeoutSignal(ms) {
|
|
18
|
+
if (typeof AbortSignal.timeout === "function") return AbortSignal.timeout(ms);
|
|
19
|
+
var ctrl = new AbortController();
|
|
20
|
+
setTimeout(function() { ctrl.abort(); }, ms);
|
|
21
|
+
return ctrl.signal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function verifyAndNotify() {
|
|
25
|
+
if (typeof window === 'undefined') return false
|
|
26
|
+
|
|
27
|
+
if (!navigator.onLine) {
|
|
28
|
+
dispatchState(false)
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
await fetch(\`/\${Date.now()}?hb=1\`, {
|
|
34
|
+
method: 'HEAD',
|
|
35
|
+
cache: 'no-cache',
|
|
36
|
+
signal: createTimeoutSignal(5000),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (navigator.serviceWorker.controller) {
|
|
40
|
+
navigator.serviceWorker.controller.postMessage({ type: 'ONLINE' })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
dispatchState(true)
|
|
44
|
+
return true
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (navigator.serviceWorker.controller) {
|
|
47
|
+
navigator.serviceWorker.controller.postMessage({ type: 'OFFLINE' })
|
|
48
|
+
}
|
|
49
|
+
dispatchState(false)
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function dispatchState(isTrulyOnline: boolean) {
|
|
55
|
+
_currentOnlineStatus = isTrulyOnline
|
|
56
|
+
const event = new CustomEvent(CONNECTIVITY_EVENT, {
|
|
57
|
+
detail: { online: isTrulyOnline },
|
|
58
|
+
})
|
|
59
|
+
window.dispatchEvent(event)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function startHeartbeat() {
|
|
63
|
+
if (heartbeatIntervalId) return
|
|
64
|
+
heartbeatIntervalId = setInterval(async () => {
|
|
65
|
+
if (document.hidden) return
|
|
66
|
+
await verifyAndNotify()
|
|
67
|
+
}, HEARTBEAT_DELAY)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function stopHeartbeat() {
|
|
71
|
+
if (!heartbeatIntervalId) return
|
|
72
|
+
clearInterval(heartbeatIntervalId)
|
|
73
|
+
heartbeatIntervalId = null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function forceRetry() {
|
|
77
|
+
stopHeartbeat()
|
|
78
|
+
await verifyAndNotify()
|
|
79
|
+
startHeartbeat()
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=connectivity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectivity.js","sourceRoot":"","sources":["../../src/runtime/connectivity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,wBAAwB,CAAC,GAAmB;IAC1D,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IACnB,OAAO;;;;yBAIgB,CAAC,CAAC,EAAE,EAAE,uCAAuC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyEtE,CAAC;AACF,CAAC"}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { T, R, G } from "./utils.js";
|
|
2
|
-
export function generateFetchWrapperCode(ctx, authEnabled,
|
|
1
|
+
import { T, R, G, AS } from "./utils.js";
|
|
2
|
+
export function generateFetchWrapperCode(ctx, authEnabled, mutationQueue, requestBatchWindowMs) {
|
|
3
3
|
const { ext, ts } = ctx;
|
|
4
4
|
const importLines = [
|
|
5
|
-
`import {
|
|
6
|
-
`import {
|
|
7
|
-
`import { invalidateByTags } from "../cache/index.${ext}";`,
|
|
5
|
+
`import { generateTags } from "../cache/tags.${ext}";`,
|
|
6
|
+
`import { invalidateByTags, invalidateUrl${mutationQueue ? ", expandCascading" : ""} } from "../cache/invalidate.${ext}";`,
|
|
8
7
|
authEnabled
|
|
9
|
-
? `import { getAuth,
|
|
8
|
+
? `import { getAuth, withAuthHeaders, AUTH_WITH_CREDENTIALS } from "../auth/store.${ext}";`
|
|
10
9
|
: "",
|
|
11
10
|
mutationQueue
|
|
12
|
-
? `import { queueMutation } from "../
|
|
11
|
+
? `import { queueMutation } from "../mutation/queue.${ext}";`
|
|
13
12
|
: "",
|
|
14
|
-
`import { incrementFetchCount, decrementFetchCount } from "../fetch/state.${ext}";`,
|
|
15
13
|
]
|
|
16
14
|
.filter(Boolean)
|
|
17
15
|
.join("\n");
|
|
@@ -41,8 +39,7 @@ export interface FetchWithCacheOptions extends RequestInit {
|
|
|
41
39
|
validateSuccess?: (response: Response) => boolean | Promise<boolean>;
|
|
42
40
|
/** Override the URL used for auto-invalidation. Defaults to the request URL. Useful when the mutation URL differs from the cache tag URL. */
|
|
43
41
|
invalidateUrl?: string;
|
|
44
|
-
|
|
45
|
-
skipFetchCount?: boolean;
|
|
42
|
+
|
|
46
43
|
}
|
|
47
44
|
`
|
|
48
45
|
: "";
|
|
@@ -51,48 +48,12 @@ export interface FetchWithCacheOptions extends RequestInit {
|
|
|
51
48
|
if (options.auth) {
|
|
52
49
|
const auth = await getAuth();
|
|
53
50
|
withAuthHeaders(headers, auth);
|
|
54
|
-
}`
|
|
55
|
-
: "";
|
|
56
|
-
const authUrlsBlock = authEnabled
|
|
57
|
-
? `
|
|
58
|
-
// Auth endpoints bypass SW cache
|
|
59
|
-
if (options.auth && isAuthUrl(url) && !headers.has("X-SW-Cache-Strategy")) {
|
|
60
|
-
headers.set("X-SW-Cache-Strategy", "mutation");
|
|
61
51
|
}`
|
|
62
52
|
: "";
|
|
63
53
|
const authCredentialsBlock = authEnabled
|
|
64
54
|
? `
|
|
65
55
|
if (AUTH_WITH_CREDENTIALS) {
|
|
66
56
|
fetchOptions.credentials = "include";
|
|
67
|
-
}`
|
|
68
|
-
: "";
|
|
69
|
-
const auth401Block = authEnabled
|
|
70
|
-
? `
|
|
71
|
-
if (options.auth && response.status === 401) {
|
|
72
|
-
// Check if the token is actually expired by probing the user endpoint
|
|
73
|
-
try {
|
|
74
|
-
const authCheck = await fetch(API_BASE + "${userEndpoint}", {
|
|
75
|
-
headers: { "Authorization": headers.get("Authorization") } as HeadersInit,
|
|
76
|
-
credentials: AUTH_WITH_CREDENTIALS ? "include" : undefined,
|
|
77
|
-
});
|
|
78
|
-
if (authCheck.status === 401) {
|
|
79
|
-
// Token expired — try silent refresh
|
|
80
|
-
const refreshed = await ensureValidAuth();
|
|
81
|
-
if (refreshed?.token) {
|
|
82
|
-
// Retry original request with fresh token
|
|
83
|
-
const retryHeaders = new Headers(options.headers);
|
|
84
|
-
withAuthHeaders(retryHeaders, refreshed);
|
|
85
|
-
response = await fetch(resolvedInput, { ...fetchOptions, headers: retryHeaders });
|
|
86
|
-
} else {
|
|
87
|
-
await clearAuth();
|
|
88
|
-
window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// authCheck 200: user IS authenticated but lacks permission — let original 401 propagate
|
|
92
|
-
} catch {
|
|
93
|
-
await clearAuth();
|
|
94
|
-
window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
|
|
95
|
-
}
|
|
96
57
|
}`
|
|
97
58
|
: "";
|
|
98
59
|
const mutationTagsBlock = mutationQueue
|
|
@@ -228,18 +189,16 @@ const BATCH_WINDOW_MS = ${requestBatchWindowMs};
|
|
|
228
189
|
|
|
229
190
|
/** Fetch with caching, auth, offline queue, auto-invalidation, and per-request strategy override. Returns { response, fromCache }. Use { auth: true } for authenticated requests — works with bearer, cookie, and custom auth types. */
|
|
230
191
|
export async function fetchWithCache${G(ts, "T")}(input${T(ts, "RequestInfo")}, options${T(ts, "RequestInit & FetchWithCacheOptions")} = {})${R(ts, "Promise<FetchWithCacheResult<T>>")}{
|
|
231
|
-
if (!options.skipFetchCount) incrementFetchCount();
|
|
232
|
-
try {
|
|
233
192
|
const method = (options.method || "GET").toUpperCase();
|
|
234
193
|
const isRead = options.type === "read" || (options.type !== "mutation" && (method === "GET" || method === "HEAD" || method === "OPTIONS"));
|
|
235
|
-
const resolvedInput =
|
|
194
|
+
const resolvedInput = input;
|
|
236
195
|
const url = typeof resolvedInput === "string" ? resolvedInput : resolvedInput.url;
|
|
237
196
|
|
|
238
197
|
const headers = new Headers(options.headers);
|
|
239
198
|
|
|
240
199
|
// Set cache strategy
|
|
241
|
-
if (!headers.has("X-SW-
|
|
242
|
-
headers.set("X-SW-
|
|
200
|
+
if (!headers.has("X-SW-Type")) {
|
|
201
|
+
headers.set("X-SW-Type", isRead ? "read" : "mutation");
|
|
243
202
|
}
|
|
244
203
|
${autoTagsBlock}
|
|
245
204
|
// Custom tags override auto-generated
|
|
@@ -262,7 +221,7 @@ ${autoTagsBlock}
|
|
|
262
221
|
if (options.refetchOnReconnect !== undefined) {
|
|
263
222
|
headers.set("X-SW-Refetch-On-Reconnect", String(options.refetchOnReconnect));
|
|
264
223
|
}
|
|
265
|
-
${authBlock}
|
|
224
|
+
${authBlock}
|
|
266
225
|
// Forward no-queue option to SW
|
|
267
226
|
if (options.queueOffline === false) {
|
|
268
227
|
headers.set("X-SW-No-Queue", "true");
|
|
@@ -288,12 +247,18 @@ ${mutationTagsBlock}
|
|
|
288
247
|
} else {
|
|
289
248
|
const abortHandler = () => {
|
|
290
249
|
inFlightRequests.delete(url);
|
|
250
|
+
if (pendingBatches.has(url)) {
|
|
251
|
+
const batch = pendingBatches.get(url)!;
|
|
252
|
+
clearTimeout(batch.timer);
|
|
253
|
+
pendingBatches.delete(url);
|
|
254
|
+
batch.rejectors.forEach((rej) => rej(new DOMException("The operation was aborted", "AbortError")));
|
|
255
|
+
}
|
|
291
256
|
};
|
|
292
257
|
if (options.signal) {
|
|
293
258
|
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
294
259
|
}
|
|
295
260
|
if (isRead && BATCH_WINDOW_MS > 0) {
|
|
296
|
-
const batch${T(ts, "{ resolvers: Array<(r: Response) => void>; rejectors: Array<(e: unknown) => void>; timer: ReturnType<typeof setTimeout> }")} = { resolvers: [], rejectors: [], timer: 0 };
|
|
261
|
+
const batch${T(ts, "{ resolvers: Array<(r: Response) => void>; rejectors: Array<(e: unknown) => void>; timer: ReturnType<typeof setTimeout> }")} = { resolvers: [], rejectors: [], timer: 0${AS(ts, "unknown as ReturnType<typeof setTimeout>")} };
|
|
297
262
|
pendingBatches.set(url, batch);
|
|
298
263
|
batch.timer = setTimeout(() => {
|
|
299
264
|
pendingBatches.delete(url);
|
|
@@ -316,7 +281,12 @@ ${mutationTagsBlock}
|
|
|
316
281
|
batch.rejectors.push(reject);
|
|
317
282
|
});
|
|
318
283
|
} else {
|
|
319
|
-
responsePromise = fetch(resolvedInput, fetchOptions)
|
|
284
|
+
responsePromise = fetch(resolvedInput, fetchOptions).finally(() => {
|
|
285
|
+
inFlightRequests.delete(url);
|
|
286
|
+
if (options.signal) {
|
|
287
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
320
290
|
}
|
|
321
291
|
}
|
|
322
292
|
|
|
@@ -335,18 +305,15 @@ ${offlineWriteFallbackBlock}
|
|
|
335
305
|
}
|
|
336
306
|
throw err;
|
|
337
307
|
}
|
|
338
|
-
${autoInvalidateBlock}
|
|
308
|
+
${autoInvalidateBlock}
|
|
339
309
|
const fromCache = response.headers.get("X-SW-From-Cache") === "true";
|
|
340
310
|
const queued = response.headers.get("X-SW-Mutation-Queued") === "true";
|
|
341
311
|
return { response, fromCache, queued };
|
|
342
|
-
} finally {
|
|
343
|
-
if (!options.skipFetchCount) decrementFetchCount();
|
|
344
|
-
}
|
|
345
312
|
}
|
|
346
313
|
|
|
347
314
|
/** Fire-and-forget prefetch: warms the cache for a URL without blocking. Useful for route prefetching or link hover prefetching. */
|
|
348
315
|
export function prefetchCache(input${T(ts, "RequestInfo")}, options${T(ts, "RequestInit & FetchWithCacheOptions")} = {})${R(ts, "void")}{
|
|
349
|
-
fetchWithCache(input,
|
|
316
|
+
fetchWithCache(input, options).catch(() => {
|
|
350
317
|
// Prefetch failures are intentionally silent
|
|
351
318
|
});
|
|
352
319
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../src/runtime/fetch-wrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../src/runtime/fetch-wrapper.ts"],"names":[],"mappings":"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"}
|