@rozenite/storage-plugin 1.9.0 → 1.10.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.
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 100%;--foreground: 0 0% 3.9%;--card: 0 0% 100%;--card-foreground: 0 0% 3.9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 3.9%;--primary: 0 0% 9%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96.1%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96.1%;--muted-foreground: 0 0% 45.1%;--accent: 0 0% 96.1%;--accent-foreground: 0 0% 9%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 98%;--border: 0 0% 89.8%;--input: 0 0% 89.8%;--ring: 0 0% 3.9%;--chart-1: 12 76% 61%;--chart-2: 173 58% 39%;--chart-3: 197 37% 24%;--chart-4: 43 74% 66%;--chart-5: 27 87% 67%;--radius: .5rem;--sidebar-background: 0 0% 98%;--sidebar-foreground: 240 5.3% 26.1%;--sidebar-primary: 240 5.9% 10%;--sidebar-primary-foreground: 0 0% 98%;--sidebar-accent: 240 4.8% 95.9%;--sidebar-accent-foreground: 240 5.9% 10%;--sidebar-border: 220 13% 91%;--sidebar-ring: 217.2 91.2% 59.8%}.dark{--background: 0 0% 3.9%;--foreground: 0 0% 98%;--card: 0 0% 3.9%;--card-foreground: 0 0% 98%;--popover: 0 0% 3.9%;--popover-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 9%;--secondary: 0 0% 14.9%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 14.9%;--muted-foreground: 0 0% 63.9%;--accent: 0 0% 14.9%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14.9%;--input: 0 0% 14.9%;--ring: 0 0% 83.1%;--chart-1: 220 70% 50%;--chart-2: 160 60% 45%;--chart-3: 30 80% 55%;--chart-4: 280 65% 60%;--chart-5: 340 75% 55%;--sidebar-background: 240 5.9% 10%;--sidebar-foreground: 240 4.8% 95.9%;--sidebar-primary: 224.3 76.3% 48%;--sidebar-primary-foreground: 0 0% 100%;--sidebar-accent: 240 3.7% 15.9%;--sidebar-accent-foreground: 240 4.8% 95.9%;--sidebar-border: 240 3.7% 15.9%;--sidebar-ring: 217.2 91.2% 59.8%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.left-2{left:.5rem}.top-0{top:0}.top-1\/2{top:50%}.z-10{z-index:10}.z-50{z-index:50}.mx-4{margin-left:1rem;margin-right:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-2{margin-left:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-20{max-height:5rem}.max-h-24{max-height:6rem}.max-h-96{max-height:24rem}.max-h-\[300px\]{max-height:300px}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0px}.min-h-\[120px\]{min-height:120px}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-96{width:24rem}.w-\[28rem\]{width:28rem}.w-\[32rem\]{width:32rem}.w-\[90vw\]{width:90vw}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-red-700{--tw-border-opacity: 1;border-color:rgb(185 28 28 / var(--tw-border-opacity, 1))}.border-yellow-700{--tw-border-opacity: 1;border-color:rgb(161 98 7 / var(--tw-border-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-purple-600{--tw-bg-opacity: 1;background-color:rgb(147 51 234 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-red-900\/30{background-color:#7f1d1d4d}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.bg-yellow-900\/30{background-color:#713f124d}.bg-opacity-50{--tw-bg-opacity: .5}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pl-8{padding-left:2rem}.pr-3{padding-right:.75rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.lowercase{text-transform:lowercase}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-cyan-500{--tw-text-opacity: 1;color:rgb(6 182 212 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-green-300{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-orange-500{--tw-text-opacity: 1;color:rgb(249 115 22 / var(--tw-text-opacity, 1))}.text-pink-500{--tw-text-opacity: 1;color:rgb(236 72 153 / var(--tw-text-opacity, 1))}.text-purple-200{--tw-text-opacity: 1;color:rgb(233 213 255 / var(--tw-text-opacity, 1))}.text-purple-300{--tw-text-opacity: 1;color:rgb(216 180 254 / var(--tw-text-opacity, 1))}.text-purple-500{--tw-text-opacity: 1;color:rgb(168 85 247 / var(--tw-text-opacity, 1))}.text-red-100{--tw-text-opacity: 1;color:rgb(254 226 226 / var(--tw-text-opacity, 1))}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-200{--tw-text-opacity: 1;color:rgb(254 240 138 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.opacity-0{opacity:0}.outline{outline-style:solid}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#1f2937;border-radius:4px}::-webkit-scrollbar-thumb{background:#374151;border-radius:4px;border:1px solid #1f2937}::-webkit-scrollbar-thumb:hover{background:#4b5563}::-webkit-scrollbar-thumb:active{background:#6b7280}::-webkit-scrollbar-corner{background:#1f2937}.placeholder\:text-gray-400::-moz-placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.placeholder\:text-gray-400::placeholder{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.hover\:bg-blue-700:hover{--tw-bg-opacity: 1;background-color:rgb(29 78 216 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-600:hover{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:text-blue-400:hover{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-600:disabled{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.disabled\:bg-gray-800:disabled{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}
@@ -22,8 +22,8 @@
22
22
  <script>
23
23
  var __ROZENITE_PANEL__ = true;
24
24
  </script>
25
- <script type="module" crossorigin src="../devtools/assets/panel-DnjbFMGo.js"></script>
26
- <link rel="stylesheet" crossorigin href="../devtools/assets/panel-DVxtKLI9.css">
25
+ <script type="module" crossorigin src="../devtools/assets/panel-Bm-SWF7d.js"></script>
26
+ <link rel="stylesheet" crossorigin href="../devtools/assets/panel-DIqI4WSp.css">
27
27
  </head>
28
28
  <body>
29
29
  <div id="root"></div>
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("./types.cjs"),l=require("./async-storage.require.cjs"),f=require("./secure-storage.require.cjs"),p=t=>!!(t.includes("�")||/[\u0000-\u001F\u007F-\u009F]/.test(t)||[...t].filter(n=>n>=" "&&n<="~").length/t.length<.7),g=t=>"remove"in t,A=t=>{if(Array.isArray(t)){if(t.some(g))throw new Error("[Rozenite] Storage Plugin: MMKV arrays are not supported for v4 storages. Pass a record of storage IDs and MMKV instances.");return Object.fromEntries(t.map(r=>[r.id,r]))}return t},S=t=>g(t)?{set:(e,r)=>t.set(e,r),getBoolean:e=>t.getBoolean(e),getString:e=>t.getString(e),getNumber:e=>t.getNumber(e),getBuffer:e=>t.getBuffer(e),delete:e=>t.remove(e),getAllKeys:()=>t.getAllKeys(),addOnValueChangedListener:e=>t.addOnValueChangedListener(e)}:{set:(e,r)=>t.set(e,r),getBoolean:e=>t.getBoolean(e),getString:e=>t.getString(e),getNumber:e=>t.getNumber(e),getBuffer:e=>t.getBuffer(e),delete:e=>t.delete(e),getAllKeys:()=>t.getAllKeys(),addOnValueChangedListener:e=>t.addOnValueChangedListener(e)},b=(t,e)=>{const r=t.getString(e);if(r!==void 0&&r.length>0)return p(r)?{key:e,type:"buffer",value:Array.from(new TextEncoder().encode(r))}:{key:e,type:"string",value:r};const n=t.getNumber(e);if(n!==void 0)return{key:e,type:"number",value:n};const s=t.getBoolean(e);if(s!==void 0)return{key:e,type:"boolean",value:s};const u=t.getBuffer(e);if(u!==void 0)return{key:e,type:"buffer",value:Array.from(new Uint8Array(u))}},y=(t,e)=>{if(e.type==="buffer"){t.set(e.key,new Uint8Array(e.value).buffer);return}t.set(e.key,e.value)},V=(t,e)=>{if(t)return t instanceof RegExp?t:t[e]},M=(t,e)=>{if(t instanceof RegExp)return r=>(t.lastIndex=0,t.test(`${e}:${r}`))},E=({adapterId:t="mmkv",adapterName:e="MMKV",storages:r,blacklist:n})=>{const s=A(r),u=Object.entries(s).map(([i,c])=>{const o=S(c);return{id:i,name:i,blacklist:V(n,i),shouldFilterKey:M(n,i),capabilities:{supportedTypes:d.DEFAULT_SUPPORTED_TYPES},storage:{kind:"sync",getAllKeys:()=>o.getAllKeys(),get:a=>b(o,a),set:a=>y(o,a),delete:a=>o.delete(a),subscribe:a=>o.addOnValueChangedListener(a)}}});return{id:t,name:e,storages:u}};exports.createAsyncStorageAdapter=l.createAsyncStorageAdapter;exports.createExpoAsyncStorageAdapter=l.createExpoAsyncStorageAdapter;exports.createExpoSecureStorageAdapter=f.createExpoSecureStorageAdapter;exports.createMMKVStorageAdapter=E;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("./types.cjs"),g=require("./async-storage.require.cjs"),f=require("./secure-storage.require.cjs"),l=t=>"remove"in t,S=t=>{if(Array.isArray(t)){if(t.some(l))throw new Error("[Rozenite] Storage Plugin: MMKV arrays are not supported for v4 storages. Pass a record of storage IDs and MMKV instances.");return Object.fromEntries(t.map(r=>[r.id,r]))}return t},p=t=>l(t)?{set:(e,r)=>t.set(e,r),getBoolean:e=>t.getBoolean(e),getString:e=>t.getString(e),getNumber:e=>t.getNumber(e),getBuffer:e=>t.getBuffer(e),delete:e=>t.remove(e),getAllKeys:()=>t.getAllKeys(),addOnValueChangedListener:e=>t.addOnValueChangedListener(e)}:{set:(e,r)=>t.set(e,r),getBoolean:e=>t.getBoolean(e),getString:e=>t.getString(e),getNumber:e=>t.getNumber(e),getBuffer:e=>t.getBuffer(e),delete:e=>t.delete(e),getAllKeys:()=>t.getAllKeys(),addOnValueChangedListener:e=>t.addOnValueChangedListener(e)},A=(t,e)=>{const r=t.getString(e);if(r!==void 0&&r.length>0)return{key:e,type:"string",value:r};const a=t.getBuffer(e);if(a!==void 0&&a.byteLength>0)return{key:e,type:"buffer",value:Array.from(new Uint8Array(a))};const s=t.getNumber(e);if(s!==void 0)return{key:e,type:"number",value:s};const u=t.getBoolean(e);if(u!==void 0)return{key:e,type:"boolean",value:u};if(r!==void 0)return{key:e,type:"string",value:r}},y=(t,e)=>{e.type==="buffer"?t.set(e.key,new Uint8Array(e.value).buffer):t.set(e.key,e.value)},b=(t,e)=>{if(t)return t instanceof RegExp?t:t[e]},V=(t,e)=>{if(t instanceof RegExp)return r=>(t.lastIndex=0,t.test(`${e}:${r}`))},M=({adapterId:t="mmkv",adapterName:e="MMKV",storages:r,blacklist:a})=>{const s=S(r),u=Object.entries(s).map(([i,c])=>{const o=p(c);return{id:i,name:i,blacklist:b(a,i),shouldFilterKey:V(a,i),capabilities:{supportedTypes:d.DEFAULT_SUPPORTED_TYPES},storage:{kind:"sync",getAllKeys:()=>o.getAllKeys(),get:n=>A(o,n),set:n=>y(o,n),delete:n=>o.delete(n),subscribe:n=>o.addOnValueChangedListener(n)}}});return{id:t,name:e,storages:u}};exports.createAsyncStorageAdapter=g.createAsyncStorageAdapter;exports.createExpoAsyncStorageAdapter=g.createExpoAsyncStorageAdapter;exports.createExpoSecureStorageAdapter=f.createExpoSecureStorageAdapter;exports.createMMKVStorageAdapter=M;
@@ -1,7 +1,7 @@
1
- import { D as f } from "./types.js";
1
+ import { D as d } from "./types.js";
2
2
  import { createAsyncStorageAdapter as B, createExpoAsyncStorageAdapter as E } from "./async-storage.require.js";
3
- import { createExpoSecureStorageAdapter as O } from "./secure-storage.require.js";
4
- const d = (t) => !!(t.includes("�") || /[\u0000-\u001F\u007F-\u009F]/.test(t) || [...t].filter((n) => n >= " " && n <= "~").length / t.length < 0.7), l = (t) => "remove" in t, c = (t) => {
3
+ import { createExpoSecureStorageAdapter as x } from "./secure-storage.require.js";
4
+ const l = (t) => "remove" in t, f = (t) => {
5
5
  if (Array.isArray(t)) {
6
6
  if (t.some(l))
7
7
  throw new Error(
@@ -12,7 +12,7 @@ const d = (t) => !!(t.includes("�") || /[\u0000-\u001F\u007F-\u009F]/.test(t)
12
12
  );
13
13
  }
14
14
  return t;
15
- }, p = (t) => l(t) ? {
15
+ }, c = (t) => l(t) ? {
16
16
  set: (e, r) => t.set(e, r),
17
17
  getBoolean: (e) => t.getBoolean(e),
18
18
  getString: (e) => t.getString(e),
@@ -30,75 +30,57 @@ const d = (t) => !!(t.includes("�") || /[\u0000-\u001F\u007F-\u009F]/.test(t)
30
30
  delete: (e) => t.delete(e),
31
31
  getAllKeys: () => t.getAllKeys(),
32
32
  addOnValueChangedListener: (e) => t.addOnValueChangedListener(e)
33
- }, b = (t, e) => {
33
+ }, p = (t, e) => {
34
34
  const r = t.getString(e);
35
35
  if (r !== void 0 && r.length > 0)
36
- return d(r) ? {
37
- key: e,
38
- type: "buffer",
39
- value: Array.from(new TextEncoder().encode(r))
40
- } : {
41
- key: e,
42
- type: "string",
43
- value: r
44
- };
45
- const n = t.getNumber(e);
46
- if (n !== void 0)
47
- return {
48
- key: e,
49
- type: "number",
50
- value: n
51
- };
52
- const s = t.getBoolean(e);
53
- if (s !== void 0)
54
- return {
55
- key: e,
56
- type: "boolean",
57
- value: s
58
- };
59
- const u = t.getBuffer(e);
60
- if (u !== void 0)
36
+ return { key: e, type: "string", value: r };
37
+ const a = t.getBuffer(e);
38
+ if (a !== void 0 && a.byteLength > 0)
61
39
  return {
62
40
  key: e,
63
41
  type: "buffer",
64
- value: Array.from(new Uint8Array(u))
42
+ value: Array.from(new Uint8Array(a))
65
43
  };
44
+ const s = t.getNumber(e);
45
+ if (s !== void 0)
46
+ return { key: e, type: "number", value: s };
47
+ const i = t.getBoolean(e);
48
+ if (i !== void 0)
49
+ return { key: e, type: "boolean", value: i };
50
+ if (r !== void 0)
51
+ return { key: e, type: "string", value: r };
66
52
  }, A = (t, e) => {
67
- if (e.type === "buffer") {
68
- t.set(e.key, new Uint8Array(e.value).buffer);
69
- return;
70
- }
71
- t.set(e.key, e.value);
72
- }, S = (t, e) => {
53
+ e.type === "buffer" ? t.set(e.key, new Uint8Array(e.value).buffer) : t.set(e.key, e.value);
54
+ }, b = (t, e) => {
73
55
  if (t)
74
56
  return t instanceof RegExp ? t : t[e];
75
- }, y = (t, e) => {
57
+ }, S = (t, e) => {
76
58
  if (t instanceof RegExp)
77
59
  return (r) => (t.lastIndex = 0, t.test(`${e}:${r}`));
78
- }, h = ({
60
+ }, V = ({
79
61
  adapterId: t = "mmkv",
80
62
  adapterName: e = "MMKV",
81
63
  storages: r,
82
- blacklist: n
64
+ blacklist: a
83
65
  }) => {
84
- const s = c(r), u = Object.entries(s).map(
85
- ([i, g]) => {
86
- const a = p(g);
66
+ const s = f(r), i = Object.entries(s).map(
67
+ ([u, g]) => {
68
+ const o = c(g);
87
69
  return {
88
- id: i,
89
- name: i,
90
- blacklist: S(n, i),
91
- shouldFilterKey: y(n, i),
70
+ id: u,
71
+ name: u,
72
+ blacklist: b(a, u),
73
+ shouldFilterKey: S(a, u),
92
74
  capabilities: {
93
- supportedTypes: f
75
+ supportedTypes: d
94
76
  },
95
77
  storage: {
96
78
  kind: "sync",
97
- getAllKeys: () => a.getAllKeys(),
98
- get: (o) => b(a, o),
99
- set: (o) => A(a, o),
100
- delete: (o) => a.delete(o),
101
- subscribe: (o) => a.addOnValueChangedListener(o)
79
+ getAllKeys: () => o.getAllKeys(),
80
+ get: (n) => p(o, n),
81
+ set: (n) => A(o, n),
82
+ delete: (n) => o.delete(n),
83
+ subscribe: (n) => o.addOnValueChangedListener(n)
102
84
  }
103
85
  };
104
86
  }
@@ -106,12 +88,12 @@ const d = (t) => !!(t.includes("�") || /[\u0000-\u001F\u007F-\u009F]/.test(t)
106
88
  return {
107
89
  id: t,
108
90
  name: e,
109
- storages: u
91
+ storages: i
110
92
  };
111
93
  };
112
94
  export {
113
95
  B as createAsyncStorageAdapter,
114
96
  E as createExpoAsyncStorageAdapter,
115
- O as createExpoSecureStorageAdapter,
116
- h as createMMKVStorageAdapter
97
+ x as createExpoSecureStorageAdapter,
98
+ V as createMMKVStorageAdapter
117
99
  };
@@ -1 +1 @@
1
- {"name":"@rozenite/storage-plugin","version":"1.9.0","description":"Generic Storage Inspector for Rozenite.","panels":[{"name":"Storage","source":"/devtools/panel.html"}]}
1
+ {"name":"@rozenite/storage-plugin","version":"1.10.0","description":"Generic Storage Inspector for Rozenite.","panels":[{"name":"Storage","source":"/devtools/panel.html"}]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozenite/storage-plugin",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Generic Storage Inspector for Rozenite.",
5
5
  "type": "module",
6
6
  "main": "./dist/react-native/index.cjs",
@@ -15,11 +15,14 @@
15
15
  "url": "https://github.com/callstackincubator/rozenite.git"
16
16
  },
17
17
  "dependencies": {
18
- "@rozenite/plugin-bridge": "1.9.0",
19
- "@rozenite/agent-shared": "1.9.0",
20
- "@rozenite/agent-bridge": "1.9.0"
18
+ "@rozenite/agent-shared": "1.10.0",
19
+ "@rozenite/agent-bridge": "1.10.0",
20
+ "@rozenite/plugin-bridge": "1.10.0"
21
21
  },
22
22
  "devDependencies": {
23
+ "@codemirror/commands": "^6.10.3",
24
+ "@codemirror/state": "^6.6.0",
25
+ "@codemirror/view": "^6.40.0",
23
26
  "@tanstack/react-table": "^8.21.3",
24
27
  "@types/react": "~19.2.2",
25
28
  "autoprefixer": "^10.4.21",
@@ -38,8 +41,8 @@
38
41
  "tailwindcss-animate": "^1.0.7",
39
42
  "typescript": "~5.9.3",
40
43
  "vite": "^7.3.1",
41
- "@rozenite/vite-plugin": "1.9.0",
42
- "rozenite": "1.9.0"
44
+ "@rozenite/vite-plugin": "1.10.0",
45
+ "rozenite": "1.10.0"
43
46
  },
44
47
  "peerDependencies": {
45
48
  "@react-native-async-storage/async-storage": "*",
@@ -109,9 +109,9 @@ describe('createMMKVStorageAdapter', () => {
109
109
  it('rejects MMKV v4 storages passed as an array', () => {
110
110
  const storage = createFakeMMKVV4();
111
111
 
112
- expect(() => createMMKVStorageAdapter({ storages: [storage as any] })).toThrow(
113
- /MMKV arrays are not supported for v4 storages/
114
- );
112
+ expect(() =>
113
+ createMMKVStorageAdapter({ storages: [storage as any] }),
114
+ ).toThrow(/MMKV arrays are not supported for v4 storages/);
115
115
  });
116
116
 
117
117
  it('applies global blacklist patterns against storageId:key', async () => {
@@ -129,8 +129,12 @@ describe('createMMKVStorageAdapter', () => {
129
129
  });
130
130
 
131
131
  const views = createStorageViews([adapter]);
132
- const userView = views.find((view) => view.target.storageId === 'user-storage');
133
- const cacheView = views.find((view) => view.target.storageId === 'cache-storage');
132
+ const userView = views.find(
133
+ (view) => view.target.storageId === 'user-storage',
134
+ );
135
+ const cacheView = views.find(
136
+ (view) => view.target.storageId === 'cache-storage',
137
+ );
134
138
 
135
139
  expect(userView).toBeDefined();
136
140
  expect(cacheView).toBeDefined();
@@ -139,3 +143,294 @@ describe('createMMKVStorageAdapter', () => {
139
143
  await expect(cacheView?.getAllKeys()).resolves.toEqual(['persisted']);
140
144
  });
141
145
  });
146
+
147
+ // MMKV in production stores raw bytes per key without a type tag, so
148
+ // getString returns data for any stored value whose bytes decode as
149
+ // valid UTF-8 — including values the app wrote via setBuffer. This
150
+ // fake mirrors that behaviour: getString attempts a strict UTF-8 decode
151
+ // and returns undefined when the bytes are not valid UTF-8, exposing
152
+ // the read priority chain that the adapter relies on.
153
+ //
154
+ // `lenientGetString`, when true, returns `""` instead of `undefined`
155
+ // on a failed UTF-8 decode — matching real MMKV builds on certain
156
+ // platforms (notably iOS NSString conversion). The adapter has to
157
+ // handle that case by falling through to getBuffer.
158
+ //
159
+ // `lenientGetNumber`, when true, reinterprets 8-byte buffer payloads
160
+ // as IEEE 754 doubles (instead of returning undefined). Some MMKV
161
+ // builds expose this behavior — without the buffer-before-number
162
+ // priority, the adapter would misreport such buffers as numbers.
163
+ const createAmbiguousFakeMMKVV4 = ({
164
+ lenientGetString = false,
165
+ lenientGetNumber = false,
166
+ }: { lenientGetString?: boolean; lenientGetNumber?: boolean } = {}) => {
167
+ type Stored =
168
+ | { kind: 'string'; value: string }
169
+ | { kind: 'number'; value: number }
170
+ | { kind: 'boolean'; value: boolean }
171
+ | { kind: 'buffer'; bytes: Uint8Array };
172
+
173
+ const values = new Map<string, Stored>();
174
+
175
+ return {
176
+ set: vi.fn(
177
+ (key: string, value: string | number | boolean | ArrayBuffer) => {
178
+ if (typeof value === 'string') {
179
+ values.set(key, { kind: 'string', value });
180
+ } else if (typeof value === 'number') {
181
+ values.set(key, { kind: 'number', value });
182
+ } else if (typeof value === 'boolean') {
183
+ values.set(key, { kind: 'boolean', value });
184
+ } else {
185
+ values.set(key, {
186
+ kind: 'buffer',
187
+ bytes: new Uint8Array(value),
188
+ });
189
+ }
190
+ },
191
+ ),
192
+ getString: vi.fn((key: string) => {
193
+ const stored = values.get(key);
194
+ if (!stored) return undefined;
195
+ if (stored.kind === 'string') return stored.value;
196
+ if (stored.kind === 'buffer') {
197
+ try {
198
+ return new TextDecoder('utf-8', { fatal: true }).decode(stored.bytes);
199
+ } catch {
200
+ return lenientGetString ? '' : undefined;
201
+ }
202
+ }
203
+ return undefined;
204
+ }),
205
+ getNumber: vi.fn((key: string) => {
206
+ const stored = values.get(key);
207
+ if (stored?.kind === 'number') return stored.value;
208
+ if (
209
+ lenientGetNumber &&
210
+ stored?.kind === 'buffer' &&
211
+ stored.bytes.byteLength === 8
212
+ ) {
213
+ return new DataView(stored.bytes.buffer).getFloat64(0, true);
214
+ }
215
+ return undefined;
216
+ }),
217
+ getBoolean: vi.fn((key: string) => {
218
+ const stored = values.get(key);
219
+ return stored?.kind === 'boolean' ? stored.value : undefined;
220
+ }),
221
+ getBuffer: vi.fn((key: string) => {
222
+ const stored = values.get(key);
223
+ return stored?.kind === 'buffer' ? stored.bytes.buffer : undefined;
224
+ }),
225
+ remove: vi.fn((key: string) => {
226
+ values.delete(key);
227
+ }),
228
+ getAllKeys: vi.fn(() => [...values.keys()]),
229
+ addOnValueChangedListener: vi.fn(() => ({ remove: vi.fn() })),
230
+ };
231
+ };
232
+
233
+ describe('createMMKVStorageAdapter read priority', () => {
234
+ it('classifies a buffer of valid UTF-8 bytes as a string (documented limitation)', async () => {
235
+ const storage = createAmbiguousFakeMMKVV4();
236
+ const adapter = createMMKVStorageAdapter({
237
+ storages: { 'user-storage': storage as any },
238
+ });
239
+ const [view] = createStorageViews([adapter]);
240
+
241
+ // 0x68 0x65 0x6c 0x6c 0x6f = "hello" in ASCII. MMKV's getString
242
+ // decodes the bytes as UTF-8 and we trust that report — users who
243
+ // know the value is binary disambiguate at edit time via the Hex
244
+ // editor.
245
+ await view.set({
246
+ key: 'token',
247
+ type: 'buffer',
248
+ value: [0x68, 0x65, 0x6c, 0x6c, 0x6f],
249
+ });
250
+
251
+ await expect(view.get('token')).resolves.toEqual({
252
+ key: 'token',
253
+ type: 'string',
254
+ value: 'hello',
255
+ });
256
+ });
257
+
258
+ it('classifies a buffer of non-UTF-8 bytes as a buffer', async () => {
259
+ const storage = createAmbiguousFakeMMKVV4();
260
+ const adapter = createMMKVStorageAdapter({
261
+ storages: { 'user-storage': storage as any },
262
+ });
263
+ const [view] = createStorageViews([adapter]);
264
+
265
+ // 0xFF 0xFE is invalid UTF-8 — getString returns undefined, the
266
+ // chain falls through to getBuffer.
267
+ await view.set({
268
+ key: 'bin',
269
+ type: 'buffer',
270
+ value: [0xff, 0xfe, 0xfd],
271
+ });
272
+
273
+ await expect(view.get('bin')).resolves.toEqual({
274
+ key: 'bin',
275
+ type: 'buffer',
276
+ value: [0xff, 0xfe, 0xfd],
277
+ });
278
+ });
279
+
280
+ it('overwriting a buffer with a string returns the string on the next read', async () => {
281
+ const storage = createAmbiguousFakeMMKVV4();
282
+ const adapter = createMMKVStorageAdapter({
283
+ storages: { 'user-storage': storage as any },
284
+ });
285
+ const [view] = createStorageViews([adapter]);
286
+
287
+ await view.set({
288
+ key: 'flip',
289
+ type: 'buffer',
290
+ value: [0xff, 0xfe],
291
+ });
292
+ await view.set({
293
+ key: 'flip',
294
+ type: 'string',
295
+ value: 'world',
296
+ });
297
+
298
+ await expect(view.get('flip')).resolves.toEqual({
299
+ key: 'flip',
300
+ type: 'string',
301
+ value: 'world',
302
+ });
303
+ });
304
+
305
+ it('returns numbers via getNumber when no string is present at the key', async () => {
306
+ const storage = createAmbiguousFakeMMKVV4();
307
+ const adapter = createMMKVStorageAdapter({
308
+ storages: { 'user-storage': storage as any },
309
+ });
310
+ const [view] = createStorageViews([adapter]);
311
+
312
+ await view.set({ key: 'count', type: 'number', value: 42 });
313
+
314
+ await expect(view.get('count')).resolves.toEqual({
315
+ key: 'count',
316
+ type: 'number',
317
+ value: 42,
318
+ });
319
+ });
320
+
321
+ it('returns booleans via getBoolean when no string or number is present at the key', async () => {
322
+ const storage = createAmbiguousFakeMMKVV4();
323
+ const adapter = createMMKVStorageAdapter({
324
+ storages: { 'user-storage': storage as any },
325
+ });
326
+ const [view] = createStorageViews([adapter]);
327
+
328
+ await view.set({ key: 'flag', type: 'boolean', value: true });
329
+
330
+ await expect(view.get('flag')).resolves.toEqual({
331
+ key: 'flag',
332
+ type: 'boolean',
333
+ value: true,
334
+ });
335
+ });
336
+
337
+ it('deleting a key removes it from the storage view', async () => {
338
+ const storage = createAmbiguousFakeMMKVV4();
339
+ const adapter = createMMKVStorageAdapter({
340
+ storages: { 'user-storage': storage as any },
341
+ });
342
+ const [view] = createStorageViews([adapter]);
343
+
344
+ await view.set({ key: 'temp', type: 'string', value: 'hi' });
345
+ await view.delete('temp');
346
+
347
+ await expect(view.get('temp')).resolves.toBeUndefined();
348
+ });
349
+ });
350
+
351
+ describe('createMMKVStorageAdapter read priority — lenient getString platforms', () => {
352
+ // Real MMKV on some platforms returns `""` for invalid UTF-8 bytes
353
+ // rather than `undefined`. Without the empty-string carve-out in the
354
+ // priority chain, the adapter would misreport such buffers as empty
355
+ // strings and the bytes would appear lost from the panel's POV.
356
+ it('falls through to getBuffer when getString returns an empty string', async () => {
357
+ const storage = createAmbiguousFakeMMKVV4({ lenientGetString: true });
358
+ const adapter = createMMKVStorageAdapter({
359
+ storages: { 'user-storage': storage as any },
360
+ });
361
+ const [view] = createStorageViews([adapter]);
362
+
363
+ // PNG magic bytes — first byte (0x89) is an invalid UTF-8 lead.
364
+ await view.set({
365
+ key: 'png-magic',
366
+ type: 'buffer',
367
+ value: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
368
+ });
369
+
370
+ await expect(view.get('png-magic')).resolves.toEqual({
371
+ key: 'png-magic',
372
+ type: 'buffer',
373
+ value: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
374
+ });
375
+ });
376
+
377
+ it('preserves an intentional empty string when no buffer is present', async () => {
378
+ const storage = createAmbiguousFakeMMKVV4({ lenientGetString: true });
379
+ const adapter = createMMKVStorageAdapter({
380
+ storages: { 'user-storage': storage as any },
381
+ });
382
+ const [view] = createStorageViews([adapter]);
383
+
384
+ await view.set({ key: 'blank', type: 'string', value: '' });
385
+
386
+ await expect(view.get('blank')).resolves.toEqual({
387
+ key: 'blank',
388
+ type: 'string',
389
+ value: '',
390
+ });
391
+ });
392
+
393
+ it('keeps an 8-byte buffer as buffer when getNumber lenient-decodes it as a double', async () => {
394
+ // Some MMKV builds reinterpret 8-byte setBuffer payloads as IEEE
395
+ // 754 doubles via getNumber. The priority chain has to consult
396
+ // getBuffer before getNumber so the bytes that are actually on
397
+ // disk win — otherwise PNG-magic-shaped buffers would be reported
398
+ // as some arbitrary number.
399
+ const storage = createAmbiguousFakeMMKVV4({
400
+ lenientGetString: true,
401
+ lenientGetNumber: true,
402
+ });
403
+ const adapter = createMMKVStorageAdapter({
404
+ storages: { 'user-storage': storage as any },
405
+ });
406
+ const [view] = createStorageViews([adapter]);
407
+
408
+ const pngMagic = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
409
+ await view.set({ key: 'png-magic', type: 'buffer', value: pngMagic });
410
+
411
+ await expect(view.get('png-magic')).resolves.toEqual({
412
+ key: 'png-magic',
413
+ type: 'buffer',
414
+ value: pngMagic,
415
+ });
416
+ });
417
+
418
+ it('reports an empty buffer as buffer (matching its setBuffer write)', async () => {
419
+ const storage = createAmbiguousFakeMMKVV4({ lenientGetString: true });
420
+ const adapter = createMMKVStorageAdapter({
421
+ storages: { 'user-storage': storage as any },
422
+ });
423
+ const [view] = createStorageViews([adapter]);
424
+
425
+ // 0xFF is invalid UTF-8 — getString returns "" under lenient decode.
426
+ // The bytes are still present via getBuffer; we want the buffer
427
+ // reading to win since byteLength > 0.
428
+ await view.set({ key: 'invalid', type: 'buffer', value: [0xff] });
429
+
430
+ await expect(view.get('invalid')).resolves.toEqual({
431
+ key: 'invalid',
432
+ type: 'buffer',
433
+ value: [0xff],
434
+ });
435
+ });
436
+ });