@swoff/cli 0.3.5 → 0.3.6
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 +327 -25
- package/dist/__tests__/assemble-sw.test.js +15 -1
- package/dist/__tests__/assemble-sw.test.js.map +1 -1
- package/dist/__tests__/cli-integration.test.js +9 -8
- package/dist/__tests__/cli-integration.test.js.map +1 -1
- package/dist/__tests__/config-types.test.js +6 -6
- package/dist/__tests__/config-types.test.js.map +1 -1
- package/dist/__tests__/file-generators.test.js +67 -59
- package/dist/__tests__/file-generators.test.js.map +1 -1
- package/dist/__tests__/loader.test.js +12 -13
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/validator.test.js +41 -21
- package/dist/__tests__/validator.test.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/cli/help.js +3 -3
- package/dist/lib/cli/help.js.map +1 -1
- package/dist/lib/commands/add.js +7 -1
- package/dist/lib/commands/add.js.map +1 -1
- package/dist/lib/commands/clean.js +5 -0
- package/dist/lib/commands/clean.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +12 -9
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/generate.js +7 -3
- package/dist/lib/commands/generate.js.map +1 -1
- package/dist/lib/commands/info.js +135 -32
- package/dist/lib/commands/info.js.map +1 -1
- package/dist/lib/commands/validate.js +3 -1
- package/dist/lib/commands/validate.js.map +1 -1
- package/dist/lib/config/validator.js +25 -16
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-fetch.js +29 -34
- package/dist/lib/generators/file-generators/auth-fetch.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-state.js +8 -5
- package/dist/lib/generators/file-generators/auth-state.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-store.js +71 -40
- package/dist/lib/generators/file-generators/auth-store.js.map +1 -1
- package/dist/lib/generators/file-generators/auth-user.js +27 -31
- package/dist/lib/generators/file-generators/auth-user.js.map +1 -1
- package/dist/lib/generators/file-generators/background-sync.js +9 -5
- package/dist/lib/generators/file-generators/background-sync.js.map +1 -1
- package/dist/lib/generators/file-generators/cache.js +7 -2
- package/dist/lib/generators/file-generators/cache.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +113 -0
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -0
- package/dist/lib/generators/file-generators/context.js +7 -6
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/fetch-wrapper.js +170 -40
- package/dist/lib/generators/file-generators/fetch-wrapper.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-hooks.js +61 -3
- package/dist/lib/generators/file-generators/generate-hooks.js.map +1 -1
- package/dist/lib/generators/file-generators/guide-generator.js +333 -0
- package/dist/lib/generators/file-generators/guide-generator.js.map +1 -0
- package/dist/lib/generators/file-generators/invalidation-tags.js +12 -10
- package/dist/lib/generators/file-generators/invalidation-tags.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-queue.js +34 -84
- package/dist/lib/generators/file-generators/mutation-queue.js.map +1 -1
- package/dist/lib/generators/file-generators/mutation-reconcile.js +57 -0
- package/dist/lib/generators/file-generators/mutation-reconcile.js.map +1 -0
- package/dist/lib/generators/file-generators/pwa-install.js +41 -9
- package/dist/lib/generators/file-generators/pwa-install.js.map +1 -1
- package/dist/lib/generators/file-generators/quick-readme.js +78 -0
- package/dist/lib/generators/file-generators/quick-readme.js.map +1 -0
- package/dist/lib/generators/file-generators/reconcile.js +7 -12
- package/dist/lib/generators/file-generators/reconcile.js.map +1 -1
- package/dist/lib/generators/file-generators/store.js +30 -20
- package/dist/lib/generators/file-generators/store.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-generator-build.js +37 -16
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +81 -110
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-template.js +1 -1
- package/dist/lib/generators/file-generators/type-definitions.js +33 -10
- package/dist/lib/generators/file-generators/type-definitions.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +19 -10
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +32 -12
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/config-header.js +1 -1
- package/dist/lib/generators/sw-sections/config-header.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +25 -10
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/tag-management.js +25 -5
- package/dist/lib/generators/sw-sections/tag-management.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +7 -5
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +21 -6
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/build-script.js +35 -0
- package/dist/lib/utils/build-script.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generates auth-fetch.ts/js —
|
|
3
|
-
* Uses auth config to generate appropriate withAuthHeaders() and isAuthUrl().
|
|
2
|
+
* Generates auth-fetch.ts/js — thin auth wrapper around fetchWithCache.
|
|
4
3
|
*/
|
|
5
4
|
import { writeFile } from "./context.js";
|
|
6
5
|
export function generateAuthFetch(ctx) {
|
|
7
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
8
10
|
const authConfig = ctx.config.features.auth;
|
|
9
11
|
const { refreshPath, userEndpoint, type } = authConfig;
|
|
10
12
|
function generateWithAuthHeaders() {
|
|
13
|
+
const a = T("AuthData | null");
|
|
14
|
+
const h = T("Headers");
|
|
15
|
+
const hd = T("Headers");
|
|
11
16
|
switch (type) {
|
|
12
17
|
case "cookie":
|
|
13
|
-
return `function withAuthHeaders(headers, _auth)
|
|
14
|
-
// Cookie/Session auth — browser auto-sends cookies.
|
|
15
|
-
// No header modification needed.
|
|
18
|
+
return `function withAuthHeaders(headers${hd}, _auth${a})${h}{
|
|
16
19
|
return headers;
|
|
17
20
|
}`;
|
|
18
21
|
case "bearer":
|
|
19
|
-
return `function withAuthHeaders(headers, auth)
|
|
20
|
-
// --- EDIT THIS BLOCK FOR YOUR BACKEND ---
|
|
21
|
-
// JWT Bearer token:
|
|
22
|
+
return `function withAuthHeaders(headers${hd}, auth${a})${h}{
|
|
22
23
|
if (auth?.token) {
|
|
23
24
|
headers.set("Authorization", \`Bearer \${auth.token}\`);
|
|
24
25
|
}
|
|
25
|
-
// --- END OF EDITABLE BLOCK ---
|
|
26
26
|
return headers;
|
|
27
27
|
}`;
|
|
28
28
|
case "custom":
|
|
29
|
-
return `function withAuthHeaders(headers, auth)
|
|
29
|
+
return `function withAuthHeaders(headers${hd}, auth${a})${h}{
|
|
30
30
|
// --- EDIT THIS BLOCK FOR YOUR BACKEND ---
|
|
31
|
-
// Custom header (e.g., X-API-Key, X-Auth-Token):
|
|
32
31
|
// if (auth?.token) {
|
|
33
32
|
// headers.set("X-Auth-Token", auth.token);
|
|
34
33
|
// }
|
|
@@ -36,30 +35,32 @@ export function generateAuthFetch(ctx) {
|
|
|
36
35
|
return headers;
|
|
37
36
|
}`;
|
|
38
37
|
default:
|
|
39
|
-
return `function withAuthHeaders(headers, auth)
|
|
40
|
-
// --- EDIT THIS BLOCK FOR YOUR BACKEND ---
|
|
38
|
+
return `function withAuthHeaders(headers${hd}, auth${a})${h}{
|
|
41
39
|
if (auth?.token) {
|
|
42
40
|
headers.set("Authorization", \`Bearer \${auth.token}\`);
|
|
43
41
|
}
|
|
44
|
-
// --- END OF EDITABLE BLOCK ---
|
|
45
42
|
return headers;
|
|
46
43
|
}`;
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
46
|
const credentialsLine = type === "cookie"
|
|
50
|
-
? `const fetchOptions = { ...options, headers, credentials: "include" };`
|
|
51
|
-
: `const fetchOptions = { ...options, headers };`;
|
|
47
|
+
? ` const fetchOptions = { ...options, headers, credentials: "include" };`
|
|
48
|
+
: ` const fetchOptions = { ...options, headers };`;
|
|
52
49
|
const code = `/**
|
|
53
|
-
* Auth-
|
|
54
|
-
* and handles 401 responses.
|
|
50
|
+
* Auth-Fetch — attaches auth headers, excludes auth endpoints from cache,
|
|
51
|
+
* and handles 401 responses. Thin wrapper around fetchWithCache.
|
|
55
52
|
*
|
|
56
53
|
* Usage:
|
|
57
|
-
* import { authenticatedFetch } from "./auth
|
|
54
|
+
* import { authenticatedFetch } from "./auth/fetch.${ext}";
|
|
58
55
|
* const data = await authenticatedFetch("/api/data").then((r) => r.json());
|
|
56
|
+
*
|
|
57
|
+
* Or use fetchWithCache directly:
|
|
58
|
+
* import { fetchWithCache } from "../fetch-wrapper.${ext}";
|
|
59
|
+
* const data = await fetchWithCache("/api/me", { auth: true });
|
|
59
60
|
*/
|
|
60
61
|
|
|
61
|
-
import { fetchWithCache } from "
|
|
62
|
-
import { getAuth, setAuth, clearAuth } from "./
|
|
62
|
+
import { fetchWithCache } from "../fetch-wrapper.${ext}";
|
|
63
|
+
import { getAuth, setAuth, clearAuth${ts ? ", type AuthData" : ""} } from "./store.${ext}";
|
|
63
64
|
|
|
64
65
|
${generateWithAuthHeaders()}
|
|
65
66
|
|
|
@@ -67,7 +68,7 @@ ${generateWithAuthHeaders()}
|
|
|
67
68
|
* Auth endpoints that should never be cached by the SW.
|
|
68
69
|
* Edit this list to match your backend's auth routes.
|
|
69
70
|
*/
|
|
70
|
-
function isAuthUrl(url)
|
|
71
|
+
function isAuthUrl(url${T("string")})${R("boolean")}{
|
|
71
72
|
const authPaths = [
|
|
72
73
|
"/login",
|
|
73
74
|
"/logout",
|
|
@@ -81,18 +82,13 @@ function isAuthUrl(url) {
|
|
|
81
82
|
return authPaths.some((path) => url.includes(path));
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
* Attaches identity, excludes auth endpoints from cache, handles 401.
|
|
87
|
-
*/
|
|
88
|
-
export async function authenticatedFetch(input, options = {}) {
|
|
85
|
+
/** Auth-aware fetch. Delegates to fetchWithCache with auth headers, cache bypass for auth endpoints, and 401 handling. */
|
|
86
|
+
export async function authenticatedFetch(input${T("RequestInfo")}, options${T("RequestInit")} = {})${R("Promise<Response>")}{
|
|
89
87
|
const auth = await getAuth();
|
|
90
88
|
const headers = new Headers(options.headers);
|
|
91
89
|
|
|
92
|
-
// Attach auth headers (your backend determines how)
|
|
93
90
|
withAuthHeaders(headers, auth);
|
|
94
91
|
|
|
95
|
-
// Determine if this is an auth endpoint (login, logout, refresh)
|
|
96
92
|
const url = typeof input === "string" ? input : input.url;
|
|
97
93
|
|
|
98
94
|
// Mark auth endpoints as mutation to bypass SW cache
|
|
@@ -101,7 +97,7 @@ export async function authenticatedFetch(input, options = {}) {
|
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
${credentialsLine}
|
|
104
|
-
const response = await fetchWithCache(input, fetchOptions);
|
|
100
|
+
const { response } = await fetchWithCache(input, fetchOptions);
|
|
105
101
|
|
|
106
102
|
// Handle 401 — auth expired or invalidated server-side
|
|
107
103
|
if (response.status === 401) {
|
|
@@ -116,14 +112,13 @@ export async function authenticatedFetch(input, options = {}) {
|
|
|
116
112
|
* Token refresh helper — called before requests when token may be expired.
|
|
117
113
|
* Uses refreshPath from config.
|
|
118
114
|
*/
|
|
119
|
-
let refreshPromise = null;
|
|
115
|
+
let refreshPromise${T("Promise<AuthData | null> | null")} = null;
|
|
120
116
|
|
|
121
|
-
export async function ensureValidAuth() {
|
|
117
|
+
export async function ensureValidAuth()${R("Promise<AuthData | null>")}{
|
|
122
118
|
const auth = await getAuth();
|
|
123
119
|
if (!auth) return null;
|
|
124
120
|
if (!auth.expiresAt || Date.now() < auth.expiresAt) return auth;
|
|
125
121
|
|
|
126
|
-
// Token expired — refresh it (deduplicated)
|
|
127
122
|
if (!refreshPromise) {
|
|
128
123
|
refreshPromise = (async () => {
|
|
129
124
|
const response = await authenticatedFetch("${refreshPath}", {
|
|
@@ -151,6 +146,6 @@ export async function ensureValidAuth() {
|
|
|
151
146
|
}
|
|
152
147
|
}
|
|
153
148
|
`;
|
|
154
|
-
writeFile(ctx, `auth
|
|
149
|
+
writeFile(ctx, `auth/fetch.${ext}`, code);
|
|
155
150
|
}
|
|
156
151
|
//# sourceMappingURL=auth-fetch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-fetch.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-fetch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth-fetch.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5C,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC;IAEvD,SAAS,uBAAuB;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;QACvB,MAAM,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;QACxB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,QAAQ;gBACX,OAAO,mCAAmC,EAAE,UAAU,CAAC,IAAI,CAAC;;EAElE,CAAC;YACG,KAAK,QAAQ;gBACX,OAAO,mCAAmC,EAAE,SAAS,CAAC,IAAI,CAAC;;;;;EAKjE,CAAC;YACG,KAAK,QAAQ;gBACX,OAAO,mCAAmC,EAAE,SAAS,CAAC,IAAI,CAAC;;;;;;;EAOjE,CAAC;YACG;gBACE,OAAO,mCAAmC,EAAE,SAAS,CAAC,IAAI,CAAC;;;;;EAKjE,CAAC;QACC,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GACnB,IAAI,KAAK,QAAQ;QACf,CAAC,CAAC,yEAAyE;QAC3E,CAAC,CAAC,iDAAiD,CAAC;IAExD,MAAM,IAAI,GAAG;;;;;wDAKyC,GAAG;;;;wDAIH,GAAG;;;;mDAIR,GAAG;sCAChB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,oBAAoB,GAAG;;EAEtF,uBAAuB,EAAE;;;;;;wBAMH,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC;;;;;;;;OAQ5C,WAAW;OACX,YAAY;;;;;;gDAM6B,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,mBAAmB,CAAC;;;;;;;;;;;;;IAavH,eAAe;;;;;;;;;;;;;;;;oBAgBC,CAAC,CAAC,iCAAiC,CAAC;;yCAEf,CAAC,CAAC,0BAA0B,CAAC;;;;;;;mDAOnB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;CAwB7D,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateAuthState(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
7
9
|
const code = `/**
|
|
8
10
|
* Auth State — detect the four auth states for offline/online handling.
|
|
9
11
|
*
|
|
@@ -14,14 +16,15 @@ export function generateAuthState(ctx) {
|
|
|
14
16
|
* 4. Offline + Unauthenticated → Strict offline (public content only)
|
|
15
17
|
*
|
|
16
18
|
* Usage:
|
|
17
|
-
* import { getAuthState } from "./auth
|
|
19
|
+
* import { getAuthState } from "./auth/state.${ext}";
|
|
18
20
|
* const { authenticated, user, online } = await getAuthState();
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
|
-
import { getAuth, isAuthValid } from "./
|
|
22
|
-
import { getCachedUser } from "./
|
|
23
|
+
import { getAuth, isAuthValid } from "./store.${ext}";
|
|
24
|
+
import { getCachedUser } from "./user.${ext}";
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
/** Detect current auth state across the 4-state matrix (online/offline × authenticated/not). */
|
|
27
|
+
export async function getAuthState()${R("Promise<{ authenticated: boolean; user: Record<string, unknown> | null; online: boolean }>")}{
|
|
25
28
|
const auth = await getAuth();
|
|
26
29
|
const valid = isAuthValid(auth);
|
|
27
30
|
const user = valid ? await getCachedUser() : null;
|
|
@@ -33,6 +36,6 @@ export async function getAuthState() {
|
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
38
|
`;
|
|
36
|
-
writeFile(ctx, `auth
|
|
39
|
+
writeFile(ctx, `auth/state.${ext}`, code);
|
|
37
40
|
}
|
|
38
41
|
//# sourceMappingURL=auth-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-state.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-state.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-state.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-state.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG;;;;;;;;;;kDAUmC,GAAG;;;;gDAIL,GAAG;wCACX,GAAG;;;sCAGL,CAAC,CAAC,4FAA4F,CAAC;;;;;;;;;;;CAWpI,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -9,6 +9,49 @@
|
|
|
9
9
|
import { writeFile } from "./context.js";
|
|
10
10
|
export function generateAuthStore(ctx) {
|
|
11
11
|
const ext = ctx.ext;
|
|
12
|
+
const ts = ext === "ts";
|
|
13
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
14
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
15
|
+
const PT = (type) => (ts ? `<${type}>` : "");
|
|
16
|
+
const AS = (type) => (ts ? ` as ${type}` : "");
|
|
17
|
+
const authDataInterface = ts ? `export interface AuthData {
|
|
18
|
+
token?: string;
|
|
19
|
+
user?: Record<string, unknown>;
|
|
20
|
+
expiresAt?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AuthResponse extends Record<string, unknown> {
|
|
24
|
+
// Uncomment and type the fields your backend's login response returns:
|
|
25
|
+
// token: string;
|
|
26
|
+
// user: Record<string, unknown>;
|
|
27
|
+
// expiresAt?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
` : "";
|
|
31
|
+
const createAuthFromResponseBlock = ts
|
|
32
|
+
? `/** Extract AuthData from your backend's login response. Edit this function to match your backend's response shape. */
|
|
33
|
+
export function createAuthFromResponse(response: AuthResponse): AuthData {
|
|
34
|
+
// --- EDIT THIS TO MATCH YOUR BACKEND ---
|
|
35
|
+
const data = response as Record<string, unknown>;
|
|
36
|
+
return {
|
|
37
|
+
token: data.token as string,
|
|
38
|
+
user: data.user as Record<string, unknown> | undefined,
|
|
39
|
+
expiresAt: data.expiresAt as number | undefined,
|
|
40
|
+
};
|
|
41
|
+
// --- END OF EDITABLE BLOCK ---
|
|
42
|
+
}
|
|
43
|
+
`
|
|
44
|
+
: `/** Extract AuthData from your backend's login response. Edit this function to match your backend's response shape. */
|
|
45
|
+
export function createAuthFromResponse(response) {
|
|
46
|
+
// --- EDIT THIS TO MATCH YOUR BACKEND ---
|
|
47
|
+
return {
|
|
48
|
+
token: response.token,
|
|
49
|
+
user: response.user,
|
|
50
|
+
expiresAt: response.expiresAt,
|
|
51
|
+
};
|
|
52
|
+
// --- END OF EDITABLE BLOCK ---
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
12
55
|
const code = `/**
|
|
13
56
|
* Auth Store — Token in memory only; user info in IndexedDB for offline access.
|
|
14
57
|
*
|
|
@@ -18,110 +61,98 @@ export function generateAuthStore(ctx) {
|
|
|
18
61
|
* user info offline. After a page refresh, re-login is required.
|
|
19
62
|
*
|
|
20
63
|
* Usage:
|
|
21
|
-
* import { setAuth, getAuth, clearAuth, isAuthValid } from "./auth
|
|
64
|
+
* import { setAuth, getAuth, clearAuth, isAuthValid } from "./auth/store.${ext}";
|
|
22
65
|
*
|
|
23
66
|
* await setAuth({ token, user, expiresAt });
|
|
24
67
|
* const auth = await getAuth();
|
|
25
68
|
* await clearAuth();
|
|
26
69
|
*/
|
|
27
70
|
|
|
28
|
-
const DB_NAME = "swoff-auth";
|
|
71
|
+
${authDataInterface}const DB_NAME = "swoff-auth";
|
|
29
72
|
const STORE_NAME = "auth";
|
|
30
73
|
|
|
31
|
-
let memoryAuth = null;
|
|
74
|
+
let memoryAuth${T("AuthData | null")} = null;
|
|
32
75
|
|
|
33
|
-
|
|
34
|
-
|
|
76
|
+
${createAuthFromResponseBlock}
|
|
77
|
+
function openAuthDB()${R("Promise<IDBDatabase>")}{
|
|
78
|
+
return new Promise${PT("IDBDatabase")}((resolve, reject) => {
|
|
35
79
|
const request = indexedDB.open(DB_NAME, 1);
|
|
36
80
|
request.onupgradeneeded = (e) => {
|
|
37
|
-
const db = e.target.result;
|
|
81
|
+
const db = (e.target${AS("IDBOpenDBRequest")}).result;
|
|
38
82
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
39
83
|
db.createObjectStore(STORE_NAME, { keyPath: "key" });
|
|
40
84
|
}
|
|
41
85
|
};
|
|
42
|
-
request.onsuccess = (e) => resolve(e.target.result);
|
|
43
|
-
request.onerror = (e) => reject(e.target.error);
|
|
86
|
+
request.onsuccess = (e) => resolve((e.target${AS("IDBOpenDBRequest")}).result);
|
|
87
|
+
request.onerror = (e) => reject((e.target${AS("IDBRequest")}).error);
|
|
44
88
|
});
|
|
45
89
|
}
|
|
46
90
|
|
|
47
|
-
async function persistUserData(authData)
|
|
91
|
+
async function persistUserData(authData${T("AuthData | null")})${R("Promise<void>")}{
|
|
48
92
|
// Only persist { user, expiresAt } — never the token
|
|
49
93
|
const userData = { user: authData?.user, expiresAt: authData?.expiresAt };
|
|
50
94
|
const db = await openAuthDB();
|
|
51
|
-
return new Promise((resolve, reject) => {
|
|
95
|
+
return new Promise${PT("void")}((resolve, reject) => {
|
|
52
96
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
53
97
|
const store = tx.objectStore(STORE_NAME);
|
|
54
98
|
const request = store.put({ key: "session", value: userData });
|
|
55
99
|
request.onsuccess = () => resolve();
|
|
56
|
-
request.onerror = () => reject(request.error);
|
|
100
|
+
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
57
101
|
});
|
|
58
102
|
}
|
|
59
103
|
|
|
60
|
-
async function loadUserData() {
|
|
104
|
+
async function loadUserData()${R("Promise<{ user?: Record<string, unknown>; expiresAt?: number } | null>")}{
|
|
61
105
|
const db = await openAuthDB();
|
|
62
|
-
return new Promise((resolve, reject) => {
|
|
106
|
+
return new Promise${PT("any")}((resolve, reject) => {
|
|
63
107
|
const tx = db.transaction(STORE_NAME, "readonly");
|
|
64
108
|
const store = tx.objectStore(STORE_NAME);
|
|
65
109
|
const request = store.get("session");
|
|
66
|
-
request.onsuccess = () => resolve(request.result?.value
|
|
67
|
-
request.onerror = () => reject(request.error);
|
|
110
|
+
request.onsuccess = () => resolve((request${AS("IDBRequest")}).result?.value ?? null);
|
|
111
|
+
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
68
112
|
});
|
|
69
113
|
}
|
|
70
114
|
|
|
71
|
-
async function clearPersistedData()
|
|
115
|
+
async function clearPersistedData()${R("Promise<void>")}{
|
|
72
116
|
const db = await openAuthDB();
|
|
73
|
-
return new Promise((resolve, reject) => {
|
|
117
|
+
return new Promise${PT("void")}((resolve, reject) => {
|
|
74
118
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
75
119
|
const store = tx.objectStore(STORE_NAME);
|
|
76
120
|
const request = store.delete("session");
|
|
77
121
|
request.onsuccess = () => resolve();
|
|
78
|
-
request.onerror = () => reject(request.error);
|
|
122
|
+
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
79
123
|
});
|
|
80
124
|
}
|
|
81
125
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
* Only user info and expiry are persisted to IndexedDB.
|
|
85
|
-
* @param {{ token?: string, user?: object, expiresAt?: number }} authData
|
|
86
|
-
*/
|
|
87
|
-
export async function setAuth(authData) {
|
|
126
|
+
/** Store auth data in memory and persist user info to IndexedDB for offline access. */
|
|
127
|
+
export async function setAuth(authData${T("AuthData")})${R("Promise<void>")}{
|
|
88
128
|
memoryAuth = authData;
|
|
89
129
|
await persistUserData(authData);
|
|
90
130
|
}
|
|
91
131
|
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
* falls back to IndexedDB (user info only, no token after refresh).
|
|
95
|
-
*/
|
|
96
|
-
export async function getAuth() {
|
|
132
|
+
/** Get auth data from memory (or IndexedDB if memory is empty after page refresh). */
|
|
133
|
+
export async function getAuth()${R("Promise<AuthData | null>")}{
|
|
97
134
|
if (memoryAuth) return memoryAuth;
|
|
98
135
|
|
|
99
136
|
const userData = await loadUserData();
|
|
100
137
|
if (userData) {
|
|
101
|
-
// After refresh: user info available, but no token — re-login required
|
|
102
138
|
memoryAuth = userData;
|
|
103
139
|
}
|
|
104
140
|
return memoryAuth;
|
|
105
141
|
}
|
|
106
142
|
|
|
107
|
-
/**
|
|
108
|
-
|
|
109
|
-
*/
|
|
110
|
-
export async function clearAuth() {
|
|
143
|
+
/** Clear auth from memory and IndexedDB. Call on logout or 401. */
|
|
144
|
+
export async function clearAuth()${R("Promise<void>")}{
|
|
111
145
|
memoryAuth = null;
|
|
112
146
|
await clearPersistedData();
|
|
113
147
|
}
|
|
114
148
|
|
|
115
|
-
/**
|
|
116
|
-
|
|
117
|
-
* @param {object | null} auth — The auth object from getAuth()
|
|
118
|
-
*/
|
|
119
|
-
export function isAuthValid(auth) {
|
|
149
|
+
/** Check if auth exists and has not expired. Returns true if no expiresAt is set. */
|
|
150
|
+
export function isAuthValid(auth${T("AuthData | null")})${R("boolean")}{
|
|
120
151
|
if (!auth) return false;
|
|
121
152
|
if (!auth.expiresAt) return true;
|
|
122
153
|
return Date.now() < auth.expiresAt;
|
|
123
154
|
}
|
|
124
155
|
`;
|
|
125
|
-
writeFile(ctx, `auth
|
|
156
|
+
writeFile(ctx, `auth/store.${ext}`, code);
|
|
126
157
|
}
|
|
127
158
|
//# sourceMappingURL=auth-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-store.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-store.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/auth-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,iBAAiB,CAAC,GAAqB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEvD,MAAM,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAC;;;;;;;;;;;;;CAahC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEL,MAAM,2BAA2B,GAAG,EAAE;QACpC,CAAC,CAAC;;;;;;;;;;;CAWL;QACG,CAAC,CAAC;;;;;;;;;;CAUL,CAAC;IAEA,MAAM,IAAI,GAAG;;;;;;;;;8EAS+D,GAAG;;;;;;;EAO/E,iBAAiB;;;gBAGH,CAAC,CAAC,iBAAiB,CAAC;;EAElC,2BAA2B;uBACN,CAAC,CAAC,sBAAsB,CAAC;sBAC1B,EAAE,CAAC,aAAa,CAAC;;;4BAGX,EAAE,CAAC,kBAAkB,CAAC;;;;;kDAKA,EAAE,CAAC,kBAAkB,CAAC;+CACzB,EAAE,CAAC,YAAY,CAAC;;;;yCAItB,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;sBAI7D,EAAE,CAAC,MAAM,CAAC;;;;;6CAKa,EAAE,CAAC,YAAY,CAAC;;;;+BAI9B,CAAC,CAAC,wEAAwE,CAAC;;sBAEpF,EAAE,CAAC,KAAK,CAAC;;;;gDAIiB,EAAE,CAAC,YAAY,CAAC;6CACnB,EAAE,CAAC,YAAY,CAAC;;;;qCAIxB,CAAC,CAAC,eAAe,CAAC;;sBAEjC,EAAE,CAAC,MAAM,CAAC;;;;;6CAKa,EAAE,CAAC,YAAY,CAAC;;;;;wCAKrB,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;iCAM1C,CAAC,CAAC,0BAA0B,CAAC;;;;;;;;;;;mCAW3B,CAAC,CAAC,eAAe,CAAC;;;;;;kCAMnB,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC;;;;;CAKrE,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -4,42 +4,44 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateAuthUser(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
10
|
+
const PT = (type) => (ts ? `<${type}>` : "");
|
|
11
|
+
const AS = (type) => (ts ? ` as ${type}` : "");
|
|
7
12
|
const userEndpoint = ctx.config.features.auth.userEndpoint;
|
|
8
13
|
const code = `/**
|
|
9
14
|
* Current User — fetch, cache, and invalidate the current user for offline access.
|
|
10
15
|
*
|
|
11
16
|
* Usage:
|
|
12
|
-
* import { fetchCurrentUser, cacheUser, getCachedUser, clearCachedUser } from "./auth
|
|
17
|
+
* import { fetchCurrentUser, cacheUser, getCachedUser, clearCachedUser } from "./auth/user.${ext}";
|
|
13
18
|
*
|
|
14
19
|
* await fetchCurrentUser(); // Fetch from server & cache
|
|
15
20
|
* const user = await getCachedUser(); // Get cached (offline-capable)
|
|
16
21
|
* await clearCachedUser(); // Clear on logout
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
|
-
import { authenticatedFetch } from "./
|
|
24
|
+
import { authenticatedFetch } from "./fetch.${ext}";
|
|
20
25
|
|
|
21
26
|
const DB_NAME = "swoff-auth-user";
|
|
22
27
|
const STORE_NAME = "current-user";
|
|
23
28
|
|
|
24
|
-
function openAuthDB()
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
29
|
+
function openAuthDB()${R("Promise<IDBDatabase>")}{
|
|
30
|
+
return new Promise${PT("IDBDatabase")}((resolve, reject) => {
|
|
26
31
|
const request = indexedDB.open(DB_NAME, 1);
|
|
27
32
|
request.onupgradeneeded = (e) => {
|
|
28
|
-
const db = e.target.result;
|
|
33
|
+
const db = (e.target${AS("IDBOpenDBRequest")}).result;
|
|
29
34
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
30
35
|
db.createObjectStore(STORE_NAME, { keyPath: "key" });
|
|
31
36
|
}
|
|
32
37
|
};
|
|
33
|
-
request.onsuccess = (e) => resolve(e.target.result);
|
|
34
|
-
request.onerror = (e) => reject(e.target.error);
|
|
38
|
+
request.onsuccess = (e) => resolve((e.target${AS("IDBOpenDBRequest")}).result);
|
|
39
|
+
request.onerror = (e) => reject((e.target${AS("IDBRequest")}).error);
|
|
35
40
|
});
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
* Uses userEndpoint from swoff.config.json.
|
|
41
|
-
*/
|
|
42
|
-
export async function fetchCurrentUser() {
|
|
43
|
+
/** Fetch current user from the user endpoint and cache the result in IndexedDB. */
|
|
44
|
+
export async function fetchCurrentUser()${R("Promise<Record<string, unknown>>")}{
|
|
43
45
|
const response = await authenticatedFetch("${userEndpoint}");
|
|
44
46
|
if (!response.ok) throw new Error("Failed to fetch user");
|
|
45
47
|
|
|
@@ -48,12 +50,10 @@ export async function fetchCurrentUser() {
|
|
|
48
50
|
return user;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
*/
|
|
54
|
-
export async function cacheUser(user) {
|
|
53
|
+
/** Persist user data to IndexedDB for offline access. */
|
|
54
|
+
export async function cacheUser(user${T("Record<string, unknown>")})${R("Promise<void>")}{
|
|
55
55
|
const db = await openAuthDB();
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
56
|
+
return new Promise<void>((resolve, reject) => {
|
|
57
57
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
58
58
|
const store = tx.objectStore(STORE_NAME);
|
|
59
59
|
store.put({ key: "user", value: user });
|
|
@@ -62,34 +62,30 @@ export async function cacheUser(user) {
|
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
*/
|
|
68
|
-
export async function getCachedUser() {
|
|
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>")}{
|
|
69
67
|
const db = await openAuthDB();
|
|
70
|
-
return new Promise((resolve, reject) => {
|
|
68
|
+
return new Promise${PT("Record<string, unknown> | null")}((resolve, reject) => {
|
|
71
69
|
const tx = db.transaction(STORE_NAME, "readonly");
|
|
72
70
|
const store = tx.objectStore(STORE_NAME);
|
|
73
71
|
const request = store.get("user");
|
|
74
|
-
request.onsuccess = () => resolve(request.result?.value
|
|
75
|
-
request.onerror = () => reject(request.error);
|
|
72
|
+
request.onsuccess = () => resolve((request${AS("IDBRequest")}).result?.value ?? null);
|
|
73
|
+
request.onerror = () => reject((request${AS("IDBRequest")}).error);
|
|
76
74
|
});
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
*/
|
|
82
|
-
export async function clearCachedUser() {
|
|
77
|
+
/** Remove user data from IndexedDB cache. Call on logout. */
|
|
78
|
+
export async function clearCachedUser()${R("Promise<void>")}{
|
|
83
79
|
const db = await openAuthDB();
|
|
84
|
-
return new Promise((resolve, reject) => {
|
|
80
|
+
return new Promise${PT("void")}((resolve, reject) => {
|
|
85
81
|
const tx = db.transaction(STORE_NAME, "readwrite");
|
|
86
82
|
const store = tx.objectStore(STORE_NAME);
|
|
87
83
|
store.delete("user");
|
|
88
84
|
tx.oncomplete = () => resolve();
|
|
89
|
-
tx.onerror = () => reject(tx.error);
|
|
85
|
+
tx.onerror = () => reject((tx${AS("IDBTransaction")}).error);
|
|
90
86
|
});
|
|
91
87
|
}
|
|
92
88
|
`;
|
|
93
|
-
writeFile(ctx, `auth
|
|
89
|
+
writeFile(ctx, `auth/user.${ext}`, code);
|
|
94
90
|
}
|
|
95
91
|
//# 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;AAE3D,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;IAE3D,MAAM,IAAI,GAAG;;;;gGAIiF,GAAG;;;;;;;
|
|
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;AAE3D,MAAM,UAAU,gBAAgB,CAAC,GAAqB;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;IAE3D,MAAM,IAAI,GAAG;;;;gGAIiF,GAAG;;;;;;;8CAOrD,GAAG;;;;;uBAK1B,CAAC,CAAC,sBAAsB,CAAC;sBAC1B,EAAE,CAAC,aAAa,CAAC;;;4BAGX,EAAE,CAAC,kBAAkB,CAAC;;;;;kDAKA,EAAE,CAAC,kBAAkB,CAAC;+CACzB,EAAE,CAAC,YAAY,CAAC;;;;;0CAKrB,CAAC,CAAC,kCAAkC,CAAC;+CAChC,YAAY;;;;;;;;;sCASrB,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;uCAYjD,CAAC,CAAC,yCAAyC,CAAC;;sBAE7D,EAAE,CAAC,gCAAgC,CAAC;;;;gDAIV,EAAE,CAAC,YAAY,CAAC;6CACnB,EAAE,CAAC,YAAY,CAAC;;;;;yCAKpB,CAAC,CAAC,eAAe,CAAC;;sBAErC,EAAE,CAAC,MAAM,CAAC;;;;;mCAKG,EAAE,CAAC,gBAAgB,CAAC;;;CAGtD,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateBackgroundSync(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
7
10
|
const code = `/**
|
|
8
11
|
* Swoff Background Sync
|
|
9
12
|
* Register sync events for processing mutation queue after tab close.
|
|
@@ -17,8 +20,7 @@ export function generateBackgroundSync(ctx) {
|
|
|
17
20
|
* url: "/api/todos",
|
|
18
21
|
* body: { title: "Grocery" },
|
|
19
22
|
* tags: ["todos"],
|
|
20
|
-
*
|
|
21
|
-
* tempId: "temp_abc123",
|
|
23
|
+
* tags: ["todos"],
|
|
22
24
|
* });
|
|
23
25
|
*/
|
|
24
26
|
|
|
@@ -26,7 +28,7 @@ import { queueMutation, processMutationQueue, getPendingCount } from "./mutation
|
|
|
26
28
|
|
|
27
29
|
const SYNC_TAG = "sync-mutations";
|
|
28
30
|
|
|
29
|
-
async function registerSync()
|
|
31
|
+
async function registerSync()${R("Promise<void>")}{
|
|
30
32
|
if (!("serviceWorker" in navigator) || !("SyncManager" in window)) {
|
|
31
33
|
window.addEventListener("online", processMutationQueue, { once: true });
|
|
32
34
|
return;
|
|
@@ -40,12 +42,14 @@ async function registerSync() {
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
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>")}{
|
|
44
47
|
await queueMutation(mutation);
|
|
45
48
|
await registerSync();
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
|
|
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>")}{
|
|
49
53
|
if (!("serviceWorker" in navigator) || !("SyncManager" in window)) return;
|
|
50
54
|
const count = await getPendingCount();
|
|
51
55
|
if (count > 0) {
|
|
@@ -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;AAE3D,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,IAAI,GAAG;;;;;;iEAMkD,GAAG
|
|
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;AAE3D,MAAM,UAAU,sBAAsB,CAAC,GAAqB;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG;;;;;;iEAMkD,GAAG;;;;;;;;;;;yFAWqB,GAAG;;;;+BAI7D,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;;iDAeA,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;mCAM/C,CAAC,CAAC,eAAe,CAAC;;;;;;;;;CASpD,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,mBAAmB,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { writeFile } from "./context.js";
|
|
5
5
|
export function generateCache(ctx) {
|
|
6
6
|
const ext = ctx.ext;
|
|
7
|
+
const ts = ext === "ts";
|
|
8
|
+
const T = (type) => (ts ? `: ${type}` : "");
|
|
9
|
+
const R = (type) => (ts ? `: ${type} ` : " ");
|
|
7
10
|
const code = `/**
|
|
8
11
|
* Swoff Cache Invalidation
|
|
9
12
|
* Framework-agnostic cache tag invalidation.
|
|
@@ -15,7 +18,8 @@ export function generateCache(ctx) {
|
|
|
15
18
|
* await invalidateByTag("todos");
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
|
-
|
|
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>")}{
|
|
19
23
|
if (!navigator.serviceWorker?.controller) return;
|
|
20
24
|
|
|
21
25
|
navigator.serviceWorker.controller.postMessage({
|
|
@@ -28,7 +32,8 @@ export async function invalidateByTag(tag) {
|
|
|
28
32
|
);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
/** Invalidate all cached responses matching any of the given tags. */
|
|
36
|
+
export async function invalidateByTags(tags${T("string[]")})${R("Promise<void>")}{
|
|
32
37
|
for (const tag of tags) {
|
|
33
38
|
await invalidateByTag(tag);
|
|
34
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/generators/file-generators/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,GAAqB;IACjD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;IACpB,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG;;;;;sDAKuC,GAAG;;;;;;;2CAOd,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;;;;;;;;;;6CAc/B,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;;;;;CAK/E,CAAC;IAEA,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
|