@swoff/cli 0.4.4 → 0.4.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/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/commands/generate-guide.js +1 -1
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/init.js +122 -26
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/config/minimal-config.js +9 -3
- package/dist/lib/config/minimal-config.js.map +1 -1
- package/dist/lib/config/validator.js +1 -6
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -1
- package/dist/lib/generators/file-generators/context.js +1 -1
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +2 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-build-utils.js +93 -0
- package/dist/lib/generators/file-generators/sw-build-utils.js.map +1 -0
- package/dist/lib/generators/file-generators/sw-generator-build.js +7 -78
- package/dist/lib/generators/file-generators/sw-generator-build.js.map +1 -1
- package/dist/lib/generators/file-generators/sw-injector.js +0 -1
- package/dist/lib/generators/file-generators/sw-injector.js.map +1 -1
- package/dist/lib/generators/sw-generator.js +1 -2
- package/dist/lib/generators/sw-generator.js.map +1 -1
- package/dist/lib/generators/sw-sections/assemble-sw.js +1 -2
- package/dist/lib/generators/sw-sections/assemble-sw.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-precache.js +4 -1
- package/dist/lib/generators/sw-sections/background-precache.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +44 -8
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/generators/swoff-files-generator.js +5 -1
- package/dist/lib/generators/swoff-files-generator.js.map +1 -1
- package/dist/lib/shared/config-types.js +1 -116
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/detect-framework.js +2 -1
- package/dist/lib/utils/detect-framework.js.map +1 -1
- package/dist/runtime/client-injector-bundle.js +6 -7
- package/dist/runtime/client-injector-bundle.js.map +1 -1
- package/dist/runtime/client-injector.js +7 -11
- package/dist/runtime/client-injector.js.map +1 -1
- package/dist/runtime/sw-injector.js +2 -2
- package/dist/runtime/sw-injector.js.map +1 -1
- package/dist/runtime/swoff-api-bundle.js +66 -882
- package/dist/runtime/swoff-api-bundle.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
import { generateAuthStoreCode } from "./auth-store.js";
|
|
2
|
+
import { generateAuthStateCode } from "./auth-state.js";
|
|
3
|
+
import { generateAuthAdapterCode } from "./auth-adapter.js";
|
|
4
|
+
import { generateMutationQueueCode } from "./mutation-queue.js";
|
|
5
|
+
import { generateMutationStateCode } from "./mutation-state.js";
|
|
6
|
+
import { generateBackgroundSyncCode } from "./background-sync.js";
|
|
7
|
+
import { generatePushCode } from "./push.js";
|
|
8
|
+
import { generatePwaPromptCode } from "./pwa-prompt.js";
|
|
9
|
+
import { generateGqlWrapperCode } from "./gql-wrapper.js";
|
|
10
|
+
import { generateServerPushCode } from "./server-push.js";
|
|
11
|
+
const IIFE_CTX = { ts: false, ext: "js" };
|
|
12
|
+
function stripModuleWrappers(code, renames) {
|
|
13
|
+
code = code.replace(/^import\s+.*$/gm, "");
|
|
14
|
+
code = code.replace(/^import\s+type\s+.*$/gm, "");
|
|
15
|
+
code = code.replace(/^export\s+default\s+/gm, "");
|
|
16
|
+
code = code.replace(/^export\s+(async\s+)?function\s+(\w+)\s*\(/gm, (_, asyncKw, name) => {
|
|
17
|
+
const renamed = renames?.[name] ?? name;
|
|
18
|
+
return `var ${renamed} = ${asyncKw || ""}function(`;
|
|
19
|
+
});
|
|
20
|
+
code = code.replace(/^export\s+(const|let|var)\s+(\w+)/gm, (_, _kw, name) => {
|
|
21
|
+
const renamed = renames?.[name] ?? name;
|
|
22
|
+
return `var ${renamed}`;
|
|
23
|
+
});
|
|
24
|
+
code = code.replace(/^export\s+(interface|type)\s+\w+/gm, "");
|
|
25
|
+
code = code.replace(/^export\s+\{\s*[\s\S]*?\s*\};\s*$/gm, "");
|
|
26
|
+
code = code.replace(/^const\s+/gm, "var ");
|
|
27
|
+
code = code.replace(/^let\s+/gm, "var ");
|
|
28
|
+
if (renames) {
|
|
29
|
+
for (const [from, to] of Object.entries(renames)) {
|
|
30
|
+
code = code.replace(new RegExp(`\\b${from}\\b`, "g"), to);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
code = code.replace(/\n{3,}/g, "\n\n");
|
|
34
|
+
return code.trim();
|
|
35
|
+
}
|
|
1
36
|
export function generateSwoffApiBundleCode(ctx, flags) {
|
|
2
37
|
const cascadingCode = flags.tagInvalidationCascading && Object.keys(flags.tagInvalidationCascading).length > 0
|
|
3
38
|
? JSON.stringify(flags.tagInvalidationCascading)
|
|
@@ -6,12 +41,32 @@ export function generateSwoffApiBundleCode(ctx, flags) {
|
|
|
6
41
|
const singularizationCode = flags.tagInvalidationSingularization && Object.keys(flags.tagInvalidationSingularization).length > 0
|
|
7
42
|
? JSON.stringify(flags.tagInvalidationSingularization)
|
|
8
43
|
: "null";
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
44
|
+
const AUTH_RENAMES = { DB_NAME: "AUTH_DB_NAME", STORE_NAME: "AUTH_STORE_NAME" };
|
|
45
|
+
const authAdapterCode = stripModuleWrappers(generateAuthAdapterCode(IIFE_CTX, flags.authType), AUTH_RENAMES);
|
|
46
|
+
const authStoreCode = stripModuleWrappers(generateAuthStoreCode(IIFE_CTX, flags.authType, flags.authRoutePaths, flags.mutationQueueEnabled), AUTH_RENAMES);
|
|
47
|
+
const authStateCode = stripModuleWrappers(generateAuthStateCode(IIFE_CTX), AUTH_RENAMES);
|
|
48
|
+
const authCode = flags.authEnabled
|
|
49
|
+
? authAdapterCode + "\n" + authStoreCode + "\n" + authStateCode
|
|
50
|
+
: "";
|
|
51
|
+
const MUTATION_RENAMES = { DB_NAME: "QUEUE_DB_NAME", STORE_NAME: "QUEUE_STORE_NAME" };
|
|
52
|
+
const mutationCode = flags.mutationQueueEnabled
|
|
53
|
+
? stripModuleWrappers(generateMutationQueueCode(IIFE_CTX, flags.authEnabled, flags.mutationQueueBatchSize, flags.mutationQueueBatchDelayMs, flags.mutationQueueMaxRetries, flags.mutationQueueRetryBackoffMs, flags.mutationQueueRetryMaxBackoffMs, flags.mutationQueueRetryJitterMs), MUTATION_RENAMES)
|
|
54
|
+
+ "\n" + stripModuleWrappers(generateMutationStateCode(IIFE_CTX), MUTATION_RENAMES)
|
|
55
|
+
+ "\n" + stripModuleWrappers(generateBackgroundSyncCode(IIFE_CTX), MUTATION_RENAMES)
|
|
56
|
+
: "";
|
|
57
|
+
const pwaCode = flags.pwaEnabled
|
|
58
|
+
? stripModuleWrappers(generatePwaPromptCode({ ...IIFE_CTX, preventDefaultInstall: flags.pwaPreventDefaultInstall }))
|
|
59
|
+
+ "\nif (typeof window !== \"undefined\" && typeof document !== \"undefined\") { setupPwaInstall(); }"
|
|
60
|
+
: "";
|
|
61
|
+
const gqlCode = flags.gqlEnabled
|
|
62
|
+
? stripModuleWrappers(generateGqlWrapperCode(IIFE_CTX, flags.gqlEndpoints))
|
|
63
|
+
: "";
|
|
64
|
+
const pushCode = flags.pushNotificationsEnabled
|
|
65
|
+
? stripModuleWrappers(generatePushCode(IIFE_CTX))
|
|
66
|
+
: "";
|
|
67
|
+
const serverPushCode = flags.serverPushEnabled
|
|
68
|
+
? stripModuleWrappers(generateServerPushCode(IIFE_CTX, flags.serverPushType, flags.serverPushEndpoint, flags.serverPushReconnectDelayMs))
|
|
69
|
+
: "";
|
|
15
70
|
return `(function () {
|
|
16
71
|
"use strict";
|
|
17
72
|
|
|
@@ -39,6 +94,11 @@ export function generateSwoffApiBundleCode(ctx, flags) {
|
|
|
39
94
|
});
|
|
40
95
|
}
|
|
41
96
|
|
|
97
|
+
// ── Auth Failure Check ──
|
|
98
|
+
async function isAuthFailureResponse(response) {
|
|
99
|
+
return response.status === 401;
|
|
100
|
+
}
|
|
101
|
+
|
|
42
102
|
// ── Cache Tags ──
|
|
43
103
|
var TAG_PATTERNS = ${compilePatterns(flags.tagInvalidationPatterns)};
|
|
44
104
|
|
|
@@ -536,246 +596,6 @@ ${flags.serverPushEnabled ? " startPushEvents: startPushEvents,\n stopPush
|
|
|
536
596
|
})();
|
|
537
597
|
`;
|
|
538
598
|
}
|
|
539
|
-
function generatePushSection() {
|
|
540
|
-
return `
|
|
541
|
-
// ── Push Notifications ──
|
|
542
|
-
var pushPermissionState = typeof Notification !== "undefined" ? Notification.permission : undefined;
|
|
543
|
-
|
|
544
|
-
function requestNotificationPermission() {
|
|
545
|
-
if (pushPermissionState === "granted") return Promise.resolve(true);
|
|
546
|
-
if (pushPermissionState === "denied") return Promise.resolve(false);
|
|
547
|
-
return Notification.requestPermission().then(function (result) {
|
|
548
|
-
pushPermissionState = result;
|
|
549
|
-
window.dispatchEvent(new CustomEvent("push-permission-changed", { detail: { permission: result } }));
|
|
550
|
-
return result === "granted";
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
function getPushSubscription() {
|
|
555
|
-
try {
|
|
556
|
-
return navigator.serviceWorker.ready.then(function (registration) {
|
|
557
|
-
return registration.pushManager.getSubscription();
|
|
558
|
-
});
|
|
559
|
-
} catch (e) {
|
|
560
|
-
return Promise.resolve(null);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
function subscribeToPush() {
|
|
565
|
-
return requestNotificationPermission().then(function (granted) {
|
|
566
|
-
if (!granted) return null;
|
|
567
|
-
return navigator.serviceWorker.ready.then(function (registration) {
|
|
568
|
-
return registration.pushManager.subscribe({
|
|
569
|
-
userVisibleOnly: true,
|
|
570
|
-
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
|
|
571
|
-
});
|
|
572
|
-
}).then(function (subscription) {
|
|
573
|
-
return openDB("swoff-push", "subscription", "id").then(function (db) {
|
|
574
|
-
var tx = db.transaction("subscription", "readwrite");
|
|
575
|
-
tx.objectStore("subscription").put({
|
|
576
|
-
id: "current",
|
|
577
|
-
endpoint: subscription.endpoint,
|
|
578
|
-
keys: subscription.toJSON().keys,
|
|
579
|
-
subscribedAt: Date.now(),
|
|
580
|
-
});
|
|
581
|
-
return new Promise(function (resolve, reject) {
|
|
582
|
-
tx.oncomplete = function () { db.close(); resolve(); };
|
|
583
|
-
tx.onerror = function () { db.close(); reject(tx.error); };
|
|
584
|
-
});
|
|
585
|
-
}).then(function () {
|
|
586
|
-
window.dispatchEvent(new CustomEvent("push-subscription-changed", { detail: { subscribed: true } }));
|
|
587
|
-
return subscription;
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function unsubscribeFromPush() {
|
|
594
|
-
return getPushSubscription().then(function (subscription) {
|
|
595
|
-
if (!subscription) return;
|
|
596
|
-
return subscription.unsubscribe().then(function () {
|
|
597
|
-
return openDB("swoff-push", "subscription", "id").then(function (db) {
|
|
598
|
-
var tx = db.transaction("subscription", "readwrite");
|
|
599
|
-
tx.objectStore("subscription").delete("current");
|
|
600
|
-
return new Promise(function (resolve, reject) {
|
|
601
|
-
tx.oncomplete = function () { db.close(); resolve(); };
|
|
602
|
-
tx.onerror = function () { db.close(); reject(tx.error); };
|
|
603
|
-
});
|
|
604
|
-
});
|
|
605
|
-
});
|
|
606
|
-
}).then(function () {
|
|
607
|
-
window.dispatchEvent(new CustomEvent("push-subscription-changed", { detail: { subscribed: false } }));
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
function isSubscribed() {
|
|
612
|
-
return getPushSubscription().then(function (sub) {
|
|
613
|
-
return sub !== null;
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function urlBase64ToUint8Array(base64String) {
|
|
618
|
-
var padding = "=".repeat((4 - (base64String.length % 4)) % 4);
|
|
619
|
-
var base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
|
|
620
|
-
var rawData = atob(base64);
|
|
621
|
-
return Uint8Array.from(rawData, function (c) { return c.charCodeAt(0); });
|
|
622
|
-
}
|
|
623
|
-
`;
|
|
624
|
-
}
|
|
625
|
-
function generateServerPushSection(flags) {
|
|
626
|
-
const connectImpl = flags.serverPushType === "sse"
|
|
627
|
-
? `
|
|
628
|
-
return new Promise(function (resolve) {
|
|
629
|
-
fetch(API_BASE + SERVER_PUSH_ENDPOINT, {
|
|
630
|
-
headers: { Accept: "text/event-stream" },
|
|
631
|
-
credentials: "include",
|
|
632
|
-
signal: options.signal,
|
|
633
|
-
}).then(function (response) {
|
|
634
|
-
serverPushFetchController = null;
|
|
635
|
-
if (!serverPushActive) { resolve(); return; }
|
|
636
|
-
if (!response.ok || !response.body) { resolve(); return; }
|
|
637
|
-
serverPushNotifyStatus(true);
|
|
638
|
-
var reader = response.body.getReader();
|
|
639
|
-
var decoder = new TextDecoder();
|
|
640
|
-
var buffer = "";
|
|
641
|
-
var eventType = "";
|
|
642
|
-
var dataStr = "";
|
|
643
|
-
function readNext() {
|
|
644
|
-
reader.read().then(function (result) {
|
|
645
|
-
if (result.done) {
|
|
646
|
-
serverPushNotifyStatus(false);
|
|
647
|
-
resolve();
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
buffer += decoder.decode(result.value, { stream: true });
|
|
651
|
-
var lines = buffer.split("\\n");
|
|
652
|
-
buffer = lines.pop() || "";
|
|
653
|
-
for (var i = 0; i < lines.length; i++) {
|
|
654
|
-
var line = lines[i];
|
|
655
|
-
if (line.indexOf("event: ") === 0) eventType = line.slice(7).trim();
|
|
656
|
-
else if (line.indexOf("data: ") === 0) dataStr = line.slice(6);
|
|
657
|
-
else if (line === "" && eventType === "invalidate" && dataStr) {
|
|
658
|
-
try {
|
|
659
|
-
var p = JSON.parse(dataStr);
|
|
660
|
-
if (p.tags) serverPushHandleInvalidation(p.tags);
|
|
661
|
-
} catch (e) {}
|
|
662
|
-
eventType = "";
|
|
663
|
-
dataStr = "";
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
readNext();
|
|
667
|
-
}, function () {
|
|
668
|
-
serverPushNotifyStatus(false);
|
|
669
|
-
resolve();
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
readNext();
|
|
673
|
-
}, function () {
|
|
674
|
-
serverPushNotifyStatus(false);
|
|
675
|
-
resolve();
|
|
676
|
-
});
|
|
677
|
-
});`
|
|
678
|
-
: `
|
|
679
|
-
return new Promise(function (resolve) {
|
|
680
|
-
try {
|
|
681
|
-
var ws = new WebSocket(API_BASE + SERVER_PUSH_ENDPOINT);
|
|
682
|
-
serverPushWs = ws;
|
|
683
|
-
ws.onopen = function () { serverPushNotifyStatus(true); };
|
|
684
|
-
ws.onmessage = function (event) {
|
|
685
|
-
try {
|
|
686
|
-
var d = JSON.parse(event.data);
|
|
687
|
-
if (d.type === "invalidate" && d.tags) serverPushHandleInvalidation(d.tags);
|
|
688
|
-
} catch (e) {}
|
|
689
|
-
};
|
|
690
|
-
ws.onclose = function () { serverPushWs = null; serverPushNotifyStatus(false); resolve(); };
|
|
691
|
-
ws.onerror = function () { serverPushWs = null; resolve(); };
|
|
692
|
-
if (options.signal) {
|
|
693
|
-
options.signal.addEventListener("abort", function () { ws.close(); serverPushWs = null; });
|
|
694
|
-
}
|
|
695
|
-
} catch (e) { resolve(); }
|
|
696
|
-
});`;
|
|
697
|
-
return `
|
|
698
|
-
// ── Server Push Events ──
|
|
699
|
-
var SERVER_PUSH_ENDPOINT = ${JSON.stringify(flags.serverPushEndpoint)};
|
|
700
|
-
var SERVER_PUSH_RECONNECT_DELAY_MS = ${flags.serverPushReconnectDelayMs};
|
|
701
|
-
var serverPushActive = false;
|
|
702
|
-
var serverPushSwConnected = false;
|
|
703
|
-
var serverPushReconnectTimer = null;
|
|
704
|
-
var serverPushWs = null;
|
|
705
|
-
var serverPushFetchController = null;
|
|
706
|
-
|
|
707
|
-
function serverPushHandleInvalidation(tags) {
|
|
708
|
-
var i;
|
|
709
|
-
for (i = 0; i < tags.length; i++) {
|
|
710
|
-
invalidateByTag(tags[i]);
|
|
711
|
-
}
|
|
712
|
-
window.dispatchEvent(new CustomEvent("cache-invalidated", { detail: { tags: tags } }));
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
function serverPushNotifyStatus(connected) {
|
|
716
|
-
window.dispatchEvent(new CustomEvent("push-events-status", { detail: { connected: connected } }));
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
if (typeof navigator !== "undefined" && "serviceWorker" in navigator) {
|
|
720
|
-
navigator.serviceWorker.addEventListener("message", function (event) {
|
|
721
|
-
if (event.data && event.data.type === "SSE_STATUS") {
|
|
722
|
-
serverPushSwConnected = event.data.connected;
|
|
723
|
-
serverPushNotifyStatus(serverPushSwConnected);
|
|
724
|
-
}
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function serverPushConnect() {
|
|
729
|
-
var controller = new AbortController();
|
|
730
|
-
serverPushFetchController = controller;
|
|
731
|
-
var options = { signal: controller.signal };
|
|
732
|
-
${connectImpl}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
function startPushEvents() {
|
|
736
|
-
if (serverPushActive) return;
|
|
737
|
-
if (navigator.serviceWorker && navigator.serviceWorker.controller) return;
|
|
738
|
-
var onControllerChange = function () { stopPushEvents(); };
|
|
739
|
-
if (navigator.serviceWorker) {
|
|
740
|
-
navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
741
|
-
}
|
|
742
|
-
serverPushActive = true;
|
|
743
|
-
var delay = Math.max(1000, SERVER_PUSH_RECONNECT_DELAY_MS);
|
|
744
|
-
function serverPushLoop() {
|
|
745
|
-
if (!serverPushActive) {
|
|
746
|
-
if (navigator.serviceWorker) {
|
|
747
|
-
navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange);
|
|
748
|
-
}
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
serverPushConnect().then(function () {
|
|
752
|
-
if (!serverPushActive) {
|
|
753
|
-
if (navigator.serviceWorker) {
|
|
754
|
-
navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange);
|
|
755
|
-
}
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
serverPushReconnectTimer = setTimeout(function () {
|
|
759
|
-
delay = Math.min(delay * 1.5, 30000);
|
|
760
|
-
serverPushLoop();
|
|
761
|
-
}, delay);
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
serverPushLoop();
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function stopPushEvents() {
|
|
768
|
-
serverPushActive = false;
|
|
769
|
-
if (serverPushReconnectTimer) { clearTimeout(serverPushReconnectTimer); serverPushReconnectTimer = null; }
|
|
770
|
-
if (serverPushWs) { serverPushWs.close(); serverPushWs = null; }
|
|
771
|
-
if (serverPushFetchController) { serverPushFetchController.abort(); serverPushFetchController = null; }
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
function isPushConnected() {
|
|
775
|
-
return serverPushActive || serverPushSwConnected;
|
|
776
|
-
}
|
|
777
|
-
`;
|
|
778
|
-
}
|
|
779
599
|
function compilePatterns(patterns) {
|
|
780
600
|
const entries = [];
|
|
781
601
|
for (const [pattern, templates] of Object.entries(patterns)) {
|
|
@@ -842,170 +662,6 @@ function compilePatternEntry(rawPattern, tagTemplates) {
|
|
|
842
662
|
}
|
|
843
663
|
return { regex, params: paramNames, templates: tagTemplates };
|
|
844
664
|
}
|
|
845
|
-
function generateAuthSection(flags) {
|
|
846
|
-
const isCookie = flags.authType === "cookie";
|
|
847
|
-
return `
|
|
848
|
-
// ── Auth Store ──
|
|
849
|
-
var AUTH_DB_NAME = "swoff-auth";
|
|
850
|
-
var AUTH_STORE_NAME = "auth";
|
|
851
|
-
var memoryAuth = null;
|
|
852
|
-
var _fetchingUser = false;
|
|
853
|
-
|
|
854
|
-
var adapter = {
|
|
855
|
-
type: ${JSON.stringify(flags.authType)},
|
|
856
|
-
getHeaders: function (auth) {
|
|
857
|
-
if (!auth || !auth.token) return {};
|
|
858
|
-
return { Authorization: "Bearer " + auth.token };
|
|
859
|
-
},
|
|
860
|
-
getAuth: function () { return Promise.resolve(null); },
|
|
861
|
-
refresh: function () { return Promise.resolve(null); },
|
|
862
|
-
fetchUser: function () { return Promise.resolve(null); },
|
|
863
|
-
};
|
|
864
|
-
|
|
865
|
-
function persistUserData(authData) {
|
|
866
|
-
var userData = { user: authData ? authData.user : null, expiresAt: authData ? authData.expiresAt : null };
|
|
867
|
-
return openDB(AUTH_DB_NAME, AUTH_STORE_NAME, "key").then(function (db) {
|
|
868
|
-
return new Promise(function (resolve, reject) {
|
|
869
|
-
var tx = db.transaction(AUTH_STORE_NAME, "readwrite");
|
|
870
|
-
var store = tx.objectStore(AUTH_STORE_NAME);
|
|
871
|
-
var request = store.put({ key: "session", value: userData });
|
|
872
|
-
request.onsuccess = function () { resolve(); };
|
|
873
|
-
request.onerror = function () { reject(request.error); };
|
|
874
|
-
}).then(function () { db.close(); });
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function loadUserData() {
|
|
879
|
-
return openDB(AUTH_DB_NAME, AUTH_STORE_NAME, "key").then(function (db) {
|
|
880
|
-
return new Promise(function (resolve, reject) {
|
|
881
|
-
var tx = db.transaction(AUTH_STORE_NAME, "readonly");
|
|
882
|
-
var store = tx.objectStore(AUTH_STORE_NAME);
|
|
883
|
-
var request = store.get("session");
|
|
884
|
-
request.onsuccess = function () { resolve(request.result ? request.result.value : null); };
|
|
885
|
-
request.onerror = function () { reject(request.error); };
|
|
886
|
-
}).then(function (result) { db.close(); return result; });
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
function clearPersistedData() {
|
|
891
|
-
return openDB(AUTH_DB_NAME, AUTH_STORE_NAME, "key").then(function (db) {
|
|
892
|
-
return new Promise(function (resolve, reject) {
|
|
893
|
-
var tx = db.transaction(AUTH_STORE_NAME, "readwrite");
|
|
894
|
-
var store = tx.objectStore(AUTH_STORE_NAME);
|
|
895
|
-
var request = store.delete("session");
|
|
896
|
-
request.onsuccess = function () { resolve(); };
|
|
897
|
-
request.onerror = function () { reject(request.error); };
|
|
898
|
-
}).then(function () { db.close(); });
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
function setAuth(authData) {
|
|
903
|
-
memoryAuth = authData;
|
|
904
|
-
return persistUserData(authData);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
function getAuth() {
|
|
908
|
-
if (memoryAuth) return Promise.resolve(memoryAuth);
|
|
909
|
-
return adapter.getAuth().then(function (adapterAuth) {
|
|
910
|
-
if (adapterAuth) {
|
|
911
|
-
memoryAuth = adapterAuth;
|
|
912
|
-
return persistUserData(adapterAuth).then(function () { return memoryAuth; });
|
|
913
|
-
}
|
|
914
|
-
return null;
|
|
915
|
-
}).catch(function () {
|
|
916
|
-
return loadUserData().then(function (userData) {
|
|
917
|
-
if (userData) {
|
|
918
|
-
memoryAuth = userData;
|
|
919
|
-
return memoryAuth;
|
|
920
|
-
}
|
|
921
|
-
if (_fetchingUser) return null;
|
|
922
|
-
_fetchingUser = true;
|
|
923
|
-
return adapter.fetchUser().then(function (fetched) {
|
|
924
|
-
if (fetched) {
|
|
925
|
-
memoryAuth = fetched;
|
|
926
|
-
return persistUserData(fetched).then(function () { return memoryAuth; });
|
|
927
|
-
}
|
|
928
|
-
return null;
|
|
929
|
-
}).catch(function () { return null; }).then(function (result) { _fetchingUser = false; return result; });
|
|
930
|
-
});
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
function clearAuth(options) {
|
|
935
|
-
options = options || {};
|
|
936
|
-
if (options.broadcast !== false) {
|
|
937
|
-
if (navigator.serviceWorker && navigator.serviceWorker.controller) {
|
|
938
|
-
navigator.serviceWorker.controller.postMessage({ type: "AUTH_CLEARED" });
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
memoryAuth = null;
|
|
942
|
-
return clearPersistedData().then(function () {
|
|
943
|
-
try {
|
|
944
|
-
return caches.keys().then(function (keys) {
|
|
945
|
-
return Promise.all(keys.filter(function (name) { return name.indexOf("swoff-runtime") === 0; }).map(function (name) { return caches.delete(name); }));
|
|
946
|
-
});
|
|
947
|
-
} catch (e) { return Promise.resolve(); }
|
|
948
|
-
}).then(function () {
|
|
949
|
-
if (typeof clearQueue === "function") return clearQueue();
|
|
950
|
-
}).then(function () {
|
|
951
|
-
window.dispatchEvent(new CustomEvent("sw-auth-state-change", { detail: { type: "clear" } }));
|
|
952
|
-
});
|
|
953
|
-
}
|
|
954
|
-
${isCookie ? `
|
|
955
|
-
function ensureValidAuth() {
|
|
956
|
-
return getAuth();
|
|
957
|
-
}
|
|
958
|
-
` : `
|
|
959
|
-
function tryRestoreSession() {
|
|
960
|
-
return getAuth().then(function (auth) {
|
|
961
|
-
if (!auth) return null;
|
|
962
|
-
return adapter.refresh(auth).then(function (refreshed) {
|
|
963
|
-
if (refreshed) { return setAuth(refreshed).then(function () { return refreshed; }); }
|
|
964
|
-
return null;
|
|
965
|
-
});
|
|
966
|
-
}).catch(function () { return null; });
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
var restorePromise = null;
|
|
970
|
-
var refreshPromise = null;
|
|
971
|
-
|
|
972
|
-
function ensureValidAuth() {
|
|
973
|
-
return getAuth().then(function (auth) {
|
|
974
|
-
if (!auth) return null;
|
|
975
|
-
if (!auth.token) {
|
|
976
|
-
if (restorePromise) return restorePromise.then(function (r) { restorePromise = null; return r; });
|
|
977
|
-
restorePromise = tryRestoreSession();
|
|
978
|
-
return restorePromise.then(function (r) { restorePromise = null; return r; });
|
|
979
|
-
}
|
|
980
|
-
if (!auth.expiresAt || Date.now() < auth.expiresAt) return auth;
|
|
981
|
-
if (!refreshPromise) {
|
|
982
|
-
refreshPromise = adapter.refresh(auth).then(function (refreshed) {
|
|
983
|
-
if (refreshed) { return setAuth(refreshed).then(function () { return refreshed; }); }
|
|
984
|
-
return clearAuth().then(function () { return null; });
|
|
985
|
-
}).catch(function () {
|
|
986
|
-
return clearAuth().then(function () { return null; });
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
return refreshPromise.then(function (r) { refreshPromise = null; return r; });
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
`}
|
|
993
|
-
|
|
994
|
-
function clearMemoryAuth() {
|
|
995
|
-
memoryAuth = null;
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
function getAuthState() {
|
|
999
|
-
return getAuth().then(function (auth) {
|
|
1000
|
-
return {
|
|
1001
|
-
authenticated: !!(auth && (auth.token || auth.user)),
|
|
1002
|
-
auth: auth,
|
|
1003
|
-
online: getCurrentOnlineStatus(),
|
|
1004
|
-
};
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
`;
|
|
1008
|
-
}
|
|
1009
665
|
function generateAuthFetchBlock(flags) {
|
|
1010
666
|
return `
|
|
1011
667
|
if (options.auth) {
|
|
@@ -1017,476 +673,4 @@ function generateAuthFetchBlock(flags) {
|
|
|
1017
673
|
}
|
|
1018
674
|
`;
|
|
1019
675
|
}
|
|
1020
|
-
function generateMutationSection(flags) {
|
|
1021
|
-
return `
|
|
1022
|
-
// ── Mutation Queue ──
|
|
1023
|
-
var QUEUE_DB_NAME = "swoff-queue";
|
|
1024
|
-
var QUEUE_STORE_NAME = "mutations";
|
|
1025
|
-
var QUEUE_BATCH_SIZE = ${flags.mutationQueueBatchSize};
|
|
1026
|
-
var QUEUE_BATCH_DELAY_MS = ${flags.mutationQueueBatchDelayMs};
|
|
1027
|
-
var QUEUE_MAX_RETRIES = ${flags.mutationQueueMaxRetries};
|
|
1028
|
-
var QUEUE_RETRY_BACKOFF_MS = ${flags.mutationQueueRetryBackoffMs};
|
|
1029
|
-
var QUEUE_RETRY_MAX_BACKOFF_MS = ${flags.mutationQueueRetryMaxBackoffMs};
|
|
1030
|
-
var QUEUE_RETRY_JITTER_MS = ${flags.mutationQueueRetryJitterMs};
|
|
1031
|
-
|
|
1032
|
-
function queueBackoffDelay(attempt) {
|
|
1033
|
-
var delay = Math.min(QUEUE_RETRY_BACKOFF_MS * Math.pow(2, attempt), QUEUE_RETRY_MAX_BACKOFF_MS);
|
|
1034
|
-
return delay + (QUEUE_RETRY_JITTER_MS > 0 ? Math.random() * QUEUE_RETRY_JITTER_MS : 0);
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
var isSyncing = false;
|
|
1038
|
-
|
|
1039
|
-
function openQueueDB() {
|
|
1040
|
-
return openDB(QUEUE_DB_NAME, QUEUE_STORE_NAME, "id", function (db) {
|
|
1041
|
-
if (!db.objectStoreNames.contains(QUEUE_STORE_NAME)) {
|
|
1042
|
-
var store = db.createObjectStore(QUEUE_STORE_NAME, { keyPath: "id" });
|
|
1043
|
-
store.createIndex("by-timestamp", "timestamp");
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
function queueMutation(mutation) {
|
|
1049
|
-
return openQueueDB().then(function (db) {
|
|
1050
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readwrite");
|
|
1051
|
-
var store = tx.objectStore(QUEUE_STORE_NAME);
|
|
1052
|
-
|
|
1053
|
-
var body = mutation.body;
|
|
1054
|
-
var bodyType = "json";
|
|
1055
|
-
if (typeof body === "string") {
|
|
1056
|
-
bodyType = "text";
|
|
1057
|
-
} else if (body instanceof FormData) {
|
|
1058
|
-
bodyType = "formdata";
|
|
1059
|
-
body = Array.from(body.entries());
|
|
1060
|
-
} else if (body instanceof Blob) {
|
|
1061
|
-
bodyType = "blob";
|
|
1062
|
-
} else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
|
|
1063
|
-
bodyType = "buffer";
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
var safeHeaders = Object.assign({}, mutation.headers || {});
|
|
1067
|
-
delete safeHeaders["authorization"];
|
|
1068
|
-
delete safeHeaders["Authorization"];
|
|
1069
|
-
|
|
1070
|
-
store.add({
|
|
1071
|
-
id: crypto.randomUUID(),
|
|
1072
|
-
method: mutation.method,
|
|
1073
|
-
url: mutation.url,
|
|
1074
|
-
body: body,
|
|
1075
|
-
bodyType: bodyType,
|
|
1076
|
-
headers: safeHeaders,
|
|
1077
|
-
timestamp: Date.now(),
|
|
1078
|
-
retryCount: 0,
|
|
1079
|
-
nextRetryAt: 0,
|
|
1080
|
-
tags: mutation.tags || [],
|
|
1081
|
-
});
|
|
1082
|
-
|
|
1083
|
-
return new Promise(function (resolve, reject) {
|
|
1084
|
-
tx.oncomplete = function () { db.close(); resolve(); };
|
|
1085
|
-
tx.onerror = function () { db.close(); reject(tx.error); };
|
|
1086
|
-
});
|
|
1087
|
-
}).then(function () {
|
|
1088
|
-
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
1089
|
-
});
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
function removeFromQueue(id, db) {
|
|
1093
|
-
var ownDb = false;
|
|
1094
|
-
var p;
|
|
1095
|
-
if (!db) {
|
|
1096
|
-
p = openQueueDB().then(function (d) { db = d; ownDb = true; return db; });
|
|
1097
|
-
} else {
|
|
1098
|
-
p = Promise.resolve(db);
|
|
1099
|
-
}
|
|
1100
|
-
return p.then(function (db) {
|
|
1101
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readwrite");
|
|
1102
|
-
tx.objectStore(QUEUE_STORE_NAME).delete(id);
|
|
1103
|
-
return new Promise(function (resolve, reject) {
|
|
1104
|
-
tx.oncomplete = function () { if (ownDb) db.close(); resolve(); };
|
|
1105
|
-
tx.onerror = function () { if (ownDb) db.close(); reject(tx.error); };
|
|
1106
|
-
});
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
function updateInQueue(item, db) {
|
|
1111
|
-
var ownDb = false;
|
|
1112
|
-
var p;
|
|
1113
|
-
if (!db) {
|
|
1114
|
-
p = openQueueDB().then(function (d) { db = d; ownDb = true; return db; });
|
|
1115
|
-
} else {
|
|
1116
|
-
p = Promise.resolve(db);
|
|
1117
|
-
}
|
|
1118
|
-
return p.then(function (db) {
|
|
1119
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readwrite");
|
|
1120
|
-
tx.objectStore(QUEUE_STORE_NAME).put(item);
|
|
1121
|
-
return new Promise(function (resolve, reject) {
|
|
1122
|
-
tx.oncomplete = function () { if (ownDb) db.close(); resolve(); };
|
|
1123
|
-
tx.onerror = function () { if (ownDb) db.close(); reject(tx.error); };
|
|
1124
|
-
});
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
function clearQueue() {
|
|
1129
|
-
return openQueueDB().then(function (db) {
|
|
1130
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readwrite");
|
|
1131
|
-
tx.objectStore(QUEUE_STORE_NAME).clear();
|
|
1132
|
-
return new Promise(function (resolve, reject) {
|
|
1133
|
-
tx.oncomplete = function () { db.close(); resolve(); };
|
|
1134
|
-
tx.onerror = function () { db.close(); reject(tx.error); };
|
|
1135
|
-
});
|
|
1136
|
-
}).then(function () {
|
|
1137
|
-
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
function processMutationQueue() {
|
|
1142
|
-
if (isSyncing) return Promise.resolve();
|
|
1143
|
-
return openQueueDB().then(function (db) {
|
|
1144
|
-
isSyncing = true;
|
|
1145
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readonly");
|
|
1146
|
-
var store = tx.objectStore(QUEUE_STORE_NAME);
|
|
1147
|
-
var index = store.index("by-timestamp");
|
|
1148
|
-
return new Promise(function (resolve, reject) {
|
|
1149
|
-
var request = index.getAll();
|
|
1150
|
-
request.onsuccess = function () { resolve(request.result); };
|
|
1151
|
-
request.onerror = function () { reject(request.error); };
|
|
1152
|
-
}).then(function (queue) {
|
|
1153
|
-
if (queue.length === 0) { isSyncing = false; db.close(); return; }
|
|
1154
|
-
|
|
1155
|
-
var succeeded = 0;
|
|
1156
|
-
var failed = 0;
|
|
1157
|
-
var total = queue.length;
|
|
1158
|
-
var earliestRetry = Infinity;
|
|
1159
|
-
|
|
1160
|
-
function processNext(index) {
|
|
1161
|
-
if (index >= queue.length) {
|
|
1162
|
-
window.dispatchEvent(new CustomEvent("mutation-sync-complete", { detail: { succeeded: succeeded, failed: failed, total: total } }));
|
|
1163
|
-
if (earliestRetry < Infinity && earliestRetry > Date.now()) {
|
|
1164
|
-
setTimeout(function () { if (!isSyncing) processMutationQueue(); }, earliestRetry - Date.now());
|
|
1165
|
-
}
|
|
1166
|
-
isSyncing = false;
|
|
1167
|
-
db.close();
|
|
1168
|
-
window.dispatchEvent(new CustomEvent("mutation-queue-changed"));
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
var item = queue[index];
|
|
1173
|
-
|
|
1174
|
-
if (item.retryCount >= QUEUE_MAX_RETRIES) {
|
|
1175
|
-
removeFromQueue(item.id, db).then(function () {
|
|
1176
|
-
failed++; emitProgress(succeeded, failed, total);
|
|
1177
|
-
if (QUEUE_BATCH_DELAY_MS > 0 && succeeded + failed < total) {
|
|
1178
|
-
setTimeout(function () { processNext(index + 1); }, QUEUE_BATCH_DELAY_MS);
|
|
1179
|
-
} else { processNext(index + 1); }
|
|
1180
|
-
});
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
if (item.nextRetryAt && Date.now() < item.nextRetryAt) {
|
|
1185
|
-
if (item.nextRetryAt < earliestRetry) earliestRetry = item.nextRetryAt;
|
|
1186
|
-
processNext(index + 1);
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
replayMutation(item, db).then(function (ok) {
|
|
1191
|
-
if (ok) { succeeded++; } else { failed++; }
|
|
1192
|
-
emitProgress(succeeded, failed, total);
|
|
1193
|
-
if (QUEUE_BATCH_DELAY_MS > 0 && succeeded + failed < total) {
|
|
1194
|
-
setTimeout(function () { processNext(index + 1); }, QUEUE_BATCH_DELAY_MS);
|
|
1195
|
-
} else { processNext(index + 1); }
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
processNext(0);
|
|
1200
|
-
});
|
|
1201
|
-
});
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
function emitProgress(succeeded, failed, total) {
|
|
1205
|
-
if ((succeeded + failed) % QUEUE_BATCH_SIZE === 0 || succeeded + failed === total) {
|
|
1206
|
-
window.dispatchEvent(new CustomEvent("mutation-sync-progress", { detail: { succeeded: succeeded, failed: failed, total: total } }));
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
function replayMutation(item, db) {
|
|
1211
|
-
var authPromise = ${flags.authEnabled
|
|
1212
|
-
? "getAuth().then(function (auth) { return auth; })"
|
|
1213
|
-
: "Promise.resolve(null)"};
|
|
1214
|
-
return authPromise.then(function (auth) {
|
|
1215
|
-
var authHeader = {};
|
|
1216
|
-
if (auth && auth.token) {
|
|
1217
|
-
authHeader = { Authorization: "Bearer " + auth.token };
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
var replayBody = null;
|
|
1221
|
-
var contentType = undefined;
|
|
1222
|
-
var bt = item.bodyType || "json";
|
|
1223
|
-
|
|
1224
|
-
if (bt === "formdata") {
|
|
1225
|
-
replayBody = new FormData();
|
|
1226
|
-
for (var i = 0; i < (item.body || []).length; i++) {
|
|
1227
|
-
replayBody.append(item.body[i][0], item.body[i][1]);
|
|
1228
|
-
}
|
|
1229
|
-
} else if (bt === "blob" || bt === "buffer") {
|
|
1230
|
-
replayBody = item.body;
|
|
1231
|
-
} else if (bt === "text") {
|
|
1232
|
-
replayBody = item.body;
|
|
1233
|
-
} else if (item.body != null) {
|
|
1234
|
-
replayBody = JSON.stringify(item.body);
|
|
1235
|
-
contentType = "application/json";
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
return fetch(item.url, {
|
|
1239
|
-
method: item.method,
|
|
1240
|
-
headers: Object.assign({}, contentType ? { "Content-Type": contentType } : {}, item.headers, authHeader),
|
|
1241
|
-
body: replayBody,
|
|
1242
|
-
}).then(function (response) {
|
|
1243
|
-
return handleReplayResponse(response, item, db);
|
|
1244
|
-
}).catch(function () {
|
|
1245
|
-
item.retryCount++;
|
|
1246
|
-
item.nextRetryAt = Date.now() + queueBackoffDelay(item.retryCount - 1);
|
|
1247
|
-
return updateInQueue(item, db).then(function () { return false; });
|
|
1248
|
-
});
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
function handleReplayResponse(response, item, db) {
|
|
1253
|
-
if (response.ok) {
|
|
1254
|
-
if (item.tags && item.tags.length > 0) {
|
|
1255
|
-
return invalidateByTags(item.tags).then(function () {
|
|
1256
|
-
return removeFromQueue(item.id, db).then(function () { return true; });
|
|
1257
|
-
});
|
|
1258
|
-
}
|
|
1259
|
-
return removeFromQueue(item.id, db).then(function () { return true; });
|
|
1260
|
-
}
|
|
1261
|
-
if (response.status === 401) {
|
|
1262
|
-
return ensureValidAuth().then(function (refreshed) {
|
|
1263
|
-
if (refreshed && refreshed.token) {
|
|
1264
|
-
var retryHeader = { Authorization: "Bearer " + refreshed.token };
|
|
1265
|
-
return fetch(item.url, {
|
|
1266
|
-
method: item.method,
|
|
1267
|
-
headers: Object.assign({}, item.headers, retryHeader),
|
|
1268
|
-
body: item.body ? JSON.stringify(item.body) : null,
|
|
1269
|
-
}).then(function (retryResponse) {
|
|
1270
|
-
if (retryResponse.ok) {
|
|
1271
|
-
if (item.tags && item.tags.length > 0) {
|
|
1272
|
-
return invalidateByTags(item.tags);
|
|
1273
|
-
}
|
|
1274
|
-
return removeFromQueue(item.id, db).then(function () { return true; });
|
|
1275
|
-
}
|
|
1276
|
-
return removeFromQueue(item.id, db).then(function () { return true; });
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
|
-
return clearAuth().then(function () { return false; });
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
item.retryCount++;
|
|
1283
|
-
item.nextRetryAt = Date.now() + queueBackoffDelay(item.retryCount - 1);
|
|
1284
|
-
return updateInQueue(item, db).then(function () { return false; });
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
function flushMutations() {
|
|
1288
|
-
return processMutationQueue();
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
function getPendingCount() {
|
|
1292
|
-
return openQueueDB().then(function (db) {
|
|
1293
|
-
return new Promise(function (resolve, reject) {
|
|
1294
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readonly");
|
|
1295
|
-
var request = tx.objectStore(QUEUE_STORE_NAME).count();
|
|
1296
|
-
request.onsuccess = function () { db.close(); resolve(request.result); };
|
|
1297
|
-
request.onerror = function () { db.close(); reject(request.error); };
|
|
1298
|
-
});
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
function getQueueItems() {
|
|
1303
|
-
return openQueueDB().then(function (db) {
|
|
1304
|
-
var tx = db.transaction(QUEUE_STORE_NAME, "readonly");
|
|
1305
|
-
var store = tx.objectStore(QUEUE_STORE_NAME);
|
|
1306
|
-
var index = store.index("by-timestamp");
|
|
1307
|
-
return new Promise(function (resolve, reject) {
|
|
1308
|
-
var request = index.getAll();
|
|
1309
|
-
request.onsuccess = function () { resolve(request.result); };
|
|
1310
|
-
request.onerror = function () { reject(request.error); };
|
|
1311
|
-
}).then(function (result) { db.close(); return result; });
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
function getQueuePosition(id) {
|
|
1316
|
-
return getQueueItems().then(function (items) {
|
|
1317
|
-
for (var i = 0; i < items.length; i++) {
|
|
1318
|
-
if (items[i].id === id) return i;
|
|
1319
|
-
}
|
|
1320
|
-
return -1;
|
|
1321
|
-
});
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
function registerSync() {
|
|
1325
|
-
if (!("SyncManager" in window)) {
|
|
1326
|
-
window.addEventListener("online", processMutationQueue, { once: true });
|
|
1327
|
-
return Promise.resolve();
|
|
1328
|
-
}
|
|
1329
|
-
return navigator.serviceWorker.ready.then(function (registration) {
|
|
1330
|
-
return registration.sync.register("sync-mutations").catch(function () {
|
|
1331
|
-
window.addEventListener("online", processMutationQueue, { once: true });
|
|
1332
|
-
});
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
function syncWhenPossible(mutation) {
|
|
1337
|
-
return queueMutation(mutation).then(function () {
|
|
1338
|
-
return registerSync();
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
function retrySync() {
|
|
1343
|
-
window.addEventListener("mutation-sync-complete", retrySync, { once: true });
|
|
1344
|
-
if (!("SyncManager" in window)) return Promise.resolve();
|
|
1345
|
-
return getPendingCount().then(function (count) {
|
|
1346
|
-
if (count > 0) return registerSync();
|
|
1347
|
-
});
|
|
1348
|
-
}
|
|
1349
|
-
`;
|
|
1350
|
-
}
|
|
1351
|
-
function generatePwaSection(flags) {
|
|
1352
|
-
const preventLine = flags.pwaPreventDefaultInstall
|
|
1353
|
-
? " e.preventDefault();\n"
|
|
1354
|
-
: "";
|
|
1355
|
-
return `
|
|
1356
|
-
// ── PWA Install Prompt ──
|
|
1357
|
-
function setupPwaInstall() {
|
|
1358
|
-
window.addEventListener("beforeinstallprompt", function (e) {
|
|
1359
|
-
window.deferredInstallPrompt = e;
|
|
1360
|
-
window.pwaInstallable = true;
|
|
1361
|
-
${preventLine} window.dispatchEvent(new CustomEvent("pwa-installable", { detail: { isInstallable: true } }));
|
|
1362
|
-
});
|
|
1363
|
-
window.addEventListener("appinstalled", function () {
|
|
1364
|
-
window.deferredInstallPrompt = null;
|
|
1365
|
-
window.pwaInstallable = false;
|
|
1366
|
-
window.dispatchEvent(new CustomEvent("pwa-installed", { detail: { outcome: "accepted" } }));
|
|
1367
|
-
});
|
|
1368
|
-
}
|
|
1369
|
-
if (typeof window !== "undefined" && typeof document !== "undefined") { setupPwaInstall(); }
|
|
1370
|
-
|
|
1371
|
-
function isInstallable() {
|
|
1372
|
-
if (typeof window === "undefined") return false;
|
|
1373
|
-
return !!window.deferredInstallPrompt;
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
function promptInstall() {
|
|
1377
|
-
if (!window.deferredInstallPrompt) {
|
|
1378
|
-
return Promise.reject(new Error("Install prompt not available"));
|
|
1379
|
-
}
|
|
1380
|
-
var promptEvent = window.deferredInstallPrompt;
|
|
1381
|
-
return promptEvent.prompt().then(function () {
|
|
1382
|
-
return promptEvent.userChoice;
|
|
1383
|
-
}).then(function (choice) {
|
|
1384
|
-
if (choice.outcome === "accepted") {
|
|
1385
|
-
window.dispatchEvent(new CustomEvent("pwa-installed", { detail: { outcome: "accepted" } }));
|
|
1386
|
-
} else {
|
|
1387
|
-
window.dispatchEvent(new CustomEvent("pwa-dismissed", { detail: { outcome: "dismissed" } }));
|
|
1388
|
-
}
|
|
1389
|
-
window.deferredInstallPrompt = null;
|
|
1390
|
-
return choice;
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
`;
|
|
1394
|
-
}
|
|
1395
|
-
function generateGqlSection(flags) {
|
|
1396
|
-
const endpointsCode = JSON.stringify(flags.gqlEndpoints);
|
|
1397
|
-
return `
|
|
1398
|
-
// ── GraphQL Wrapper ──
|
|
1399
|
-
var GQL_ENDPOINTS = ${endpointsCode};
|
|
1400
|
-
|
|
1401
|
-
function getOperationName(query) {
|
|
1402
|
-
var match = query.match(/(query|mutation|subscription)\\s+(\\w+)/);
|
|
1403
|
-
return match ? match[2] : null;
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
function isReadOperation(query) {
|
|
1407
|
-
var trimmed = query.trim();
|
|
1408
|
-
if (trimmed.startsWith("mutation") || trimmed.startsWith("subscription")) return false;
|
|
1409
|
-
return true;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
function simpleHash(str) {
|
|
1413
|
-
var hash = 0;
|
|
1414
|
-
for (var i = 0; i < str.length; i++) {
|
|
1415
|
-
var char = str.charCodeAt(i);
|
|
1416
|
-
hash = ((hash << 5) - hash) + char;
|
|
1417
|
-
hash = hash & hash;
|
|
1418
|
-
}
|
|
1419
|
-
return Math.abs(hash).toString(16).slice(0, 16);
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
function bodyHash(obj) {
|
|
1423
|
-
var json = JSON.stringify(obj);
|
|
1424
|
-
if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
|
|
1425
|
-
try {
|
|
1426
|
-
var bytes = new TextEncoder().encode(json);
|
|
1427
|
-
return crypto.subtle.digest("SHA-256", bytes).then(function (hash) {
|
|
1428
|
-
return Array.from(new Uint8Array(hash)).map(function (b) { return b.toString(16).padStart(2, "0"); }).join("").slice(0, 16);
|
|
1429
|
-
});
|
|
1430
|
-
} catch (e) {}
|
|
1431
|
-
}
|
|
1432
|
-
return Promise.resolve(simpleHash(json));
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
function tagsFromOpName(name) {
|
|
1436
|
-
if (!name) return [];
|
|
1437
|
-
var stripped = name.replace(/^(get|fetch|list|all|query)/i, "").replace(/^(create|set|add|new|update|delete|remove)/i, "");
|
|
1438
|
-
if (!stripped) return [name.toLowerCase()];
|
|
1439
|
-
var tag = stripped.toLowerCase();
|
|
1440
|
-
var plural = tag.replace(/s$/, "") + "s";
|
|
1441
|
-
return [plural, tag];
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
function fetchWithGql(query, options, endpointIndex) {
|
|
1445
|
-
options = options || {};
|
|
1446
|
-
endpointIndex = endpointIndex || 0;
|
|
1447
|
-
var isRead = isReadOperation(query);
|
|
1448
|
-
var opName = getOperationName(query);
|
|
1449
|
-
var variables = options.variables;
|
|
1450
|
-
var tags = options.tags || tagsFromOpName(opName);
|
|
1451
|
-
var endpoint = GQL_ENDPOINTS[endpointIndex] || GQL_ENDPOINTS[0];
|
|
1452
|
-
var invalidateTags = isRead ? undefined : (options.invalidate ? options.invalidate : (opName ? tagsFromOpName(opName) : undefined));
|
|
1453
|
-
|
|
1454
|
-
return bodyHash({ query: query, variables: variables }).then(function (hash) {
|
|
1455
|
-
return fetchWithCache(endpoint, {
|
|
1456
|
-
method: "POST",
|
|
1457
|
-
body: JSON.stringify({ query: query, variables: variables }),
|
|
1458
|
-
headers: {
|
|
1459
|
-
"Content-Type": "application/json",
|
|
1460
|
-
"X-SW-Cache-Key": "gql:" + hash,
|
|
1461
|
-
},
|
|
1462
|
-
tags: tags,
|
|
1463
|
-
type: isRead ? "read" : "mutation",
|
|
1464
|
-
auth: options.auth,
|
|
1465
|
-
queueOffline: options.queueOffline,
|
|
1466
|
-
invalidate: invalidateTags,
|
|
1467
|
-
});
|
|
1468
|
-
}).then(function (result) {
|
|
1469
|
-
var response = result.response;
|
|
1470
|
-
if (!response.ok) {
|
|
1471
|
-
throw new Error("GraphQL request failed with status " + response.status);
|
|
1472
|
-
}
|
|
1473
|
-
return response.json().then(function (json) {
|
|
1474
|
-
return { data: json.data, fromCache: result.fromCache };
|
|
1475
|
-
});
|
|
1476
|
-
});
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
function queryGql(query, variables, options, endpointIndex) {
|
|
1480
|
-
options = options || {};
|
|
1481
|
-
options.variables = variables;
|
|
1482
|
-
return fetchWithGql(query, options, endpointIndex);
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
function mutateGql(mutation, variables, options, endpointIndex) {
|
|
1486
|
-
options = options || {};
|
|
1487
|
-
options.variables = variables;
|
|
1488
|
-
return fetchWithGql(mutation, options, endpointIndex);
|
|
1489
|
-
}
|
|
1490
|
-
`;
|
|
1491
|
-
}
|
|
1492
676
|
//# sourceMappingURL=swoff-api-bundle.js.map
|