tiny-essentials 1.28.18 → 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/css/TinyCookieConsent.min.css +1 -0
- package/dist/v1/css/TinyCookieConsent.min.css.map +1 -0
- package/dist/v1/css/TinyDraggerExample.min.css +1 -0
- package/dist/v1/css/TinyDraggerExample.min.css.map +1 -0
- package/dist/v1/css/TinyLoadingScreen.min.css +1 -0
- package/dist/v1/css/TinyLoadingScreen.min.css.map +1 -0
- package/dist/v1/css/TinyNotify.min.css +1 -0
- package/dist/v1/css/TinyNotify.min.css.map +1 -0
- package/dist/v1/css/aiMarker.min.css +1 -0
- package/dist/v1/css/aiMarker.min.css.map +1 -0
- 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 +725 -0
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.d.mts +293 -0
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.d.mts.map +1 -0
- package/dist/v1/webTemplates/bootstrap/5.3/html/BootstrapDialogs.mjs +614 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/input/Placeholder-Colors-Fix.scss +64 -0
- package/dist/v1/webTemplates/bootstrap/5.3/scss/spacing/Spacing-Plus.scss +31 -0
- 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 +111 -0
- package/docs/v1/README.md +117 -13
- 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 +174 -0
- 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 +313 -18
- package/types/EventEmitter.d.ts +48 -0
- package/dist/v1/libs/text/TinyI18.d.mts.map +0 -1
|
@@ -0,0 +1,1167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var TinyPromiseQueue = require('../utils/TinyPromiseQueue.cjs');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A key that can be stored in an IndexedDB object store.
|
|
7
|
+
* @typedef {IDBValidKey | IDBKeyRange} TableValue
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validates that a value is a non-empty string.
|
|
12
|
+
* @param {unknown} value - The value to validate.
|
|
13
|
+
* @param {string} [name='value'] - Parameter name used in the error message.
|
|
14
|
+
* @returns {void}
|
|
15
|
+
* @throws {TypeError} If the value is not a non-empty string.
|
|
16
|
+
*/
|
|
17
|
+
function assertNonEmptyString(value, name = 'value') {
|
|
18
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
19
|
+
throw new TypeError(`The "${name}" argument must be a non-empty string.`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A single persisted record.
|
|
25
|
+
* @template {TableValue} Key
|
|
26
|
+
* @template {*} Value
|
|
27
|
+
* @typedef {Object} TinyTableDbRecord
|
|
28
|
+
* @property {Key} key - The record key.
|
|
29
|
+
* @property {Value} value - The record value.
|
|
30
|
+
* @property {number} timestamp - Unix timestamp (in milliseconds).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Shared IndexedDB plumbing for {@link TinySetDb} and {@link TinyMapDb}.
|
|
35
|
+
*
|
|
36
|
+
* This class owns the connection, the write queue, the action queue and the
|
|
37
|
+
* hydration lifecycle. Subclasses only implement the in-memory cache and the
|
|
38
|
+
* public API, which keeps both collections perfectly consistent.
|
|
39
|
+
*/
|
|
40
|
+
class TinyTableDb {
|
|
41
|
+
/** @type {string} */
|
|
42
|
+
#storeName;
|
|
43
|
+
|
|
44
|
+
/** @type {() => Promise<IDBDatabase>} */
|
|
45
|
+
#openDatabase;
|
|
46
|
+
|
|
47
|
+
/** @type {TinyPromiseQueue} */
|
|
48
|
+
#writeQueue = new TinyPromiseQueue();
|
|
49
|
+
|
|
50
|
+
/** @type {TinyPromiseQueue} */
|
|
51
|
+
#actionQueue = new TinyPromiseQueue();
|
|
52
|
+
|
|
53
|
+
/** @type {Promise<void>|null} */
|
|
54
|
+
#ready = null;
|
|
55
|
+
|
|
56
|
+
/** @type {boolean} */
|
|
57
|
+
#hydrated = false;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {{ storeName: string, openDatabase: () => Promise<IDBDatabase> }} options - Table options.
|
|
61
|
+
* @throws {TypeError} If `storeName` is not a non-empty string.
|
|
62
|
+
* @throws {TypeError} If `openDatabase` is not a function.
|
|
63
|
+
*/
|
|
64
|
+
constructor({ storeName, openDatabase }) {
|
|
65
|
+
assertNonEmptyString(storeName, 'storeName');
|
|
66
|
+
if (typeof openDatabase !== 'function') {
|
|
67
|
+
throw new TypeError('The "openDatabase" option must be a function.');
|
|
68
|
+
}
|
|
69
|
+
this.#storeName = storeName;
|
|
70
|
+
this.#openDatabase = openDatabase;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Name of the object store that backs this table.
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
get storeName() {
|
|
78
|
+
return this.#storeName;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Whether the in-memory cache was already hydrated from IndexedDB.
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
get isReady() {
|
|
86
|
+
return this.#hydrated;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Tag used by `Object.prototype.toString`.
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
93
|
+
get [Symbol.toStringTag]() {
|
|
94
|
+
return this.constructor.name;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Loads every persisted value into the in-memory cache.
|
|
99
|
+
* Safe to call multiple times, the work happens only once.
|
|
100
|
+
* @returns {Promise<void>}
|
|
101
|
+
*/
|
|
102
|
+
hydrate() {
|
|
103
|
+
this.#ready ??= this.#hydrate();
|
|
104
|
+
return this.#ready;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Runs an operation with exclusive access to the in-memory cache.
|
|
109
|
+
* @param {() => Promise<void>} operation - The operation to run.
|
|
110
|
+
* @returns {Promise<void>}
|
|
111
|
+
*/
|
|
112
|
+
_action(operation) {
|
|
113
|
+
return this.#actionQueue.enqueue(operation);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Runs a write operation with exclusive access to the object store.
|
|
118
|
+
* @param {() => Promise<void>} operation - The operation to run.
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
_write(operation) {
|
|
122
|
+
return this.#writeQueue.enqueue(operation);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Persists a single record.
|
|
127
|
+
* @param {TinyTableDbRecord<TableValue, unknown>|TinySetDbRecord<TableValue>} record - The record to persist.
|
|
128
|
+
* @returns {Promise<void>}
|
|
129
|
+
*/
|
|
130
|
+
async _persist(record) {
|
|
131
|
+
const database = await this.#openDatabase();
|
|
132
|
+
await new Promise((resolve, reject) => {
|
|
133
|
+
const transaction = database.transaction(this.#storeName, 'readwrite');
|
|
134
|
+
const request = transaction.objectStore(this.#storeName).put(record);
|
|
135
|
+
request.onsuccess = () => resolve(undefined);
|
|
136
|
+
request.onerror = () => reject(request.error);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Deletes a single record by its key.
|
|
142
|
+
* @param {TableValue} key - The key to delete.
|
|
143
|
+
* @returns {Promise<void>}
|
|
144
|
+
*/
|
|
145
|
+
async _remove(key) {
|
|
146
|
+
const database = await this.#openDatabase();
|
|
147
|
+
await new Promise((resolve, reject) => {
|
|
148
|
+
const transaction = database.transaction(this.#storeName, 'readwrite');
|
|
149
|
+
const request = transaction.objectStore(this.#storeName).delete(key);
|
|
150
|
+
request.onsuccess = () => resolve(undefined);
|
|
151
|
+
request.onerror = () => reject(request.error);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Deletes every record from the object store.
|
|
157
|
+
* @returns {Promise<void>}
|
|
158
|
+
*/
|
|
159
|
+
async _clear() {
|
|
160
|
+
const database = await this.#openDatabase();
|
|
161
|
+
await new Promise((resolve, reject) => {
|
|
162
|
+
const transaction = database.transaction(this.#storeName, 'readwrite');
|
|
163
|
+
const request = transaction.objectStore(this.#storeName).clear();
|
|
164
|
+
request.onsuccess = () => resolve(undefined);
|
|
165
|
+
request.onerror = () => reject(request.error);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Reads every stored value into the cache.
|
|
171
|
+
* @returns {Promise<void>}
|
|
172
|
+
*/
|
|
173
|
+
async #hydrate() {
|
|
174
|
+
const database = await this.#openDatabase();
|
|
175
|
+
const records = await TinyTableDb.#readAll(database, this.#storeName);
|
|
176
|
+
this._load(records);
|
|
177
|
+
this.#hydrated = true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Populates the in-memory cache from the persisted records.
|
|
182
|
+
* @abstract
|
|
183
|
+
* @param {(TinyTableDbRecord<TableValue, unknown>|TinySetDbRecord<TableValue>)[]} records - The persisted records.
|
|
184
|
+
* @returns {void}
|
|
185
|
+
*/
|
|
186
|
+
_load(records) {
|
|
187
|
+
throw new Error(`The "_load" method of "${this.constructor.name}" is not implemented.`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Reads every persisted record from the object store.
|
|
192
|
+
* @param {IDBDatabase} database - The open database connection.
|
|
193
|
+
* @param {string} storeName - Name of the object store to read.
|
|
194
|
+
* @returns {Promise<TinyTableDbRecord<TableValue, unknown>[]>} Every record currently stored.
|
|
195
|
+
*/
|
|
196
|
+
static #readAll(database, storeName) {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
const transaction = database.transaction(storeName, 'readonly');
|
|
199
|
+
const request = transaction.objectStore(storeName).getAll();
|
|
200
|
+
request.onsuccess = () => resolve(request.result);
|
|
201
|
+
request.onerror = () => reject(request.error);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* A single persisted value entry.
|
|
208
|
+
* @template {TableValue} Value
|
|
209
|
+
* @typedef {Object} TinySetDbRecord
|
|
210
|
+
* @property {Value} value - The unique value that was stored.
|
|
211
|
+
* @property {number} timestamp - Unix timestamp (in milliseconds) of the first detection.
|
|
212
|
+
*/
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Predicate used to reject values that must never reach a table.
|
|
216
|
+
* @template {TableValue} Value
|
|
217
|
+
* @callback TinySetDbValidator
|
|
218
|
+
* @param {Value} value - The candidate value.
|
|
219
|
+
* @returns {boolean} A truthy value when the value is accepted.
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @template {TableValue} Value
|
|
224
|
+
* @typedef {Object} TinySetDbOptions
|
|
225
|
+
* @property {string} storeName - Name of the object store (table) that backs this set.
|
|
226
|
+
* @property {() => Promise<IDBDatabase>} openDatabase - Resolves with the shared connection.
|
|
227
|
+
* @property {TinySetDbValidator<Value>|null} [validate] - Optional predicate applied to every value.
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* A source of values accepted by the set operations.
|
|
232
|
+
* @template {TableValue} Value
|
|
233
|
+
* @typedef {TinySetDb<Value>|Set<Value>|Value[]} TinySetDbSource
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Callback invoked by {@link TinySetDb#forEach}.
|
|
238
|
+
* @template {TableValue} Value
|
|
239
|
+
* @callback TinySetDbForEachCallback
|
|
240
|
+
* @param {Value} value - The current value.
|
|
241
|
+
* @param {Value} sameValue - The same value, mirroring `Set.prototype.forEach`.
|
|
242
|
+
* @param {TinySetDb<Value>} set - The set being iterated.
|
|
243
|
+
* @returns {void}
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @template {TableValue} Value
|
|
248
|
+
* @extends {TinyTableDb}
|
|
249
|
+
* Persistent set of values backed by a single IndexedDB object store.
|
|
250
|
+
*/
|
|
251
|
+
class TinySetDb extends TinyTableDb {
|
|
252
|
+
/** @type {TinySetDbValidator<Value>|null} */
|
|
253
|
+
#validate;
|
|
254
|
+
|
|
255
|
+
/** @type {Set<Value>} */
|
|
256
|
+
#cache = new Set();
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @param {TinySetDbOptions<Value>} options - Table options.
|
|
260
|
+
* @throws {TypeError} If `validate` is neither a function nor `null`.
|
|
261
|
+
*/
|
|
262
|
+
constructor({ storeName, openDatabase, validate = null }) {
|
|
263
|
+
super({ storeName, openDatabase });
|
|
264
|
+
if (validate !== null && typeof validate !== 'function') {
|
|
265
|
+
throw new TypeError('The "validate" option must be a function or null.');
|
|
266
|
+
}
|
|
267
|
+
this.#validate = validate;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Number of values currently held in the in-memory cache.
|
|
272
|
+
* @returns {number}
|
|
273
|
+
*/
|
|
274
|
+
get size() {
|
|
275
|
+
return this.#cache.size;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Checks whether the given value is already stored.
|
|
280
|
+
* @param {Value} value - The value to look up.
|
|
281
|
+
* @returns {Promise<boolean>} `true` when the value was already logged.
|
|
282
|
+
* @throws {TypeError} If `value` is not a non-empty string.
|
|
283
|
+
* @throws {TypeError} If the table validator rejects the value.
|
|
284
|
+
*/
|
|
285
|
+
async has(value) {
|
|
286
|
+
assertNonEmptyString(value);
|
|
287
|
+
this.#assertAccepted(value);
|
|
288
|
+
await this.hydrate();
|
|
289
|
+
let found = false;
|
|
290
|
+
await this._action(async () => {
|
|
291
|
+
found = this.#cache.has(value);
|
|
292
|
+
});
|
|
293
|
+
return found;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Adds a value to the cache and persists it.
|
|
298
|
+
* @param {Value} value - The value to store.
|
|
299
|
+
* @returns {Promise<boolean>} `true` when the value was added, `false` when it already existed.
|
|
300
|
+
* @throws {TypeError} If `value` is not a non-empty string.
|
|
301
|
+
* @throws {TypeError} If the table validator rejects the value.
|
|
302
|
+
*/
|
|
303
|
+
async add(value) {
|
|
304
|
+
assertNonEmptyString(value);
|
|
305
|
+
this.#assertAccepted(value);
|
|
306
|
+
await this.hydrate();
|
|
307
|
+
let added = false;
|
|
308
|
+
await this._action(async () => {
|
|
309
|
+
if (this.#cache.has(value)) return;
|
|
310
|
+
await this._write(() => this._persist({ value, timestamp: Date.now() }));
|
|
311
|
+
this.#cache.add(value);
|
|
312
|
+
added = true;
|
|
313
|
+
});
|
|
314
|
+
return added;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Removes a value from the cache and from the object store.
|
|
319
|
+
* @param {Value} value - The value to remove.
|
|
320
|
+
* @returns {Promise<boolean>} `true` when the value existed and was removed.
|
|
321
|
+
* @throws {TypeError} If `value` is not a non-empty string.
|
|
322
|
+
* @throws {TypeError} If the table validator rejects the value.
|
|
323
|
+
*/
|
|
324
|
+
async delete(value) {
|
|
325
|
+
assertNonEmptyString(value);
|
|
326
|
+
this.#assertAccepted(value);
|
|
327
|
+
await this.hydrate();
|
|
328
|
+
let deleted = false;
|
|
329
|
+
await this._action(async () => {
|
|
330
|
+
if (!this.#cache.has(value)) return;
|
|
331
|
+
await this._write(() => this._remove(value));
|
|
332
|
+
this.#cache.delete(value);
|
|
333
|
+
deleted = true;
|
|
334
|
+
});
|
|
335
|
+
return deleted;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Removes every value from both the cache and the object store.
|
|
340
|
+
* @returns {Promise<void>}
|
|
341
|
+
*/
|
|
342
|
+
async clear() {
|
|
343
|
+
await this.hydrate();
|
|
344
|
+
await this._action(async () => {
|
|
345
|
+
await this._write(() => this._clear());
|
|
346
|
+
this.#cache.clear();
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Returns a snapshot of every stored value.
|
|
352
|
+
* @returns {Promise<Value[]>} A new array with the current values.
|
|
353
|
+
*/
|
|
354
|
+
async toArray() {
|
|
355
|
+
await this.hydrate();
|
|
356
|
+
return [...this.#cache];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Serialization hook used by `JSON.stringify`.
|
|
361
|
+
* @returns {Promise<Value[]>} A new array with the current values.
|
|
362
|
+
*/
|
|
363
|
+
async toJSON() {
|
|
364
|
+
return this.toArray();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Iterates over the stored values.
|
|
369
|
+
* @returns {AsyncGenerator<Value, void, void>} An async generator of values.
|
|
370
|
+
*/
|
|
371
|
+
async *values() {
|
|
372
|
+
await this.hydrate();
|
|
373
|
+
for (const value of [...this.#cache]) {
|
|
374
|
+
yield value;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Alias of {@link TinySetDb#values}, mirroring `Set.prototype.keys`.
|
|
380
|
+
* @returns {AsyncGenerator<Value, void, void>} An async generator of values.
|
|
381
|
+
*/
|
|
382
|
+
keys() {
|
|
383
|
+
return this.values();
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Iterates over `[value, value]` pairs, mirroring `Set.prototype.entries`.
|
|
388
|
+
* @returns {AsyncGenerator<[Value, Value], void, void>} An async generator of pairs.
|
|
389
|
+
*/
|
|
390
|
+
async *entries() {
|
|
391
|
+
for await (const value of this.values()) {
|
|
392
|
+
yield [value, value];
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Iterates over the stored values.
|
|
398
|
+
* @returns {AsyncGenerator<Value, void, void>} An async generator of values.
|
|
399
|
+
*/
|
|
400
|
+
[Symbol.asyncIterator]() {
|
|
401
|
+
return this.values();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Runs a callback for every stored value.
|
|
406
|
+
* @param {TinySetDbForEachCallback<Value>} callbackFn - Callback invoked per value.
|
|
407
|
+
* @param {unknown} [thisArg] - Value used as `this` inside the callback.
|
|
408
|
+
* @returns {Promise<void>}
|
|
409
|
+
* @throws {TypeError} If `callbackFn` is not a function.
|
|
410
|
+
*/
|
|
411
|
+
async forEach(callbackFn, thisArg) {
|
|
412
|
+
if (typeof callbackFn !== 'function') {
|
|
413
|
+
throw new TypeError('The "callbackFn" argument must be a function.');
|
|
414
|
+
}
|
|
415
|
+
await this.hydrate();
|
|
416
|
+
for (const value of [...this.#cache]) {
|
|
417
|
+
callbackFn.call(thisArg, value, value, this);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Returns a new native `Set` with the values of this set and every source.
|
|
423
|
+
* @param {...TinySetDbSource<Value>} sources - Extra value sources.
|
|
424
|
+
* @returns {Promise<Set<Value>>} A new native `Set`.
|
|
425
|
+
*/
|
|
426
|
+
async union(...sources) {
|
|
427
|
+
await this.hydrate();
|
|
428
|
+
const result = new Set(this.#cache);
|
|
429
|
+
for (const source of sources) {
|
|
430
|
+
const values = await TinySetDb.#resolveValues(source);
|
|
431
|
+
for (const value of values) result.add(value);
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Returns a new native `Set` with the values present in both sets.
|
|
438
|
+
* @param {TinySetDbSource<Value>} other - The other value source.
|
|
439
|
+
* @returns {Promise<Set<Value>>} A new native `Set`.
|
|
440
|
+
*/
|
|
441
|
+
async intersection(other) {
|
|
442
|
+
const values = await TinySetDb.#resolveValues(other);
|
|
443
|
+
await this.hydrate();
|
|
444
|
+
const result = new Set();
|
|
445
|
+
for (const value of this.#cache) {
|
|
446
|
+
if (values.has(value)) result.add(value);
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Returns a new native `Set` with the values of this set that are not in `other`.
|
|
453
|
+
* @param {TinySetDbSource<Value>} other - The other value source.
|
|
454
|
+
* @returns {Promise<Set<Value>>} A new native `Set`.
|
|
455
|
+
*/
|
|
456
|
+
async difference(other) {
|
|
457
|
+
const values = await TinySetDb.#resolveValues(other);
|
|
458
|
+
await this.hydrate();
|
|
459
|
+
const result = new Set();
|
|
460
|
+
for (const value of this.#cache) {
|
|
461
|
+
if (!values.has(value)) result.add(value);
|
|
462
|
+
}
|
|
463
|
+
return result;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Returns a new native `Set` with the values present in only one of the sets.
|
|
468
|
+
* @param {TinySetDbSource<Value>} other - The other value source.
|
|
469
|
+
* @returns {Promise<Set<Value>>} A new native `Set`.
|
|
470
|
+
*/
|
|
471
|
+
async symmetricDifference(other) {
|
|
472
|
+
const values = await TinySetDb.#resolveValues(other);
|
|
473
|
+
await this.hydrate();
|
|
474
|
+
const result = new Set();
|
|
475
|
+
for (const value of this.#cache) {
|
|
476
|
+
if (!values.has(value)) result.add(value);
|
|
477
|
+
}
|
|
478
|
+
for (const value of values) {
|
|
479
|
+
if (!this.#cache.has(value)) result.add(value);
|
|
480
|
+
}
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Checks whether every value of this set exists in `other`.
|
|
486
|
+
* @param {TinySetDbSource<Value>} other - The other value source.
|
|
487
|
+
* @returns {Promise<boolean>} `true` when this set is a subset of `other`.
|
|
488
|
+
*/
|
|
489
|
+
async isSubsetOf(other) {
|
|
490
|
+
const values = await TinySetDb.#resolveValues(other);
|
|
491
|
+
await this.hydrate();
|
|
492
|
+
for (const value of this.#cache) {
|
|
493
|
+
if (!values.has(value)) return false;
|
|
494
|
+
}
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Checks whether every value of `other` exists in this set.
|
|
500
|
+
* @param {TinySetDbSource<Value>} other - The other value source.
|
|
501
|
+
* @returns {Promise<boolean>} `true` when this set is a superset of `other`.
|
|
502
|
+
*/
|
|
503
|
+
async isSupersetOf(other) {
|
|
504
|
+
const values = await TinySetDb.#resolveValues(other);
|
|
505
|
+
await this.hydrate();
|
|
506
|
+
for (const value of values) {
|
|
507
|
+
if (!this.#cache.has(value)) return false;
|
|
508
|
+
}
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Checks whether this set and `other` share no values.
|
|
514
|
+
* @param {TinySetDbSource<Value>} other - The other value source.
|
|
515
|
+
* @returns {Promise<boolean>} `true` when the sets are disjoint.
|
|
516
|
+
*/
|
|
517
|
+
async isDisjointFrom(other) {
|
|
518
|
+
const values = await TinySetDb.#resolveValues(other);
|
|
519
|
+
await this.hydrate();
|
|
520
|
+
for (const value of this.#cache) {
|
|
521
|
+
if (values.has(value)) return false;
|
|
522
|
+
}
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Populates the in-memory cache from the persisted records.
|
|
528
|
+
* @param {TinySetDbRecord<Value>[]} records - The persisted records.
|
|
529
|
+
* @returns {void}
|
|
530
|
+
*/
|
|
531
|
+
_load(records) {
|
|
532
|
+
for (const record of records.sort((a, b) => a.timestamp - b.timestamp)) {
|
|
533
|
+
this.#cache.add(record.value);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Normalizes a value source into a native `Set`.
|
|
539
|
+
* @template {TableValue} Value
|
|
540
|
+
* @param {TinySetDbSource<Value>} source - The value source to normalize.
|
|
541
|
+
* @returns {Promise<Set<Value>>} A native `Set` with the same values.
|
|
542
|
+
* @throws {TypeError} If `source` is not a `TinySetDb`, a `Set` or an array.
|
|
543
|
+
*/
|
|
544
|
+
static async #resolveValues(source) {
|
|
545
|
+
if (source instanceof TinySetDb) {
|
|
546
|
+
return new Set(await source.toArray());
|
|
547
|
+
}
|
|
548
|
+
if (source instanceof Set) {
|
|
549
|
+
return new Set(source);
|
|
550
|
+
}
|
|
551
|
+
if (Array.isArray(source)) {
|
|
552
|
+
return new Set(source);
|
|
553
|
+
}
|
|
554
|
+
throw new TypeError('The "other" argument must be a TinySetDb, a Set, or an Array.');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Runs the optional table validator against a candidate value.
|
|
559
|
+
* @param {Value} value - The candidate value.
|
|
560
|
+
* @returns {void}
|
|
561
|
+
* @throws {TypeError} If the validator rejects the value.
|
|
562
|
+
*/
|
|
563
|
+
#assertAccepted(value) {
|
|
564
|
+
if (this.#validate === null) return;
|
|
565
|
+
if (!this.#validate(value)) {
|
|
566
|
+
throw new TypeError(`The value is rejected by the "${this.storeName}" table validator.`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* A single persisted entry.
|
|
573
|
+
* @template {TableValue} Key
|
|
574
|
+
* @template {*} Value
|
|
575
|
+
* @typedef {Object} TinyMapDbRecord
|
|
576
|
+
* @property {Key} key - The unique key that was stored.
|
|
577
|
+
* @property {Value} value - The value bound to the key.
|
|
578
|
+
* @property {number} timestamp - Unix timestamp (in milliseconds) of the last write.
|
|
579
|
+
*/
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Predicate used to reject entries that must never reach a table.
|
|
583
|
+
* @template {TableValue} Key
|
|
584
|
+
* @template {*} Value
|
|
585
|
+
* @callback TinyMapDbValidator
|
|
586
|
+
* @param {Key} key - The candidate key.
|
|
587
|
+
* @param {Value} value - The candidate value.
|
|
588
|
+
* @returns {boolean} A truthy value when the entry is accepted.
|
|
589
|
+
*/
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* @template {TableValue} Key
|
|
593
|
+
* @template {*} Value
|
|
594
|
+
* @typedef {Object} TinyMapDbOptions
|
|
595
|
+
* @property {string} storeName - Name of the object store (table) that backs this map.
|
|
596
|
+
* @property {() => Promise<IDBDatabase>} openDatabase - Resolves with the shared connection.
|
|
597
|
+
* @property {TinyMapDbValidator<Key, Value>|null} [validate] - Optional predicate applied to every entry.
|
|
598
|
+
*/
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Callback invoked by {@link TinyMapDb#forEach}.
|
|
602
|
+
* @template {TableValue} Key
|
|
603
|
+
* @template {*} Value
|
|
604
|
+
* @callback TinyMapDbForEachCallback
|
|
605
|
+
* @param {Value} value - The current value.
|
|
606
|
+
* @param {Key} key - The current key.
|
|
607
|
+
* @param {TinyMapDb<Key, Value>} map - The map being iterated.
|
|
608
|
+
* @returns {void}
|
|
609
|
+
*/
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* @template {TableValue} Key
|
|
613
|
+
* @template {*} Value
|
|
614
|
+
* @extends {TinyTableDb}
|
|
615
|
+
* Persistent key/value map backed by a single IndexedDB object store.
|
|
616
|
+
*
|
|
617
|
+
* The class keeps a synchronous in-memory cache so it can be queried from
|
|
618
|
+
* synchronous code while every write is mirrored to IndexedDB, so the data
|
|
619
|
+
* survives a service worker restart.
|
|
620
|
+
*/
|
|
621
|
+
class TinyMapDb extends TinyTableDb {
|
|
622
|
+
/** @type {TinyMapDbValidator<Key, Value>|null} */
|
|
623
|
+
#validate;
|
|
624
|
+
|
|
625
|
+
/** @type {Map<Key, Value>} */
|
|
626
|
+
#cache = new Map();
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* @param {TinyMapDbOptions<Key, Value>} options - Table options.
|
|
630
|
+
* @throws {TypeError} If `validate` is neither a function nor `null`.
|
|
631
|
+
*/
|
|
632
|
+
constructor({ storeName, openDatabase, validate = null }) {
|
|
633
|
+
super({ storeName, openDatabase });
|
|
634
|
+
if (validate !== null && typeof validate !== 'function') {
|
|
635
|
+
throw new TypeError('The "validate" option must be a function or null.');
|
|
636
|
+
}
|
|
637
|
+
this.#validate = validate;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Number of entries currently held in the in-memory cache.
|
|
642
|
+
* @returns {number}
|
|
643
|
+
*/
|
|
644
|
+
get size() {
|
|
645
|
+
return this.#cache.size;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Checks whether the given key is already stored.
|
|
650
|
+
* @param {Key} key - The key to look up.
|
|
651
|
+
* @returns {Promise<boolean>} `true` when the key exists.
|
|
652
|
+
* @throws {TypeError} If `key` is not a non-empty string.
|
|
653
|
+
*/
|
|
654
|
+
async has(key) {
|
|
655
|
+
assertNonEmptyString(key, 'key');
|
|
656
|
+
await this.hydrate();
|
|
657
|
+
let found = false;
|
|
658
|
+
await this._action(async () => {
|
|
659
|
+
found = this.#cache.has(key);
|
|
660
|
+
});
|
|
661
|
+
return found;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Reads the value bound to the given key.
|
|
666
|
+
* @param {Key} key - The key to look up.
|
|
667
|
+
* @returns {Promise<Value|undefined>} The stored value, or `undefined` when absent.
|
|
668
|
+
* @throws {TypeError} If `key` is not a non-empty string.
|
|
669
|
+
*/
|
|
670
|
+
async get(key) {
|
|
671
|
+
assertNonEmptyString(key, 'key');
|
|
672
|
+
await this.hydrate();
|
|
673
|
+
let value;
|
|
674
|
+
await this._action(async () => {
|
|
675
|
+
value = this.#cache.get(key);
|
|
676
|
+
});
|
|
677
|
+
return value;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Binds a value to a key, in the cache and in the object store.
|
|
682
|
+
* @param {Key} key - The key to write.
|
|
683
|
+
* @param {Value} value - The value to bind.
|
|
684
|
+
* @returns {Promise<this>} The same instance, allowing chained calls.
|
|
685
|
+
* @throws {TypeError} If `key` is not a non-empty string.
|
|
686
|
+
* @throws {TypeError} If the table validator rejects the entry.
|
|
687
|
+
*/
|
|
688
|
+
async set(key, value) {
|
|
689
|
+
assertNonEmptyString(key, 'key');
|
|
690
|
+
this.#assertAccepted(key, value);
|
|
691
|
+
await this.hydrate();
|
|
692
|
+
await this._action(async () => {
|
|
693
|
+
await this._write(() => this._persist({ key, value, timestamp: Date.now() }));
|
|
694
|
+
this.#cache.set(key, value);
|
|
695
|
+
});
|
|
696
|
+
return this;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Removes an entry from the cache and from the object store.
|
|
701
|
+
* @param {Key} key - The key to remove.
|
|
702
|
+
* @returns {Promise<boolean>} `true` when the key existed and was removed.
|
|
703
|
+
* @throws {TypeError} If `key` is not a non-empty string.
|
|
704
|
+
*/
|
|
705
|
+
async delete(key) {
|
|
706
|
+
assertNonEmptyString(key, 'key');
|
|
707
|
+
await this.hydrate();
|
|
708
|
+
let deleted = false;
|
|
709
|
+
await this._action(async () => {
|
|
710
|
+
if (!this.#cache.has(key)) return;
|
|
711
|
+
await this._write(() => this._remove(key));
|
|
712
|
+
this.#cache.delete(key);
|
|
713
|
+
deleted = true;
|
|
714
|
+
});
|
|
715
|
+
return deleted;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Removes every entry from both the cache and the object store.
|
|
720
|
+
* @returns {Promise<void>}
|
|
721
|
+
*/
|
|
722
|
+
async clear() {
|
|
723
|
+
await this.hydrate();
|
|
724
|
+
await this._action(async () => {
|
|
725
|
+
await this._write(() => this._clear());
|
|
726
|
+
this.#cache.clear();
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Returns a snapshot of every stored entry.
|
|
732
|
+
* @returns {Promise<Array<[Key, Value]>>} A new array with the current entries.
|
|
733
|
+
*/
|
|
734
|
+
async toArray() {
|
|
735
|
+
await this.hydrate();
|
|
736
|
+
return [...this.#cache.entries()];
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Serialization hook used by `JSON.stringify`.
|
|
741
|
+
* @returns {Promise<Array<[Key, Value]>>} A new array with the current entries.
|
|
742
|
+
*/
|
|
743
|
+
async toJSON() {
|
|
744
|
+
return this.toArray();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Iterates over the stored keys.
|
|
749
|
+
* @returns {AsyncGenerator<Key, void, void>} An async generator of keys.
|
|
750
|
+
*/
|
|
751
|
+
async *keys() {
|
|
752
|
+
await this.hydrate();
|
|
753
|
+
for (const key of [...this.#cache.keys()]) {
|
|
754
|
+
yield key;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Iterates over the stored values.
|
|
760
|
+
* @returns {AsyncGenerator<Value, void, void>} An async generator of values.
|
|
761
|
+
*/
|
|
762
|
+
async *values() {
|
|
763
|
+
await this.hydrate();
|
|
764
|
+
for (const value of [...this.#cache.values()]) {
|
|
765
|
+
yield value;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Iterates over the stored entries.
|
|
771
|
+
* @returns {AsyncGenerator<[Key, Value], void, void>} An async generator of entries.
|
|
772
|
+
*/
|
|
773
|
+
async *entries() {
|
|
774
|
+
await this.hydrate();
|
|
775
|
+
for (const entry of [...this.#cache.entries()]) {
|
|
776
|
+
yield entry;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Iterates over the stored entries.
|
|
782
|
+
* @returns {AsyncGenerator<[Key, Value], void, void>} An async generator of entries.
|
|
783
|
+
*/
|
|
784
|
+
[Symbol.asyncIterator]() {
|
|
785
|
+
return this.entries();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Runs a callback for every stored entry.
|
|
790
|
+
* @param {TinyMapDbForEachCallback<Key, Value>} callbackFn - Callback invoked per entry.
|
|
791
|
+
* @param {unknown} [thisArg] - Value used as `this` inside the callback.
|
|
792
|
+
* @returns {Promise<void>}
|
|
793
|
+
* @throws {TypeError} If `callbackFn` is not a function.
|
|
794
|
+
*/
|
|
795
|
+
async forEach(callbackFn, thisArg) {
|
|
796
|
+
if (typeof callbackFn !== 'function') {
|
|
797
|
+
throw new TypeError('The "callbackFn" argument must be a function.');
|
|
798
|
+
}
|
|
799
|
+
await this.hydrate();
|
|
800
|
+
for (const [key, value] of [...this.#cache]) {
|
|
801
|
+
callbackFn.call(thisArg, value, key, this);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Populates the in-memory cache from the persisted records.
|
|
807
|
+
* @param {TinyMapDbRecord<Key, Value>[]} records - The persisted records.
|
|
808
|
+
* @returns {void}
|
|
809
|
+
*/
|
|
810
|
+
_load(records) {
|
|
811
|
+
for (const record of records.sort((a, b) => a.timestamp - b.timestamp)) {
|
|
812
|
+
this.#cache.set(record.key, record.value);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Runs the optional table validator against a candidate entry.
|
|
818
|
+
* @param {Key} key - The candidate key.
|
|
819
|
+
* @param {Value} value - The candidate value.
|
|
820
|
+
* @returns {void}
|
|
821
|
+
* @throws {TypeError} If the validator rejects the entry.
|
|
822
|
+
*/
|
|
823
|
+
#assertAccepted(key, value) {
|
|
824
|
+
if (this.#validate === null) return;
|
|
825
|
+
if (!this.#validate(key, value)) {
|
|
826
|
+
throw new TypeError(`The entry is rejected by the "${this.storeName}" table validator.`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* A single table definition.
|
|
833
|
+
* @typedef {Object} TinyDatabaseTableConfig
|
|
834
|
+
* @property {string} name - Name of the object store.
|
|
835
|
+
* @property {'set'|'map'} type - Kind of table.
|
|
836
|
+
* @property {((...args: any[]) => boolean)|null} [validate] - Optional predicate, read from the latest migration that creates the table.
|
|
837
|
+
*/
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* A table can be declared by name or with a full configuration object.
|
|
841
|
+
* @typedef {string|TinyDatabaseTableConfig} TinyDatabaseTableDefinition
|
|
842
|
+
*/
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* A single schema migration.
|
|
846
|
+
*
|
|
847
|
+
* The version number of a migration is its 1-based position in the array, so
|
|
848
|
+
* the first entry is version 1, the second is version 2, and so on. Dropping a
|
|
849
|
+
* table and creating it again inside the same entry is the supported way of
|
|
850
|
+
* changing a key path.
|
|
851
|
+
*
|
|
852
|
+
* @typedef {Object} TinyDatabaseMigration
|
|
853
|
+
* @property {TinyDatabaseTableDefinition[]} [create] - Tables created by this version.
|
|
854
|
+
* @property {string[]} [delete] - Names of the tables dropped by this version.
|
|
855
|
+
*/
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* A migration whose table definitions were already normalized.
|
|
859
|
+
*
|
|
860
|
+
* Unlike {@link TinyDatabaseMigration}, every property is present and every
|
|
861
|
+
* table is a full configuration object, so the upgrade handler can read
|
|
862
|
+
* `name` and `type` without narrowing the union first.
|
|
863
|
+
*
|
|
864
|
+
* @typedef {Object} TinyDatabaseNormalizedMigration
|
|
865
|
+
* @property {Required<TinyDatabaseTableConfig>[]} create - Tables created by this version.
|
|
866
|
+
* @property {string[]} delete - Names of the tables dropped by this version.
|
|
867
|
+
*/
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* A single IndexedDB database that owns several {@link TinySetDb} and
|
|
871
|
+
* {@link TinyMapDb} tables.
|
|
872
|
+
*
|
|
873
|
+
* The migration array is the single source of truth: the schema is the result
|
|
874
|
+
* of replaying every entry, and the database version is the array length. Every
|
|
875
|
+
* table is an object store inside the same database, so a service worker only
|
|
876
|
+
* ever opens one connection.
|
|
877
|
+
*/
|
|
878
|
+
class TinySetMapDatabase {
|
|
879
|
+
/** @type {string} */
|
|
880
|
+
#name;
|
|
881
|
+
|
|
882
|
+
/** @type {number} */
|
|
883
|
+
#version;
|
|
884
|
+
|
|
885
|
+
/** @type {Map<string, TinySetDb<any>|TinyMapDb<any, any>>} */
|
|
886
|
+
#tables = new Map();
|
|
887
|
+
|
|
888
|
+
/** @type {Promise<IDBDatabase>} */
|
|
889
|
+
#connection;
|
|
890
|
+
|
|
891
|
+
/** @type {Promise<void>} */
|
|
892
|
+
#ready;
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* @param {string} name - Name of the IndexedDB database.
|
|
896
|
+
* @param {TinyDatabaseMigration[]} migrations - Ordered schema history, one entry per version.
|
|
897
|
+
* @throws {TypeError} If `name` is not a non-empty string.
|
|
898
|
+
* @throws {TypeError} If `migrations` is not a non-empty array.
|
|
899
|
+
* @throws {TypeError} If a migration is invalid.
|
|
900
|
+
* @throws {Error} If a table is created twice without being dropped in between.
|
|
901
|
+
*/
|
|
902
|
+
constructor(name, migrations) {
|
|
903
|
+
assertNonEmptyString(name, 'name');
|
|
904
|
+
if (!Array.isArray(migrations) || migrations.length === 0) {
|
|
905
|
+
throw new TypeError('The "migrations" argument must be a non-empty array.');
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
this.#name = name;
|
|
909
|
+
this.#version = migrations.length;
|
|
910
|
+
|
|
911
|
+
const history = migrations.map((migration, index) =>
|
|
912
|
+
TinySetMapDatabase.#normalizeMigration(migration, index),
|
|
913
|
+
);
|
|
914
|
+
const schema = TinySetMapDatabase.#resolveSchema(history);
|
|
915
|
+
|
|
916
|
+
this.#connection = TinySetMapDatabase.#openDatabase(name, history);
|
|
917
|
+
|
|
918
|
+
for (const definition of schema.values()) {
|
|
919
|
+
const tableCfg = {
|
|
920
|
+
storeName: definition.name,
|
|
921
|
+
validate: definition.validate,
|
|
922
|
+
openDatabase: () => this.#connection,
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
if (definition.type === 'map') {
|
|
926
|
+
this.#tables.set(definition.name, new TinyMapDb(tableCfg));
|
|
927
|
+
} else {
|
|
928
|
+
this.#tables.set(definition.name, new TinySetDb(tableCfg));
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
this.#ready = this.#connection.then(() =>
|
|
933
|
+
Promise.all([...this.#tables.values()].map((table) => table.hydrate())).then(() => undefined),
|
|
934
|
+
);
|
|
935
|
+
this.#ready.catch(() => {});
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Name of the IndexedDB database in use.
|
|
940
|
+
* @returns {string}
|
|
941
|
+
*/
|
|
942
|
+
get name() {
|
|
943
|
+
return this.#name;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Schema version of the IndexedDB database in use.
|
|
948
|
+
* @returns {number}
|
|
949
|
+
*/
|
|
950
|
+
get version() {
|
|
951
|
+
return this.#version;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Names of every table exposed by this database.
|
|
956
|
+
* @returns {string[]}
|
|
957
|
+
*/
|
|
958
|
+
get tableNames() {
|
|
959
|
+
return [...this.#tables.keys()];
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Resolves once the connection is open and every table is hydrated.
|
|
964
|
+
* @returns {Promise<void>}
|
|
965
|
+
*/
|
|
966
|
+
get ready() {
|
|
967
|
+
return this.#ready;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Returns a Set instance of table by name.
|
|
972
|
+
* @param {string} tableName - Name of the table.
|
|
973
|
+
* @returns {TinySetDb<any>} The requested table.
|
|
974
|
+
* @throws {TypeError} If `tableName` is not a non-empty string.
|
|
975
|
+
* @throws {RangeError} If the table was not declared in the constructor.
|
|
976
|
+
*/
|
|
977
|
+
tableSet(tableName) {
|
|
978
|
+
assertNonEmptyString(tableName, 'tableName');
|
|
979
|
+
const table = this.#tables.get(tableName);
|
|
980
|
+
if (table === undefined) {
|
|
981
|
+
throw new RangeError(`The "${tableName}" table is not declared in this database.`);
|
|
982
|
+
}
|
|
983
|
+
if (!(table instanceof TinySetDb))
|
|
984
|
+
throw new Error(`The "${tableName}" table is not a Set instance.`);
|
|
985
|
+
return table;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Returns a Map instance of table by name.
|
|
990
|
+
* @param {string} tableName - Name of the table.
|
|
991
|
+
* @returns {TinyMapDb<any, any>} The requested table.
|
|
992
|
+
* @throws {TypeError} If `tableName` is not a non-empty string.
|
|
993
|
+
* @throws {RangeError} If the table was not declared in the constructor.
|
|
994
|
+
*/
|
|
995
|
+
tableMap(tableName) {
|
|
996
|
+
assertNonEmptyString(tableName, 'tableName');
|
|
997
|
+
const table = this.#tables.get(tableName);
|
|
998
|
+
if (table === undefined) {
|
|
999
|
+
throw new RangeError(`The "${tableName}" table is not declared in this database.`);
|
|
1000
|
+
}
|
|
1001
|
+
if (!(table instanceof TinyMapDb))
|
|
1002
|
+
throw new Error(`The "${tableName}" table is not a Map instance.`);
|
|
1003
|
+
return table;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Checks whether a table was declared in the constructor.
|
|
1008
|
+
* @param {string} tableName - Name of the table.
|
|
1009
|
+
* @returns {boolean} `true` when the table exists.
|
|
1010
|
+
* @throws {TypeError} If `tableName` is not a non-empty string.
|
|
1011
|
+
*/
|
|
1012
|
+
has(tableName) {
|
|
1013
|
+
assertNonEmptyString(tableName, 'tableName');
|
|
1014
|
+
return this.#tables.has(tableName);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Closes the underlying connection.
|
|
1019
|
+
* @returns {Promise<void>}
|
|
1020
|
+
*/
|
|
1021
|
+
async close() {
|
|
1022
|
+
const database = await this.#connection;
|
|
1023
|
+
database.close();
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Normalizes a table definition into a full configuration object.
|
|
1028
|
+
* @param {TinyDatabaseTableDefinition} definition - The raw table definition.
|
|
1029
|
+
* @returns {Required<TinyDatabaseTableConfig>} The normalized configuration.
|
|
1030
|
+
* @throws {TypeError} If the definition is not a string or a valid object.
|
|
1031
|
+
*/
|
|
1032
|
+
static #normalizeTable(definition) {
|
|
1033
|
+
if (typeof definition === 'string') {
|
|
1034
|
+
return { name: definition, type: 'set', validate: null };
|
|
1035
|
+
}
|
|
1036
|
+
if (typeof definition !== 'object' || definition === null) {
|
|
1037
|
+
throw new TypeError('Each table must be a string or an object with a "name" property.');
|
|
1038
|
+
}
|
|
1039
|
+
const { name, type, validate = null } = definition;
|
|
1040
|
+
assertNonEmptyString(name, 'table.name');
|
|
1041
|
+
if (type !== 'set' && type !== 'map') {
|
|
1042
|
+
throw new TypeError(`The "type" option of the "${name}" table must be "set" or "map".`);
|
|
1043
|
+
}
|
|
1044
|
+
if (validate !== null && typeof validate !== 'function') {
|
|
1045
|
+
throw new TypeError(`The "validate" option of the "${name}" table must be a function.`);
|
|
1046
|
+
}
|
|
1047
|
+
return { name, type, validate };
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Opens the IndexedDB database and replays every pending migration.
|
|
1052
|
+
* @param {string} name - Name of the database.
|
|
1053
|
+
* @param {TinyDatabaseNormalizedMigration[]} history - Normalized migrations, in order.
|
|
1054
|
+
* @returns {Promise<IDBDatabase>} The open database connection.
|
|
1055
|
+
*/
|
|
1056
|
+
static #openDatabase(name, history) {
|
|
1057
|
+
return new Promise((resolve, reject) => {
|
|
1058
|
+
if (typeof indexedDB === 'undefined') {
|
|
1059
|
+
reject(new ReferenceError('IndexedDB is not available in the current environment.'));
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const request = indexedDB.open(name, history.length);
|
|
1064
|
+
|
|
1065
|
+
request.onupgradeneeded = (event) => {
|
|
1066
|
+
try {
|
|
1067
|
+
for (let version = event.oldVersion + 1; version <= history.length; version += 1) {
|
|
1068
|
+
TinySetMapDatabase.#applyMigration(request.result, history[version - 1]);
|
|
1069
|
+
}
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
reject(error);
|
|
1072
|
+
request.transaction?.abort();
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
request.onsuccess = () => {
|
|
1076
|
+
const database = request.result;
|
|
1077
|
+
database.onversionchange = () => database.close();
|
|
1078
|
+
resolve(database);
|
|
1079
|
+
};
|
|
1080
|
+
request.onerror = () => reject(request.error);
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Applies a single migration to the version change transaction.
|
|
1086
|
+
* @param {IDBDatabase} database - The database being upgraded.
|
|
1087
|
+
* @param {TinyDatabaseNormalizedMigration} migration - The normalized migration.
|
|
1088
|
+
* @returns {void}
|
|
1089
|
+
*/
|
|
1090
|
+
static #applyMigration(database, migration) {
|
|
1091
|
+
for (const name of migration.delete) {
|
|
1092
|
+
if (database.objectStoreNames.contains(name)) {
|
|
1093
|
+
database.deleteObjectStore(name);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
for (const definition of migration.create) {
|
|
1098
|
+
database.createObjectStore(definition.name, {
|
|
1099
|
+
keyPath: definition.type === 'map' ? 'key' : 'value',
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Validates a single migration and normalizes its table definitions.
|
|
1106
|
+
* @param {TinyDatabaseMigration} migration - The migration to validate.
|
|
1107
|
+
* @param {number} index - Zero-based position of the migration.
|
|
1108
|
+
* @returns {TinyDatabaseNormalizedMigration} The normalized migration.
|
|
1109
|
+
* @throws {TypeError} If the migration is not an object.
|
|
1110
|
+
* @throws {TypeError} If `create` is not an array of table definitions.
|
|
1111
|
+
* @throws {TypeError} If `delete` is not an array of non-empty strings.
|
|
1112
|
+
*/
|
|
1113
|
+
static #normalizeMigration(migration, index) {
|
|
1114
|
+
if (typeof migration !== 'object' || migration === null) {
|
|
1115
|
+
throw new TypeError(`The migration at index ${index} must be an object.`);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
const { create = [], delete: drop = [] } = migration;
|
|
1119
|
+
|
|
1120
|
+
if (!Array.isArray(create)) {
|
|
1121
|
+
throw new TypeError(`The "create" list of the migration at index ${index} must be an array.`);
|
|
1122
|
+
}
|
|
1123
|
+
if (!Array.isArray(drop)) {
|
|
1124
|
+
throw new TypeError(`The "delete" list of the migration at index ${index} must be an array.`);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
for (const name of drop) {
|
|
1128
|
+
assertNonEmptyString(name, `migrations[${index}].delete[]`);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
return {
|
|
1132
|
+
create: create.map((table) => TinySetMapDatabase.#normalizeTable(table)),
|
|
1133
|
+
delete: [...drop],
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Replays every migration and returns the resulting table set.
|
|
1139
|
+
* @param {TinyDatabaseNormalizedMigration[]} history - Normalized migrations, in order.
|
|
1140
|
+
* @returns {Map<string, Required<TinyDatabaseTableConfig>>} The final schema, keyed by table name.
|
|
1141
|
+
* @throws {Error} If a table is created twice without being dropped in between.
|
|
1142
|
+
*/
|
|
1143
|
+
static #resolveSchema(history) {
|
|
1144
|
+
/** @type {Map<string, Required<TinyDatabaseTableConfig>>} */
|
|
1145
|
+
const schema = new Map();
|
|
1146
|
+
|
|
1147
|
+
for (const migration of history) {
|
|
1148
|
+
for (const name of migration.delete) {
|
|
1149
|
+
schema.delete(name);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
for (const definition of migration.create) {
|
|
1153
|
+
if (schema.has(definition.name)) {
|
|
1154
|
+
throw new Error(`The "${definition.name}" table is created twice.`);
|
|
1155
|
+
}
|
|
1156
|
+
schema.set(definition.name, definition);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return schema;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
exports.TinyMapDb = TinyMapDb;
|
|
1165
|
+
exports.TinySetDb = TinySetDb;
|
|
1166
|
+
exports.TinySetMapDatabase = TinySetMapDatabase;
|
|
1167
|
+
exports.TinyTableDb = TinyTableDb;
|