@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.
- package/README.md +47 -33
- package/dist/__tests__/assemble-sw.test.js +14 -53
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +119 -11
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +44 -17
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +3 -3
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/glob-matcher.test.js +86 -0
- package/dist/__tests__/glob-matcher.test.js.map +1 -0
- package/dist/__tests__/invalidation-tags.test.js +207 -23
- package/dist/__tests__/invalidation-tags.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +16 -12
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +140 -100
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/index.js +39 -34
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +12 -12
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +84 -36
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/assets.js +37 -0
- package/dist/lib/commands/assets.js.map +1 -0
- package/dist/lib/commands/clean.js +32 -30
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-assets.js +44 -0
- package/dist/lib/commands/generate-assets.js.map +1 -0
- package/dist/lib/commands/generate-guide.js +22 -29
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +17 -24
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +41 -48
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/init.js +27 -13
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/commands/validate.js +4 -4
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/loader.js +25 -23
- package/dist/lib/config/loader.js.map +1 -1
- package/dist/lib/config/validator.js +169 -91
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/asset-generator/dependency-resolver.js +76 -0
- package/dist/lib/generators/asset-generator/dependency-resolver.js.map +1 -0
- package/dist/lib/generators/asset-generator/generate.js +111 -0
- package/dist/lib/generators/asset-generator/generate.js.map +1 -0
- package/dist/lib/generators/asset-generator/guide.js +43 -0
- package/dist/lib/generators/asset-generator/guide.js.map +1 -0
- package/dist/lib/generators/asset-generator/html-patcher.js +65 -0
- package/dist/lib/generators/asset-generator/html-patcher.js.map +1 -0
- package/dist/lib/generators/asset-generator/ico-encoder.js +29 -0
- package/dist/lib/generators/asset-generator/ico-encoder.js.map +1 -0
- package/dist/lib/generators/asset-generator/manifest-patcher.js +23 -0
- package/dist/lib/generators/asset-generator/manifest-patcher.js.map +1 -0
- package/dist/lib/generators/asset-generator/maskable.js +15 -0
- package/dist/lib/generators/asset-generator/maskable.js.map +1 -0
- package/dist/lib/generators/asset-generator/rasterize.js +32 -0
- package/dist/lib/generators/asset-generator/rasterize.js.map +1 -0
- package/dist/lib/generators/asset-generator/sizes.js +25 -0
- package/dist/lib/generators/asset-generator/sizes.js.map +1 -0
- package/dist/lib/generators/file-generators/api-config.js +6 -0
- package/dist/lib/generators/file-generators/api-config.js.map +1 -0
- package/dist/lib/generators/file-generators/auth-state.js +2 -34
- package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-store.js +2 -250
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-user.js +3 -81
- package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +2 -57
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +2 -38
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +2 -91
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-state.js +6 -0
- package/dist/lib/generators/file-generators/fetch-state.js.map +1 -0
- package/dist/lib/generators/file-generators/fetch-wrapper.js +3 -267
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +56 -0
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -0
- package/dist/lib/generators/file-generators/generate-hooks.js +4 -3
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
- package/dist/lib/generators/file-generators/gql-wrapper.js +3 -145
- package/dist/lib/generators/file-generators/gql-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +24 -630
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -1
- package/dist/lib/generators/file-generators/invalidation-tags.js +18 -86
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/manifest.js +15 -5
- package/dist/lib/generators/file-generators/manifest.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +3 -285
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-state.js +2 -117
- package/dist/lib/generators/file-generators/mutation-state.js.map +1 -1
- package/dist/lib/generators/file-generators/push.js +6 -139
- package/dist/lib/generators/file-generators/push.js.map +1 -1
- package/dist/lib/generators/file-generators/pwa-install.js +11 -84
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/quick-readme.js +8 -125
- package/dist/lib/generators/file-generators/quick-readme.js.map +1 -1
- package/dist/lib/generators/file-generators/reset.js +6 -0
- package/dist/lib/generators/file-generators/reset.js.map +1 -0
- package/dist/lib/generators/file-generators/server-push.js +3 -124
- package/dist/lib/generators/file-generators/server-push.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +8 -9
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +10 -230
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/type-definitions.js +2 -114
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +10 -20
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +25 -3
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +20 -16
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-sync-handler.js +68 -19
- package/dist/lib/generators/sw-sections/background-sync-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/config-header.js +2 -2
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +506 -144
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/glob-matcher-code.js.map +1 -0
- package/dist/lib/generators/sw-sections/message-handler.js +55 -15
- package/dist/lib/generators/sw-sections/message-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/server-push-handler.js +12 -0
- package/dist/lib/generators/sw-sections/server-push-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/sw-push.js +67 -0
- package/dist/lib/generators/sw-sections/sw-push.js.map +1 -0
- package/dist/lib/generators/sw-sections/tag-management.js +65 -23
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +66 -45
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +182 -71
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/shared/glob-matcher.js +77 -0
- package/dist/lib/shared/glob-matcher.js.map +1 -0
- package/dist/lib/utils/tty-status.js +21 -0
- package/dist/lib/utils/tty-status.js.map +1 -0
- package/dist/runtime/api-config.js +18 -0
- package/dist/runtime/api-config.js.map +1 -0
- package/dist/runtime/auth-state.js +35 -0
- package/dist/runtime/auth-state.js.map +1 -0
- package/dist/runtime/auth-store.js +273 -0
- package/dist/runtime/auth-store.js.map +1 -0
- package/dist/runtime/auth-user.js +168 -0
- package/dist/runtime/auth-user.js.map +1 -0
- package/dist/runtime/background-sync.js +59 -0
- package/dist/runtime/background-sync.js.map +1 -0
- package/dist/runtime/cache.js +30 -0
- package/dist/runtime/cache.js.map +1 -0
- package/dist/runtime/client-injector.js +136 -0
- package/dist/runtime/client-injector.js.map +1 -0
- package/dist/runtime/fetch-state.js +42 -0
- package/dist/runtime/fetch-state.js.map +1 -0
- package/dist/runtime/fetch-wrapper.js +355 -0
- package/dist/runtime/fetch-wrapper.js.map +1 -0
- package/dist/runtime/gql-wrapper.js +135 -0
- package/dist/runtime/gql-wrapper.js.map +1 -0
- package/dist/runtime/invalidation-tags.js +260 -0
- package/dist/runtime/invalidation-tags.js.map +1 -0
- package/dist/runtime/mutation-queue.js +304 -0
- package/dist/runtime/mutation-queue.js.map +1 -0
- package/dist/runtime/mutation-state.js +117 -0
- package/dist/runtime/mutation-state.js.map +1 -0
- package/dist/runtime/push.js +138 -0
- package/dist/runtime/push.js.map +1 -0
- package/dist/runtime/pwa-index.js +11 -0
- package/dist/runtime/pwa-index.js.map +1 -0
- package/dist/runtime/pwa-injector.js +44 -0
- package/dist/runtime/pwa-injector.js.map +1 -0
- package/dist/runtime/pwa-install.js +83 -0
- package/dist/runtime/pwa-install.js.map +1 -0
- package/dist/runtime/pwa-prompt.js +54 -0
- package/dist/runtime/pwa-prompt.js.map +1 -0
- package/dist/runtime/reset.js +148 -0
- package/dist/runtime/reset.js.map +1 -0
- package/dist/runtime/server-push.js +148 -0
- package/dist/runtime/server-push.js.map +1 -0
- package/dist/runtime/sw-injector.js +236 -0
- package/dist/runtime/sw-injector.js.map +1 -0
- package/dist/runtime/type-definitions.js +138 -0
- package/dist/runtime/type-definitions.js.map +1 -0
- package/dist/runtime/utils.js +27 -0
- package/dist/runtime/utils.js.map +1 -0
- package/package.json +3 -1
- package/templates/react/useAuth.jsx +106 -0
- package/templates/react/useAuth.tsx +118 -0
- package/templates/{hooks → react}/useBackgroundSync.jsx +6 -4
- package/templates/{hooks → react}/useBackgroundSync.tsx +6 -4
- package/templates/{hooks → react}/useCacheInvalidation.jsx +2 -2
- package/templates/react/useCacheInvalidation.tsx +52 -0
- package/templates/react/useCachedFetch.jsx +140 -0
- package/templates/react/useCachedFetch.tsx +160 -0
- package/templates/react/useIsFetching.jsx +32 -0
- package/templates/react/useIsFetching.tsx +32 -0
- package/templates/react/useMutation.jsx +141 -0
- package/templates/react/useMutation.tsx +180 -0
- package/templates/react/useMutationQueue.jsx +69 -0
- package/templates/react/useMutationQueue.tsx +83 -0
- package/templates/react/useMutationState.jsx +13 -0
- package/templates/react/useMutationState.tsx +25 -0
- package/templates/react/useNetworkStatus.jsx +61 -0
- package/templates/react/useNetworkStatus.tsx +76 -0
- package/templates/react/usePrefetch.jsx +32 -0
- package/templates/react/usePrefetch.tsx +42 -0
- package/templates/{hooks → react}/usePushSubscription.jsx +7 -7
- package/templates/{hooks → react}/usePushSubscription.tsx +7 -7
- package/templates/{hooks → react}/useSWUpdate.jsx +3 -3
- package/templates/{hooks → react}/useSWUpdate.tsx +4 -4
- package/templates/react/useSwoffReset.jsx +32 -0
- package/templates/react/useSwoffReset.tsx +39 -0
- package/templates/hooks/useAuth.jsx +0 -30
- package/templates/hooks/useAuth.tsx +0 -30
- package/templates/hooks/useCacheInvalidation.tsx +0 -19
- package/templates/hooks/useCachedFetch.jsx +0 -94
- package/templates/hooks/useCachedFetch.tsx +0 -112
- package/templates/hooks/useMutation.jsx +0 -76
- package/templates/hooks/useMutation.tsx +0 -97
- package/templates/hooks/useMutationQueue.jsx +0 -42
- package/templates/hooks/useMutationQueue.tsx +0 -49
- package/templates/hooks/useMutationState.jsx +0 -27
- package/templates/hooks/useMutationState.tsx +0 -41
- package/templates/hooks/useNetworkStatus.jsx +0 -19
- package/templates/hooks/useNetworkStatus.tsx +0 -19
- package/templates/hooks/usePrefetch.jsx +0 -10
- package/templates/hooks/usePrefetch.tsx +0 -25
|
@@ -2,90 +2,12 @@
|
|
|
2
2
|
* Generates auth-user.ts/js — fetch, cache, and invalidate current user.
|
|
3
3
|
*/
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
|
+
import { generateAuthUserCode } from "../../../runtime/auth-user.js";
|
|
5
6
|
export function generateAuthUser(ctx) {
|
|
6
7
|
const ext = ctx.ext;
|
|
7
8
|
const ts = ext === "ts";
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const PT = (type) => (ts ? `<${type}>` : "");
|
|
11
|
-
const AS = (type) => (ts ? ` as ${type}` : "");
|
|
12
|
-
const userEndpoint = ctx.config.features.auth.userEndpoint;
|
|
13
|
-
const code = `/**
|
|
14
|
-
* Current User — fetch, cache, and invalidate the current user for offline access.
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* import { fetchCurrentUser, cacheUser, getCachedUser, clearCachedUser } from "./auth/user.${ext}";
|
|
18
|
-
*
|
|
19
|
-
* await fetchCurrentUser(); // Fetch from server & cache
|
|
20
|
-
* const user = await getCachedUser(); // Get cached (offline-capable)
|
|
21
|
-
* await clearCachedUser(); // Clear on logout
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { fetchWithCache } from "../fetch-wrapper.${ext}";
|
|
25
|
-
|
|
26
|
-
const DB_NAME = "swoff-auth-user";
|
|
27
|
-
const STORE_NAME = "current-user";
|
|
28
|
-
|
|
29
|
-
function openAuthDB()${R("Promise<IDBDatabase>")}{
|
|
30
|
-
return new Promise${PT("IDBDatabase")}((resolve, reject) => {
|
|
31
|
-
const request = indexedDB.open(DB_NAME, 1);
|
|
32
|
-
request.onupgradeneeded = (e) => {
|
|
33
|
-
const db = (e.target${AS("IDBOpenDBRequest")}).result;
|
|
34
|
-
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
35
|
-
db.createObjectStore(STORE_NAME, { keyPath: "key" });
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
request.onsuccess = (e) => resolve((e.target${AS("IDBOpenDBRequest")}).result);
|
|
39
|
-
request.onerror = (e) => reject((e.target${AS("IDBRequest")}).error);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Fetch current user from the user endpoint and cache the result in IndexedDB. */
|
|
44
|
-
export async function fetchCurrentUser()${R("Promise<Record<string, unknown>>")}{
|
|
45
|
-
const { response } = await fetchWithCache("${userEndpoint}", { auth: true });
|
|
46
|
-
if (!response.ok) throw new Error("Failed to fetch user");
|
|
47
|
-
|
|
48
|
-
const user = await response.json();
|
|
49
|
-
await cacheUser(user);
|
|
50
|
-
return user;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Persist user data to IndexedDB for offline access. */
|
|
54
|
-
export async function cacheUser(user${T("Record<string, unknown>")})${R("Promise<void>")}{
|
|
55
|
-
const db = await openAuthDB();
|
|
56
|
-
return new Promise<void>((resolve, reject) => {
|
|
57
|
-
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
58
|
-
const store = tx.objectStore(STORE_NAME);
|
|
59
|
-
store.put({ key: "user", value: user });
|
|
60
|
-
tx.oncomplete = () => resolve();
|
|
61
|
-
tx.onerror = () => reject(tx.error);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Load user data from IndexedDB cache (no token — only user object survives refresh). */
|
|
66
|
-
export async function getCachedUser()${R("Promise<Record<string, unknown> | null>")}{
|
|
67
|
-
const db = await openAuthDB();
|
|
68
|
-
return new Promise${PT("Record<string, unknown> | null")}((resolve, reject) => {
|
|
69
|
-
const tx = db.transaction(STORE_NAME, "readonly");
|
|
70
|
-
const store = tx.objectStore(STORE_NAME);
|
|
71
|
-
const request = store.get("user");
|
|
72
|
-
request.onsuccess = () => resolve((request${AS("IDBRequest")}).result?.value ?? null);
|
|
73
|
-
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Remove user data from IndexedDB cache. Call on logout. */
|
|
78
|
-
export async function clearCachedUser()${R("Promise<void>")}{
|
|
79
|
-
const db = await openAuthDB();
|
|
80
|
-
return new Promise${PT("void")}((resolve, reject) => {
|
|
81
|
-
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
82
|
-
const store = tx.objectStore(STORE_NAME);
|
|
83
|
-
store.delete("user");
|
|
84
|
-
tx.oncomplete = () => resolve();
|
|
85
|
-
tx.onerror = () => reject((tx${AS("IDBTransaction")}).error);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
`;
|
|
9
|
+
const authConfig = ctx.config.features.auth;
|
|
10
|
+
const code = generateAuthUserCode({ ts, ext }, authConfig.refreshPath, authConfig.userEndpoint, authConfig.type);
|
|
89
11
|
writeFile(ctx, `auth/user.${ext}`, code);
|
|
90
12
|
}
|
|
91
13
|
//# sourceMappingURL=auth-user.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-user.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-user.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAE5C,MAAM,IAAI,GAAG,oBAAoB,CAC/B,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,UAAU,CAAC,WAAW,EACtB,UAAU,CAAC,YAAY,EACvB,UAAU,CAAC,IAAI,CAChB,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -2,63 +2,8 @@
|
|
|
2
2
|
* Generates background-sync.js - Background Sync API registration.
|
|
3
3
|
*/
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
|
+
import { generateBackgroundSyncCode } from "../../../runtime/background-sync.js";
|
|
5
6
|
export function generateBackgroundSync(ctx) {
|
|
6
|
-
|
|
7
|
-
const ts = ext === "ts";
|
|
8
|
-
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
-
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
10
|
-
const code = `/**
|
|
11
|
-
* Swoff Background Sync
|
|
12
|
-
* Register sync events for processing mutation queue after tab close.
|
|
13
|
-
* Falls back to online event listener in unsupported browsers.
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* import { syncWhenPossible } from './swoff/background-sync.${ext}';
|
|
17
|
-
*
|
|
18
|
-
* await syncWhenPossible({
|
|
19
|
-
* method: "POST",
|
|
20
|
-
* url: "/api/todos",
|
|
21
|
-
* body: { title: "Grocery" },
|
|
22
|
-
* tags: ["todos"],
|
|
23
|
-
* tags: ["todos"],
|
|
24
|
-
* });
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import { queueMutation, processMutationQueue, getPendingCount } from "./mutation-queue.${ext}";
|
|
28
|
-
|
|
29
|
-
const SYNC_TAG = "sync-mutations";
|
|
30
|
-
|
|
31
|
-
async function registerSync()${R("Promise<void>")}{
|
|
32
|
-
if (!("serviceWorker" in navigator) || !("SyncManager" in window)) {
|
|
33
|
-
window.addEventListener("online", processMutationQueue, { once: true });
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const registration = await navigator.serviceWorker.ready;
|
|
39
|
-
await registration.sync.register(SYNC_TAG);
|
|
40
|
-
} catch {
|
|
41
|
-
window.addEventListener("online", processMutationQueue, { once: true });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Queue a mutation and register a background sync event so it runs even after tab close. */
|
|
46
|
-
export async function syncWhenPossible(mutation${T("object")})${R("Promise<void>")}{
|
|
47
|
-
await queueMutation(mutation);
|
|
48
|
-
await registerSync();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Re-register a background sync if mutations are still pending. Called automatically after each sync cycle. */
|
|
52
|
-
export async function retrySync()${R("Promise<void>")}{
|
|
53
|
-
if (!("serviceWorker" in navigator) || !("SyncManager" in window)) return;
|
|
54
|
-
const count = await getPendingCount();
|
|
55
|
-
if (count > 0) {
|
|
56
|
-
await registerSync();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
window.addEventListener("mutation-sync-complete", retrySync);
|
|
61
|
-
`;
|
|
62
|
-
writeFile(ctx, `background-sync.${ext}`, code);
|
|
7
|
+
writeFile(ctx, `offline/sync.${ctx.ext}`, generateBackgroundSyncCode({ ts: ctx.ext === "ts", ext: ctx.ext }));
|
|
63
8
|
}
|
|
64
9
|
//# sourceMappingURL=background-sync.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"background-sync.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/background-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"background-sync.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/background-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,SAAS,CAAC,GAAG,EAAE,gBAAgB,GAAG,CAAC,GAAG,EAAE,EAAE,0BAA0B,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAChH,CAAC"}
|
|
@@ -1,42 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates cache.{js|ts} - cache tag invalidation utilities.
|
|
3
|
-
*/
|
|
4
1
|
import { writeFile } from "./context.js";
|
|
2
|
+
import { generateCacheCode } from "../../../runtime/cache.js";
|
|
5
3
|
export function generateCache(ctx) {
|
|
6
|
-
|
|
7
|
-
const ts = ext === "ts";
|
|
8
|
-
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
-
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
10
|
-
const code = `/**
|
|
11
|
-
* Swoff Cache Invalidation
|
|
12
|
-
* Framework-agnostic cache tag invalidation.
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* import { invalidateByTag } from './swoff/cache.${ext}';
|
|
16
|
-
*
|
|
17
|
-
* // After a mutation, invalidate related cache
|
|
18
|
-
* await invalidateByTag("todos");
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/** Invalidate all cached responses tagged with the given tag. Dispatches cache-invalidated event. */
|
|
22
|
-
export async function invalidateByTag(tag${T("string")})${R("Promise<void>")}{
|
|
23
|
-
if (!navigator.serviceWorker?.controller) return;
|
|
24
|
-
|
|
25
|
-
navigator.serviceWorker.controller.postMessage({
|
|
26
|
-
type: "INVALIDATE_TAG",
|
|
27
|
-
tag,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
window.dispatchEvent(
|
|
31
|
-
new CustomEvent("cache-invalidated", { detail: { tags: [tag] } })
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Invalidate all cached responses matching any of the given tags. */
|
|
36
|
-
export async function invalidateByTags(tags${T("string[]")})${R("Promise<void>")}{
|
|
37
|
-
await Promise.all(tags.map((tag) => invalidateByTag(tag)));
|
|
38
|
-
}
|
|
39
|
-
`;
|
|
40
|
-
writeFile(ctx, `cache.${ext}`, code);
|
|
4
|
+
writeFile(ctx, `cache/index.${ctx.ext}`, generateCacheCode({ ts: ctx.ext === "ts", ext: ctx.ext }));
|
|
41
5
|
}
|
|
42
6
|
//# sourceMappingURL=cache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACtG,CAAC"}
|
|
@@ -2,100 +2,11 @@
|
|
|
2
2
|
* Generates client-injector.{js|ts} - orchestrator that wires feature modules together.
|
|
3
3
|
*/
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
|
+
import { generateClientInjectorCode } from "../../../runtime/client-injector.js";
|
|
5
6
|
export function generateClientInjector(ctx) {
|
|
6
7
|
const ext = ctx.ext;
|
|
7
8
|
const ts = ext === "ts";
|
|
8
|
-
const
|
|
9
|
-
const mutationQueueEnabled = ctx.config.features.mutationQueue.enabled;
|
|
10
|
-
const crossTabSync = ctx.config.features.crossTabSync;
|
|
11
|
-
const pwaImport = pwaEnabled
|
|
12
|
-
? `import { setupPwaInstall } from "./pwa/install.${ext}";
|
|
13
|
-
`
|
|
14
|
-
: "";
|
|
15
|
-
const pwaCall = pwaEnabled ? `setupPwaInstall();\n` : "";
|
|
16
|
-
const mutationImport = mutationQueueEnabled
|
|
17
|
-
? `import { processMutationQueue } from "./mutation-queue.${ext}";
|
|
18
|
-
`
|
|
19
|
-
: "";
|
|
20
|
-
const mutationOnlineListener = mutationQueueEnabled
|
|
21
|
-
? `
|
|
22
|
-
// --- Mutation Queue Online Listener ---
|
|
23
|
-
window.addEventListener("online", processMutationQueue);
|
|
24
|
-
`
|
|
25
|
-
: "";
|
|
26
|
-
const crossTabHandler = crossTabSync
|
|
27
|
-
? `
|
|
28
|
-
if (event.data.type === "TAG_INVALIDATED" && event.data.tag) {
|
|
29
|
-
window.dispatchEvent(
|
|
30
|
-
new CustomEvent("cache-invalidated", {
|
|
31
|
-
detail: { tags: [event.data.tag] },
|
|
32
|
-
})
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
`
|
|
36
|
-
: "";
|
|
37
|
-
const swImport = `import { initServiceWorker as swInit } from "./sw/injector.${ext}";
|
|
38
|
-
`;
|
|
39
|
-
const code = `/**
|
|
40
|
-
* Swoff Client Injector
|
|
41
|
-
* Orchestrates SW registration, PWA install, and cross-tab sync.
|
|
42
|
-
* Single entry point for all Swoff client-side features.
|
|
43
|
-
*
|
|
44
|
-
* Usage:
|
|
45
|
-
* import { initServiceWorker } from './swoff/client-injector.${ext}';
|
|
46
|
-
* initServiceWorker();
|
|
47
|
-
*
|
|
48
|
-
* Window events (dispatched by this module):
|
|
49
|
-
* sw-progress - Download progress (detail: { percent, downloaded, total })
|
|
50
|
-
* sw-ready - SW active and controlling page
|
|
51
|
-
* sw-error - SW registration failed
|
|
52
|
-
* cache-invalidated - Cache entries cleared (detail: { tags })
|
|
53
|
-
* mutation-sync-complete - Queued mutations synced (detail: { succeeded, failed })
|
|
54
|
-
* mutation-queue-changed - Queue modified
|
|
55
|
-
*
|
|
56
|
-
* Window events (dispatched by feature modules):
|
|
57
|
-
* pwa-installable - PWA can be installed (detail: { isInstallable: true })
|
|
58
|
-
* pwa-installed - User accepted install (detail: { outcome: 'accepted' })
|
|
59
|
-
* sw-auth-unauthorized - 401 response received
|
|
60
|
-
* sw-auth-state-change - Login or logout (detail: { authenticated: boolean })
|
|
61
|
-
* mutation-rollback - Mutation exhausted retries
|
|
62
|
-
* sw-update-available - New version ready (detail: { version })
|
|
63
|
-
* sw-version-detected - Version info available
|
|
64
|
-
*/
|
|
65
|
-
${pwaImport}${mutationImport}${swImport}
|
|
66
|
-
${pwaCall}${mutationOnlineListener}
|
|
67
|
-
// --- SW Message Listener ---
|
|
68
|
-
if (typeof window !== "undefined" && "serviceWorker" in navigator) {
|
|
69
|
-
navigator.serviceWorker.addEventListener("message", (event) => {
|
|
70
|
-
if (event.data.type === "SW_PROGRESS") {
|
|
71
|
-
const { percent, downloaded, total } = event.data;
|
|
72
|
-
window.dispatchEvent(
|
|
73
|
-
new CustomEvent("sw-progress", {
|
|
74
|
-
detail: { percent, downloaded, total },
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
if (event.data.type === "BACKGROUND_SYNC_COMPLETE") {
|
|
79
|
-
const { succeeded, failed, tags } = event.data.detail;
|
|
80
|
-
window.dispatchEvent(
|
|
81
|
-
new CustomEvent("mutation-sync-complete", {
|
|
82
|
-
detail: { succeeded, failed },
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
if (tags && tags.length > 0) {
|
|
86
|
-
window.dispatchEvent(
|
|
87
|
-
new CustomEvent("cache-invalidated", { detail: { tags } })
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
91
|
-
}${crossTabHandler} });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Initialize SW registration and all client-side features (PWA install, mutation queue, cross-tab sync). Call once at app startup. */
|
|
95
|
-
export async function initServiceWorker()${ts ? ": Promise<void>" : " "}{
|
|
96
|
-
await swInit();
|
|
97
|
-
}
|
|
98
|
-
`;
|
|
9
|
+
const code = generateClientInjectorCode({ ts, ext }, ctx.config.features.pwa.enabled, ctx.config.features.mutationQueue.enabled, ctx.config.features.serverPush?.enabled ?? false);
|
|
99
10
|
writeFile(ctx, `client-injector.${ext}`, code);
|
|
100
11
|
}
|
|
101
12
|
//# sourceMappingURL=client-injector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/client-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"client-injector.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/client-injector.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAEjF,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,IAAI,GAAG,0BAA0B,CACrC,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAC/B,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,EACzC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,IAAI,KAAK,CACjD,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { writeFile } from "./context.js";
|
|
2
|
+
import { generateFetchStateCode } from "../../../runtime/fetch-state.js";
|
|
3
|
+
export function generateFetchState(ctx) {
|
|
4
|
+
writeFile(ctx, `fetch/state.${ctx.ext}`, generateFetchStateCode({ ts: ctx.ext === "ts", ext: ctx.ext }));
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=fetch-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-state.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,MAAM,UAAU,kBAAkB,CAAC,GAAqB;IACtD,SAAS,CAAC,GAAG,EAAE,eAAe,GAAG,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAC3G,CAAC"}
|
|
@@ -3,275 +3,11 @@
|
|
|
3
3
|
* staleTime, prefetching, and cancellation.
|
|
4
4
|
*/
|
|
5
5
|
import { writeFile } from "./context.js";
|
|
6
|
+
import { generateFetchWrapperCode } from "../../../runtime/fetch-wrapper.js";
|
|
6
7
|
export function generateFetchWrapper(ctx) {
|
|
7
8
|
const ext = ctx.ext;
|
|
8
9
|
const ts = ext === "ts";
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const G = (type) => (ts ? `<${type}>` : "");
|
|
12
|
-
const tagInvalidation = ctx.config.features.tagInvalidation;
|
|
13
|
-
const authEnabled = ctx.config.features.auth.enabled;
|
|
14
|
-
const mutationQueue = ctx.config.features.mutationQueue.enabled;
|
|
15
|
-
const importLines = [
|
|
16
|
-
tagInvalidation
|
|
17
|
-
? `import { generateTags } from "./invalidation-tags.${ext}";`
|
|
18
|
-
: "",
|
|
19
|
-
tagInvalidation ? `import { invalidateByTags } from "./cache.${ext}";` : "",
|
|
20
|
-
authEnabled
|
|
21
|
-
? `import { getAuth, clearAuth, withAuthHeaders, isAuthUrl, AUTH_WITH_CREDENTIALS } from "./auth/store.${ext}";`
|
|
22
|
-
: "",
|
|
23
|
-
mutationQueue
|
|
24
|
-
? `import { queueMutation } from "./mutation-queue.${ext}";`
|
|
25
|
-
: "",
|
|
26
|
-
]
|
|
27
|
-
.filter(Boolean)
|
|
28
|
-
.join("\n");
|
|
29
|
-
const interfaceBlock = ts
|
|
30
|
-
? `
|
|
31
|
-
export interface FetchWithCacheResult${G("T")} {
|
|
32
|
-
response${T("Response & { json(): Promise<T> }")};
|
|
33
|
-
fromCache${T("boolean")};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface ResolvedFetchConfig {
|
|
37
|
-
staleTime${T("number")};
|
|
38
|
-
refetchOnWindowFocus${T("boolean")};
|
|
39
|
-
refetchOnReconnect${T("boolean")};
|
|
40
|
-
refetchInterval${T("number")};
|
|
41
|
-
}
|
|
42
|
-
`
|
|
43
|
-
: "";
|
|
44
|
-
const optionsInterface = ts
|
|
45
|
-
? `
|
|
46
|
-
export interface FetchWithCacheOptions extends RequestInit {
|
|
47
|
-
tags?: string[];
|
|
48
|
-
staleWhileRevalidate?: boolean;
|
|
49
|
-
auth?: boolean;
|
|
50
|
-
queueOffline?: boolean;
|
|
51
|
-
invalidate?: 'auto' | string[] | false;
|
|
52
|
-
type?: 'read' | 'mutation';
|
|
53
|
-
strategy?: 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'cache-only' | 'network-only';
|
|
54
|
-
staleTime?: number;
|
|
55
|
-
}
|
|
56
|
-
`
|
|
57
|
-
: "";
|
|
58
|
-
const authBlock = authEnabled
|
|
59
|
-
? `
|
|
60
|
-
if (options.auth) {
|
|
61
|
-
const auth = await getAuth();
|
|
62
|
-
withAuthHeaders(headers, auth);
|
|
63
|
-
}`
|
|
64
|
-
: "";
|
|
65
|
-
const authUrlsBlock = authEnabled
|
|
66
|
-
? `
|
|
67
|
-
// Auth endpoints bypass SW cache
|
|
68
|
-
if (options.auth && isAuthUrl(url) && !headers.has("X-SW-Cache-Strategy")) {
|
|
69
|
-
headers.set("X-SW-Cache-Strategy", "mutation");
|
|
70
|
-
}`
|
|
71
|
-
: "";
|
|
72
|
-
const authCredentialsBlock = authEnabled
|
|
73
|
-
? `
|
|
74
|
-
if (AUTH_WITH_CREDENTIALS) {
|
|
75
|
-
fetchOptions.credentials = "include";
|
|
76
|
-
}`
|
|
77
|
-
: "";
|
|
78
|
-
const auth401Block = authEnabled
|
|
79
|
-
? `
|
|
80
|
-
if (options.auth && response.status === 401) {
|
|
81
|
-
await clearAuth();
|
|
82
|
-
window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
|
|
83
|
-
}`
|
|
84
|
-
: "";
|
|
85
|
-
const offlineReadBlock = ` if (isRead) {
|
|
86
|
-
if (options.signal?.aborted) throw new DOMException("The operation was aborted", "AbortError");
|
|
87
|
-
const cached = await caches.match(input);
|
|
88
|
-
if (cached) return { response: cached, fromCache: true };
|
|
89
|
-
throw new Error("Offline: no cached data available");
|
|
90
|
-
}`;
|
|
91
|
-
const offlineWriteBlock = mutationQueue
|
|
92
|
-
? ` // Write offline — auto-queue
|
|
93
|
-
if (options.queueOffline !== false) {
|
|
94
|
-
const headerObj: Record<string, string> = {};
|
|
95
|
-
headers.forEach((value, key) => {
|
|
96
|
-
headerObj[key] = value;
|
|
97
|
-
});
|
|
98
|
-
await queueMutation({
|
|
99
|
-
method,
|
|
100
|
-
url,
|
|
101
|
-
body: options.body,
|
|
102
|
-
headers: headerObj,
|
|
103
|
-
tags: options.tags || [],
|
|
104
|
-
timestamp: Date.now(),
|
|
105
|
-
});
|
|
106
|
-
return {
|
|
107
|
-
response: new Response(null, { status: 202, statusText: "Queued" }),
|
|
108
|
-
fromCache: false,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
throw new Error("Offline: write operation failed");`
|
|
112
|
-
: ` throw new Error("Offline: write operation failed");`;
|
|
113
|
-
const autoInvalidateBlock = tagInvalidation
|
|
114
|
-
? `
|
|
115
|
-
// Auto-invalidate after mutation success
|
|
116
|
-
if (!isRead && response.ok) {
|
|
117
|
-
const invalidateSetting = options.invalidate !== false ? (options.invalidate || 'auto') : false;
|
|
118
|
-
if (invalidateSetting !== false) {
|
|
119
|
-
const tagsToInvalidate${T("string[]")} = Array.isArray(invalidateSetting)
|
|
120
|
-
? invalidateSetting
|
|
121
|
-
: (options.tags || generateTags(url));
|
|
122
|
-
if (tagsToInvalidate.length > 0) {
|
|
123
|
-
await invalidateByTags(tagsToInvalidate);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}`
|
|
127
|
-
: "";
|
|
128
|
-
const autoTagsBlock = tagInvalidation
|
|
129
|
-
? `
|
|
130
|
-
// Auto-generate tags from URL if not provided
|
|
131
|
-
if (!options.tags && isRead) {
|
|
132
|
-
const urlTags = generateTags(url);
|
|
133
|
-
if (urlTags.length > 0) {
|
|
134
|
-
headers.set("X-SW-Cache-Tags", urlTags.join(","));
|
|
135
|
-
}
|
|
136
|
-
}`
|
|
137
|
-
: "";
|
|
138
|
-
const staleTimeHeaders = ` // Pass staleTime to SW if set at per-request level
|
|
139
|
-
if (options.staleTime !== undefined) {
|
|
140
|
-
headers.set("X-SW-Stale-Time", String(options.staleTime));
|
|
141
|
-
}`;
|
|
142
|
-
const code = `/**
|
|
143
|
-
* Swoff Fetch Wrapper
|
|
144
|
-
* Unified fetch with caching, auth, offline queue, auto-invalidation, staleTime, prefetching, and
|
|
145
|
-
* per-request strategy override.
|
|
146
|
-
*
|
|
147
|
-
* Usage:
|
|
148
|
-
* import { fetchWithCache, prefetchCache } from './swoff/fetch-wrapper.${ext}';
|
|
149
|
-
*
|
|
150
|
-
* // GET — cached with auto-generated tags
|
|
151
|
-
* const { response } = await fetchWithCache("/api/todos");
|
|
152
|
-
* const data = await response.json();
|
|
153
|
-
*
|
|
154
|
-
* // POST — mutation with auto-invalidation
|
|
155
|
-
* await fetchWithCache("/api/todos", {
|
|
156
|
-
* method: "POST",
|
|
157
|
-
* body: JSON.stringify({ title: "New task" }),
|
|
158
|
-
* });
|
|
159
|
-
*
|
|
160
|
-
* // Authenticated request (works with bearer, cookie, custom)
|
|
161
|
-
* const { response: userRes } = await fetchWithCache("/api/me", { auth: true });
|
|
162
|
-
*
|
|
163
|
-
* // Custom tags + stale-while-revalidate
|
|
164
|
-
* const { response: staleRes, fromCache } = await fetchWithCache("/api/data", {
|
|
165
|
-
* tags: ["data"],
|
|
166
|
-
* staleWhileRevalidate: true,
|
|
167
|
-
* });
|
|
168
|
-
*
|
|
169
|
-
* // Set staleTime per-request (overrides config — SW handles bg refresh)
|
|
170
|
-
* await fetchWithCache("/api/todos", { staleTime: 30 });
|
|
171
|
-
*
|
|
172
|
-
* // Prefetch (fire-and-forget warm the cache)
|
|
173
|
-
* prefetchCache("/api/todos");
|
|
174
|
-
*
|
|
175
|
-
* // Override caching strategy per-request (highest priority)
|
|
176
|
-
* await fetchWithCache("/api/checkout", {
|
|
177
|
-
* method: "POST",
|
|
178
|
-
* type: "read",
|
|
179
|
-
* strategy: "network-only",
|
|
180
|
-
* });
|
|
181
|
-
*
|
|
182
|
-
* // Support AbortController for cancellation
|
|
183
|
-
* const controller = new AbortController();
|
|
184
|
-
* setTimeout(() => controller.abort(), 5000);
|
|
185
|
-
* const { response } = await fetchWithCache("/api/todos", { signal: controller.signal });
|
|
186
|
-
*
|
|
187
|
-
* // Offline: auto-queues writes (disable with queueOffline: false)
|
|
188
|
-
* await fetchWithCache("/api/todos", {
|
|
189
|
-
* method: "POST",
|
|
190
|
-
* body: JSON.stringify({ title: "Offline task" }),
|
|
191
|
-
* });
|
|
192
|
-
* // When back online, processMutationQueue() replays them
|
|
193
|
-
*/
|
|
194
|
-
|
|
195
|
-
${importLines}
|
|
196
|
-
${interfaceBlock}${optionsInterface}
|
|
197
|
-
const inFlightRequests = new Map${G("string, Promise<Response>")}();
|
|
198
|
-
|
|
199
|
-
/** 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. */
|
|
200
|
-
export async function fetchWithCache${G("T")}(input${T("RequestInfo")}, options${T("RequestInit & { tags?: string[]; staleWhileRevalidate?: boolean; auth?: boolean; queueOffline?: boolean; invalidate?: 'auto' | string[] | false; type?: 'read' | 'mutation'; strategy?: 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'cache-only' | 'network-only'; staleTime?: number }")} = {})${R("Promise<FetchWithCacheResult<T>>")}{
|
|
201
|
-
const method = (options.method || "GET").toUpperCase();
|
|
202
|
-
const isRead = options.type === "read" || (options.type !== "mutation" && (method === "GET" || method === "HEAD"));
|
|
203
|
-
const url = typeof input === "string" ? input : input.url;
|
|
204
|
-
|
|
205
|
-
const headers = new Headers(options.headers);
|
|
206
|
-
|
|
207
|
-
// Set cache strategy
|
|
208
|
-
if (!headers.has("X-SW-Cache-Strategy")) {
|
|
209
|
-
headers.set("X-SW-Cache-Strategy", isRead ? "read" : "mutation");
|
|
210
|
-
}
|
|
211
|
-
${autoTagsBlock}
|
|
212
|
-
// Custom tags override auto-generated
|
|
213
|
-
if (options.tags && options.tags.length > 0) {
|
|
214
|
-
headers.set("X-SW-Cache-Tags", options.tags.join(","));
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (options.staleWhileRevalidate && !options.strategy) {
|
|
218
|
-
headers.set("X-SW-Strategy", "stale-while-revalidate");
|
|
219
|
-
}
|
|
220
|
-
if (options.strategy) {
|
|
221
|
-
headers.set("X-SW-Strategy", options.strategy);
|
|
222
|
-
}
|
|
223
|
-
${staleTimeHeaders}
|
|
224
|
-
${authBlock}${authUrlsBlock}
|
|
225
|
-
const fetchOptions${T("RequestInit")} = { ...options, headers };
|
|
226
|
-
${authCredentialsBlock}
|
|
227
|
-
|
|
228
|
-
// Offline handling
|
|
229
|
-
if (!navigator.onLine) {
|
|
230
|
-
${offlineReadBlock}
|
|
231
|
-
${offlineWriteBlock}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Check for abort before proceeding
|
|
235
|
-
if (options.signal?.aborted) {
|
|
236
|
-
throw new DOMException("The operation was aborted", "AbortError");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Deduplicate in-flight GET requests
|
|
240
|
-
let responsePromise${T("Promise<Response>")};
|
|
241
|
-
if (isRead && inFlightRequests.has(url)) {
|
|
242
|
-
responsePromise = inFlightRequests.get(url)!.then((r) => r.clone());
|
|
243
|
-
} else {
|
|
244
|
-
const abortHandler = () => {
|
|
245
|
-
inFlightRequests.delete(url);
|
|
246
|
-
};
|
|
247
|
-
if (options.signal) {
|
|
248
|
-
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
249
|
-
}
|
|
250
|
-
responsePromise = fetch(input, fetchOptions);
|
|
251
|
-
if (isRead) {
|
|
252
|
-
const cleanup = () => {
|
|
253
|
-
inFlightRequests.delete(url);
|
|
254
|
-
if (options.signal) {
|
|
255
|
-
options.signal.removeEventListener("abort", abortHandler);
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
inFlightRequests.set(url, responsePromise.finally(cleanup));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const response = await responsePromise;
|
|
263
|
-
${autoInvalidateBlock}${auth401Block}
|
|
264
|
-
const fromCache = response.headers.get("X-SW-From-Cache") === "true";
|
|
265
|
-
return { response, fromCache };
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/** Fire-and-forget prefetch: warms the cache for a URL without blocking. Useful for route prefetching or link hover prefetching. */
|
|
269
|
-
export function prefetchCache(input${T("RequestInfo")}, options${T("RequestInit & { tags?: string[]; staleWhileRevalidate?: boolean; auth?: boolean; queueOffline?: boolean; invalidate?: 'auto' | string[] | false; type?: 'read' | 'mutation'; strategy?: 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'cache-only' | 'network-only'; staleTime?: number }")} = {})${R("void")}{
|
|
270
|
-
fetchWithCache(input, { ...options }).catch(() => {
|
|
271
|
-
// Prefetch failures are intentionally silent
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
`;
|
|
275
|
-
writeFile(ctx, `fetch-wrapper.${ext}`, code);
|
|
10
|
+
const code = generateFetchWrapperCode({ ts, ext }, ctx.config.features.auth.enabled, ctx.config.features.auth.userEndpoint, ctx.config.features.mutationQueue.enabled, ctx.config.features.serviceWorker.requestBatchWindowMs);
|
|
11
|
+
writeFile(ctx, `fetch/core.${ext}`, code);
|
|
276
12
|
}
|
|
277
13
|
//# sourceMappingURL=fetch-wrapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-wrapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch-wrapper.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/fetch-wrapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,UAAU,oBAAoB,CAAC,GAAqB;IACxD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,IAAI,GAAG,wBAAwB,CACnC,EAAE,EAAE,EAAE,GAAG,EAAE,EACX,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAChC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EACrC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,EACzC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,oBAAoB,CACvD,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}
|