tiny-essentials 1.28.19 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/changelog/1/28/19.md +24 -0
- package/changelog/1/29/0.md +58 -0
- package/dist/v1/ColorSafeStringify.min.js +1 -1
- package/dist/v1/TinyAdvancedRaffle.min.js +1 -1
- package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
- package/dist/v1/TinyAnalogClock.min.js +1 -1
- package/dist/v1/TinyArrayComparator.min.js +1 -1
- package/dist/v1/TinyArrayPaginator.min.js +1 -1
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyBrowserMonitor.min.js +1 -0
- package/dist/v1/TinyClassManager.min.js +1 -1
- package/dist/v1/TinyClipboard.min.js +1 -1
- package/dist/v1/TinyCloner.min.js +1 -0
- package/dist/v1/TinyColorConverter.min.js +1 -1
- package/dist/v1/TinyColorValidator.min.js +1 -1
- package/dist/v1/TinyCookieConsent.min.js +1 -1
- package/dist/v1/TinyDayNightCycle.min.js +1 -1
- package/dist/v1/TinyDebugger.min.js +1 -1
- package/dist/v1/TinyDomReadyManager.min.js +1 -1
- package/dist/v1/TinyDragDropDetector.min.js +1 -1
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyElementObserver.min.js +1 -1
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyGamepad.min.js +1 -1
- package/dist/v1/TinyHtml.min.js +1 -1
- package/dist/v1/TinyHtmlTagRegexBuilder.min.js +1 -0
- package/dist/v1/TinyHttpResponseRegistry.min.js +1 -0
- package/dist/v1/TinyI18.min.js +1 -1
- package/dist/v1/TinyIframeEvents.min.js +1 -1
- package/dist/v1/TinyInventory.min.js +1 -1
- package/dist/v1/TinyInventoryTrader.min.js +1 -1
- package/dist/v1/TinyLevelUp.min.js +1 -1
- package/dist/v1/TinyLoadingScreen.min.js +1 -1
- package/dist/v1/TinyLocalStorage.min.js +1 -1
- package/dist/v1/TinyMaInSys.min.js +1 -1
- package/dist/v1/TinyMapCache.min.js +1 -0
- package/dist/v1/TinyMediaPlayer.min.js +1 -1
- package/dist/v1/TinyNeedBar.min.js +1 -1
- package/dist/v1/TinyNewWinEvents.min.js +1 -1
- package/dist/v1/TinyNotificationAdapter.min.js +1 -0
- package/dist/v1/TinyNotifications.min.js +1 -1
- package/dist/v1/TinyNotifyCenter.min.js +1 -1
- package/dist/v1/TinyOPFSFileSystem.min.js +1 -0
- package/dist/v1/TinyPasswordValidator.min.js +1 -0
- package/dist/v1/TinyPluginPackage.min.js +1 -0
- package/dist/v1/TinyPromiseQueue.min.js +1 -1
- package/dist/v1/TinyPushManager.min.js +1 -0
- package/dist/v1/TinyPushPayload.min.js +1 -0
- package/dist/v1/TinyPushRouter.min.js +1 -0
- package/dist/v1/TinyRadioFm.min.js +1 -1
- package/dist/v1/TinyRateLimiter.min.js +1 -1
- package/dist/v1/TinyRegexEs.min.js +1 -0
- package/dist/v1/TinyRouter.min.js +1 -0
- package/dist/v1/TinyServiceWorker.min.js +1 -0
- package/dist/v1/TinyServiceWorkerEngine.min.js +1 -0
- package/dist/v1/TinySetMapPackage.min.js +1 -0
- package/dist/v1/TinySimpleDice.min.js +1 -1
- package/dist/v1/TinySmartScroller.min.js +1 -1
- package/dist/v1/TinyStorageBucketFileSystem.min.js +1 -0
- package/dist/v1/TinyTextDiffer.min.js +1 -1
- package/dist/v1/TinyTextRangeEditor.min.js +1 -1
- package/dist/v1/TinyTextarea.min.js +1 -1
- package/dist/v1/TinyThrottledApi.min.js +1 -0
- package/dist/v1/TinyTimeout.min.js +1 -1
- package/dist/v1/TinyToastNotify.min.js +1 -1
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/TinyUriParser.min.js +1 -0
- package/dist/v1/TinyVersion.min.js +1 -0
- package/dist/v1/TinyWebWorker.min.js +1 -0
- package/dist/v1/TinyWebWorkerEngine.min.js +1 -0
- package/dist/v1/UltraRandomMsgGen.min.js +1 -1
- package/dist/v1/basics/array.cjs +2 -2
- package/dist/v1/basics/array.d.mts +2 -0
- package/dist/v1/basics/array.d.mts.map +1 -1
- package/dist/v1/basics/array.mjs +2 -1
- package/dist/v1/basics/browserDetector.cjs +37 -12
- package/dist/v1/basics/browserDetector.d.mts +8 -0
- package/dist/v1/basics/browserDetector.d.mts.map +1 -1
- package/dist/v1/basics/browserDetector.mjs +32 -13
- package/dist/v1/basics/crypto.cjs +23 -0
- package/dist/v1/basics/crypto.d.mts +10 -0
- package/dist/v1/basics/crypto.d.mts.map +1 -0
- package/dist/v1/basics/crypto.mjs +18 -0
- package/dist/v1/basics/html.cjs +0 -3
- package/dist/v1/basics/html.d.mts.map +1 -1
- package/dist/v1/basics/html.mjs +0 -2
- package/dist/v1/basics/index.cjs +8 -1
- package/dist/v1/basics/index.d.mts +6 -5
- package/dist/v1/basics/index.d.mts.map +1 -1
- package/dist/v1/basics/index.mjs +6 -5
- package/dist/v1/basics/objChecker.cjs +88 -0
- package/dist/v1/basics/objChecker.d.mts +26 -0
- package/dist/v1/basics/objChecker.d.mts.map +1 -1
- package/dist/v1/basics/objChecker.mjs +74 -0
- package/dist/v1/basics/objFilter.cjs +234 -93
- package/dist/v1/basics/objFilter.d.mts +96 -22
- package/dist/v1/basics/objFilter.d.mts.map +1 -1
- package/dist/v1/basics/objFilter.mjs +216 -92
- package/dist/v1/basics/promiseUtils.cjs +48 -12
- package/dist/v1/basics/promiseUtils.d.mts +45 -12
- package/dist/v1/basics/promiseUtils.d.mts.map +1 -1
- package/dist/v1/basics/promiseUtils.mjs +47 -12
- package/dist/v1/basics/text.cjs +0 -12
- package/dist/v1/basics/text.d.mts +0 -10
- package/dist/v1/basics/text.d.mts.map +1 -1
- package/dist/v1/basics/text.mjs +0 -10
- package/dist/v1/index.cjs +88 -3
- package/dist/v1/index.d.mts +31 -6
- package/dist/v1/index.d.mts.map +1 -1
- package/dist/v1/index.mjs +32 -7
- package/dist/v1/jsdoc/EventEmitter.cjs +4 -0
- package/dist/v1/jsdoc/EventEmitter.d.mts +33 -0
- package/dist/v1/jsdoc/EventEmitter.d.mts.map +1 -0
- package/dist/v1/jsdoc/EventEmitter.mjs +18 -0
- package/dist/v1/libs/array/TinyArrayComparator.cjs +240 -25
- package/dist/v1/libs/array/TinyArrayComparator.d.mts +130 -22
- package/dist/v1/libs/array/TinyArrayComparator.d.mts.map +1 -1
- package/dist/v1/libs/array/TinyArrayComparator.mjs +210 -24
- package/dist/v1/libs/fs/constants.cjs +74 -0
- package/dist/v1/libs/fs/constants.d.mts +22 -0
- package/dist/v1/libs/fs/constants.d.mts.map +1 -0
- package/dist/v1/libs/fs/constants.mjs +53 -0
- package/dist/v1/libs/fs/encoding.cjs +118 -0
- package/dist/v1/libs/fs/encoding.d.mts +50 -0
- package/dist/v1/libs/fs/encoding.d.mts.map +1 -0
- package/dist/v1/libs/fs/encoding.mjs +102 -0
- package/dist/v1/libs/fs/error.cjs +188 -0
- package/dist/v1/libs/fs/error.d.mts +105 -0
- package/dist/v1/libs/fs/error.d.mts.map +1 -0
- package/dist/v1/libs/fs/error.mjs +158 -0
- package/dist/v1/libs/fs/handle.cjs +121 -0
- package/dist/v1/libs/fs/handle.d.mts +56 -0
- package/dist/v1/libs/fs/handle.d.mts.map +1 -0
- package/dist/v1/libs/fs/handle.mjs +100 -0
- package/dist/v1/libs/fs/index.cjs +700 -0
- package/dist/v1/libs/fs/index.d.mts +377 -0
- package/dist/v1/libs/fs/index.d.mts.map +1 -0
- package/dist/v1/libs/fs/index.mjs +630 -0
- package/dist/v1/libs/fs/path.cjs +234 -0
- package/dist/v1/libs/fs/path.d.mts +176 -0
- package/dist/v1/libs/fs/path.d.mts.map +1 -0
- package/dist/v1/libs/fs/path.mjs +197 -0
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.cjs +202 -0
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts +121 -0
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.d.mts.map +1 -0
- package/dist/v1/libs/fs/plugins/OPFS/StorageBucket.mjs +172 -0
- package/dist/v1/libs/fs/plugins/OPFS/index.cjs +334 -0
- package/dist/v1/libs/fs/plugins/OPFS/index.d.mts +98 -0
- package/dist/v1/libs/fs/plugins/OPFS/index.d.mts.map +1 -0
- package/dist/v1/libs/fs/plugins/OPFS/index.mjs +300 -0
- package/dist/v1/libs/fs/stats.cjs +245 -0
- package/dist/v1/libs/fs/stats.d.mts +226 -0
- package/dist/v1/libs/fs/stats.d.mts.map +1 -0
- package/dist/v1/libs/fs/stats.mjs +197 -0
- package/dist/v1/libs/html/events/TinyIframeEvents.cjs +10 -1
- package/dist/v1/libs/html/events/TinyIframeEvents.d.mts +9 -2
- package/dist/v1/libs/html/events/TinyIframeEvents.d.mts.map +1 -1
- package/dist/v1/libs/html/events/TinyIframeEvents.mjs +8 -1
- package/dist/v1/libs/html/events/TinyNewWinEvents.cjs +14 -3
- package/dist/v1/libs/html/events/TinyNewWinEvents.d.mts +21 -5
- package/dist/v1/libs/html/events/TinyNewWinEvents.d.mts.map +1 -1
- package/dist/v1/libs/html/events/TinyNewWinEvents.mjs +12 -3
- package/dist/v1/libs/html/scroll/TinySmartScroller.cjs +13 -1
- package/dist/v1/libs/html/scroll/TinySmartScroller.d.mts +29 -2
- package/dist/v1/libs/html/scroll/TinySmartScroller.d.mts.map +1 -1
- package/dist/v1/libs/html/scroll/TinySmartScroller.mjs +11 -1
- package/dist/v1/libs/math/TinyAdvancedRaffle.cjs +13 -1
- package/dist/v1/libs/math/TinyAdvancedRaffle.d.mts +32 -2
- package/dist/v1/libs/math/TinyAdvancedRaffle.d.mts.map +1 -1
- package/dist/v1/libs/math/TinyAdvancedRaffle.mjs +11 -1
- package/dist/v1/libs/math/TinyTimeout.cjs +0 -14
- package/dist/v1/libs/math/TinyTimeout.d.mts +0 -11
- package/dist/v1/libs/math/TinyTimeout.d.mts.map +1 -1
- package/dist/v1/libs/math/TinyTimeout.mjs +0 -13
- package/dist/v1/libs/media/TinyMediaPlayer/index.cjs +25 -1
- package/dist/v1/libs/media/TinyMediaPlayer/index.d.mts +35 -2
- package/dist/v1/libs/media/TinyMediaPlayer/index.d.mts.map +1 -1
- package/dist/v1/libs/media/TinyMediaPlayer/index.mjs +23 -1
- package/dist/v1/libs/media/TinyMediaPlayer.cjs +2 -2
- package/dist/v1/libs/media/TinyMediaPlayer.d.mts +2 -2
- package/dist/v1/libs/media/TinyMediaPlayer.d.mts.map +1 -1
- package/dist/v1/libs/media/TinyMediaPlayer.mjs +2 -2
- package/dist/v1/libs/media/TinyRadioFm.cjs +19 -1
- package/dist/v1/libs/media/TinyRadioFm.d.mts +78 -2
- package/dist/v1/libs/media/TinyRadioFm.d.mts.map +1 -1
- package/dist/v1/libs/media/TinyRadioFm.mjs +17 -1
- package/dist/v1/libs/plugin/TinyPlugin.cjs +1630 -0
- package/dist/v1/libs/plugin/TinyPlugin.d.mts +753 -0
- package/dist/v1/libs/plugin/TinyPlugin.d.mts.map +1 -0
- package/dist/v1/libs/plugin/TinyPlugin.mjs +1376 -0
- package/dist/v1/libs/plugin/TinyVersion.cjs +204 -0
- package/dist/v1/libs/plugin/TinyVersion.d.mts +70 -0
- package/dist/v1/libs/plugin/TinyVersion.d.mts.map +1 -0
- package/dist/v1/libs/plugin/TinyVersion.mjs +179 -0
- package/dist/v1/libs/router/TinyRouter.cjs +661 -0
- package/dist/v1/libs/router/TinyRouter.d.mts +320 -0
- package/dist/v1/libs/router/TinyRouter.d.mts.map +1 -0
- package/dist/v1/libs/router/TinyRouter.mjs +572 -0
- package/dist/v1/libs/storage/TinyLocalStorage.cjs +20 -1
- package/dist/v1/libs/storage/TinyLocalStorage.d.mts +64 -2
- package/dist/v1/libs/storage/TinyLocalStorage.d.mts.map +1 -1
- package/dist/v1/libs/storage/TinyLocalStorage.mjs +18 -1
- package/dist/v1/libs/storage/TinyMapCache.cjs +373 -0
- package/dist/v1/libs/storage/TinyMapCache.d.mts +274 -0
- package/dist/v1/libs/storage/TinyMapCache.d.mts.map +1 -0
- package/dist/v1/libs/storage/TinyMapCache.mjs +333 -0
- package/dist/v1/libs/storage/TinySetMapDatabase.cjs +1167 -0
- package/dist/v1/libs/storage/TinySetMapDatabase.d.mts +603 -0
- package/dist/v1/libs/storage/TinySetMapDatabase.d.mts.map +1 -0
- package/dist/v1/libs/storage/TinySetMapDatabase.mjs +1059 -0
- package/dist/v1/libs/sw/browser/TinyNotificationAdapter.cjs +126 -0
- package/dist/v1/libs/sw/browser/TinyNotificationAdapter.d.mts +132 -0
- package/dist/v1/libs/sw/browser/TinyNotificationAdapter.d.mts.map +1 -0
- package/dist/v1/libs/sw/browser/TinyNotificationAdapter.mjs +109 -0
- package/dist/v1/libs/sw/browser/TinyPushManager.cjs +310 -0
- package/dist/v1/libs/sw/browser/TinyPushManager.d.mts +168 -0
- package/dist/v1/libs/sw/browser/TinyPushManager.d.mts.map +1 -0
- package/dist/v1/libs/sw/browser/TinyPushManager.mjs +264 -0
- package/dist/v1/libs/sw/browser/TinyServiceWorker.cjs +1060 -0
- package/dist/v1/libs/sw/browser/TinyServiceWorker.d.mts +348 -0
- package/dist/v1/libs/sw/browser/TinyServiceWorker.d.mts.map +1 -0
- package/dist/v1/libs/sw/browser/TinyServiceWorker.mjs +933 -0
- package/dist/v1/libs/sw/browser/plugins/TabManager.cjs +315 -0
- package/dist/v1/libs/sw/browser/plugins/TabManager.d.mts +178 -0
- package/dist/v1/libs/sw/browser/plugins/TabManager.d.mts.map +1 -0
- package/dist/v1/libs/sw/browser/plugins/TabManager.mjs +274 -0
- package/dist/v1/libs/sw/browser/utils.cjs +28 -0
- package/dist/v1/libs/sw/browser/utils.d.mts +8 -0
- package/dist/v1/libs/sw/browser/utils.d.mts.map +1 -0
- package/dist/v1/libs/sw/browser/utils.mjs +21 -0
- package/dist/v1/libs/sw/service/TinyPushRouter.cjs +222 -0
- package/dist/v1/libs/sw/service/TinyPushRouter.d.mts +138 -0
- package/dist/v1/libs/sw/service/TinyPushRouter.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/TinyPushRouter.mjs +201 -0
- package/dist/v1/libs/sw/service/TinyServiceWorkerEngine.cjs +3016 -0
- package/dist/v1/libs/sw/service/TinyServiceWorkerEngine.d.mts +993 -0
- package/dist/v1/libs/sw/service/TinyServiceWorkerEngine.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/TinyServiceWorkerEngine.mjs +2558 -0
- package/dist/v1/libs/sw/service/plugins/GlobBypassPlugin.cjs +88 -0
- package/dist/v1/libs/sw/service/plugins/GlobBypassPlugin.d.mts +32 -0
- package/dist/v1/libs/sw/service/plugins/GlobBypassPlugin.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/plugins/GlobBypassPlugin.mjs +69 -0
- package/dist/v1/libs/sw/service/plugins/GlobCachePlugin.cjs +112 -0
- package/dist/v1/libs/sw/service/plugins/GlobCachePlugin.d.mts +31 -0
- package/dist/v1/libs/sw/service/plugins/GlobCachePlugin.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/plugins/GlobCachePlugin.mjs +91 -0
- package/dist/v1/libs/sw/service/plugins/Ping.cjs +32 -0
- package/dist/v1/libs/sw/service/plugins/Ping.d.mts +9 -0
- package/dist/v1/libs/sw/service/plugins/Ping.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/plugins/Ping.mjs +25 -0
- package/dist/v1/libs/sw/service/plugins/TabManager.cjs +457 -0
- package/dist/v1/libs/sw/service/plugins/TabManager.d.mts +91 -0
- package/dist/v1/libs/sw/service/plugins/TabManager.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/plugins/TabManager.mjs +372 -0
- package/dist/v1/libs/sw/service/plugins/ViteFileDetector.cjs +180 -0
- package/dist/v1/libs/sw/service/plugins/ViteFileDetector.d.mts +28 -0
- package/dist/v1/libs/sw/service/plugins/ViteFileDetector.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/plugins/ViteFileDetector.mjs +148 -0
- package/dist/v1/libs/sw/service/shared/TinyPushPayload.cjs +199 -0
- package/dist/v1/libs/sw/service/shared/TinyPushPayload.d.mts +80 -0
- package/dist/v1/libs/sw/service/shared/TinyPushPayload.d.mts.map +1 -0
- package/dist/v1/libs/sw/service/shared/TinyPushPayload.mjs +174 -0
- package/dist/v1/libs/sw/utils.cjs +57 -0
- package/dist/v1/libs/sw/utils.d.mts +173 -0
- package/dist/v1/libs/sw/utils.d.mts.map +1 -0
- package/dist/v1/libs/sw/utils.mjs +50 -0
- package/dist/v1/libs/text/TinyI18/Browser-NON-DOM.cjs +57 -0
- package/dist/v1/libs/text/TinyI18/Browser-NON-DOM.d.mts +39 -0
- package/dist/v1/libs/text/TinyI18/Browser-NON-DOM.d.mts.map +1 -0
- package/dist/v1/libs/text/TinyI18/Browser-NON-DOM.mjs +48 -0
- package/dist/v1/libs/text/TinyI18/Browser.cjs +64 -0
- package/dist/v1/libs/text/TinyI18/Browser.d.mts +47 -0
- package/dist/v1/libs/text/TinyI18/Browser.d.mts.map +1 -0
- package/dist/v1/libs/text/TinyI18/Browser.mjs +57 -0
- package/dist/v1/libs/text/TinyI18/Node.cjs +49 -0
- package/dist/v1/libs/text/TinyI18/Node.d.mts +38 -0
- package/dist/v1/libs/text/TinyI18/Node.d.mts.map +1 -0
- package/dist/v1/libs/text/TinyI18/Node.mjs +43 -0
- package/dist/v1/libs/text/TinyI18/browser-utils.cjs +17 -0
- package/dist/v1/libs/text/TinyI18/browser-utils.d.mts +8 -0
- package/dist/v1/libs/text/TinyI18/browser-utils.d.mts.map +1 -0
- package/dist/v1/libs/text/TinyI18/browser-utils.mjs +13 -0
- package/dist/v1/libs/text/{TinyI18.cjs → TinyI18/index.cjs} +205 -104
- package/dist/v1/libs/text/{TinyI18.d.mts → TinyI18/index.d.mts} +141 -92
- package/dist/v1/libs/text/TinyI18/index.d.mts.map +1 -0
- package/dist/v1/libs/text/{TinyI18.mjs → TinyI18/index.mjs} +195 -101
- package/dist/v1/libs/tools/TinyBrowserMonitor.cjs +1088 -0
- package/dist/v1/libs/tools/TinyBrowserMonitor.d.mts +621 -0
- package/dist/v1/libs/tools/TinyBrowserMonitor.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyBrowserMonitor.mjs +940 -0
- package/dist/v1/libs/tools/TinyDebugger.cjs +180 -19
- package/dist/v1/libs/tools/TinyDebugger.d.mts +220 -24
- package/dist/v1/libs/tools/TinyDebugger.d.mts.map +1 -1
- package/dist/v1/libs/tools/TinyDebugger.mjs +161 -19
- package/dist/v1/libs/tools/TinyHtmlTagRegexBuilder.cjs +256 -0
- package/dist/v1/libs/tools/TinyHtmlTagRegexBuilder.d.mts +125 -0
- package/dist/v1/libs/tools/TinyHtmlTagRegexBuilder.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHtmlTagRegexBuilder.mjs +223 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser-NON-DOM.cjs +23 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser-NON-DOM.d.mts +17 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser-NON-DOM.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser-NON-DOM.mjs +19 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser.cjs +23 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser.d.mts +17 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Browser.mjs +19 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/EmptyRequestCodes.cjs +390 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/EmptyRequestCodes.d.mts +9 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/EmptyRequestCodes.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/EmptyRequestCodes.mjs +387 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/JsDoc.cjs +2 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/JsDoc.d.mts +27 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/JsDoc.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/JsDoc.mjs +12 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/MozillaRequestCodes/LICENSE +53 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/MozillaRequestCodes/index.cjs +405 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/MozillaRequestCodes/index.d.mts +34 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/MozillaRequestCodes/index.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/MozillaRequestCodes/index.mjs +342 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Node.cjs +23 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Node.d.mts +17 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Node.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/Node.mjs +19 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/RequestCodes.cjs +327 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/RequestCodes.d.mts +9 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/RequestCodes.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/RequestCodes.mjs +324 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/index.cjs +268 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/index.d.mts +107 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/index.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyHttpResponseRegistry/index.mjs +238 -0
- package/dist/v1/libs/tools/TinyPasswordValidator.cjs +491 -0
- package/dist/v1/libs/tools/TinyPasswordValidator.d.mts +246 -0
- package/dist/v1/libs/tools/TinyPasswordValidator.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyPasswordValidator.mjs +434 -0
- package/dist/v1/libs/tools/TinyPkgExportValidator.cjs +48 -0
- package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts +14 -0
- package/dist/v1/libs/tools/TinyPkgExportValidator.d.mts.map +1 -1
- package/dist/v1/libs/tools/TinyPkgExportValidator.mjs +44 -0
- package/dist/v1/libs/tools/TinySiteMap.cjs +1029 -0
- package/dist/v1/libs/tools/TinySiteMap.d.mts +429 -0
- package/dist/v1/libs/tools/TinySiteMap.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinySiteMap.mjs +910 -0
- package/dist/v1/libs/tools/TinySiteMapStream.cjs +121 -0
- package/dist/v1/libs/tools/TinySiteMapStream.d.mts +51 -0
- package/dist/v1/libs/tools/TinySiteMapStream.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinySiteMapStream.mjs +85 -0
- package/dist/v1/libs/tools/TinyURLSecurityVerifier.cjs +444 -0
- package/dist/v1/libs/tools/TinyURLSecurityVerifier.d.mts +217 -0
- package/dist/v1/libs/tools/TinyURLSecurityVerifier.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyURLSecurityVerifier.mjs +394 -0
- package/dist/v1/libs/tools/TinyUriParser/BlueSky.cjs +613 -0
- package/dist/v1/libs/tools/TinyUriParser/BlueSky.d.mts +152 -0
- package/dist/v1/libs/tools/TinyUriParser/BlueSky.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyUriParser/BlueSky.mjs +527 -0
- package/dist/v1/libs/tools/TinyUriParser/Discord.cjs +1119 -0
- package/dist/v1/libs/tools/TinyUriParser/Discord.d.mts +306 -0
- package/dist/v1/libs/tools/TinyUriParser/Discord.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyUriParser/Discord.mjs +968 -0
- package/dist/v1/libs/tools/TinyUriParser/MatrixProtocol.cjs +469 -0
- package/dist/v1/libs/tools/TinyUriParser/MatrixProtocol.d.mts +80 -0
- package/dist/v1/libs/tools/TinyUriParser/MatrixProtocol.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyUriParser/MatrixProtocol.mjs +379 -0
- package/dist/v1/libs/tools/TinyUriParser.cjs +217 -0
- package/dist/v1/libs/tools/TinyUriParser.d.mts +109 -0
- package/dist/v1/libs/tools/TinyUriParser.d.mts.map +1 -0
- package/dist/v1/libs/tools/TinyUriParser.mjs +183 -0
- package/dist/v1/libs/utils/TinyCloner.cjs +466 -0
- package/dist/v1/libs/utils/TinyCloner.d.mts +221 -0
- package/dist/v1/libs/utils/TinyCloner.d.mts.map +1 -0
- package/dist/v1/libs/utils/TinyCloner.mjs +416 -0
- package/dist/v1/libs/utils/TinyPromiseQueue.cjs +1 -1
- package/dist/v1/libs/utils/TinyPromiseQueue.d.mts +2 -2
- package/dist/v1/libs/utils/TinyPromiseQueue.d.mts.map +1 -1
- package/dist/v1/libs/utils/TinyPromiseQueue.mjs +1 -1
- package/dist/v1/libs/utils/TinyThrottledApi.cjs +333 -0
- package/dist/v1/libs/utils/TinyThrottledApi.d.mts +163 -0
- package/dist/v1/libs/utils/TinyThrottledApi.d.mts.map +1 -0
- package/dist/v1/libs/utils/TinyThrottledApi.mjs +294 -0
- package/dist/v1/libs/worker/browser/TinyWebWorker.cjs +342 -0
- package/dist/v1/libs/worker/browser/TinyWebWorker.d.mts +97 -0
- package/dist/v1/libs/worker/browser/TinyWebWorker.d.mts.map +1 -0
- package/dist/v1/libs/worker/browser/TinyWebWorker.mjs +305 -0
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.cjs +346 -0
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts +127 -0
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.d.mts.map +1 -0
- package/dist/v1/libs/worker/engine/TinyWebWorkerEngine.mjs +304 -0
- package/dist/v1/regexp/Email.cjs +185 -0
- package/dist/v1/regexp/Email.d.mts +73 -0
- package/dist/v1/regexp/Email.d.mts.map +1 -0
- package/dist/v1/regexp/Email.mjs +153 -0
- package/dist/v1/regexp/Glob.cjs +293 -0
- package/dist/v1/regexp/Glob.d.mts +45 -0
- package/dist/v1/regexp/Glob.d.mts.map +1 -0
- package/dist/v1/regexp/Glob.mjs +260 -0
- package/dist/v1/regexp/Ipv4.cjs +57 -0
- package/dist/v1/regexp/Ipv4.d.mts +29 -0
- package/dist/v1/regexp/Ipv4.d.mts.map +1 -0
- package/dist/v1/regexp/Ipv4.mjs +45 -0
- package/dist/v1/regexp/Ipv6.cjs +67 -0
- package/dist/v1/regexp/Ipv6.d.mts +29 -0
- package/dist/v1/regexp/Ipv6.d.mts.map +1 -0
- package/dist/v1/regexp/Ipv6.mjs +54 -0
- package/dist/v1/regexp/SegmentExtractor.cjs +145 -0
- package/dist/v1/regexp/SegmentExtractor.d.mts +91 -0
- package/dist/v1/regexp/SegmentExtractor.d.mts.map +1 -0
- package/dist/v1/regexp/SegmentExtractor.mjs +116 -0
- package/dist/v1/regexp/UrlDetector.cjs +125 -0
- package/dist/v1/regexp/UrlDetector.d.mts +67 -0
- package/dist/v1/regexp/UrlDetector.d.mts.map +1 -0
- package/dist/v1/regexp/UrlDetector.mjs +102 -0
- package/dist/v1/regexp/Username/index.cjs +233 -0
- package/dist/v1/regexp/Username/index.d.mts +77 -0
- package/dist/v1/regexp/Username/index.d.mts.map +1 -0
- package/dist/v1/regexp/Username/index.mjs +199 -0
- package/dist/v1/regexp/Username/jsDoc.cjs +2 -0
- package/dist/v1/regexp/Username/jsDoc.d.mts +57 -0
- package/dist/v1/regexp/Username/jsDoc.d.mts.map +1 -0
- package/dist/v1/regexp/Username/jsDoc.mjs +18 -0
- package/dist/v1/regexp/Username/templates/BlueSky.cjs +56 -0
- package/dist/v1/regexp/Username/templates/BlueSky.d.mts +35 -0
- package/dist/v1/regexp/Username/templates/BlueSky.d.mts.map +1 -0
- package/dist/v1/regexp/Username/templates/BlueSky.mjs +52 -0
- package/dist/v1/regexp/Username/templates/Discord.cjs +505 -0
- package/dist/v1/regexp/Username/templates/Discord.d.mts +209 -0
- package/dist/v1/regexp/Username/templates/Discord.d.mts.map +1 -0
- package/dist/v1/regexp/Username/templates/Discord.mjs +458 -0
- package/dist/v1/regexp/Username/templates/MatrixProtocol.cjs +143 -0
- package/dist/v1/regexp/Username/templates/MatrixProtocol.d.mts +70 -0
- package/dist/v1/regexp/Username/templates/MatrixProtocol.d.mts.map +1 -0
- package/dist/v1/regexp/Username/templates/MatrixProtocol.mjs +128 -0
- package/dist/v1/regexp/Username/templates/deprecated/Discord.cjs +24 -0
- package/dist/v1/regexp/Username/templates/deprecated/Discord.d.mts +17 -0
- package/dist/v1/regexp/Username/templates/deprecated/Discord.d.mts.map +1 -0
- package/dist/v1/regexp/Username/templates/deprecated/Discord.mjs +20 -0
- package/dist/v1/regexp/Username/templates/index.cjs +11 -0
- package/dist/v1/regexp/Username/templates/index.d.mts +5 -0
- package/dist/v1/regexp/Username/templates/index.d.mts.map +1 -0
- package/dist/v1/regexp/Username/templates/index.mjs +4 -0
- package/dist/v1/regexp/index.cjs +41 -0
- package/dist/v1/regexp/index.d.mts +9 -0
- package/dist/v1/regexp/index.d.mts.map +1 -0
- package/dist/v1/regexp/index.mjs +8 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Browser.cjs +64 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Browser.d.mts +11 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Browser.d.mts.map +1 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Browser.mjs +51 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/FileInput.cjs +66 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/FileInput.d.mts +11 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/FileInput.d.mts.map +1 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/FileInput.mjs +51 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Node.cjs +39 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Node.d.mts +11 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Node.d.mts.map +1 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Node.mjs +28 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Template.cjs +494 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Template.d.mts +142 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Template.d.mts.map +1 -0
- package/dist/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters/Template.mjs +445 -0
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.cjs +30 -4
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.d.mts +5 -2
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.d.mts.map +1 -1
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.mjs +29 -5
- package/dist/v1/webTemplates/media/MediaValidator/v1/Browser/index.cjs +135 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Browser/index.d.mts +83 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Browser/index.d.mts.map +1 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Browser/index.mjs +120 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Node/index.cjs +127 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Node/index.d.mts +95 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Node/index.d.mts.map +1 -0
- package/dist/v1/webTemplates/media/MediaValidator/v1/Node/index.mjs +109 -0
- package/dist/v1/webTemplates/multiformats/14.0/IPFSHasher/index.cjs +83 -0
- package/dist/v1/webTemplates/multiformats/14.0/IPFSHasher/index.d.mts +46 -0
- package/dist/v1/webTemplates/multiformats/14.0/IPFSHasher/index.d.mts.map +1 -0
- package/dist/v1/webTemplates/multiformats/14.0/IPFSHasher/index.mjs +71 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/TinyVitePwaPlugin.cjs +493 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/TinyVitePwaPlugin.d.mts +67 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/TinyVitePwaPlugin.d.mts.map +1 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/TinyVitePwaPlugin.mjs +399 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/githubUtils.cjs +35 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/githubUtils.d.mts +6 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/githubUtils.d.mts.map +1 -0
- package/dist/v1/webTemplates/vite/7.3/plugins/githubUtils.mjs +29 -0
- package/docs/info/RegExp.md +74 -0
- package/docs/v1/Exports.md +101 -0
- package/docs/v1/README.md +113 -15
- package/docs/v1/basics/asyncReplace.md +2 -0
- package/docs/v1/basics/browserDetector.md +28 -11
- package/docs/v1/basics/crypto.md +85 -0
- package/docs/v1/basics/deprecated/html.md +2 -0
- package/docs/v1/basics/deprecated/objFilter.md +2 -0
- package/docs/v1/basics/html.md +2 -0
- package/docs/v1/basics/objChecker.md +55 -0
- package/docs/v1/basics/objFilter.md +57 -86
- package/docs/v1/basics/promiseUtils.md +12 -3
- package/docs/v1/basics/text.md +0 -1
- package/docs/v1/libs/array/TinyArrayComparator.md +50 -39
- package/docs/v1/libs/fs/TinyFS.md +752 -0
- package/docs/v1/libs/fs/TinyOPFSFileSystem.md +495 -0
- package/docs/v1/libs/fs/TinyStorageBucketFileSystem.md +407 -0
- package/docs/v1/libs/plugin/TinyPlugin.md +441 -0
- package/docs/v1/libs/plugin/TinyVersion.md +145 -0
- package/docs/v1/libs/router/TinyRouter.md +216 -0
- package/docs/v1/libs/storage/TinyMapCache.md +219 -0
- package/docs/v1/libs/storage/TinySetMapDatabase.md +478 -0
- package/docs/v1/libs/sw/browser/TinyNotificationAdapter.md +280 -0
- package/docs/v1/libs/sw/browser/TinyPushManager.md +782 -0
- package/docs/v1/libs/sw/browser/TinyServiceWorker.md +365 -0
- package/docs/v1/libs/sw/browser/plugins/TabManager.md +133 -0
- package/docs/v1/libs/sw/service/TinyPushRouter.md +314 -0
- package/docs/v1/libs/sw/service/TinyServiceWorkerEngine.md +748 -0
- package/docs/v1/libs/sw/service/plugins/GlobBypassPlugin.md +229 -0
- package/docs/v1/libs/sw/service/plugins/GlobCachePlugin.md +91 -0
- package/docs/v1/libs/sw/service/plugins/Ping.md +36 -0
- package/docs/v1/libs/sw/service/plugins/TabManager.md +64 -0
- package/docs/v1/libs/sw/service/plugins/ViteFileDetector.md +92 -0
- package/docs/v1/libs/sw/service/shared/TinyPushPayload.md +316 -0
- package/docs/v1/libs/text/TinyI18.md +80 -56
- package/docs/v1/libs/tools/TinyBrowserMonitor.md +170 -0
- package/docs/v1/libs/tools/TinyDebugger.md +117 -1
- package/docs/v1/libs/tools/TinyHtmlTagRegexBuilder.md +141 -0
- package/docs/v1/libs/tools/TinyHttpResponseRegistry.md +129 -0
- package/docs/v1/libs/tools/TinyPasswordValidator.md +153 -0
- package/docs/v1/libs/tools/TinyPkgExportValidator.md +45 -1
- package/docs/v1/libs/tools/TinySiteMap.md +134 -0
- package/docs/v1/libs/tools/TinySiteMapStream.md +97 -0
- package/docs/v1/libs/tools/TinyURLSecurityVerifier.md +146 -0
- package/docs/v1/libs/tools/TinyUriParser/Discord.md +374 -0
- package/docs/v1/libs/tools/TinyUriParser/MatrixProtocol.md +54 -0
- package/docs/v1/libs/tools/TinyUriParser.md +128 -0
- package/docs/v1/libs/utils/TinyCloner.md +126 -0
- package/docs/v1/libs/utils/TinyPromiseQueue.md +1 -1
- package/docs/v1/libs/utils/TinyThrottledApi.md +219 -0
- package/docs/v1/libs/worker/browser/TinyWebWorker.md +331 -0
- package/docs/v1/libs/worker/engine/TinyWebWorkerEngine.md +137 -0
- package/docs/v1/regexp/Email.md +127 -0
- package/docs/v1/regexp/Glob.md +155 -0
- package/docs/v1/regexp/IPv4.md +103 -0
- package/docs/v1/regexp/IPv6.md +122 -0
- package/docs/v1/regexp/SegmentExtractor.md +120 -0
- package/docs/v1/regexp/UrlDetector.md +117 -0
- package/docs/v1/regexp/Username/Base.md +131 -0
- package/docs/v1/regexp/Username/Discord.md +267 -0
- package/docs/v1/regexp/Username/MatrixProtocol.md +131 -0
- package/docs/v1/webTemplates/ag-psd/31.0/ProcessSolidFilters.md +569 -0
- package/docs/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.md +19 -7
- package/docs/v1/webTemplates/media/MediaValidator/v1/Browser.md +123 -0
- package/docs/v1/webTemplates/media/MediaValidator/v1/Node.md +136 -0
- package/docs/v1/webTemplates/multiformats/14.0/IPFSHasher.md +310 -0
- package/docs/v1/webTemplates/vite/7.3/plugins/TinyVitePwaPlugin.md +131 -0
- package/docs/v1/webTemplates/vite/7.3/plugins/githubUtils.md +50 -0
- package/package.json +303 -13
- package/types/EventEmitter.d.ts +48 -0
- package/dist/v1/libs/text/TinyI18.d.mts.map +0 -1
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { TinyPluginCore, TinyPluginLayer, TinyPlugin } from '../../plugin/TinyPlugin.mjs';
|
|
3
|
+
import TinyNotificationAdapter from './TinyNotificationAdapter.mjs';
|
|
4
|
+
import { createCheckDestroyed } from '../../utils/tools.mjs';
|
|
5
|
+
import { PUSH_TYPE } from '../utils.mjs';
|
|
6
|
+
import { requestNotificationPermission } from './utils.mjs';
|
|
7
|
+
const checkDestroy = createCheckDestroyed('TinyServiceWorker');
|
|
8
|
+
/**
|
|
9
|
+
* The data payload contained within the message.
|
|
10
|
+
* @typedef {any} MessagePayload - A key-value map representing the message content.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Represents the options object passed to an API handler registered via `onApi`.
|
|
14
|
+
* @typedef {Object} ApiHandlerOptions
|
|
15
|
+
* @property {MessagePayload} [data] - The payload received from the browser.
|
|
16
|
+
* @property {string} correlationId - The request ID.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* A function that processes an incoming API request coming from the Service Worker
|
|
20
|
+
* and optionally returns the response payload.
|
|
21
|
+
* @callback ApiHandlerCallback
|
|
22
|
+
* @param {ApiHandlerOptions} options - The options for the API handler, containing the request data and correlation ID.
|
|
23
|
+
* @returns {Promise<MessagePayload|undefined> | (MessagePayload|undefined)} The response payload.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Represents a message exchanged between the page and the Service Worker.
|
|
27
|
+
* @typedef {Object} ServiceWorkerMessagePayload
|
|
28
|
+
* @property {string} type - The identifier for the message type.
|
|
29
|
+
* @property {MessagePayload} [data] - The actual data content of the message.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Represents the non-standard `beforeinstallprompt` event used to trigger
|
|
33
|
+
* the native PWA installation flow.
|
|
34
|
+
* @typedef {Object} BeforeInstallPromptEvent
|
|
35
|
+
* @property {() => void} preventDefault - Prevents the default browser behavior.
|
|
36
|
+
* @property {Promise<{ outcome: 'accepted' | 'dismissed' }>} userChoice - A promise that resolves with the user's choice.
|
|
37
|
+
* @property {() => Promise<void>} prompt - The method to show the native installation prompt.
|
|
38
|
+
* @property {boolean} canShare - Indicates if the event can be shared.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* A function used to install a plugin into the engine.
|
|
42
|
+
* @template {TinyPluginLayer} Layer
|
|
43
|
+
* @template {string} IdString
|
|
44
|
+
* @template {string} VersionString
|
|
45
|
+
* @template {any[]} Options
|
|
46
|
+
* @typedef {import('../../plugin/TinyPlugin.mjs').TinyPluginInstaller<TinyServiceWorker<any, any>, Layer, IdString, VersionString, Options>} SwPluginInstaller
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* Represents a plugin instance designed to be integrated into a TinyServiceWorker.
|
|
50
|
+
* @template {TinyPluginLayer} Layer
|
|
51
|
+
* @template {string} IdString
|
|
52
|
+
* @template {string} VersionString
|
|
53
|
+
* @template {any[]} Options
|
|
54
|
+
* @typedef {TinyPlugin<TinyServiceWorker<any, any>, Layer, IdString, VersionString, Options>} TinyServiceWorkerPlugin
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Sends a message to the service worker controller.
|
|
58
|
+
*
|
|
59
|
+
* @param {*} message - The message to be sent to the service worker.
|
|
60
|
+
* @param {Transferable[]} [transfer] - An optional array of transferable objects to transfer ownership of.
|
|
61
|
+
* @returns {void} This function does not return a value.
|
|
62
|
+
* @throws {Error} If the Service Worker is not available or not controlling the page.
|
|
63
|
+
* @throws {TypeError} If the transfer argument is provided but is not an array.
|
|
64
|
+
*/
|
|
65
|
+
const postMessage = (message, transfer) => {
|
|
66
|
+
if (!('serviceWorker' in navigator) || !navigator.serviceWorker.controller) {
|
|
67
|
+
throw new Error('Service Worker is not available or not controlling the page.');
|
|
68
|
+
}
|
|
69
|
+
return navigator.serviceWorker.controller.postMessage(message, transfer ?? []);
|
|
70
|
+
};
|
|
71
|
+
/** @typedef {import('../utils.mjs').TinyPushMessage} TinyPushMessage */
|
|
72
|
+
/**
|
|
73
|
+
* Represents the object emitted for every push event received from the Service Worker.
|
|
74
|
+
* @typedef {Object} PushEventData
|
|
75
|
+
* @property {TinyPushMessage & { notificationShown: boolean }} data - The push payload merged with the notification state.
|
|
76
|
+
* @property {MessageEvent<any>} event - The original message event.
|
|
77
|
+
* @property {boolean} notificationShown - Whether the Service Worker already displayed a notification.
|
|
78
|
+
* @property {(overrides?: Partial<import('./TinyNotificationAdapter.mjs').BrowserNotificationOptions>) => Notification|null} showNotification - Helper that renders a fallback notification in the page.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* An event emitter that dispatches push events keyed by topic.
|
|
82
|
+
* @typedef {import('../../../jsdoc/EventEmitter.mjs').ICustomEventEmitter<Record<string, (payload: PushEventData) => void>>} PushEventEmitter
|
|
83
|
+
*/
|
|
84
|
+
/**
|
|
85
|
+
* @template {string} IdWorker
|
|
86
|
+
* @template {string | URL} SwUrl
|
|
87
|
+
* Manages Service Worker registration, versioning, and messaging.
|
|
88
|
+
*/
|
|
89
|
+
class TinyServiceWorker extends TinyPluginCore {
|
|
90
|
+
static postMessage = postMessage;
|
|
91
|
+
static #PUSH_TYPE = PUSH_TYPE;
|
|
92
|
+
static get PUSH_TYPE() {
|
|
93
|
+
return TinyServiceWorker.#PUSH_TYPE;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Validates if an event type is a reserved name for the internal lifecycle.
|
|
97
|
+
* @param {string} type - The name of the event to validate.
|
|
98
|
+
* @throws {TypeError} If the event name is not a non-empty string.
|
|
99
|
+
* @throws {TypeError} If the event name starts with the reserved prefix 'sw:'.
|
|
100
|
+
*/
|
|
101
|
+
static #validateEventType(type) {
|
|
102
|
+
if (typeof type !== 'string' || type.trim() === '') {
|
|
103
|
+
throw new TypeError('The event type must be a non-empty string.');
|
|
104
|
+
}
|
|
105
|
+
if (type.startsWith('sw:')) {
|
|
106
|
+
throw new TypeError(`The event type "${type}" is reserved for internal PWA lifecycle management and cannot be used for Service Worker messaging.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validates that a value is a non-empty string usable as an event name.
|
|
111
|
+
* @param {string} type - The value to validate.
|
|
112
|
+
* @throws {TypeError} If the value is not a non-empty string.
|
|
113
|
+
*/
|
|
114
|
+
static #assertEventName(type) {
|
|
115
|
+
if (typeof type !== 'string' || type.trim() === '') {
|
|
116
|
+
throw new TypeError('The event name must be a non-empty string.');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Returns a promise that resolves when the Service Worker is ready.
|
|
121
|
+
* @returns {Promise<ServiceWorkerRegistration>} A promise that resolves to the Service Worker registration.
|
|
122
|
+
*/
|
|
123
|
+
static async waitForReady() {
|
|
124
|
+
return navigator.serviceWorker.ready;
|
|
125
|
+
}
|
|
126
|
+
/** @type {ServiceWorkerRegistration | null} The active Service Worker registration instance. */
|
|
127
|
+
#registration = null;
|
|
128
|
+
/** @type {IdWorker} The unique identifier for this instance. */
|
|
129
|
+
#id;
|
|
130
|
+
/** @type {SwUrl} The URL of the service worker file. */
|
|
131
|
+
#swUrl;
|
|
132
|
+
/** @type {string} The current version of the application. */
|
|
133
|
+
#version;
|
|
134
|
+
/** @type {boolean} Indicates if the service worker is fully initialized and ready. */
|
|
135
|
+
#isReady = false;
|
|
136
|
+
/** @type {boolean} Indicates if the registration process failed. */
|
|
137
|
+
#isFailed = false;
|
|
138
|
+
/** @type {((event: MessageEvent) => void) | null} The internal handler for Service Worker messages. */
|
|
139
|
+
#messageHandler = null;
|
|
140
|
+
/** @type {BeforeInstallPromptEvent | null} The deferred installation prompt event. */
|
|
141
|
+
#deferredPrompt = null;
|
|
142
|
+
/** @type {'twa' | 'standalone' | 'browser'} The current PWA display mode. */
|
|
143
|
+
#displayMode = 'browser';
|
|
144
|
+
/** @type {Map<string, {resolve: (value: any) => void, reject: (reason: Error) => void, timer: NodeJS.Timeout}>} A map of pending API requests, keyed by their correlation ID. */
|
|
145
|
+
#pendingRequests = new Map();
|
|
146
|
+
/** @type {Map<string, ApiHandlerCallback>} A map of registered API handlers, keyed by the message type. */
|
|
147
|
+
#apiHandlers = new Map();
|
|
148
|
+
/** @type {((evt: MediaQueryListEvent) => void) | null} Handler for display mode changes. */
|
|
149
|
+
#displayModeChangeHandler = null;
|
|
150
|
+
/** @type {((e: Event) => void) | null} Handler for the beforeinstallprompt event. */
|
|
151
|
+
#beforeInstallPromptHandler = null;
|
|
152
|
+
/** @type {(() => void) | null} Handler for the appinstalled event. */
|
|
153
|
+
#appInstalledHandler = null;
|
|
154
|
+
/** @type {PushEventEmitter} Emitter used to dispatch push events to subscribers. */
|
|
155
|
+
#pushEvents = new EventEmitter();
|
|
156
|
+
/** @type {boolean} Internal flag to track if the instance has been destroyed. */
|
|
157
|
+
#isDestroyed = false;
|
|
158
|
+
/**
|
|
159
|
+
* A set of event names that must be ignored when received from the Service Worker.
|
|
160
|
+
* Useful to avoid collisions with third-party Service Workers that share the same scope.
|
|
161
|
+
* @type {Set<string>}
|
|
162
|
+
*/
|
|
163
|
+
#eventBlacklist = new Set();
|
|
164
|
+
/** @returns {PushEventEmitter} The emitter to receive push events. */
|
|
165
|
+
get pushEvents() {
|
|
166
|
+
return this.#pushEvents;
|
|
167
|
+
}
|
|
168
|
+
/** @returns {boolean} True if the service worker is ready. */
|
|
169
|
+
get isReady() {
|
|
170
|
+
return this.#isReady;
|
|
171
|
+
}
|
|
172
|
+
/** @returns {boolean} True if the registration process failed. */
|
|
173
|
+
get isFailed() {
|
|
174
|
+
return this.#isFailed;
|
|
175
|
+
}
|
|
176
|
+
/** @returns {boolean} True if the instance has been destroyed. */
|
|
177
|
+
get isDestroyed() {
|
|
178
|
+
return this.#isDestroyed;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Emits a warning event and logs a message when no Service Worker controller is available.
|
|
182
|
+
*/
|
|
183
|
+
#noSwControllerWarn() {
|
|
184
|
+
this.emit('sw:NoSwControllerWarn');
|
|
185
|
+
this.log('warn', 'No active controller to receive message.');
|
|
186
|
+
}
|
|
187
|
+
/** @returns {boolean} True if a service worker is supported and an active controller exists. */
|
|
188
|
+
get isSwAvailable() {
|
|
189
|
+
checkDestroy(this.#isDestroyed);
|
|
190
|
+
return 'serviceWorker' in navigator && !!navigator.serviceWorker.controller;
|
|
191
|
+
}
|
|
192
|
+
/** @returns {IdWorker} The instance ID. */
|
|
193
|
+
get id() {
|
|
194
|
+
checkDestroy(this.#isDestroyed);
|
|
195
|
+
return this.#id;
|
|
196
|
+
}
|
|
197
|
+
/** @returns {SwUrl} The service worker URL. */
|
|
198
|
+
get swUrl() {
|
|
199
|
+
checkDestroy(this.#isDestroyed);
|
|
200
|
+
return this.#swUrl;
|
|
201
|
+
}
|
|
202
|
+
/** @returns {string} The current version. */
|
|
203
|
+
get version() {
|
|
204
|
+
checkDestroy(this.#isDestroyed);
|
|
205
|
+
return this.#version;
|
|
206
|
+
}
|
|
207
|
+
/** @returns {ServiceWorkerRegistration | null} The current registration object. */
|
|
208
|
+
get registration() {
|
|
209
|
+
checkDestroy(this.#isDestroyed);
|
|
210
|
+
return this.#registration;
|
|
211
|
+
}
|
|
212
|
+
/** @type {Set<EventListener>} A set of registered event listeners. */
|
|
213
|
+
#eventListeners = new Set();
|
|
214
|
+
/**
|
|
215
|
+
* Returns a list of all registered event callbacks.
|
|
216
|
+
* @returns {EventListener[]} An array of event listeners.
|
|
217
|
+
*/
|
|
218
|
+
get eventListeners() {
|
|
219
|
+
checkDestroy(this.#isDestroyed);
|
|
220
|
+
return Array.from(this.#eventListeners);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Determines the current PWA display mode.
|
|
224
|
+
* @returns {'twa' | 'standalone' | 'browser'} The current display mode.
|
|
225
|
+
*/
|
|
226
|
+
get displayMode() {
|
|
227
|
+
checkDestroy(this.#isDestroyed);
|
|
228
|
+
return this.#displayMode;
|
|
229
|
+
}
|
|
230
|
+
/** @type {boolean} Indicates if the browser should automatically show a notification when a push is received in browser mode. */
|
|
231
|
+
#autoNotifyPush = true;
|
|
232
|
+
/** @returns {boolean} True if auto-notification is enabled. */
|
|
233
|
+
get autoNotifyPush() {
|
|
234
|
+
checkDestroy(this.#isDestroyed);
|
|
235
|
+
return this.#autoNotifyPush;
|
|
236
|
+
}
|
|
237
|
+
/** @param {boolean} value - Whether to enable auto-notification. */
|
|
238
|
+
set autoNotifyPush(value) {
|
|
239
|
+
checkDestroy(this.#isDestroyed);
|
|
240
|
+
if (typeof value !== 'boolean') {
|
|
241
|
+
throw new TypeError('autoNotifyPush must be a boolean.');
|
|
242
|
+
}
|
|
243
|
+
this.#autoNotifyPush = value;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Returns a copy of the blacklisted event names.
|
|
247
|
+
* @returns {string[]} An array with every blacklisted event name.
|
|
248
|
+
*/
|
|
249
|
+
get eventBlacklist() {
|
|
250
|
+
checkDestroy(this.#isDestroyed);
|
|
251
|
+
return Array.from(this.#eventBlacklist);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Adds an event name to the blacklist. Blacklisted events are ignored when
|
|
255
|
+
* they arrive from the Service Worker.
|
|
256
|
+
* @param {string} type - The event name to ignore.
|
|
257
|
+
* @returns {void}
|
|
258
|
+
* @throws {TypeError} If the type is not a non-empty string.
|
|
259
|
+
*/
|
|
260
|
+
addToEventBlacklist(type) {
|
|
261
|
+
checkDestroy(this.#isDestroyed);
|
|
262
|
+
TinyServiceWorker.#assertEventName(type);
|
|
263
|
+
this.#eventBlacklist.add(type);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Removes an event name from the blacklist.
|
|
267
|
+
* @param {string} type - The event name to remove.
|
|
268
|
+
* @returns {boolean} True if the event was removed, false otherwise.
|
|
269
|
+
* @throws {TypeError} If the type is not a non-empty string.
|
|
270
|
+
*/
|
|
271
|
+
removeFromEventBlacklist(type) {
|
|
272
|
+
checkDestroy(this.#isDestroyed);
|
|
273
|
+
TinyServiceWorker.#assertEventName(type);
|
|
274
|
+
return this.#eventBlacklist.delete(type);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Checks whether an event name is blacklisted.
|
|
278
|
+
* @param {string} type - The event name to check.
|
|
279
|
+
* @returns {boolean} True if the event is blacklisted, false otherwise.
|
|
280
|
+
* @throws {TypeError} If the type is not a non-empty string.
|
|
281
|
+
*/
|
|
282
|
+
hasInEventBlacklist(type) {
|
|
283
|
+
checkDestroy(this.#isDestroyed);
|
|
284
|
+
TinyServiceWorker.#assertEventName(type);
|
|
285
|
+
return this.#eventBlacklist.has(type);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Removes every event name from the blacklist.
|
|
289
|
+
* @returns {void}
|
|
290
|
+
*/
|
|
291
|
+
clearEventBlacklist() {
|
|
292
|
+
checkDestroy(this.#isDestroyed);
|
|
293
|
+
this.#eventBlacklist.clear();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* @param {Object} options - Configuration options for the instance.
|
|
297
|
+
* @param {IdWorker} options.id - The unique identifier for this manager instance.
|
|
298
|
+
* @param {SwUrl} options.swUrl - The path to the service worker file.
|
|
299
|
+
* @param {string} options.version - The current application version.
|
|
300
|
+
* @param {boolean} [options.debugMode=false] - Whether to enable internal debug logging.
|
|
301
|
+
* @param {boolean} [options.useLogColors=false] - Whether to enable log color support.
|
|
302
|
+
* @param {Partial<Console>} [options.logger=console] - A custom logger object (must implement console methods).
|
|
303
|
+
* @throws {TypeError} If parameters are not the correct types or if id is empty.
|
|
304
|
+
*/
|
|
305
|
+
constructor(options) {
|
|
306
|
+
if (Array.isArray(options) || typeof options !== 'object' || options === null) {
|
|
307
|
+
throw new TypeError('The "options" argument must be a non-null object.');
|
|
308
|
+
}
|
|
309
|
+
const { id, swUrl, version, logger, debugMode, useLogColors } = options;
|
|
310
|
+
if (typeof id !== 'string' || id.trim() === '') {
|
|
311
|
+
throw new TypeError('The "id" parameter must be a non-empty string.');
|
|
312
|
+
}
|
|
313
|
+
if (typeof swUrl !== 'string' && !(swUrl instanceof URL)) {
|
|
314
|
+
throw new TypeError('The "swUrl" parameter must be a string or URL.');
|
|
315
|
+
}
|
|
316
|
+
if (typeof version !== 'string') {
|
|
317
|
+
throw new TypeError('The "version" parameter must be a string.');
|
|
318
|
+
}
|
|
319
|
+
super({
|
|
320
|
+
logCfg: {
|
|
321
|
+
id: '[_sub_class_TinyServiceWorker_reset_]',
|
|
322
|
+
logger: logger ?? console,
|
|
323
|
+
debugMode: debugMode ?? false,
|
|
324
|
+
useLogColors: useLogColors ?? false,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
this.#id = id;
|
|
328
|
+
this.#swUrl = swUrl;
|
|
329
|
+
this.#version = version;
|
|
330
|
+
this.#updateDisplayMode();
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Updates the internal displayMode state and emits an event.
|
|
334
|
+
*/
|
|
335
|
+
#updateDisplayMode() {
|
|
336
|
+
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
|
|
337
|
+
const isTwa = document.referrer.startsWith('android-app://');
|
|
338
|
+
if (isTwa) {
|
|
339
|
+
this.#displayMode = 'twa';
|
|
340
|
+
// @ts-ignore
|
|
341
|
+
}
|
|
342
|
+
else if (navigator.standalone || isStandalone) {
|
|
343
|
+
this.#displayMode = 'standalone';
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
this.#displayMode = 'browser';
|
|
347
|
+
}
|
|
348
|
+
this.emit('sw:DisplayModeChanged', { displayMode: this.#displayMode });
|
|
349
|
+
this.log('info', `DISPLAY_MODE_CHANGED: ${this.#displayMode}`);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Sets up listeners for PWA lifecycle events.
|
|
353
|
+
*/
|
|
354
|
+
#setupPwaListeners() {
|
|
355
|
+
// 1. Handle Display Mode Changes
|
|
356
|
+
this.#displayModeChangeHandler = () => this.#updateDisplayMode();
|
|
357
|
+
window
|
|
358
|
+
.matchMedia('(display-mode: standalone)')
|
|
359
|
+
.addEventListener('change', this.#displayModeChangeHandler);
|
|
360
|
+
// 2. Handle Before Install Prompt
|
|
361
|
+
this.#beforeInstallPromptHandler = (e) => {
|
|
362
|
+
// @ts-ignore
|
|
363
|
+
this.#deferredPrompt = e;
|
|
364
|
+
this.emit('sw:BeforeInstallPrompt', { event: e });
|
|
365
|
+
this.log('info', 'beforeinstallprompt event fired.');
|
|
366
|
+
};
|
|
367
|
+
window.addEventListener('beforeinstallprompt', this.#beforeInstallPromptHandler);
|
|
368
|
+
// 3. Handle App Installed
|
|
369
|
+
this.#appInstalledHandler = () => {
|
|
370
|
+
this.#deferredPrompt = null;
|
|
371
|
+
this.emit('sw:AppInstalled');
|
|
372
|
+
this.log('info', 'PWA was installed');
|
|
373
|
+
};
|
|
374
|
+
window.addEventListener('appinstalled', this.#appInstalledHandler);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Triggers the native PWA installation prompt.
|
|
378
|
+
* @returns {Promise<void>} A promise that resolves when the prompt has been shown.
|
|
379
|
+
* @throws {Error} If the prompt cannot be shown because the `beforeinstallprompt` event has not fired.
|
|
380
|
+
*/
|
|
381
|
+
async promptInstallation() {
|
|
382
|
+
checkDestroy(this.#isDestroyed);
|
|
383
|
+
if (!this.#deferredPrompt) {
|
|
384
|
+
throw new Error('Cannot show installation prompt: beforeinstallprompt event has not fired.');
|
|
385
|
+
}
|
|
386
|
+
this.#deferredPrompt.prompt();
|
|
387
|
+
const { outcome } = await this.#deferredPrompt.userChoice;
|
|
388
|
+
this.log('info', `User installation choice: ${outcome}`);
|
|
389
|
+
this.#deferredPrompt = null;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Returns a promise that resolves when the Service Worker is fully registered,
|
|
393
|
+
* initialized, and has taken control of the current page.
|
|
394
|
+
* @returns {Promise<void>} A promise that resolves when the Service Worker is ready for use.
|
|
395
|
+
* @throws {Error} If the Service Worker registration fails.
|
|
396
|
+
*/
|
|
397
|
+
async waitForReady() {
|
|
398
|
+
checkDestroy(this.#isDestroyed);
|
|
399
|
+
// 1. If registration already failed, reject immediately.
|
|
400
|
+
if (this.#isFailed) {
|
|
401
|
+
throw new Error('Service Worker registration failed. Cannot wait for ready state.');
|
|
402
|
+
}
|
|
403
|
+
// 2. If already ready and controlling the page, resolve immediately.
|
|
404
|
+
if (this.#isReady && navigator.serviceWorker.controller) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
return new Promise((resolve, reject) => {
|
|
408
|
+
/**
|
|
409
|
+
* Handler for when the Service Worker takes control of the page.
|
|
410
|
+
*/
|
|
411
|
+
const onControllerChange = () => {
|
|
412
|
+
// Once the controller changes, check if the class is ready and the worker is controlling.
|
|
413
|
+
if (this.#isReady && navigator.serviceWorker.controller) {
|
|
414
|
+
cleanup();
|
|
415
|
+
resolve();
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Handler for when a registration error occurs during the wait.
|
|
420
|
+
* @param {Error} error - The error thrown during registration.
|
|
421
|
+
*/
|
|
422
|
+
const onRegistrationFailed = (error) => {
|
|
423
|
+
cleanup();
|
|
424
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
425
|
+
};
|
|
426
|
+
/**
|
|
427
|
+
* Cleans up event listeners to prevent memory leaks.
|
|
428
|
+
*/
|
|
429
|
+
const cleanup = () => {
|
|
430
|
+
navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
|
|
431
|
+
this.off('sw:RegistrationFailed', onRegistrationFailed);
|
|
432
|
+
};
|
|
433
|
+
// Listen for the 'controllerchange' event to know when the SW is in control.
|
|
434
|
+
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
|
435
|
+
this.on('sw:RegistrationFailed', onRegistrationFailed);
|
|
436
|
+
// 3. Safety check: if it became ready between the initial check and adding the listener.
|
|
437
|
+
if (this.#isReady && navigator.serviceWorker.controller) {
|
|
438
|
+
cleanup();
|
|
439
|
+
resolve();
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Registers a new sync tag to be handled by the Service Worker.
|
|
445
|
+
* This will trigger the 'sync' event in the Service Worker when connectivity is available.
|
|
446
|
+
*
|
|
447
|
+
* @param {string} tag - A unique identifier for the sync task.
|
|
448
|
+
* @returns {Promise<void>} A promise that resolves when the sync registration is successful.
|
|
449
|
+
* @throws {Error} If Background Sync is not supported by the browser.
|
|
450
|
+
* @throws {TypeError} If the tag is not a non-empty string.
|
|
451
|
+
*/
|
|
452
|
+
async registerSync(tag) {
|
|
453
|
+
checkDestroy(this.#isDestroyed);
|
|
454
|
+
if (typeof tag !== 'string' || tag.trim() === '') {
|
|
455
|
+
throw new TypeError('[TinyServiceWorker] registerSync: tag must be a non-empty string.');
|
|
456
|
+
}
|
|
457
|
+
// 1. Wait for the service worker to be active/ready
|
|
458
|
+
const registration = await TinyServiceWorker.waitForReady();
|
|
459
|
+
if (!registration.sync) {
|
|
460
|
+
throw new Error('[TinyServiceWorker] registerSync: Background Sync API is not supported in this browser.');
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
// 2. Register the sync tag
|
|
464
|
+
await registration.sync.register(tag);
|
|
465
|
+
this.log('info', `Sync tag registered: ${tag}`);
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
this.log('error', `Failed to register sync tag "${tag}":`, error);
|
|
469
|
+
throw error;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Requests permission from the user to display notifications.
|
|
474
|
+
*
|
|
475
|
+
* @returns {Promise<'granted' | 'denied' | 'default'>} The resulting permission status.
|
|
476
|
+
* @throws {Error} If the Notification API is not supported by the browser.
|
|
477
|
+
*/
|
|
478
|
+
async requestNotificationPermission() {
|
|
479
|
+
checkDestroy(this.#isDestroyed);
|
|
480
|
+
try {
|
|
481
|
+
const permission = await requestNotificationPermission();
|
|
482
|
+
// If permission is already 'granted', return immediately.
|
|
483
|
+
if (permission === 'granted') {
|
|
484
|
+
this.emit('sw:NotificationPermissionChanged', { permission: 'granted' });
|
|
485
|
+
return permission;
|
|
486
|
+
}
|
|
487
|
+
// If 'denied', the user has blocked it and we cannot prompt again via code.
|
|
488
|
+
else if (permission === 'denied') {
|
|
489
|
+
this.log('warn', 'Notification permission was denied by the user.');
|
|
490
|
+
this.emit('sw:NotificationPermissionChanged', { permission: 'denied' });
|
|
491
|
+
return permission;
|
|
492
|
+
}
|
|
493
|
+
// Emit an event so the UI can react (e.g., show/hide notification settings).
|
|
494
|
+
this.log('info', `Notification permission status: ${permission}`);
|
|
495
|
+
this.emit('sw:NotificationPermissionChanged', { permission });
|
|
496
|
+
return permission;
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
this.log('error', 'Error requesting notification permission:', error);
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Handles a push message forwarded by the Service Worker.
|
|
505
|
+
*
|
|
506
|
+
* The method never shows a notification on its own. It always emits the
|
|
507
|
+
* `push` event with a `showNotification` helper, so the consumer decides
|
|
508
|
+
* whether the page must display a fallback notification or not.
|
|
509
|
+
*
|
|
510
|
+
* @param {TinyPushMessage & { notificationShown?: boolean }} pushData - The payload sent by the Service Worker.
|
|
511
|
+
* @param {MessageEvent} event - The original message event.
|
|
512
|
+
* @returns {void}
|
|
513
|
+
*/
|
|
514
|
+
#handlePushReceived(pushData, event) {
|
|
515
|
+
const notificationShown = pushData.notificationShown === true;
|
|
516
|
+
/** @type {import('../utils.mjs').TinyPushNotification|null} */
|
|
517
|
+
const swNotification = pushData.notification ?? null;
|
|
518
|
+
/**
|
|
519
|
+
* Renders the notification in the page and wires the round trip back to the SW.
|
|
520
|
+
* @param {Partial<import('./TinyNotificationAdapter.mjs').BrowserNotificationOptions>} [overrides]
|
|
521
|
+
* @returns {Notification|null}
|
|
522
|
+
*/
|
|
523
|
+
const showNotification = (overrides = {}) => {
|
|
524
|
+
if (!('Notification' in window)) {
|
|
525
|
+
this.log('warn', 'Push received, but the Notification API is not supported.');
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
// SW -> Browser
|
|
529
|
+
const browserNotification = TinyNotificationAdapter.toBrowser(swNotification, overrides);
|
|
530
|
+
const instance = new Notification(browserNotification.title, browserNotification.options);
|
|
531
|
+
// Browser -> SW
|
|
532
|
+
/** @param {string} type */
|
|
533
|
+
const sendBack = (type) => {
|
|
534
|
+
const payload = TinyNotificationAdapter.toServiceWorker(browserNotification);
|
|
535
|
+
this.#emitMessage(type, payload, false);
|
|
536
|
+
};
|
|
537
|
+
instance.onclick = () => sendBack('sw:NotificationClicked');
|
|
538
|
+
instance.onclose = () => sendBack('sw:NotificationClosed');
|
|
539
|
+
return instance;
|
|
540
|
+
};
|
|
541
|
+
// The page only shows a fallback when the Service Worker did not.
|
|
542
|
+
if (!notificationShown && this.displayMode === 'browser' && this.#autoNotifyPush) {
|
|
543
|
+
showNotification();
|
|
544
|
+
}
|
|
545
|
+
this.#pushEvents.emit(pushData.topic ?? 'push', {
|
|
546
|
+
data: { ...pushData, notificationShown },
|
|
547
|
+
event,
|
|
548
|
+
notificationShown,
|
|
549
|
+
showNotification,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Registers the service worker and handles version updates.
|
|
554
|
+
* @param {RegistrationOptions} [options] - Standard Service Worker registration options.
|
|
555
|
+
* @returns {Promise<void>} A promise that resolves when registration is attempted.
|
|
556
|
+
* @throws {Error} If the registration process encounters an error.
|
|
557
|
+
*/
|
|
558
|
+
async register(options) {
|
|
559
|
+
checkDestroy(this.#isDestroyed);
|
|
560
|
+
if (typeof options !== 'undefined' &&
|
|
561
|
+
(Array.isArray(options) || typeof options !== 'object' || options === null)) {
|
|
562
|
+
throw new TypeError('The "options" argument must be a non-null object.');
|
|
563
|
+
}
|
|
564
|
+
if (!('serviceWorker' in navigator)) {
|
|
565
|
+
this.log('warn', 'Service Worker is not supported in this browser.');
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
const idVersion = `${this.#id}_sw_version`;
|
|
570
|
+
const savedVersion = localStorage.getItem(idVersion);
|
|
571
|
+
// Check if a version mismatch exists
|
|
572
|
+
if (savedVersion !== this.#version) {
|
|
573
|
+
this.log('warn', `Version mismatch detected: ${savedVersion} -> ${this.#version}. Signaling update...`);
|
|
574
|
+
// 1. Notify the UI that a new version is available (to show the update button)
|
|
575
|
+
this.emit('sw:VersionUpdateAvailable');
|
|
576
|
+
// 2. Signal the current Service Worker to begin downloading the new version
|
|
577
|
+
this.#emitMessage('sw:PrepareUpdate');
|
|
578
|
+
// Update localStorage so subsequent manual refreshes recognize the new version
|
|
579
|
+
localStorage.setItem(idVersion, this.#version);
|
|
580
|
+
}
|
|
581
|
+
const swUrl = new URL(this.#swUrl, location.origin);
|
|
582
|
+
swUrl.searchParams.set('v', this.#version);
|
|
583
|
+
if (this.debugMode) {
|
|
584
|
+
// Using searchParams.set is more robust than manual string concatenation.
|
|
585
|
+
// It automatically handles whether to use '?' or '&' as a separator.
|
|
586
|
+
swUrl.searchParams.set('t', Date.now().toString());
|
|
587
|
+
}
|
|
588
|
+
this.#registration = await navigator.serviceWorker.register(swUrl.toString(), options);
|
|
589
|
+
// Listen for when the new Service Worker takes control
|
|
590
|
+
navigator.serviceWorker.addEventListener('controllerchange', (event) => {
|
|
591
|
+
this.emit('sw:NewVersionReady', { event });
|
|
592
|
+
this.log('info', 'New Service Worker is now in control.');
|
|
593
|
+
});
|
|
594
|
+
// Existing message handler and lifecycle logic
|
|
595
|
+
this.#messageHandler = (event) => {
|
|
596
|
+
/** @type {ServiceWorkerMessagePayload & { correlationId?: string; error?: string; isApi?: boolean; }} */
|
|
597
|
+
const payload = event.data;
|
|
598
|
+
if (Array.isArray(payload) || typeof payload !== 'object' || payload === null)
|
|
599
|
+
return;
|
|
600
|
+
if (typeof payload.type !== 'string')
|
|
601
|
+
return;
|
|
602
|
+
if (this.#eventBlacklist.has(payload.type)) {
|
|
603
|
+
this.log('warn', `Ignored blacklisted event: ${payload.type}`);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
if (typeof payload.data !== 'undefined' &&
|
|
607
|
+
(Array.isArray(payload.data) || typeof payload.data !== 'object' || payload.data === null))
|
|
608
|
+
return;
|
|
609
|
+
// 1. Push events forwarded by the Service Worker
|
|
610
|
+
if (payload.type === 'sw:PushReceived') {
|
|
611
|
+
this.#handlePushReceived(payload.data, event);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
// 2. Logic to respond to API calls (Response from SW -> Browser)
|
|
615
|
+
if (payload.type === 'sw:ApiResponse') {
|
|
616
|
+
if (typeof payload.correlationId !== 'string') {
|
|
617
|
+
this.log('error', 'Received message with missing or invalid "correlationId" string.');
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
const pending = this.#pendingRequests.get(payload.correlationId);
|
|
621
|
+
if (pending) {
|
|
622
|
+
clearTimeout(pending.timer);
|
|
623
|
+
this.#pendingRequests.delete(payload.correlationId);
|
|
624
|
+
if (payload.error) {
|
|
625
|
+
pending.reject(new Error(payload.error));
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
pending.resolve(payload.data);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
// 3. Logic to RECEIVE API requests (Request from SW -> Browser)
|
|
634
|
+
if (payload.isApi === true) {
|
|
635
|
+
if (typeof payload.correlationId !== 'string') {
|
|
636
|
+
this.log('error', 'Received message with missing or invalid "correlationId" string.');
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
const handler = this.#apiHandlers.get(payload.type);
|
|
640
|
+
if (!handler) {
|
|
641
|
+
postMessage({
|
|
642
|
+
correlationId: payload.correlationId,
|
|
643
|
+
type: 'sw:ApiResponse',
|
|
644
|
+
error: `No API handler registered for type: ${payload.type}`,
|
|
645
|
+
});
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
try {
|
|
649
|
+
/** @param {MessagePayload} [r] */
|
|
650
|
+
const sendResult = (r) => postMessage({
|
|
651
|
+
correlationId: payload.correlationId,
|
|
652
|
+
type: 'sw:ApiResponse',
|
|
653
|
+
data: r,
|
|
654
|
+
});
|
|
655
|
+
const result = handler({ data: payload.data, correlationId: payload.correlationId });
|
|
656
|
+
if (result instanceof Promise)
|
|
657
|
+
result.then(sendResult);
|
|
658
|
+
else
|
|
659
|
+
sendResult(result);
|
|
660
|
+
}
|
|
661
|
+
catch (error) {
|
|
662
|
+
postMessage({
|
|
663
|
+
correlationId: payload.correlationId,
|
|
664
|
+
type: 'sw:ApiResponse',
|
|
665
|
+
error: error instanceof Error ? error.message : String(error),
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
this.emit(payload.type, { data: payload.data, event });
|
|
671
|
+
};
|
|
672
|
+
navigator.serviceWorker.addEventListener('message', this.#messageHandler);
|
|
673
|
+
// Initialize PWA listeners
|
|
674
|
+
this.#setupPwaListeners();
|
|
675
|
+
this.#isReady = true;
|
|
676
|
+
this.log('info', 'Registered successfully.');
|
|
677
|
+
}
|
|
678
|
+
catch (error) {
|
|
679
|
+
this.#isFailed = true;
|
|
680
|
+
this.log('error', 'Registration error:', error);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Unregisters the Service Worker from the browser and destroys the manager instance.
|
|
685
|
+
* @returns {Promise<boolean>} A promise that resolves to `true` if the Service Worker
|
|
686
|
+
* was successfully unregistered, or `false` otherwise.
|
|
687
|
+
* @throws {Error} If an unexpected error occurs during the unregistration process.
|
|
688
|
+
*/
|
|
689
|
+
async unregister() {
|
|
690
|
+
checkDestroy(this.#isDestroyed);
|
|
691
|
+
if (!this.#registration) {
|
|
692
|
+
this.log('warn', 'No active ServiceWorkerRegistration found to unregister.');
|
|
693
|
+
this.destroy();
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
try {
|
|
697
|
+
const success = await this.#registration.unregister();
|
|
698
|
+
if (success) {
|
|
699
|
+
this.log('info', 'Service Worker unregistered successfully.');
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
this.log('warn', 'The unregister() method returned false (the worker might already be inactive).');
|
|
703
|
+
}
|
|
704
|
+
// Call destroy to clean up event listeners and references
|
|
705
|
+
this.destroy();
|
|
706
|
+
return success;
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
this.log('error', 'Error during Service Worker unregistration:', error);
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Sends a message to the active Service Worker controller.
|
|
715
|
+
* @param {string} type - The identifier for the message type.
|
|
716
|
+
* @param {MessagePayload} [data] - The actual data content of the message.
|
|
717
|
+
* @param {boolean} [strictMode=false] - Enable strict mode validator.
|
|
718
|
+
* @returns {boolean} True if the message was sent, false otherwise.
|
|
719
|
+
* @throws {TypeError} If the type is not a string or data is not a non-null object.
|
|
720
|
+
*/
|
|
721
|
+
#emitMessage(type, data, strictMode = false) {
|
|
722
|
+
checkDestroy(this.#isDestroyed);
|
|
723
|
+
if (typeof type !== 'string') {
|
|
724
|
+
throw new TypeError('Payload.type must be a string.');
|
|
725
|
+
}
|
|
726
|
+
if (typeof strictMode !== 'boolean') {
|
|
727
|
+
throw new TypeError('The "strictMode" argument must be a boolean.');
|
|
728
|
+
}
|
|
729
|
+
if (typeof data !== 'undefined' &&
|
|
730
|
+
(Array.isArray(data) || typeof data !== 'object' || data === null)) {
|
|
731
|
+
throw new TypeError('Payload.data must be a non-null object.');
|
|
732
|
+
}
|
|
733
|
+
// Security check: prevent sending messages that collide with internal events
|
|
734
|
+
if (strictMode)
|
|
735
|
+
TinyServiceWorker.#validateEventType(type);
|
|
736
|
+
if ('serviceWorker' in navigator && !!navigator.serviceWorker.controller) {
|
|
737
|
+
navigator.serviceWorker.controller.postMessage({ type, data });
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
this.#noSwControllerWarn();
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Removes a registered API handler.
|
|
747
|
+
* @param {string} type - The call identifier.
|
|
748
|
+
* @returns {boolean} True if a handler was removed, false otherwise.
|
|
749
|
+
* @throws {TypeError} If the type is not a non-empty string.
|
|
750
|
+
*/
|
|
751
|
+
offApi(type) {
|
|
752
|
+
if (typeof type !== 'string' || type.trim() === '') {
|
|
753
|
+
throw new TypeError('The "type" argument must be a non-empty string.');
|
|
754
|
+
}
|
|
755
|
+
return this.#apiHandlers.delete(type);
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Registers a handler for API calls coming from the Service Worker.
|
|
759
|
+
* @param {string} type - The call identifier.
|
|
760
|
+
* @param {ApiHandlerCallback} callback - Function that processes the request and returns the result.
|
|
761
|
+
* @returns {void}
|
|
762
|
+
* @throws {TypeError} If the type is not a non-empty string.
|
|
763
|
+
* @throws {TypeError} If the callback is not a function.
|
|
764
|
+
*/
|
|
765
|
+
onApi(type, callback) {
|
|
766
|
+
if (typeof type !== 'string' || type.trim() === '') {
|
|
767
|
+
throw new TypeError('The "type" argument must be a non-empty string.');
|
|
768
|
+
}
|
|
769
|
+
if (typeof callback !== 'function') {
|
|
770
|
+
throw new TypeError('Callback must be a function.');
|
|
771
|
+
}
|
|
772
|
+
this.#apiHandlers.set(type, callback);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Sends a message to the active Service Worker and waits for a response via Promise.
|
|
776
|
+
* @param {string} type - The API call identifier.
|
|
777
|
+
* @param {MessagePayload} [data] - The request payload.
|
|
778
|
+
* @param {number} [timeout=10000] - Maximum waiting time in milliseconds.
|
|
779
|
+
* @returns {Promise<any>} A promise that resolves with the result object or undefined from the Service Worker.
|
|
780
|
+
* @throws {TypeError} If the type is not a non-empty string.
|
|
781
|
+
* @throws {TypeError} If the data is not a non-null object.
|
|
782
|
+
* @throws {TypeError} If the timeout is not a number.
|
|
783
|
+
* @throws {RangeError} If the timeout is not a finite number greater than or equal to 0.
|
|
784
|
+
* @throws {Error} If the timeout is reached or if the Service Worker is unavailable.
|
|
785
|
+
*/
|
|
786
|
+
async emitApi(type, data, timeout = 10000) {
|
|
787
|
+
checkDestroy(this.#isDestroyed);
|
|
788
|
+
if (typeof type !== 'string' || type.trim() === '') {
|
|
789
|
+
throw new TypeError('The "type" argument must be a non-empty string.');
|
|
790
|
+
}
|
|
791
|
+
if (typeof data !== 'undefined' &&
|
|
792
|
+
(Array.isArray(data) || typeof data !== 'object' || data === null)) {
|
|
793
|
+
throw new TypeError('The "data" argument must be a non-null object.');
|
|
794
|
+
}
|
|
795
|
+
if (typeof timeout !== 'number') {
|
|
796
|
+
throw new TypeError('The "timeout" argument must be a number.');
|
|
797
|
+
}
|
|
798
|
+
if (!Number.isFinite(timeout) || timeout < 0) {
|
|
799
|
+
throw new RangeError('The "timeout" argument must be a finite number greater than or equal to 0.');
|
|
800
|
+
}
|
|
801
|
+
const correlationId = crypto.randomUUID();
|
|
802
|
+
return new Promise((resolve, reject) => {
|
|
803
|
+
const timer = setTimeout(() => {
|
|
804
|
+
if (this.#pendingRequests.has(correlationId)) {
|
|
805
|
+
this.#pendingRequests.delete(correlationId);
|
|
806
|
+
reject(new Error(`API request timeout: ${type} (ID: ${correlationId})`));
|
|
807
|
+
}
|
|
808
|
+
}, timeout);
|
|
809
|
+
this.#pendingRequests.set(correlationId, { resolve, reject, timer });
|
|
810
|
+
postMessage({
|
|
811
|
+
type,
|
|
812
|
+
data,
|
|
813
|
+
correlationId,
|
|
814
|
+
isApi: true,
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Sends a message to the active Service Worker controller.
|
|
820
|
+
* @param {string} type - The identifier for the message type.
|
|
821
|
+
* @param {MessagePayload} [data] - The actual data content of the message.
|
|
822
|
+
* @returns {boolean} True if the message was sent, false otherwise.
|
|
823
|
+
* @throws {TypeError} If the type is not a string or data is not a non-null object.
|
|
824
|
+
*/
|
|
825
|
+
emitMessage(type, data) {
|
|
826
|
+
return this.#emitMessage(type, data, true);
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Sends a message to the active Service Worker controller using a full payload.
|
|
830
|
+
* @param {ServiceWorkerMessagePayload} payload - The message payload.
|
|
831
|
+
* @throws {TypeError} If the payload is not an object, type is not a string, or data is not a non-null object.
|
|
832
|
+
*/
|
|
833
|
+
postMessage(payload) {
|
|
834
|
+
checkDestroy(this.#isDestroyed);
|
|
835
|
+
if (Array.isArray(payload) || typeof payload !== 'object' || payload === null) {
|
|
836
|
+
throw new TypeError('Payload must be an object.');
|
|
837
|
+
}
|
|
838
|
+
if (typeof payload.type !== 'string') {
|
|
839
|
+
throw new TypeError('Payload.type must be a string.');
|
|
840
|
+
}
|
|
841
|
+
if (typeof payload.data !== 'undefined' &&
|
|
842
|
+
(Array.isArray(payload.data) || typeof payload.data !== 'object' || payload.data === null)) {
|
|
843
|
+
throw new TypeError('Payload.data must be a non-null object.');
|
|
844
|
+
}
|
|
845
|
+
// Security check: prevent sending messages that collide with internal events
|
|
846
|
+
TinyServiceWorker.#validateEventType(payload.type);
|
|
847
|
+
if ('serviceWorker' in navigator && !!navigator.serviceWorker.controller) {
|
|
848
|
+
navigator.serviceWorker.controller.postMessage(payload);
|
|
849
|
+
}
|
|
850
|
+
else
|
|
851
|
+
this.#noSwControllerWarn();
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Adds an event listener for messages from the Service Worker.
|
|
855
|
+
* @param {EventListener} callback - The callback function to be invoked.
|
|
856
|
+
* @throws {TypeError} If the callback is not a function.
|
|
857
|
+
*/
|
|
858
|
+
addEventListener(callback) {
|
|
859
|
+
checkDestroy(this.#isDestroyed);
|
|
860
|
+
if (typeof callback !== 'function') {
|
|
861
|
+
throw new TypeError('The callback must be a function.');
|
|
862
|
+
}
|
|
863
|
+
if ('serviceWorker' in navigator) {
|
|
864
|
+
navigator.serviceWorker.addEventListener('message', callback);
|
|
865
|
+
this.#eventListeners.add(callback);
|
|
866
|
+
}
|
|
867
|
+
else
|
|
868
|
+
this.#noSwControllerWarn();
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Removes an event listener from the Service Worker.
|
|
872
|
+
* @param {EventListener} callback - The callback to be removed.
|
|
873
|
+
* @returns {boolean} True if the event listener was removed, false otherwise.
|
|
874
|
+
* @throws {TypeError} If the callback is not a function.
|
|
875
|
+
*/
|
|
876
|
+
removeEventListener(callback) {
|
|
877
|
+
checkDestroy(this.#isDestroyed);
|
|
878
|
+
if (typeof callback !== 'function') {
|
|
879
|
+
throw new TypeError('The callback must be a function.');
|
|
880
|
+
}
|
|
881
|
+
if ('serviceWorker' in navigator) {
|
|
882
|
+
navigator.serviceWorker.removeEventListener('message', callback);
|
|
883
|
+
return this.#eventListeners.delete(callback);
|
|
884
|
+
}
|
|
885
|
+
else
|
|
886
|
+
this.#noSwControllerWarn();
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Cleans up all event listeners and references to prevent memory leaks.
|
|
891
|
+
* @returns {void}
|
|
892
|
+
*/
|
|
893
|
+
destroy() {
|
|
894
|
+
if (this.#isDestroyed)
|
|
895
|
+
return;
|
|
896
|
+
// 1. Remove native Service Worker listener
|
|
897
|
+
if ('serviceWorker' in navigator) {
|
|
898
|
+
if (this.#messageHandler) {
|
|
899
|
+
navigator.serviceWorker.removeEventListener('message', this.#messageHandler);
|
|
900
|
+
this.#messageHandler = null;
|
|
901
|
+
}
|
|
902
|
+
for (const callback of this.#eventListeners) {
|
|
903
|
+
navigator.serviceWorker.removeEventListener('message', callback);
|
|
904
|
+
}
|
|
905
|
+
this.#eventListeners.clear();
|
|
906
|
+
}
|
|
907
|
+
// 2. Remove Window Listeners (Crucial for preventing memory leaks)
|
|
908
|
+
if (this.#displayModeChangeHandler) {
|
|
909
|
+
window
|
|
910
|
+
.matchMedia('(display-mode: standalone)')
|
|
911
|
+
.removeEventListener('change', this.#displayModeChangeHandler);
|
|
912
|
+
this.#displayModeChangeHandler = null;
|
|
913
|
+
}
|
|
914
|
+
if (this.#beforeInstallPromptHandler) {
|
|
915
|
+
window.removeEventListener('beforeinstallprompt', this.#beforeInstallPromptHandler);
|
|
916
|
+
this.#beforeInstallPromptHandler = null;
|
|
917
|
+
}
|
|
918
|
+
if (this.#appInstalledHandler) {
|
|
919
|
+
window.removeEventListener('appinstalled', this.#appInstalledHandler);
|
|
920
|
+
this.#appInstalledHandler = null;
|
|
921
|
+
}
|
|
922
|
+
// 3. Remove EventEmitter listeners
|
|
923
|
+
this.removeAllListeners();
|
|
924
|
+
this.#pushEvents.removeAllListeners();
|
|
925
|
+
// 4. Clear references
|
|
926
|
+
this.#registration = null;
|
|
927
|
+
this.#deferredPrompt = null;
|
|
928
|
+
this.destroyPlugins();
|
|
929
|
+
this.#isDestroyed = true;
|
|
930
|
+
this.log('info', `[${this.#id}] Destroyed successfully.`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
export default TinyServiceWorker;
|