datapeek 0.1.1

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: 222.2 84% 4.9%;--card: 0 0% 100%;--card-foreground: 222.2 84% 4.9%;--popover: 0 0% 100%;--popover-foreground: 222.2 84% 4.9%;--primary: 222.2 47.4% 11.2%;--primary-foreground: 210 40% 98%;--secondary: 210 40% 96.1%;--secondary-foreground: 222.2 47.4% 11.2%;--muted: 210 40% 96.1%;--muted-foreground: 215.4 16.3% 46.9%;--accent: 210 40% 96.1%;--accent-foreground: 222.2 47.4% 11.2%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 210 40% 98%;--border: 214.3 31.8% 91.4%;--input: 214.3 31.8% 91.4%;--ring: 222.2 84% 4.9%;--radius: .5rem;--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%}.dark{--background: 222.2 84% 4.9%;--foreground: 210 40% 98%;--card: 222.2 84% 4.9%;--card-foreground: 210 40% 98%;--popover: 222.2 84% 4.9%;--popover-foreground: 210 40% 98%;--primary: 210 40% 98%;--primary-foreground: 222.2 47.4% 11.2%;--secondary: 217.2 32.6% 17.5%;--secondary-foreground: 210 40% 98%;--muted: 217.2 32.6% 17.5%;--muted-foreground: 215 20.2% 65.1%;--accent: 217.2 32.6% 17.5%;--accent-foreground: 210 40% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 210 40% 98%;--border: 217.2 32.6% 17.5%;--input: 217.2 32.6% 17.5%;--ring: 212.7 26.8% 83.9%;--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%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}code,pre{font-family:JetBrains Mono,Menlo,Monaco,Courier New,monospace}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.left-0{left:0}.left-2{left:.5rem}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-4{margin-left:1rem}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-64{max-height:16rem}.max-h-96{max-height:24rem}.w-12{width:3rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-full{width:100%}.min-w-\[8rem\]{min-width:8rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-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))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-move{cursor:move}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.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-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))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-border\/50{border-color:hsl(var(--border) / .5)}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-input{border-color:hsl(var(--input))}.border-primary{border-color:hsl(var(--primary))}.bg-background{background-color:hsl(var(--background))}.bg-black\/50{background-color:#00000080}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive) / .1)}.bg-destructive\/90{background-color:hsl(var(--destructive) / .9)}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-popover{background-color:hsl(var(--popover))}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/90{background-color:hsl(var(--primary) / .9)}.bg-secondary{background-color:hsl(var(--secondary))}.bg-secondary\/80{background-color:hsl(var(--secondary) / .8)}.bg-transparent{background-color:transparent}.fill-yellow-500{fill:#eab308}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.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}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pl-8{padding-left:2rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.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-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-popover-foreground{color:hsl(var(--popover-foreground))}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background))}.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-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}.transition-opacity{transition-property:opacity;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))}}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:border-b-0:last-child{border-bottom-width:0px}.last\:border-r-0:last-child{border-right-width:0px}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-green-500\/20:hover{background-color:#22c55e33}.hover\:bg-muted\/30:hover{background-color:hsl(var(--muted) / .3)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:bg-transparent:hover{background-color:transparent}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-destructive\/10:focus{background-color:hsl(var(--destructive) / .1)}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:text-destructive:focus{color:hsl(var(--destructive))}.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-ring:focus{--tw-ring-color: hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--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(1px + 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-visible\:ring-ring:focus-visible{--tw-ring-color: hsl(var(--ring))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}.group:hover .group-hover\:opacity-50{opacity:.5}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.dark\:text-green-400:is(.dark *){--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}@media(min-width:640px){.sm\:max-w-\[500px\]{max-width:500px}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Datapeek - SQL Database Browser</title>
8
+ <script type="module" crossorigin src="/assets/index-BKOeaWHM.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-D1Z3cvU6.css">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,116 @@
1
+ // src/server/db/mssql.ts
2
+ import sql from "mssql";
3
+ var pool = null;
4
+ function parseConnectionString(connectionString) {
5
+ const config = {
6
+ server: "",
7
+ database: "",
8
+ options: {
9
+ encrypt: true,
10
+ trustServerCertificate: false,
11
+ enableArithAbort: true
12
+ }
13
+ };
14
+ const parts = connectionString.split(";");
15
+ for (const part of parts) {
16
+ const [key, ...valueParts] = part.split("=");
17
+ const value = valueParts.join("=").trim();
18
+ const keyLower = key.trim().toLowerCase();
19
+ switch (keyLower) {
20
+ case "server":
21
+ case "data source":
22
+ let serverValue = value;
23
+ if (serverValue.startsWith("tcp:")) {
24
+ serverValue = serverValue.substring(4);
25
+ }
26
+ const [serverHost, serverPort] = serverValue.split(",");
27
+ config.server = serverHost.trim();
28
+ if (serverPort) {
29
+ config.port = parseInt(serverPort.trim(), 10);
30
+ }
31
+ break;
32
+ case "database":
33
+ case "initial catalog":
34
+ config.database = value;
35
+ break;
36
+ case "user id":
37
+ case "userid":
38
+ case "uid":
39
+ config.user = value;
40
+ break;
41
+ case "password":
42
+ case "pwd":
43
+ config.password = value;
44
+ break;
45
+ case "port":
46
+ config.port = parseInt(value, 10);
47
+ break;
48
+ case "encrypt":
49
+ config.options.encrypt = value.toLowerCase() === "true";
50
+ break;
51
+ case "trustservercertificate":
52
+ case "trust server certificate":
53
+ config.options.trustServerCertificate = value.toLowerCase() === "true";
54
+ break;
55
+ }
56
+ }
57
+ return config;
58
+ }
59
+ async function testConnection(config) {
60
+ const testPool = typeof config === "string" ? new sql.ConnectionPool(parseConnectionString(config)) : new sql.ConnectionPool(config);
61
+ try {
62
+ await testPool.connect();
63
+ await testPool.close();
64
+ } catch (error) {
65
+ throw error;
66
+ }
67
+ }
68
+ async function connect(config) {
69
+ await disconnect();
70
+ const connectionConfig = typeof config === "string" ? parseConnectionString(config) : config;
71
+ pool = new sql.ConnectionPool(connectionConfig);
72
+ try {
73
+ await pool.connect();
74
+ } catch (error) {
75
+ pool = null;
76
+ throw error;
77
+ }
78
+ }
79
+ async function disconnect() {
80
+ if (pool) {
81
+ try {
82
+ await pool.close();
83
+ } catch (error) {
84
+ }
85
+ pool = null;
86
+ }
87
+ }
88
+ function getConnection() {
89
+ return pool;
90
+ }
91
+ async function executeQuery(query, parameters) {
92
+ if (!pool || !pool.connected) {
93
+ throw new Error("Not connected to database");
94
+ }
95
+ const request = pool.request();
96
+ if (parameters) {
97
+ for (const param of parameters) {
98
+ if (param.type) {
99
+ request.input(param.name, param.type, param.value);
100
+ } else {
101
+ request.input(param.name, param.value);
102
+ }
103
+ }
104
+ }
105
+ const result = await request.query(query);
106
+ return result.recordset || [];
107
+ }
108
+
109
+ export {
110
+ parseConnectionString,
111
+ testConnection,
112
+ connect,
113
+ disconnect,
114
+ getConnection,
115
+ executeQuery
116
+ };
@@ -0,0 +1,106 @@
1
+ // src/server/db/mssql.ts
2
+ import sql from "mssql";
3
+ var pool = null;
4
+ function parseConnectionString(connectionString) {
5
+ const config = {
6
+ server: "",
7
+ database: "",
8
+ options: {
9
+ encrypt: true,
10
+ trustServerCertificate: false,
11
+ enableArithAbort: true
12
+ }
13
+ };
14
+ const parts = connectionString.split(";");
15
+ for (const part of parts) {
16
+ const [key, ...valueParts] = part.split("=");
17
+ const value = valueParts.join("=").trim();
18
+ switch (key.trim().toLowerCase()) {
19
+ case "server":
20
+ case "data source":
21
+ config.server = value;
22
+ break;
23
+ case "database":
24
+ case "initial catalog":
25
+ config.database = value;
26
+ break;
27
+ case "user id":
28
+ case "uid":
29
+ config.user = value;
30
+ break;
31
+ case "password":
32
+ case "pwd":
33
+ config.password = value;
34
+ break;
35
+ case "port":
36
+ config.port = parseInt(value, 10);
37
+ break;
38
+ case "encrypt":
39
+ config.options.encrypt = value.toLowerCase() === "true";
40
+ break;
41
+ case "trustservercertificate":
42
+ case "trust server certificate":
43
+ config.options.trustServerCertificate = value.toLowerCase() === "true";
44
+ break;
45
+ }
46
+ }
47
+ return config;
48
+ }
49
+ async function testConnection(config) {
50
+ const testPool = typeof config === "string" ? new sql.ConnectionPool(parseConnectionString(config)) : new sql.ConnectionPool(config);
51
+ try {
52
+ await testPool.connect();
53
+ await testPool.close();
54
+ } catch (error) {
55
+ throw error;
56
+ }
57
+ }
58
+ async function connect(config) {
59
+ await disconnect();
60
+ const connectionConfig = typeof config === "string" ? parseConnectionString(config) : config;
61
+ pool = new sql.ConnectionPool(connectionConfig);
62
+ try {
63
+ await pool.connect();
64
+ } catch (error) {
65
+ pool = null;
66
+ throw error;
67
+ }
68
+ }
69
+ async function disconnect() {
70
+ if (pool) {
71
+ try {
72
+ await pool.close();
73
+ } catch (error) {
74
+ }
75
+ pool = null;
76
+ }
77
+ }
78
+ function getConnection() {
79
+ return pool;
80
+ }
81
+ async function executeQuery(query, parameters) {
82
+ if (!pool || !pool.connected) {
83
+ throw new Error("Not connected to database");
84
+ }
85
+ const request = pool.request();
86
+ if (parameters) {
87
+ for (const param of parameters) {
88
+ if (param.type) {
89
+ request.input(param.name, param.type, param.value);
90
+ } else {
91
+ request.input(param.name, param.value);
92
+ }
93
+ }
94
+ }
95
+ const result = await request.query(query);
96
+ return result.recordset || [];
97
+ }
98
+
99
+ export {
100
+ parseConnectionString,
101
+ testConnection,
102
+ connect,
103
+ disconnect,
104
+ getConnection,
105
+ executeQuery
106
+ };
@@ -0,0 +1,378 @@
1
+ import {
2
+ connect,
3
+ disconnect,
4
+ executeQuery,
5
+ getConnection,
6
+ testConnection
7
+ } from "./chunk-7G2KHS5I.js";
8
+
9
+ // src/server/index.ts
10
+ import express from "express";
11
+ import cors from "cors";
12
+ import path from "path";
13
+ import { fileURLToPath } from "url";
14
+
15
+ // src/server/routes/connection.ts
16
+ import { Router } from "express";
17
+ var connectionRoutes = Router();
18
+ connectionRoutes.post("/test", async (req, res) => {
19
+ const timeout = setTimeout(() => {
20
+ if (!res.headersSent) {
21
+ res.status(408).json({
22
+ success: false,
23
+ message: "Connection test timeout"
24
+ });
25
+ }
26
+ }, 1e4);
27
+ try {
28
+ const config = req.body;
29
+ await testConnection(config);
30
+ clearTimeout(timeout);
31
+ if (!res.headersSent) {
32
+ res.json({ success: true, message: "Connection successful" });
33
+ }
34
+ } catch (error) {
35
+ clearTimeout(timeout);
36
+ if (!res.headersSent) {
37
+ res.status(400).json({
38
+ success: false,
39
+ message: error.message || "Connection failed"
40
+ });
41
+ }
42
+ }
43
+ });
44
+ connectionRoutes.get("/provided", (req, res) => {
45
+ const connString = getProvidedConnectionString();
46
+ res.json({ connectionString: connString || null });
47
+ });
48
+ connectionRoutes.post("/", async (req, res) => {
49
+ const timeout = setTimeout(() => {
50
+ if (!res.headersSent) {
51
+ res.status(408).json({
52
+ success: false,
53
+ message: "Connection timeout"
54
+ });
55
+ }
56
+ }, 15e3);
57
+ try {
58
+ const config = req.body;
59
+ await connect(config);
60
+ clearTimeout(timeout);
61
+ if (!res.headersSent) {
62
+ res.json({ success: true, message: "Connected" });
63
+ }
64
+ } catch (error) {
65
+ clearTimeout(timeout);
66
+ if (!res.headersSent) {
67
+ res.status(400).json({
68
+ success: false,
69
+ message: error.message || "Connection failed"
70
+ });
71
+ }
72
+ }
73
+ });
74
+ connectionRoutes.delete("/", async (req, res) => {
75
+ try {
76
+ await disconnect();
77
+ res.json({ success: true, message: "Disconnected" });
78
+ } catch (error) {
79
+ res.status(500).json({
80
+ success: false,
81
+ message: error.message || "Disconnect failed"
82
+ });
83
+ }
84
+ });
85
+ connectionRoutes.get("/status", async (req, res) => {
86
+ try {
87
+ const { getConnection: getConnection2, executeQuery: executeQuery2 } = await import("./mssql-7KV6MCZK.js");
88
+ const pool = getConnection2();
89
+ if (pool && pool.connected) {
90
+ try {
91
+ const result = await executeQuery2("SELECT DB_NAME() as databaseName");
92
+ const databaseName = result[0]?.databaseName || null;
93
+ res.json({ connected: true, databaseName });
94
+ } catch {
95
+ res.json({ connected: true, databaseName: null });
96
+ }
97
+ } else {
98
+ res.json({ connected: false });
99
+ }
100
+ } catch {
101
+ res.json({ connected: false });
102
+ }
103
+ });
104
+
105
+ // src/server/routes/tables.ts
106
+ import { Router as Router2 } from "express";
107
+ import sql from "mssql";
108
+ var tableRoutes = Router2();
109
+ tableRoutes.get("/", async (req, res) => {
110
+ try {
111
+ const pool = getConnection();
112
+ if (!pool || !pool.connected) {
113
+ return res.status(400).json({ error: "Not connected to database" });
114
+ }
115
+ const query = `
116
+ SELECT
117
+ TABLE_SCHEMA as schemaName,
118
+ TABLE_NAME as tableName
119
+ FROM INFORMATION_SCHEMA.TABLES
120
+ WHERE TABLE_TYPE = 'BASE TABLE'
121
+ ORDER BY TABLE_SCHEMA, TABLE_NAME
122
+ `;
123
+ const result = await executeQuery(query);
124
+ res.json(result);
125
+ } catch (error) {
126
+ res.status(500).json({ error: error.message || "Failed to fetch tables" });
127
+ }
128
+ });
129
+ tableRoutes.get("/:schema/:table", async (req, res) => {
130
+ try {
131
+ const { schema, table } = req.params;
132
+ const pool = getConnection();
133
+ if (!pool || !pool.connected) {
134
+ return res.status(400).json({ error: "Not connected to database" });
135
+ }
136
+ const query = `
137
+ SELECT
138
+ c.COLUMN_NAME as columnName,
139
+ c.DATA_TYPE as dataType,
140
+ c.CHARACTER_MAXIMUM_LENGTH as maxLength,
141
+ c.IS_NULLABLE as isNullable,
142
+ c.COLUMN_DEFAULT as defaultValue,
143
+ CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as isPrimaryKey
144
+ FROM INFORMATION_SCHEMA.COLUMNS c
145
+ LEFT JOIN (
146
+ SELECT ku.TABLE_SCHEMA, ku.TABLE_NAME, ku.COLUMN_NAME
147
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
148
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku
149
+ ON tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
150
+ AND tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
151
+ ) pk ON c.TABLE_SCHEMA = pk.TABLE_SCHEMA
152
+ AND c.TABLE_NAME = pk.TABLE_NAME
153
+ AND c.COLUMN_NAME = pk.COLUMN_NAME
154
+ WHERE c.TABLE_SCHEMA = @schema
155
+ AND c.TABLE_NAME = @table
156
+ ORDER BY c.ORDINAL_POSITION
157
+ `;
158
+ const result = await executeQuery(query, [
159
+ { name: "schema", value: schema, type: sql.NVarChar },
160
+ { name: "table", value: table, type: sql.NVarChar }
161
+ ]);
162
+ res.json(result);
163
+ } catch (error) {
164
+ res.status(500).json({ error: error.message || "Failed to fetch table structure" });
165
+ }
166
+ });
167
+ tableRoutes.get("/:schema/:table/data", async (req, res) => {
168
+ try {
169
+ const { schema, table } = req.params;
170
+ const page = parseInt(req.query.page) || 1;
171
+ const pageSize = Math.min(parseInt(req.query.pageSize) || 100, 1e3);
172
+ const sortColumn = req.query.sortColumn;
173
+ const sortDirection = req.query.sortDirection || "asc";
174
+ const offset = (page - 1) * pageSize;
175
+ const pool = getConnection();
176
+ if (!pool || !pool.connected) {
177
+ return res.status(400).json({ error: "Not connected to database" });
178
+ }
179
+ const countQuery = `SELECT COUNT(*) as total FROM [${schema}].[${table}]`;
180
+ const countResult = await executeQuery(countQuery);
181
+ const total = countResult[0]?.total || 0;
182
+ let orderByColumn = sortColumn || "";
183
+ let orderByDirection = sortDirection?.toUpperCase() === "DESC" ? "DESC" : "ASC";
184
+ if (orderByColumn) {
185
+ try {
186
+ const validateQuery = `
187
+ SELECT COLUMN_NAME
188
+ FROM INFORMATION_SCHEMA.COLUMNS
189
+ WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table AND COLUMN_NAME = @column
190
+ `;
191
+ const validateResult = await executeQuery(validateQuery, [
192
+ { name: "schema", value: schema, type: sql.NVarChar },
193
+ { name: "table", value: table, type: sql.NVarChar },
194
+ { name: "column", value: orderByColumn, type: sql.NVarChar }
195
+ ]);
196
+ if (validateResult.length === 0) {
197
+ orderByColumn = "";
198
+ }
199
+ } catch (e) {
200
+ orderByColumn = "";
201
+ }
202
+ }
203
+ if (!orderByColumn) {
204
+ try {
205
+ const structureQuery = `
206
+ SELECT TOP 1 COLUMN_NAME
207
+ FROM INFORMATION_SCHEMA.COLUMNS
208
+ WHERE TABLE_SCHEMA = @schema AND TABLE_NAME = @table
209
+ ORDER BY ORDINAL_POSITION
210
+ `;
211
+ const structureResult = await executeQuery(structureQuery, [
212
+ { name: "schema", value: schema, type: sql.NVarChar },
213
+ { name: "table", value: table, type: sql.NVarChar }
214
+ ]);
215
+ if (structureResult.length > 0) {
216
+ orderByColumn = structureResult[0].COLUMN_NAME;
217
+ }
218
+ } catch (e) {
219
+ }
220
+ }
221
+ let data;
222
+ let generatedQuery = "";
223
+ if (orderByColumn) {
224
+ generatedQuery = `SELECT * FROM [${schema}].[${table}]
225
+ ORDER BY [${orderByColumn}] ${orderByDirection}
226
+ OFFSET ${offset} ROWS
227
+ FETCH NEXT ${pageSize} ROWS ONLY`;
228
+ const dataQuery = `
229
+ SELECT * FROM [${schema}].[${table}]
230
+ ORDER BY [${orderByColumn}] ${orderByDirection}
231
+ OFFSET @offset ROWS
232
+ FETCH NEXT @pageSize ROWS ONLY
233
+ `;
234
+ data = await executeQuery(dataQuery, [
235
+ { name: "offset", value: offset, type: sql.Int },
236
+ { name: "pageSize", value: pageSize, type: sql.Int }
237
+ ]);
238
+ } else {
239
+ generatedQuery = `SELECT * FROM (
240
+ SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
241
+ FROM [${schema}].[${table}]
242
+ ) t
243
+ WHERE rn > ${offset} AND rn <= ${offset + pageSize}
244
+ ORDER BY rn`;
245
+ const dataQuery = `
246
+ SELECT * FROM (
247
+ SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as rn
248
+ FROM [${schema}].[${table}]
249
+ ) t
250
+ WHERE rn > @offset AND rn <= @offset + @pageSize
251
+ ORDER BY rn
252
+ `;
253
+ data = await executeQuery(dataQuery, [
254
+ { name: "offset", value: offset, type: sql.Int },
255
+ { name: "pageSize", value: pageSize, type: sql.Int }
256
+ ]);
257
+ data = data.map((row) => {
258
+ const { rn, ...rest } = row;
259
+ return rest;
260
+ });
261
+ }
262
+ res.json({
263
+ data,
264
+ query: generatedQuery,
265
+ pagination: {
266
+ page,
267
+ pageSize,
268
+ total,
269
+ totalPages: Math.ceil(total / pageSize)
270
+ }
271
+ });
272
+ } catch (error) {
273
+ console.error("Error fetching table data:", error);
274
+ const errorMessage = error.message || "Failed to fetch table data";
275
+ const errorDetails = error.originalError?.message || error.originalError?.info?.message || "";
276
+ res.status(500).json({
277
+ error: errorMessage,
278
+ details: errorDetails
279
+ });
280
+ }
281
+ });
282
+
283
+ // src/server/routes/query.ts
284
+ import { Router as Router3 } from "express";
285
+ var queryRoutes = Router3();
286
+ queryRoutes.post("/", async (req, res) => {
287
+ try {
288
+ const { query: sqlQuery } = req.body;
289
+ if (!sqlQuery || typeof sqlQuery !== "string") {
290
+ return res.status(400).json({ error: "Query is required" });
291
+ }
292
+ const trimmedQuery = sqlQuery.trim().toUpperCase();
293
+ if (!trimmedQuery.startsWith("SELECT")) {
294
+ return res.status(400).json({
295
+ error: "Only SELECT queries are allowed (read-only mode)"
296
+ });
297
+ }
298
+ const dangerousKeywords = ["DROP", "DELETE", "INSERT", "UPDATE", "ALTER", "CREATE", "TRUNCATE", "EXEC", "EXECUTE"];
299
+ const hasDangerousKeyword = dangerousKeywords.some(
300
+ (keyword) => trimmedQuery.includes(keyword)
301
+ );
302
+ if (hasDangerousKeyword) {
303
+ return res.status(400).json({
304
+ error: "Query contains prohibited keywords. Only SELECT queries are allowed."
305
+ });
306
+ }
307
+ const result = await executeQuery(sqlQuery);
308
+ res.json({ data: result });
309
+ } catch (error) {
310
+ res.status(500).json({
311
+ error: error.message || "Query execution failed",
312
+ details: error.originalError?.message
313
+ });
314
+ }
315
+ });
316
+
317
+ // src/server/index.ts
318
+ var __filename = fileURLToPath(import.meta.url);
319
+ var __dirname = path.dirname(__filename);
320
+ var providedConnectionString;
321
+ async function startServer(port2, connectionString2) {
322
+ providedConnectionString = connectionString2;
323
+ const app = express();
324
+ app.use(cors());
325
+ app.use(express.json());
326
+ app.use("/api/connect", connectionRoutes);
327
+ app.use("/api/tables", tableRoutes);
328
+ app.use("/api/query", queryRoutes);
329
+ if (process.env.NODE_ENV === "production") {
330
+ const clientDist = path.join(__dirname, "../client");
331
+ app.use(express.static(clientDist));
332
+ app.get("*", (req, res) => {
333
+ if (req.path.startsWith("/api")) {
334
+ return res.status(404).json({ error: "Not found" });
335
+ }
336
+ res.sendFile(path.join(clientDist, "index.html"));
337
+ });
338
+ }
339
+ app.get("/api/health", (req, res) => {
340
+ res.json({ status: "ok", hasConnectionString: !!providedConnectionString });
341
+ });
342
+ return new Promise((resolve, reject) => {
343
+ const server = app.listen(port2, () => {
344
+ resolve(app);
345
+ });
346
+ server.on("error", (error) => {
347
+ if (error.code === "EADDRINUSE") {
348
+ reject(new Error(`Port ${port2} is already in use`));
349
+ } else {
350
+ reject(error);
351
+ }
352
+ });
353
+ });
354
+ }
355
+ function getProvidedConnectionString() {
356
+ return providedConnectionString;
357
+ }
358
+
359
+ // src/server/dev.ts
360
+ var port = parseInt(process.env.PORT || "4983", 10);
361
+ var connectionString = process.env.CONNECTION_STRING;
362
+ if (connectionString) {
363
+ console.log(`\u{1F4CA} Connection string detected: ${connectionString.substring(0, 50)}...`);
364
+ }
365
+ startServer(port, connectionString).then(() => {
366
+ console.log(`
367
+ \u{1F680} Datapeek dev server running at http://localhost:${port}`);
368
+ console.log(`\u{1F4E1} API endpoints available at http://localhost:${port}/api`);
369
+ if (connectionString) {
370
+ console.log(`\u{1F4CA} Connection string provided: ${connectionString.substring(0, 50)}...`);
371
+ }
372
+ console.log(`
373
+ Press Ctrl+C to stop
374
+ `);
375
+ }).catch((error) => {
376
+ console.error("Failed to start server:", error);
377
+ process.exit(1);
378
+ });