@julien-lin/universal-pwa-templates 1.3.7 → 1.3.8
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.cjs +1064 -3
- package/dist/index.d.cts +65 -2
- package/dist/index.d.ts +65 -2
- package/dist/index.js +1055 -3
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -21,13 +21,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
determineTemplateType: () => determineTemplateType,
|
|
24
|
+
djangoApiServiceWorkerTemplate: () => djangoApiServiceWorkerTemplate,
|
|
25
|
+
djangoSpaServiceWorkerTemplate: () => djangoSpaServiceWorkerTemplate,
|
|
26
|
+
flaskApiServiceWorkerTemplate: () => flaskApiServiceWorkerTemplate,
|
|
27
|
+
flaskSpaServiceWorkerTemplate: () => flaskSpaServiceWorkerTemplate,
|
|
24
28
|
getAvailableTemplateTypes: () => getAvailableTemplateTypes,
|
|
25
29
|
getServiceWorkerTemplate: () => getServiceWorkerTemplate,
|
|
30
|
+
laravelApiServiceWorkerTemplate: () => laravelApiServiceWorkerTemplate,
|
|
31
|
+
laravelSpaServiceWorkerTemplate: () => laravelSpaServiceWorkerTemplate,
|
|
32
|
+
laravelSsrServiceWorkerTemplate: () => laravelSsrServiceWorkerTemplate,
|
|
26
33
|
phpServiceWorkerTemplate: () => phpServiceWorkerTemplate,
|
|
27
34
|
placeholder: () => placeholder,
|
|
28
35
|
spaServiceWorkerTemplate: () => spaServiceWorkerTemplate,
|
|
29
36
|
ssrServiceWorkerTemplate: () => ssrServiceWorkerTemplate,
|
|
30
37
|
staticServiceWorkerTemplate: () => staticServiceWorkerTemplate,
|
|
38
|
+
symfonyApiServiceWorkerTemplate: () => symfonyApiServiceWorkerTemplate,
|
|
39
|
+
symfonySpaServiceWorkerTemplate: () => symfonySpaServiceWorkerTemplate,
|
|
31
40
|
wordpressServiceWorkerTemplate: () => wordpressServiceWorkerTemplate
|
|
32
41
|
});
|
|
33
42
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -537,6 +546,1007 @@ if (typeof workbox !== 'undefined') {
|
|
|
537
546
|
}
|
|
538
547
|
`;
|
|
539
548
|
|
|
549
|
+
// src/service-worker/laravel-spa.ts
|
|
550
|
+
var laravelSpaServiceWorkerTemplate = `
|
|
551
|
+
// Load Workbox from CDN
|
|
552
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
553
|
+
|
|
554
|
+
// Ensure Workbox is loaded
|
|
555
|
+
if (typeof workbox !== 'undefined') {
|
|
556
|
+
// Precache des assets statiques
|
|
557
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
558
|
+
|
|
559
|
+
// Navigation route pour SPA
|
|
560
|
+
const navigationHandler = workbox.precaching.createHandlerBoundToURL('/index.html')
|
|
561
|
+
const navigationRoute = new workbox.routing.NavigationRoute(navigationHandler, {
|
|
562
|
+
allowlist: [/^\\//],
|
|
563
|
+
denylist: [/^\\/api/, /^\\/livewire/, /^\\/_/],
|
|
564
|
+
})
|
|
565
|
+
workbox.routing.registerRoute(navigationRoute)
|
|
566
|
+
|
|
567
|
+
// CacheFirst pour images
|
|
568
|
+
workbox.routing.registerRoute(
|
|
569
|
+
({ request }) => request.destination === 'image',
|
|
570
|
+
new workbox.strategies.CacheFirst({
|
|
571
|
+
cacheName: 'laravel-images-cache',
|
|
572
|
+
plugins: [
|
|
573
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
574
|
+
new workbox.expiration.ExpirationPlugin({
|
|
575
|
+
maxEntries: 60,
|
|
576
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
577
|
+
}),
|
|
578
|
+
],
|
|
579
|
+
})
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
// CacheFirst pour fonts
|
|
583
|
+
workbox.routing.registerRoute(
|
|
584
|
+
({ request }) => request.destination === 'font',
|
|
585
|
+
new workbox.strategies.CacheFirst({
|
|
586
|
+
cacheName: 'laravel-fonts-cache',
|
|
587
|
+
plugins: [
|
|
588
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
589
|
+
new workbox.expiration.ExpirationPlugin({
|
|
590
|
+
maxEntries: 30,
|
|
591
|
+
maxAgeSeconds: 365 * 24 * 60 * 60,
|
|
592
|
+
}),
|
|
593
|
+
],
|
|
594
|
+
})
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
598
|
+
workbox.routing.registerRoute(
|
|
599
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
600
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
601
|
+
cacheName: 'laravel-assets-cache',
|
|
602
|
+
plugins: [
|
|
603
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
604
|
+
new workbox.expiration.ExpirationPlugin({
|
|
605
|
+
maxEntries: 50,
|
|
606
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
607
|
+
}),
|
|
608
|
+
],
|
|
609
|
+
})
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
// NetworkFirst pour API (CSRF-friendly)
|
|
613
|
+
workbox.routing.registerRoute(
|
|
614
|
+
({ url, request }) =>
|
|
615
|
+
request.method === 'GET' &&
|
|
616
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/graphql')),
|
|
617
|
+
new workbox.strategies.NetworkFirst({
|
|
618
|
+
cacheName: 'laravel-api-cache',
|
|
619
|
+
fetchOptions: {
|
|
620
|
+
credentials: 'same-origin',
|
|
621
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
622
|
+
},
|
|
623
|
+
plugins: [
|
|
624
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
625
|
+
new workbox.expiration.ExpirationPlugin({
|
|
626
|
+
maxEntries: 50,
|
|
627
|
+
maxAgeSeconds: 5 * 60,
|
|
628
|
+
}),
|
|
629
|
+
],
|
|
630
|
+
networkTimeoutSeconds: 3,
|
|
631
|
+
})
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
// Livewire endpoints
|
|
635
|
+
workbox.routing.registerRoute(
|
|
636
|
+
({ url, request }) =>
|
|
637
|
+
request.method === 'GET' && url.pathname.startsWith('/livewire/'),
|
|
638
|
+
new workbox.strategies.NetworkFirst({
|
|
639
|
+
cacheName: 'laravel-livewire-cache',
|
|
640
|
+
fetchOptions: {
|
|
641
|
+
credentials: 'same-origin',
|
|
642
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
643
|
+
},
|
|
644
|
+
plugins: [
|
|
645
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
646
|
+
new workbox.expiration.ExpirationPlugin({
|
|
647
|
+
maxEntries: 30,
|
|
648
|
+
maxAgeSeconds: 2 * 60,
|
|
649
|
+
}),
|
|
650
|
+
],
|
|
651
|
+
networkTimeoutSeconds: 3,
|
|
652
|
+
})
|
|
653
|
+
)
|
|
654
|
+
} else {
|
|
655
|
+
console.error('Workbox could not be loaded.')
|
|
656
|
+
}
|
|
657
|
+
`;
|
|
658
|
+
|
|
659
|
+
// src/service-worker/laravel-ssr.ts
|
|
660
|
+
var laravelSsrServiceWorkerTemplate = `
|
|
661
|
+
// Load Workbox from CDN
|
|
662
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
663
|
+
|
|
664
|
+
// Ensure Workbox is loaded
|
|
665
|
+
if (typeof workbox !== 'undefined') {
|
|
666
|
+
// Precache des assets statiques critiques
|
|
667
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
668
|
+
|
|
669
|
+
// NetworkFirst pour les pages HTML
|
|
670
|
+
workbox.routing.registerRoute(
|
|
671
|
+
({ request }) => request.mode === 'navigate',
|
|
672
|
+
new workbox.strategies.NetworkFirst({
|
|
673
|
+
cacheName: 'laravel-pages-cache',
|
|
674
|
+
plugins: [
|
|
675
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
676
|
+
new workbox.expiration.ExpirationPlugin({
|
|
677
|
+
maxEntries: 50,
|
|
678
|
+
maxAgeSeconds: 24 * 60 * 60,
|
|
679
|
+
}),
|
|
680
|
+
],
|
|
681
|
+
networkTimeoutSeconds: 3,
|
|
682
|
+
})
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
// CacheFirst pour images
|
|
686
|
+
workbox.routing.registerRoute(
|
|
687
|
+
({ request }) => request.destination === 'image',
|
|
688
|
+
new workbox.strategies.CacheFirst({
|
|
689
|
+
cacheName: 'laravel-images-cache',
|
|
690
|
+
plugins: [
|
|
691
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
692
|
+
new workbox.expiration.ExpirationPlugin({
|
|
693
|
+
maxEntries: 60,
|
|
694
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
695
|
+
}),
|
|
696
|
+
],
|
|
697
|
+
})
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
// CacheFirst pour fonts
|
|
701
|
+
workbox.routing.registerRoute(
|
|
702
|
+
({ request }) => request.destination === 'font',
|
|
703
|
+
new workbox.strategies.CacheFirst({
|
|
704
|
+
cacheName: 'laravel-fonts-cache',
|
|
705
|
+
plugins: [
|
|
706
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
707
|
+
new workbox.expiration.ExpirationPlugin({
|
|
708
|
+
maxEntries: 30,
|
|
709
|
+
maxAgeSeconds: 365 * 24 * 60 * 60,
|
|
710
|
+
}),
|
|
711
|
+
],
|
|
712
|
+
})
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
716
|
+
workbox.routing.registerRoute(
|
|
717
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
718
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
719
|
+
cacheName: 'laravel-assets-cache',
|
|
720
|
+
plugins: [
|
|
721
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
722
|
+
new workbox.expiration.ExpirationPlugin({
|
|
723
|
+
maxEntries: 50,
|
|
724
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
725
|
+
}),
|
|
726
|
+
],
|
|
727
|
+
})
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
// NetworkFirst pour API (CSRF-friendly)
|
|
731
|
+
workbox.routing.registerRoute(
|
|
732
|
+
({ url, request }) =>
|
|
733
|
+
request.method === 'GET' &&
|
|
734
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/graphql')),
|
|
735
|
+
new workbox.strategies.NetworkFirst({
|
|
736
|
+
cacheName: 'laravel-api-cache',
|
|
737
|
+
fetchOptions: {
|
|
738
|
+
credentials: 'same-origin',
|
|
739
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
740
|
+
},
|
|
741
|
+
plugins: [
|
|
742
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
743
|
+
new workbox.expiration.ExpirationPlugin({
|
|
744
|
+
maxEntries: 50,
|
|
745
|
+
maxAgeSeconds: 5 * 60,
|
|
746
|
+
}),
|
|
747
|
+
],
|
|
748
|
+
networkTimeoutSeconds: 3,
|
|
749
|
+
})
|
|
750
|
+
)
|
|
751
|
+
} else {
|
|
752
|
+
console.error('Workbox could not be loaded.')
|
|
753
|
+
}
|
|
754
|
+
`;
|
|
755
|
+
|
|
756
|
+
// src/service-worker/laravel-api.ts
|
|
757
|
+
var laravelApiServiceWorkerTemplate = `
|
|
758
|
+
// Load Workbox from CDN
|
|
759
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
760
|
+
|
|
761
|
+
// Ensure Workbox is loaded
|
|
762
|
+
if (typeof workbox !== 'undefined') {
|
|
763
|
+
// Precache des assets statiques
|
|
764
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
765
|
+
|
|
766
|
+
// CacheFirst pour assets publics
|
|
767
|
+
workbox.routing.registerRoute(
|
|
768
|
+
({ url }) => url.pathname.startsWith('/build/') || url.pathname.startsWith('/public/') || url.pathname.startsWith('/storage/'),
|
|
769
|
+
new workbox.strategies.CacheFirst({
|
|
770
|
+
cacheName: 'laravel-public-assets-cache',
|
|
771
|
+
plugins: [
|
|
772
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
773
|
+
new workbox.expiration.ExpirationPlugin({
|
|
774
|
+
maxEntries: 100,
|
|
775
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
776
|
+
}),
|
|
777
|
+
],
|
|
778
|
+
})
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
// CacheFirst pour images
|
|
782
|
+
workbox.routing.registerRoute(
|
|
783
|
+
({ request }) => request.destination === 'image',
|
|
784
|
+
new workbox.strategies.CacheFirst({
|
|
785
|
+
cacheName: 'laravel-images-cache',
|
|
786
|
+
plugins: [
|
|
787
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
788
|
+
new workbox.expiration.ExpirationPlugin({
|
|
789
|
+
maxEntries: 60,
|
|
790
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
791
|
+
}),
|
|
792
|
+
],
|
|
793
|
+
})
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
797
|
+
workbox.routing.registerRoute(
|
|
798
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
799
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
800
|
+
cacheName: 'laravel-assets-cache',
|
|
801
|
+
plugins: [
|
|
802
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
803
|
+
new workbox.expiration.ExpirationPlugin({
|
|
804
|
+
maxEntries: 50,
|
|
805
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
806
|
+
}),
|
|
807
|
+
],
|
|
808
|
+
})
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
// NetworkFirst pour API (CSRF-friendly)
|
|
812
|
+
workbox.routing.registerRoute(
|
|
813
|
+
({ url, request }) =>
|
|
814
|
+
request.method === 'GET' &&
|
|
815
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/graphql')),
|
|
816
|
+
new workbox.strategies.NetworkFirst({
|
|
817
|
+
cacheName: 'laravel-api-cache',
|
|
818
|
+
fetchOptions: {
|
|
819
|
+
credentials: 'same-origin',
|
|
820
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
821
|
+
},
|
|
822
|
+
plugins: [
|
|
823
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
824
|
+
new workbox.expiration.ExpirationPlugin({
|
|
825
|
+
maxEntries: 50,
|
|
826
|
+
maxAgeSeconds: 5 * 60,
|
|
827
|
+
}),
|
|
828
|
+
],
|
|
829
|
+
networkTimeoutSeconds: 3,
|
|
830
|
+
})
|
|
831
|
+
)
|
|
832
|
+
} else {
|
|
833
|
+
console.error('Workbox could not be loaded.')
|
|
834
|
+
}
|
|
835
|
+
`;
|
|
836
|
+
|
|
837
|
+
// src/service-worker/symfony-spa.ts
|
|
838
|
+
var symfonySpaServiceWorkerTemplate = `
|
|
839
|
+
// Load Workbox from CDN
|
|
840
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
841
|
+
|
|
842
|
+
// Ensure Workbox is loaded
|
|
843
|
+
if (typeof workbox !== 'undefined') {
|
|
844
|
+
// Precache des assets statiques
|
|
845
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
846
|
+
|
|
847
|
+
// Navigation route pour SPA Symfony
|
|
848
|
+
const navigationHandler = workbox.precaching.createHandlerBoundToURL('/index.html')
|
|
849
|
+
const navigationRoute = new workbox.routing.NavigationRoute(navigationHandler, {
|
|
850
|
+
allowlist: [/^\\//],
|
|
851
|
+
denylist: [/^\\/api/, /^\\/graphql/, /^\\/_profiler/, /^\\/_wdt/, /^\\/_fragment/, /^\\/login/, /^\\/logout/],
|
|
852
|
+
})
|
|
853
|
+
workbox.routing.registerRoute(navigationRoute)
|
|
854
|
+
|
|
855
|
+
// Routes sensibles (auth/CSRF) en r\xE9seau uniquement
|
|
856
|
+
workbox.routing.registerRoute(
|
|
857
|
+
({ url, request }) =>
|
|
858
|
+
request.method === 'GET' &&
|
|
859
|
+
(url.pathname.startsWith('/login') ||
|
|
860
|
+
url.pathname.startsWith('/logout') ||
|
|
861
|
+
url.pathname.startsWith('/auth') ||
|
|
862
|
+
url.pathname.startsWith('/_csrf')),
|
|
863
|
+
new workbox.strategies.NetworkOnly({
|
|
864
|
+
fetchOptions: {
|
|
865
|
+
credentials: 'same-origin',
|
|
866
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
867
|
+
},
|
|
868
|
+
})
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
// CacheFirst pour assets versionn\xE9s (Webpack Encore + bundles)
|
|
872
|
+
workbox.routing.registerRoute(
|
|
873
|
+
({ url }) => url.pathname.startsWith('/build/') || url.pathname.startsWith('/bundles/'),
|
|
874
|
+
new workbox.strategies.CacheFirst({
|
|
875
|
+
cacheName: 'symfony-assets-cache',
|
|
876
|
+
plugins: [
|
|
877
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
878
|
+
new workbox.expiration.ExpirationPlugin({
|
|
879
|
+
maxEntries: 100,
|
|
880
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
881
|
+
}),
|
|
882
|
+
],
|
|
883
|
+
})
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
// CacheFirst pour images
|
|
887
|
+
workbox.routing.registerRoute(
|
|
888
|
+
({ request }) => request.destination === 'image',
|
|
889
|
+
new workbox.strategies.CacheFirst({
|
|
890
|
+
cacheName: 'symfony-images-cache',
|
|
891
|
+
plugins: [
|
|
892
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
893
|
+
new workbox.expiration.ExpirationPlugin({
|
|
894
|
+
maxEntries: 60,
|
|
895
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
896
|
+
}),
|
|
897
|
+
],
|
|
898
|
+
})
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
// CacheFirst pour fonts
|
|
902
|
+
workbox.routing.registerRoute(
|
|
903
|
+
({ request }) => request.destination === 'font',
|
|
904
|
+
new workbox.strategies.CacheFirst({
|
|
905
|
+
cacheName: 'symfony-fonts-cache',
|
|
906
|
+
plugins: [
|
|
907
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
908
|
+
new workbox.expiration.ExpirationPlugin({
|
|
909
|
+
maxEntries: 30,
|
|
910
|
+
maxAgeSeconds: 365 * 24 * 60 * 60,
|
|
911
|
+
}),
|
|
912
|
+
],
|
|
913
|
+
})
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
917
|
+
workbox.routing.registerRoute(
|
|
918
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
919
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
920
|
+
cacheName: 'symfony-static-assets-cache',
|
|
921
|
+
plugins: [
|
|
922
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
923
|
+
new workbox.expiration.ExpirationPlugin({
|
|
924
|
+
maxEntries: 50,
|
|
925
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
926
|
+
}),
|
|
927
|
+
],
|
|
928
|
+
})
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
// NetworkFirst pour API (CSRF-friendly)
|
|
932
|
+
workbox.routing.registerRoute(
|
|
933
|
+
({ url, request }) =>
|
|
934
|
+
request.method === 'GET' &&
|
|
935
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/graphql')),
|
|
936
|
+
new workbox.strategies.NetworkFirst({
|
|
937
|
+
cacheName: 'symfony-api-cache',
|
|
938
|
+
fetchOptions: {
|
|
939
|
+
credentials: 'same-origin',
|
|
940
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
941
|
+
},
|
|
942
|
+
plugins: [
|
|
943
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
944
|
+
new workbox.expiration.ExpirationPlugin({
|
|
945
|
+
maxEntries: 50,
|
|
946
|
+
maxAgeSeconds: 5 * 60,
|
|
947
|
+
}),
|
|
948
|
+
],
|
|
949
|
+
networkTimeoutSeconds: 3,
|
|
950
|
+
})
|
|
951
|
+
)
|
|
952
|
+
} else {
|
|
953
|
+
console.error('Workbox could not be loaded.')
|
|
954
|
+
}
|
|
955
|
+
`;
|
|
956
|
+
|
|
957
|
+
// src/service-worker/symfony-api.ts
|
|
958
|
+
var symfonyApiServiceWorkerTemplate = `
|
|
959
|
+
// Load Workbox from CDN
|
|
960
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
961
|
+
|
|
962
|
+
// Ensure Workbox is loaded
|
|
963
|
+
if (typeof workbox !== 'undefined') {
|
|
964
|
+
// Precache des assets statiques
|
|
965
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
966
|
+
|
|
967
|
+
// Routes sensibles (auth/CSRF) en r\xE9seau uniquement
|
|
968
|
+
workbox.routing.registerRoute(
|
|
969
|
+
({ url, request }) =>
|
|
970
|
+
request.method === 'GET' &&
|
|
971
|
+
(url.pathname.startsWith('/login') ||
|
|
972
|
+
url.pathname.startsWith('/logout') ||
|
|
973
|
+
url.pathname.startsWith('/auth') ||
|
|
974
|
+
url.pathname.startsWith('/_csrf')),
|
|
975
|
+
new workbox.strategies.NetworkOnly({
|
|
976
|
+
fetchOptions: {
|
|
977
|
+
credentials: 'same-origin',
|
|
978
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
979
|
+
},
|
|
980
|
+
})
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
// CacheFirst pour assets versionn\xE9s (Webpack Encore + bundles)
|
|
984
|
+
workbox.routing.registerRoute(
|
|
985
|
+
({ url }) => url.pathname.startsWith('/build/') || url.pathname.startsWith('/bundles/'),
|
|
986
|
+
new workbox.strategies.CacheFirst({
|
|
987
|
+
cacheName: 'symfony-assets-cache',
|
|
988
|
+
plugins: [
|
|
989
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
990
|
+
new workbox.expiration.ExpirationPlugin({
|
|
991
|
+
maxEntries: 100,
|
|
992
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
993
|
+
}),
|
|
994
|
+
],
|
|
995
|
+
})
|
|
996
|
+
)
|
|
997
|
+
|
|
998
|
+
// CacheFirst pour images
|
|
999
|
+
workbox.routing.registerRoute(
|
|
1000
|
+
({ request }) => request.destination === 'image',
|
|
1001
|
+
new workbox.strategies.CacheFirst({
|
|
1002
|
+
cacheName: 'symfony-images-cache',
|
|
1003
|
+
plugins: [
|
|
1004
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1005
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1006
|
+
maxEntries: 60,
|
|
1007
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
1008
|
+
}),
|
|
1009
|
+
],
|
|
1010
|
+
})
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
1014
|
+
workbox.routing.registerRoute(
|
|
1015
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
1016
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
1017
|
+
cacheName: 'symfony-static-assets-cache',
|
|
1018
|
+
plugins: [
|
|
1019
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1020
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1021
|
+
maxEntries: 50,
|
|
1022
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
1023
|
+
}),
|
|
1024
|
+
],
|
|
1025
|
+
})
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
// NetworkFirst pour API (CSRF-friendly)
|
|
1029
|
+
workbox.routing.registerRoute(
|
|
1030
|
+
({ url, request }) =>
|
|
1031
|
+
request.method === 'GET' &&
|
|
1032
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/graphql')),
|
|
1033
|
+
new workbox.strategies.NetworkFirst({
|
|
1034
|
+
cacheName: 'symfony-api-cache',
|
|
1035
|
+
fetchOptions: {
|
|
1036
|
+
credentials: 'same-origin',
|
|
1037
|
+
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
|
1038
|
+
},
|
|
1039
|
+
plugins: [
|
|
1040
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1041
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1042
|
+
maxEntries: 50,
|
|
1043
|
+
maxAgeSeconds: 5 * 60,
|
|
1044
|
+
}),
|
|
1045
|
+
],
|
|
1046
|
+
networkTimeoutSeconds: 3,
|
|
1047
|
+
})
|
|
1048
|
+
)
|
|
1049
|
+
} else {
|
|
1050
|
+
console.error('Workbox could not be loaded.')
|
|
1051
|
+
}
|
|
1052
|
+
`;
|
|
1053
|
+
|
|
1054
|
+
// src/service-worker/django-spa.ts
|
|
1055
|
+
var djangoSpaServiceWorkerTemplate = `
|
|
1056
|
+
// Load Workbox from CDN
|
|
1057
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
1058
|
+
|
|
1059
|
+
// Ensure Workbox is loaded
|
|
1060
|
+
if (typeof workbox !== 'undefined') {
|
|
1061
|
+
// Precache des assets statiques
|
|
1062
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
1063
|
+
|
|
1064
|
+
// Navigation route pour SPA
|
|
1065
|
+
const navigationHandler = workbox.precaching.createHandlerBoundToURL('/index.html')
|
|
1066
|
+
const navigationRoute = new workbox.routing.NavigationRoute(navigationHandler, {
|
|
1067
|
+
allowlist: [/^\\//],
|
|
1068
|
+
denylist: [/^\\/api/, /^\\/admin/, /^\\/_/],
|
|
1069
|
+
})
|
|
1070
|
+
workbox.routing.registerRoute(navigationRoute)
|
|
1071
|
+
|
|
1072
|
+
// CacheFirst pour fichiers statiques Django (/static/)
|
|
1073
|
+
workbox.routing.registerRoute(
|
|
1074
|
+
({ url }) => url.pathname.startsWith('/static/'),
|
|
1075
|
+
new workbox.strategies.CacheFirst({
|
|
1076
|
+
cacheName: 'django-static-cache',
|
|
1077
|
+
plugins: [
|
|
1078
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1079
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1080
|
+
maxEntries: 100,
|
|
1081
|
+
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
1082
|
+
}),
|
|
1083
|
+
],
|
|
1084
|
+
})
|
|
1085
|
+
)
|
|
1086
|
+
|
|
1087
|
+
// CacheFirst pour fichiers m\xE9dia Django (/media/)
|
|
1088
|
+
workbox.routing.registerRoute(
|
|
1089
|
+
({ url }) => url.pathname.startsWith('/media/'),
|
|
1090
|
+
new workbox.strategies.CacheFirst({
|
|
1091
|
+
cacheName: 'django-media-cache',
|
|
1092
|
+
plugins: [
|
|
1093
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1094
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1095
|
+
maxEntries: 50,
|
|
1096
|
+
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
1097
|
+
}),
|
|
1098
|
+
],
|
|
1099
|
+
})
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
// CacheFirst pour images
|
|
1103
|
+
workbox.routing.registerRoute(
|
|
1104
|
+
({ request }) => request.destination === 'image',
|
|
1105
|
+
new workbox.strategies.CacheFirst({
|
|
1106
|
+
cacheName: 'django-images-cache',
|
|
1107
|
+
plugins: [
|
|
1108
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1109
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1110
|
+
maxEntries: 60,
|
|
1111
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
1112
|
+
}),
|
|
1113
|
+
],
|
|
1114
|
+
})
|
|
1115
|
+
)
|
|
1116
|
+
|
|
1117
|
+
// CacheFirst pour fonts
|
|
1118
|
+
workbox.routing.registerRoute(
|
|
1119
|
+
({ request }) => request.destination === 'font',
|
|
1120
|
+
new workbox.strategies.CacheFirst({
|
|
1121
|
+
cacheName: 'django-fonts-cache',
|
|
1122
|
+
plugins: [
|
|
1123
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1124
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1125
|
+
maxEntries: 30,
|
|
1126
|
+
maxAgeSeconds: 365 * 24 * 60 * 60,
|
|
1127
|
+
}),
|
|
1128
|
+
],
|
|
1129
|
+
})
|
|
1130
|
+
)
|
|
1131
|
+
|
|
1132
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
1133
|
+
workbox.routing.registerRoute(
|
|
1134
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
1135
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
1136
|
+
cacheName: 'django-assets-cache',
|
|
1137
|
+
plugins: [
|
|
1138
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1139
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1140
|
+
maxEntries: 50,
|
|
1141
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
1142
|
+
}),
|
|
1143
|
+
],
|
|
1144
|
+
})
|
|
1145
|
+
)
|
|
1146
|
+
|
|
1147
|
+
// NetworkFirst pour API (CSRF-friendly avec credentials)
|
|
1148
|
+
workbox.routing.registerRoute(
|
|
1149
|
+
({ url, request }) =>
|
|
1150
|
+
request.method === 'GET' &&
|
|
1151
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/api/v1/') || url.pathname.startsWith('/api/v2/')),
|
|
1152
|
+
new workbox.strategies.NetworkFirst({
|
|
1153
|
+
cacheName: 'django-api-cache',
|
|
1154
|
+
fetchOptions: {
|
|
1155
|
+
credentials: 'same-origin',
|
|
1156
|
+
headers: {
|
|
1157
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
plugins: [
|
|
1161
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1162
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1163
|
+
maxEntries: 50,
|
|
1164
|
+
maxAgeSeconds: 5 * 60, // 5 minutes
|
|
1165
|
+
}),
|
|
1166
|
+
],
|
|
1167
|
+
networkTimeoutSeconds: 3,
|
|
1168
|
+
})
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
// NetworkOnly pour admin (toujours frais, pas de cache)
|
|
1172
|
+
workbox.routing.registerRoute(
|
|
1173
|
+
({ url }) => url.pathname.startsWith('/admin/'),
|
|
1174
|
+
new workbox.strategies.NetworkOnly({
|
|
1175
|
+
cacheName: 'django-admin-cache',
|
|
1176
|
+
fetchOptions: {
|
|
1177
|
+
credentials: 'same-origin',
|
|
1178
|
+
},
|
|
1179
|
+
})
|
|
1180
|
+
)
|
|
1181
|
+
|
|
1182
|
+
// NetworkOnly pour CSRF token endpoint
|
|
1183
|
+
workbox.routing.registerRoute(
|
|
1184
|
+
({ url }) => url.pathname.includes('/csrf-token/') || url.pathname.includes('/csrf/'),
|
|
1185
|
+
new workbox.strategies.NetworkOnly({
|
|
1186
|
+
cacheName: 'django-csrf-cache',
|
|
1187
|
+
fetchOptions: {
|
|
1188
|
+
credentials: 'same-origin',
|
|
1189
|
+
},
|
|
1190
|
+
})
|
|
1191
|
+
)
|
|
1192
|
+
} else {
|
|
1193
|
+
console.error('Workbox could not be loaded.')
|
|
1194
|
+
}
|
|
1195
|
+
`;
|
|
1196
|
+
|
|
1197
|
+
// src/service-worker/django-api.ts
|
|
1198
|
+
var djangoApiServiceWorkerTemplate = `
|
|
1199
|
+
// Load Workbox from CDN
|
|
1200
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
1201
|
+
|
|
1202
|
+
// Ensure Workbox is loaded
|
|
1203
|
+
if (typeof workbox !== 'undefined') {
|
|
1204
|
+
// Precache des assets statiques
|
|
1205
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
1206
|
+
|
|
1207
|
+
// CacheFirst pour fichiers statiques Django (/static/)
|
|
1208
|
+
workbox.routing.registerRoute(
|
|
1209
|
+
({ url }) => url.pathname.startsWith('/static/'),
|
|
1210
|
+
new workbox.strategies.CacheFirst({
|
|
1211
|
+
cacheName: 'django-static-cache',
|
|
1212
|
+
plugins: [
|
|
1213
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1214
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1215
|
+
maxEntries: 100,
|
|
1216
|
+
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
1217
|
+
}),
|
|
1218
|
+
],
|
|
1219
|
+
})
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
// CacheFirst pour fichiers m\xE9dia Django (/media/)
|
|
1223
|
+
workbox.routing.registerRoute(
|
|
1224
|
+
({ url }) => url.pathname.startsWith('/media/'),
|
|
1225
|
+
new workbox.strategies.CacheFirst({
|
|
1226
|
+
cacheName: 'django-media-cache',
|
|
1227
|
+
plugins: [
|
|
1228
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1229
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1230
|
+
maxEntries: 50,
|
|
1231
|
+
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
1232
|
+
}),
|
|
1233
|
+
],
|
|
1234
|
+
})
|
|
1235
|
+
)
|
|
1236
|
+
|
|
1237
|
+
// CacheFirst pour images
|
|
1238
|
+
workbox.routing.registerRoute(
|
|
1239
|
+
({ request }) => request.destination === 'image',
|
|
1240
|
+
new workbox.strategies.CacheFirst({
|
|
1241
|
+
cacheName: 'django-images-cache',
|
|
1242
|
+
plugins: [
|
|
1243
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1244
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1245
|
+
maxEntries: 60,
|
|
1246
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
1247
|
+
}),
|
|
1248
|
+
],
|
|
1249
|
+
})
|
|
1250
|
+
)
|
|
1251
|
+
|
|
1252
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
1253
|
+
workbox.routing.registerRoute(
|
|
1254
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
1255
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
1256
|
+
cacheName: 'django-assets-cache',
|
|
1257
|
+
plugins: [
|
|
1258
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1259
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1260
|
+
maxEntries: 50,
|
|
1261
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
1262
|
+
}),
|
|
1263
|
+
],
|
|
1264
|
+
})
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
// NetworkFirst pour API (CSRF-friendly avec credentials)
|
|
1268
|
+
workbox.routing.registerRoute(
|
|
1269
|
+
({ url, request }) =>
|
|
1270
|
+
request.method === 'GET' &&
|
|
1271
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/api/v1/') || url.pathname.startsWith('/api/v2/')),
|
|
1272
|
+
new workbox.strategies.NetworkFirst({
|
|
1273
|
+
cacheName: 'django-api-cache',
|
|
1274
|
+
fetchOptions: {
|
|
1275
|
+
credentials: 'same-origin',
|
|
1276
|
+
headers: {
|
|
1277
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
1278
|
+
},
|
|
1279
|
+
},
|
|
1280
|
+
plugins: [
|
|
1281
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1282
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1283
|
+
maxEntries: 50,
|
|
1284
|
+
maxAgeSeconds: 5 * 60, // 5 minutes
|
|
1285
|
+
}),
|
|
1286
|
+
],
|
|
1287
|
+
networkTimeoutSeconds: 3,
|
|
1288
|
+
})
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1291
|
+
// NetworkOnly pour admin (toujours frais, pas de cache)
|
|
1292
|
+
workbox.routing.registerRoute(
|
|
1293
|
+
({ url }) => url.pathname.startsWith('/admin/'),
|
|
1294
|
+
new workbox.strategies.NetworkOnly({
|
|
1295
|
+
cacheName: 'django-admin-cache',
|
|
1296
|
+
fetchOptions: {
|
|
1297
|
+
credentials: 'same-origin',
|
|
1298
|
+
},
|
|
1299
|
+
})
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
// NetworkOnly pour CSRF token endpoint
|
|
1303
|
+
workbox.routing.registerRoute(
|
|
1304
|
+
({ url }) => url.pathname.includes('/csrf-token/') || url.pathname.includes('/csrf/'),
|
|
1305
|
+
new workbox.strategies.NetworkOnly({
|
|
1306
|
+
cacheName: 'django-csrf-cache',
|
|
1307
|
+
fetchOptions: {
|
|
1308
|
+
credentials: 'same-origin',
|
|
1309
|
+
},
|
|
1310
|
+
})
|
|
1311
|
+
)
|
|
1312
|
+
} else {
|
|
1313
|
+
console.error('Workbox could not be loaded.')
|
|
1314
|
+
}
|
|
1315
|
+
`;
|
|
1316
|
+
|
|
1317
|
+
// src/service-worker/flask-spa.ts
|
|
1318
|
+
var flaskSpaServiceWorkerTemplate = `
|
|
1319
|
+
// Load Workbox from CDN
|
|
1320
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
1321
|
+
|
|
1322
|
+
// Ensure Workbox is loaded
|
|
1323
|
+
if (typeof workbox !== 'undefined') {
|
|
1324
|
+
// Precache des assets statiques
|
|
1325
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
1326
|
+
|
|
1327
|
+
// Navigation route pour SPA
|
|
1328
|
+
const navigationHandler = workbox.precaching.createHandlerBoundToURL('/index.html')
|
|
1329
|
+
const navigationRoute = new workbox.routing.NavigationRoute(navigationHandler, {
|
|
1330
|
+
allowlist: [/^\\//],
|
|
1331
|
+
denylist: [/^\\/api/, /^\\/admin/, /^\\/_/],
|
|
1332
|
+
})
|
|
1333
|
+
workbox.routing.registerRoute(navigationRoute)
|
|
1334
|
+
|
|
1335
|
+
// CacheFirst pour fichiers statiques Flask (/static/)
|
|
1336
|
+
workbox.routing.registerRoute(
|
|
1337
|
+
({ url }) => url.pathname.startsWith('/static/'),
|
|
1338
|
+
new workbox.strategies.CacheFirst({
|
|
1339
|
+
cacheName: 'flask-static-cache',
|
|
1340
|
+
plugins: [
|
|
1341
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1342
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1343
|
+
maxEntries: 100,
|
|
1344
|
+
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
1345
|
+
}),
|
|
1346
|
+
],
|
|
1347
|
+
})
|
|
1348
|
+
)
|
|
1349
|
+
|
|
1350
|
+
// CacheFirst pour images
|
|
1351
|
+
workbox.routing.registerRoute(
|
|
1352
|
+
({ request }) => request.destination === 'image',
|
|
1353
|
+
new workbox.strategies.CacheFirst({
|
|
1354
|
+
cacheName: 'flask-images-cache',
|
|
1355
|
+
plugins: [
|
|
1356
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1357
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1358
|
+
maxEntries: 60,
|
|
1359
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
1360
|
+
}),
|
|
1361
|
+
],
|
|
1362
|
+
})
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
// CacheFirst pour fonts
|
|
1366
|
+
workbox.routing.registerRoute(
|
|
1367
|
+
({ request }) => request.destination === 'font',
|
|
1368
|
+
new workbox.strategies.CacheFirst({
|
|
1369
|
+
cacheName: 'flask-fonts-cache',
|
|
1370
|
+
plugins: [
|
|
1371
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1372
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1373
|
+
maxEntries: 30,
|
|
1374
|
+
maxAgeSeconds: 365 * 24 * 60 * 60,
|
|
1375
|
+
}),
|
|
1376
|
+
],
|
|
1377
|
+
})
|
|
1378
|
+
)
|
|
1379
|
+
|
|
1380
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
1381
|
+
workbox.routing.registerRoute(
|
|
1382
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
1383
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
1384
|
+
cacheName: 'flask-assets-cache',
|
|
1385
|
+
plugins: [
|
|
1386
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1387
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1388
|
+
maxEntries: 50,
|
|
1389
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
1390
|
+
}),
|
|
1391
|
+
],
|
|
1392
|
+
})
|
|
1393
|
+
)
|
|
1394
|
+
|
|
1395
|
+
// NetworkFirst pour API (CSRF-friendly avec credentials)
|
|
1396
|
+
workbox.routing.registerRoute(
|
|
1397
|
+
({ url, request }) =>
|
|
1398
|
+
request.method === 'GET' &&
|
|
1399
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/api/v1/') || url.pathname.startsWith('/api/v2/')),
|
|
1400
|
+
new workbox.strategies.NetworkFirst({
|
|
1401
|
+
cacheName: 'flask-api-cache',
|
|
1402
|
+
fetchOptions: {
|
|
1403
|
+
credentials: 'same-origin',
|
|
1404
|
+
headers: {
|
|
1405
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
1406
|
+
},
|
|
1407
|
+
},
|
|
1408
|
+
plugins: [
|
|
1409
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1410
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1411
|
+
maxEntries: 50,
|
|
1412
|
+
maxAgeSeconds: 5 * 60, // 5 minutes
|
|
1413
|
+
}),
|
|
1414
|
+
],
|
|
1415
|
+
networkTimeoutSeconds: 3,
|
|
1416
|
+
})
|
|
1417
|
+
)
|
|
1418
|
+
|
|
1419
|
+
// NetworkOnly pour admin (toujours frais, pas de cache)
|
|
1420
|
+
workbox.routing.registerRoute(
|
|
1421
|
+
({ url }) => url.pathname.startsWith('/admin/'),
|
|
1422
|
+
new workbox.strategies.NetworkOnly({
|
|
1423
|
+
cacheName: 'flask-admin-cache',
|
|
1424
|
+
fetchOptions: {
|
|
1425
|
+
credentials: 'same-origin',
|
|
1426
|
+
},
|
|
1427
|
+
})
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
// NetworkOnly pour CSRF token endpoint (Flask-WTF)
|
|
1431
|
+
workbox.routing.registerRoute(
|
|
1432
|
+
({ url }) => url.pathname.includes('/csrf-token/') || url.pathname.includes('/csrf/'),
|
|
1433
|
+
new workbox.strategies.NetworkOnly({
|
|
1434
|
+
cacheName: 'flask-csrf-cache',
|
|
1435
|
+
fetchOptions: {
|
|
1436
|
+
credentials: 'same-origin',
|
|
1437
|
+
},
|
|
1438
|
+
})
|
|
1439
|
+
)
|
|
1440
|
+
} else {
|
|
1441
|
+
console.error('Workbox could not be loaded.')
|
|
1442
|
+
}
|
|
1443
|
+
`;
|
|
1444
|
+
|
|
1445
|
+
// src/service-worker/flask-api.ts
|
|
1446
|
+
var flaskApiServiceWorkerTemplate = `
|
|
1447
|
+
// Load Workbox from CDN
|
|
1448
|
+
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.4.0/workbox-sw.js')
|
|
1449
|
+
|
|
1450
|
+
// Ensure Workbox is loaded
|
|
1451
|
+
if (typeof workbox !== 'undefined') {
|
|
1452
|
+
// Precache des assets statiques
|
|
1453
|
+
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST)
|
|
1454
|
+
|
|
1455
|
+
// CacheFirst pour fichiers statiques Flask (/static/)
|
|
1456
|
+
workbox.routing.registerRoute(
|
|
1457
|
+
({ url }) => url.pathname.startsWith('/static/'),
|
|
1458
|
+
new workbox.strategies.CacheFirst({
|
|
1459
|
+
cacheName: 'flask-static-cache',
|
|
1460
|
+
plugins: [
|
|
1461
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1462
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1463
|
+
maxEntries: 100,
|
|
1464
|
+
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
1465
|
+
}),
|
|
1466
|
+
],
|
|
1467
|
+
})
|
|
1468
|
+
)
|
|
1469
|
+
|
|
1470
|
+
// CacheFirst pour images
|
|
1471
|
+
workbox.routing.registerRoute(
|
|
1472
|
+
({ request }) => request.destination === 'image',
|
|
1473
|
+
new workbox.strategies.CacheFirst({
|
|
1474
|
+
cacheName: 'flask-images-cache',
|
|
1475
|
+
plugins: [
|
|
1476
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1477
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1478
|
+
maxEntries: 60,
|
|
1479
|
+
maxAgeSeconds: 30 * 24 * 60 * 60,
|
|
1480
|
+
}),
|
|
1481
|
+
],
|
|
1482
|
+
})
|
|
1483
|
+
)
|
|
1484
|
+
|
|
1485
|
+
// StaleWhileRevalidate pour CSS/JS
|
|
1486
|
+
workbox.routing.registerRoute(
|
|
1487
|
+
({ request }) => request.destination === 'style' || request.destination === 'script',
|
|
1488
|
+
new workbox.strategies.StaleWhileRevalidate({
|
|
1489
|
+
cacheName: 'flask-assets-cache',
|
|
1490
|
+
plugins: [
|
|
1491
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1492
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1493
|
+
maxEntries: 50,
|
|
1494
|
+
maxAgeSeconds: 7 * 24 * 60 * 60,
|
|
1495
|
+
}),
|
|
1496
|
+
],
|
|
1497
|
+
})
|
|
1498
|
+
)
|
|
1499
|
+
|
|
1500
|
+
// NetworkFirst pour API (CSRF-friendly avec credentials)
|
|
1501
|
+
workbox.routing.registerRoute(
|
|
1502
|
+
({ url, request }) =>
|
|
1503
|
+
request.method === 'GET' &&
|
|
1504
|
+
(url.pathname.startsWith('/api/') || url.pathname.startsWith('/api/v1/') || url.pathname.startsWith('/api/v2/')),
|
|
1505
|
+
new workbox.strategies.NetworkFirst({
|
|
1506
|
+
cacheName: 'flask-api-cache',
|
|
1507
|
+
fetchOptions: {
|
|
1508
|
+
credentials: 'same-origin',
|
|
1509
|
+
headers: {
|
|
1510
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
1511
|
+
},
|
|
1512
|
+
},
|
|
1513
|
+
plugins: [
|
|
1514
|
+
new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }),
|
|
1515
|
+
new workbox.expiration.ExpirationPlugin({
|
|
1516
|
+
maxEntries: 50,
|
|
1517
|
+
maxAgeSeconds: 5 * 60, // 5 minutes
|
|
1518
|
+
}),
|
|
1519
|
+
],
|
|
1520
|
+
networkTimeoutSeconds: 3,
|
|
1521
|
+
})
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1524
|
+
// NetworkOnly pour admin (toujours frais, pas de cache)
|
|
1525
|
+
workbox.routing.registerRoute(
|
|
1526
|
+
({ url }) => url.pathname.startsWith('/admin/'),
|
|
1527
|
+
new workbox.strategies.NetworkOnly({
|
|
1528
|
+
cacheName: 'flask-admin-cache',
|
|
1529
|
+
fetchOptions: {
|
|
1530
|
+
credentials: 'same-origin',
|
|
1531
|
+
},
|
|
1532
|
+
})
|
|
1533
|
+
)
|
|
1534
|
+
|
|
1535
|
+
// NetworkOnly pour CSRF token endpoint (Flask-WTF)
|
|
1536
|
+
workbox.routing.registerRoute(
|
|
1537
|
+
({ url }) => url.pathname.includes('/csrf-token/') || url.pathname.includes('/csrf/'),
|
|
1538
|
+
new workbox.strategies.NetworkOnly({
|
|
1539
|
+
cacheName: 'flask-csrf-cache',
|
|
1540
|
+
fetchOptions: {
|
|
1541
|
+
credentials: 'same-origin',
|
|
1542
|
+
},
|
|
1543
|
+
})
|
|
1544
|
+
)
|
|
1545
|
+
} else {
|
|
1546
|
+
console.error('Workbox could not be loaded.')
|
|
1547
|
+
}
|
|
1548
|
+
`;
|
|
1549
|
+
|
|
540
1550
|
// src/service-worker/index.ts
|
|
541
1551
|
function getServiceWorkerTemplate(type) {
|
|
542
1552
|
const templates = {
|
|
@@ -544,7 +1554,16 @@ function getServiceWorkerTemplate(type) {
|
|
|
544
1554
|
spa: spaServiceWorkerTemplate,
|
|
545
1555
|
ssr: ssrServiceWorkerTemplate,
|
|
546
1556
|
wordpress: wordpressServiceWorkerTemplate,
|
|
547
|
-
php: phpServiceWorkerTemplate
|
|
1557
|
+
php: phpServiceWorkerTemplate,
|
|
1558
|
+
"laravel-spa": laravelSpaServiceWorkerTemplate,
|
|
1559
|
+
"laravel-ssr": laravelSsrServiceWorkerTemplate,
|
|
1560
|
+
"laravel-api": laravelApiServiceWorkerTemplate,
|
|
1561
|
+
"symfony-spa": symfonySpaServiceWorkerTemplate,
|
|
1562
|
+
"symfony-api": symfonyApiServiceWorkerTemplate,
|
|
1563
|
+
"django-spa": djangoSpaServiceWorkerTemplate,
|
|
1564
|
+
"django-api": djangoApiServiceWorkerTemplate,
|
|
1565
|
+
"flask-spa": flaskSpaServiceWorkerTemplate,
|
|
1566
|
+
"flask-api": flaskApiServiceWorkerTemplate
|
|
548
1567
|
};
|
|
549
1568
|
const content = templates[type];
|
|
550
1569
|
if (!content) {
|
|
@@ -556,7 +1575,22 @@ function getServiceWorkerTemplate(type) {
|
|
|
556
1575
|
};
|
|
557
1576
|
}
|
|
558
1577
|
function getAvailableTemplateTypes() {
|
|
559
|
-
return [
|
|
1578
|
+
return [
|
|
1579
|
+
"static",
|
|
1580
|
+
"spa",
|
|
1581
|
+
"ssr",
|
|
1582
|
+
"wordpress",
|
|
1583
|
+
"php",
|
|
1584
|
+
"laravel-spa",
|
|
1585
|
+
"laravel-ssr",
|
|
1586
|
+
"laravel-api",
|
|
1587
|
+
"symfony-spa",
|
|
1588
|
+
"symfony-api",
|
|
1589
|
+
"django-spa",
|
|
1590
|
+
"django-api",
|
|
1591
|
+
"flask-spa",
|
|
1592
|
+
"flask-api"
|
|
1593
|
+
];
|
|
560
1594
|
}
|
|
561
1595
|
function determineTemplateType(architecture, framework) {
|
|
562
1596
|
if (framework === "WordPress" || framework === "WooCommerce") {
|
|
@@ -565,9 +1599,27 @@ function determineTemplateType(architecture, framework) {
|
|
|
565
1599
|
if (framework === "Drupal" || framework === "Joomla" || framework === "Magento" || framework === "Shopify" || framework === "PrestaShop") {
|
|
566
1600
|
return "php";
|
|
567
1601
|
}
|
|
568
|
-
if (framework === "Symfony"
|
|
1602
|
+
if (framework === "Symfony") {
|
|
1603
|
+
return architecture === "spa" ? "symfony-spa" : "symfony-api";
|
|
1604
|
+
}
|
|
1605
|
+
if (framework === "CodeIgniter" || framework === "CakePHP" || framework === "Yii" || framework === "Laminas") {
|
|
569
1606
|
return "php";
|
|
570
1607
|
}
|
|
1608
|
+
if (framework === "Laravel") {
|
|
1609
|
+
if (architecture === "spa") {
|
|
1610
|
+
return "laravel-spa";
|
|
1611
|
+
}
|
|
1612
|
+
if (architecture === "ssr") {
|
|
1613
|
+
return "laravel-ssr";
|
|
1614
|
+
}
|
|
1615
|
+
return "laravel-api";
|
|
1616
|
+
}
|
|
1617
|
+
if (framework === "Django") {
|
|
1618
|
+
return architecture === "spa" ? "django-spa" : "django-api";
|
|
1619
|
+
}
|
|
1620
|
+
if (framework === "Flask") {
|
|
1621
|
+
return architecture === "spa" ? "flask-spa" : "flask-api";
|
|
1622
|
+
}
|
|
571
1623
|
if (architecture === "spa") {
|
|
572
1624
|
return "spa";
|
|
573
1625
|
}
|
|
@@ -582,12 +1634,21 @@ var placeholder = true;
|
|
|
582
1634
|
// Annotate the CommonJS export names for ESM import in node:
|
|
583
1635
|
0 && (module.exports = {
|
|
584
1636
|
determineTemplateType,
|
|
1637
|
+
djangoApiServiceWorkerTemplate,
|
|
1638
|
+
djangoSpaServiceWorkerTemplate,
|
|
1639
|
+
flaskApiServiceWorkerTemplate,
|
|
1640
|
+
flaskSpaServiceWorkerTemplate,
|
|
585
1641
|
getAvailableTemplateTypes,
|
|
586
1642
|
getServiceWorkerTemplate,
|
|
1643
|
+
laravelApiServiceWorkerTemplate,
|
|
1644
|
+
laravelSpaServiceWorkerTemplate,
|
|
1645
|
+
laravelSsrServiceWorkerTemplate,
|
|
587
1646
|
phpServiceWorkerTemplate,
|
|
588
1647
|
placeholder,
|
|
589
1648
|
spaServiceWorkerTemplate,
|
|
590
1649
|
ssrServiceWorkerTemplate,
|
|
591
1650
|
staticServiceWorkerTemplate,
|
|
1651
|
+
symfonyApiServiceWorkerTemplate,
|
|
1652
|
+
symfonySpaServiceWorkerTemplate,
|
|
592
1653
|
wordpressServiceWorkerTemplate
|
|
593
1654
|
});
|