@rozenite/storage-plugin 1.9.0 → 1.11.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/CHANGELOG.md +24 -0
- package/dist/devtools/assets/panel-Bm-SWF7d.js +33 -0
- package/dist/devtools/assets/panel-DIqI4WSp.css +1 -0
- package/dist/devtools/panel.html +2 -2
- package/dist/react-native/chunks/index.require.cjs +1 -1
- package/dist/react-native/chunks/index.require.js +38 -56
- package/dist/rozenite.json +1 -1
- package/package.json +9 -6
- package/src/react-native/adapters/__tests__/mmkv.test.ts +300 -5
- package/src/react-native/adapters/mmkv.ts +51 -40
- package/src/ui/__tests__/binary-value-editor-state.test.ts +199 -0
- package/src/ui/__tests__/binary.test.ts +251 -0
- package/src/ui/__tests__/type-conversion.test.ts +123 -0
- package/src/ui/add-entry-dialog.tsx +83 -173
- package/src/ui/binary-value-editor-state.ts +125 -0
- package/src/ui/binary-value-editor.tsx +168 -0
- package/src/ui/binary.ts +123 -0
- package/src/ui/edit-entry-dialog.tsx +64 -161
- package/src/ui/editable-table.tsx +12 -10
- package/src/ui/editor-switcher.tsx +62 -0
- package/src/ui/entry-detail-dialog.tsx +14 -6
- package/src/ui/type-conversion.ts +105 -0
- package/src/ui/typed-value-editor.tsx +96 -0
- package/dist/devtools/assets/panel-DVxtKLI9.css +0 -1
- package/dist/devtools/assets/panel-DnjbFMGo.js +0 -22
- package/src/react-native/is-garbled.ts +0 -17
|
@@ -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}
|
package/dist/devtools/panel.html
CHANGED
|
@@ -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-
|
|
26
|
-
<link rel="stylesheet" crossorigin href="../devtools/assets/panel-
|
|
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"),
|
|
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
|
|
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
|
|
4
|
-
const
|
|
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
|
-
},
|
|
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
|
-
},
|
|
33
|
+
}, p = (t, e) => {
|
|
34
34
|
const r = t.getString(e);
|
|
35
35
|
if (r !== void 0 && r.length > 0)
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
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(
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
},
|
|
57
|
+
}, S = (t, e) => {
|
|
76
58
|
if (t instanceof RegExp)
|
|
77
59
|
return (r) => (t.lastIndex = 0, t.test(`${e}:${r}`));
|
|
78
|
-
},
|
|
60
|
+
}, V = ({
|
|
79
61
|
adapterId: t = "mmkv",
|
|
80
62
|
adapterName: e = "MMKV",
|
|
81
63
|
storages: r,
|
|
82
|
-
blacklist:
|
|
64
|
+
blacklist: a
|
|
83
65
|
}) => {
|
|
84
|
-
const s =
|
|
85
|
-
([
|
|
86
|
-
const
|
|
66
|
+
const s = f(r), i = Object.entries(s).map(
|
|
67
|
+
([u, g]) => {
|
|
68
|
+
const o = c(g);
|
|
87
69
|
return {
|
|
88
|
-
id:
|
|
89
|
-
name:
|
|
90
|
-
blacklist:
|
|
91
|
-
shouldFilterKey:
|
|
70
|
+
id: u,
|
|
71
|
+
name: u,
|
|
72
|
+
blacklist: b(a, u),
|
|
73
|
+
shouldFilterKey: S(a, u),
|
|
92
74
|
capabilities: {
|
|
93
|
-
supportedTypes:
|
|
75
|
+
supportedTypes: d
|
|
94
76
|
},
|
|
95
77
|
storage: {
|
|
96
78
|
kind: "sync",
|
|
97
|
-
getAllKeys: () =>
|
|
98
|
-
get: (
|
|
99
|
-
set: (
|
|
100
|
-
delete: (
|
|
101
|
-
subscribe: (
|
|
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:
|
|
91
|
+
storages: i
|
|
110
92
|
};
|
|
111
93
|
};
|
|
112
94
|
export {
|
|
113
95
|
B as createAsyncStorageAdapter,
|
|
114
96
|
E as createExpoAsyncStorageAdapter,
|
|
115
|
-
|
|
116
|
-
|
|
97
|
+
x as createExpoSecureStorageAdapter,
|
|
98
|
+
V as createMMKVStorageAdapter
|
|
117
99
|
};
|
package/dist/rozenite.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@rozenite/storage-plugin","version":"1.
|
|
1
|
+
{"name":"@rozenite/storage-plugin","version":"1.11.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.
|
|
3
|
+
"version": "1.11.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/
|
|
19
|
-
"@rozenite/agent-
|
|
20
|
-
"@rozenite/
|
|
18
|
+
"@rozenite/agent-shared": "1.11.0",
|
|
19
|
+
"@rozenite/agent-bridge": "1.11.0",
|
|
20
|
+
"@rozenite/plugin-bridge": "1.11.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.
|
|
42
|
-
"rozenite": "1.
|
|
44
|
+
"@rozenite/vite-plugin": "1.11.0",
|
|
45
|
+
"rozenite": "1.11.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(() =>
|
|
113
|
-
|
|
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(
|
|
133
|
-
|
|
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
|
+
});
|