datapeek 0.1.12 → 0.1.14

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%;--header-bg: 0 0% 100%;--sidebar-bg: 0 0% 100%;--content-bg: 0 0% 100%;--tabs-bg: 0 0% 100%;--grid-bg: 0 0% 100%}.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 70% 55%;--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%;--header-bg: 222.2 84% 6.5%;--sidebar-bg: 222.2 84% 5.5%;--content-bg: 222.2 84% 4.9%;--tabs-bg: 222.2 84% 5.2%;--grid-bg: 222.2 84% 5%}*{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}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.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}.bottom-0{bottom: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-20{z-index:20}.z-50{z-index:50}.z-\[100\]{z-index:100}.m-2{margin:.5rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.-mb-px{margin-bottom:-1px}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.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-6{height:1.5rem}.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-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-96{max-height:24rem}.max-h-\[400px\]{max-height:400px}.max-h-\[95vh\]{max-height:95vh}.max-h-\[calc\(100vh-16px\)\]{max-height:calc(100vh - 16px)}.min-h-\[240px\]{min-height:240px}.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-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-auto{width:auto}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[200px\]{min-width:200px}.min-w-\[280px\]{min-width:280px}.min-w-\[600px\]{min-width:600px}.min-w-\[8rem\]{min-width:8rem}.max-w-\[220px\]{max-width:220px}.max-w-\[300px\]{max-width:300px}.max-w-\[3800px\]{max-width:3800px}.max-w-\[400px\]{max-width:400px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.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-cell{cursor:cell}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[1fr_150px\]{grid-template-columns:1fr 150px}.grid-cols-\[1fr_200px\]{grid-template-columns:1fr 200px}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.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-3{gap:.75rem}.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-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))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.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{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-border{border-color:hsl(var(--border))}.border-border\/40{border-color:hsl(var(--border) / .4)}.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-accent{background-color:hsl(var(--accent))}.bg-background{background-color:hsl(var(--background))}.bg-background\/80{background-color:hsl(var(--background) / .8)}.bg-black\/50{background-color:#00000080}.bg-border{background-color:hsl(var(--border))}.bg-card{background-color:hsl(var(--card))}.bg-content-bg{background-color:hsl(var(--content-bg))}.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-grid-bg{background-color:hsl(var(--grid-bg))}.bg-header-bg{background-color:hsl(var(--header-bg))}.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\/20{background-color:hsl(var(--primary) / .2)}.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-sidebar-bg{background-color:hsl(var(--sidebar-bg))}.bg-tabs-bg{background-color:hsl(var(--tabs-bg))}.bg-transparent{background-color:transparent}.fill-yellow-500{fill:#eab308}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.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-0\.5{padding-top:.125rem;padding-bottom:.125rem}.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}.pb-3{padding-bottom:.75rem}.pl-4{padding-left:1rem}.pl-7{padding-left:1.75rem}.pl-8{padding-left:2rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-1\.5{padding-top:.375rem}.pt-2{padding-top:.5rem}.pt-3{padding-top:.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-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.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}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.text-accent-foreground{color:hsl(var(--accent-foreground))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-destructive{color:hsl(var(--destructive))}.text-destructive-foreground{color:hsl(var(--destructive-foreground))}.text-foreground{color:hsl(var(--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-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)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.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}.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-accent\/80:hover{background-color:hsl(var(--accent) / .8)}.hover\:bg-destructive\/10:hover{background-color:hsl(var(--destructive) / .1)}.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-muted\/70:hover{background-color:hsl(var(--muted) / .7)}.hover\:bg-primary\/50:hover{background-color:hsl(var(--primary) / .5)}.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-destructive:hover{color:hsl(var(--destructive))}.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\:ml-0{margin-left:0}.group:hover .group-hover\:opacity-100{opacity:1}.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\:bg-content-bg:is(.dark *){background-color:hsl(var(--content-bg))}.dark\:bg-grid-bg:is(.dark *){background-color:hsl(var(--grid-bg))}.dark\:bg-header-bg:is(.dark *){background-color:hsl(var(--header-bg))}.dark\:bg-muted\/40:is(.dark *){background-color:hsl(var(--muted) / .4)}.dark\:bg-sidebar-bg:is(.dark *){background-color:hsl(var(--sidebar-bg))}.dark\:bg-tabs-bg:is(.dark *){background-color:hsl(var(--tabs-bg))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.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\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/png" href="/favicon.png" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Datapeek - SQL Database Browser</title>
8
- <script type="module" crossorigin src="/assets/index-DgwSpfvt.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BXssAVuE.css">
8
+ <script type="module" crossorigin src="/assets/index-CVuQr9d7.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-Ys_niuVH.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -54,8 +54,15 @@ connectionRoutes.post("/test", async (req, res) => {
54
54
  }
55
55
  });
56
56
  connectionRoutes.get("/provided", (req, res) => {
57
- const connString = getProvidedConnectionString();
58
- res.json({ connectionString: connString || null });
57
+ try {
58
+ const connString = getProvidedConnectionString();
59
+ res.json({ connectionString: connString || null });
60
+ } catch (error) {
61
+ res.status(500).json({
62
+ connectionString: null,
63
+ error: error.message || "Failed to get connection string"
64
+ });
65
+ }
59
66
  });
60
67
  connectionRoutes.post("/", async (req, res) => {
61
68
  const timeout = setTimeout(() => {
@@ -158,12 +165,17 @@ tableRoutes.get("/", async (req, res) => {
158
165
  const result = await executeQuery(query);
159
166
  res.json(result);
160
167
  } catch (error) {
168
+ console.error("Error fetching tables:", error);
161
169
  const errorMessage = error.message || "";
162
170
  if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
163
171
  const { disconnect: disconnect2 } = await import("./db-CJPCGHL3.js");
164
172
  await disconnect2();
165
173
  }
166
- res.status(500).json({ error: error.message || "Failed to fetch tables" });
174
+ const errorDetails = error.originalError?.message || error.originalError?.info?.message || error.message || "Failed to fetch tables";
175
+ res.status(500).json({
176
+ error: error.message || "Failed to fetch tables",
177
+ details: errorDetails
178
+ });
167
179
  }
168
180
  });
169
181
  tableRoutes.get("/:schema/:table", async (req, res) => {
@@ -220,13 +232,13 @@ tableRoutes.get("/:schema/:table", async (req, res) => {
220
232
  ) fk ON c.table_schema = fk.table_schema
221
233
  AND c.table_name = fk.table_name
222
234
  AND c.column_name = fk.column_name
223
- WHERE c.table_schema = @schema
224
- AND c.table_name = @table
235
+ WHERE c.table_schema = ${dialect.param(1)}
236
+ AND c.table_name = ${dialect.param(2)}
225
237
  ORDER BY c.ordinal_position
226
238
  `;
227
239
  const result = await executeQuery(query, [
228
- { name: "schema", value: schema },
229
- { name: "table", value: table }
240
+ { name: "p1", value: schema },
241
+ { name: "p2", value: table }
230
242
  ]);
231
243
  res.json(result);
232
244
  } catch (error) {
@@ -314,9 +326,9 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
314
326
  WHERE table_schema = ${dialect.param(1)} AND table_name = ${dialect.param(2)} AND column_name IN (${placeholders})
315
327
  `;
316
328
  const validateParams = [
317
- { name: "schema", value: schema },
318
- { name: "table", value: table },
319
- ...columnNames.map((col) => ({ name: "col", value: col }))
329
+ { name: "p1", value: schema },
330
+ { name: "p2", value: table },
331
+ ...columnNames.map((col, idx) => ({ name: `p${idx + 3}`, value: col }))
320
332
  ];
321
333
  const validateResult = await executeQuery(validateQuery, validateParams);
322
334
  validateResult.forEach((r) => {
@@ -356,75 +368,111 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
356
368
  switch (operator) {
357
369
  // Text operators
358
370
  case "contains":
359
- filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
371
+ filterParams.push({ name: `p${paramIndex}`, value: `%${String(value)}%` });
360
372
  condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
361
373
  break;
362
374
  case "equals":
363
- filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
375
+ filterParams.push({ name: `p${paramIndex}`, value: String(value) });
364
376
  condition = `${quotedColumn} = ${dialect.param(paramIndex)}`;
365
377
  break;
366
378
  case "startsWith":
367
- filterParams.push({ name: `filter${paramIndex}`, value: `${String(value)}%` });
379
+ filterParams.push({ name: `p${paramIndex}`, value: `${String(value)}%` });
368
380
  condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
369
381
  break;
370
382
  case "endsWith":
371
- filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}` });
383
+ filterParams.push({ name: `p${paramIndex}`, value: `%${String(value)}` });
372
384
  condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
373
385
  break;
374
386
  case "notContains":
375
- filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
387
+ filterParams.push({ name: `p${paramIndex}`, value: `%${String(value)}%` });
376
388
  condition = `${quotedColumn} NOT LIKE ${dialect.param(paramIndex)}`;
377
389
  break;
378
390
  // Number operators
379
391
  case "eq":
380
- filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
392
+ const isNumericType = dataType && ["int", "bigint", "decimal", "numeric", "float", "double", "real", "smallint", "tinyint", "money", "smallmoney"].some((t) => dataType.toLowerCase().includes(t));
393
+ if (isNumericType) {
394
+ const numValue = Number(value);
395
+ if (isNaN(numValue)) {
396
+ console.warn(`Cannot convert filter value to number for ${columnName}, skipping`);
397
+ break;
398
+ }
399
+ filterParams.push({ name: `p${paramIndex}`, value: numValue });
400
+ } else {
401
+ filterParams.push({ name: `p${paramIndex}`, value: String(value) });
402
+ }
381
403
  condition = `${quotedColumn} = ${dialect.param(paramIndex)}`;
382
404
  break;
383
405
  case "gt":
384
- filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
406
+ const gtValue = Number(value);
407
+ if (isNaN(gtValue)) {
408
+ console.warn(`Cannot convert filter value to number for ${columnName} (gt), skipping`);
409
+ break;
410
+ }
411
+ filterParams.push({ name: `p${paramIndex}`, value: gtValue });
385
412
  condition = `${quotedColumn} > ${dialect.param(paramIndex)}`;
386
413
  break;
387
414
  case "gte":
388
- filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
415
+ const gteValue = Number(value);
416
+ if (isNaN(gteValue)) {
417
+ console.warn(`Cannot convert filter value to number for ${columnName} (gte), skipping`);
418
+ break;
419
+ }
420
+ filterParams.push({ name: `p${paramIndex}`, value: gteValue });
389
421
  condition = `${quotedColumn} >= ${dialect.param(paramIndex)}`;
390
422
  break;
391
423
  case "lt":
392
- filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
424
+ const ltValue = Number(value);
425
+ if (isNaN(ltValue)) {
426
+ console.warn(`Cannot convert filter value to number for ${columnName} (lt), skipping`);
427
+ break;
428
+ }
429
+ filterParams.push({ name: `p${paramIndex}`, value: ltValue });
393
430
  condition = `${quotedColumn} < ${dialect.param(paramIndex)}`;
394
431
  break;
395
432
  case "lte":
396
- filterParams.push({ name: `filter${paramIndex}`, value: Number(value) });
433
+ const lteValue = Number(value);
434
+ if (isNaN(lteValue)) {
435
+ console.warn(`Cannot convert filter value to number for ${columnName} (lte), skipping`);
436
+ break;
437
+ }
438
+ filterParams.push({ name: `p${paramIndex}`, value: lteValue });
397
439
  condition = `${quotedColumn} <= ${dialect.param(paramIndex)}`;
398
440
  break;
399
441
  case "between":
400
442
  if (typeof value === "object" && "from" in value && "to" in value) {
401
443
  const fromParamIndex = paramIndex;
402
444
  const toParamIndex = paramIndex + 1;
403
- filterParams.push({ name: `filter${fromParamIndex}`, value: Number(value.from) });
404
- filterParams.push({ name: `filter${toParamIndex}`, value: Number(value.to) });
445
+ const fromValue = Number(value.from);
446
+ const toValue = Number(value.to);
447
+ if (isNaN(fromValue) || isNaN(toValue)) {
448
+ console.warn(`Cannot convert filter values to numbers for ${columnName} (between), skipping`);
449
+ break;
450
+ }
451
+ filterParams.push({ name: `p${fromParamIndex}`, value: fromValue });
452
+ filterParams.push({ name: `p${toParamIndex}`, value: toValue });
405
453
  condition = `${quotedColumn} BETWEEN ${dialect.param(fromParamIndex)} AND ${dialect.param(toParamIndex)}`;
406
454
  paramIndex++;
407
455
  }
408
456
  break;
409
457
  // Date operators
410
458
  case "dateEq":
411
- filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
459
+ filterParams.push({ name: `p${paramIndex}`, value: String(value) });
412
460
  condition = `${dialect.castToDate(quotedColumn)} = ${dialect.castToDate(dialect.param(paramIndex))}`;
413
461
  break;
414
462
  case "dateAfter":
415
- filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
463
+ filterParams.push({ name: `p${paramIndex}`, value: String(value) });
416
464
  condition = `${dialect.castToDate(quotedColumn)} > ${dialect.castToDate(dialect.param(paramIndex))}`;
417
465
  break;
418
466
  case "dateBefore":
419
- filterParams.push({ name: `filter${paramIndex}`, value: String(value) });
467
+ filterParams.push({ name: `p${paramIndex}`, value: String(value) });
420
468
  condition = `${dialect.castToDate(quotedColumn)} < ${dialect.castToDate(dialect.param(paramIndex))}`;
421
469
  break;
422
470
  case "dateBetween":
423
471
  if (typeof value === "object" && "from" in value && "to" in value) {
424
472
  const fromParamIndex = paramIndex;
425
473
  const toParamIndex = paramIndex + 1;
426
- filterParams.push({ name: `filter${fromParamIndex}`, value: String(value.from) });
427
- filterParams.push({ name: `filter${toParamIndex}`, value: String(value.to) });
474
+ filterParams.push({ name: `p${fromParamIndex}`, value: String(value.from) });
475
+ filterParams.push({ name: `p${toParamIndex}`, value: String(value.to) });
428
476
  condition = `${dialect.castToDate(quotedColumn)} BETWEEN ${dialect.castToDate(dialect.param(fromParamIndex))} AND ${dialect.castToDate(dialect.param(toParamIndex))}`;
429
477
  paramIndex++;
430
478
  }
@@ -436,7 +484,7 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
436
484
  const placeholders = [];
437
485
  value.forEach((val, i) => {
438
486
  const currentIndex = paramIndex + i;
439
- filterParams.push({ name: `filter${currentIndex}`, value: val });
487
+ filterParams.push({ name: `p${currentIndex}`, value: val });
440
488
  placeholders.push(dialect.param(currentIndex));
441
489
  });
442
490
  const inOperator = operator === "in" ? "IN" : "NOT IN";
@@ -446,7 +494,7 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
446
494
  break;
447
495
  default:
448
496
  console.warn(`Unknown filter operator: ${operator}, falling back to contains`);
449
- filterParams.push({ name: `filter${paramIndex}`, value: `%${String(value)}%` });
497
+ filterParams.push({ name: `p${paramIndex}`, value: `%${String(value)}%` });
450
498
  condition = `${quotedColumn} LIKE ${dialect.param(paramIndex)}`;
451
499
  }
452
500
  if (condition) {
@@ -483,9 +531,9 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
483
531
  WHERE table_schema = ${dialect.param(1)} AND table_name = ${dialect.param(2)} AND column_name = ${dialect.param(3)}
484
532
  `;
485
533
  const validateResult = await executeQuery(validateQuery, [
486
- { name: "schema", value: schema },
487
- { name: "table", value: table },
488
- { name: "column", value: orderByColumn }
534
+ { name: "p1", value: schema },
535
+ { name: "p2", value: table },
536
+ { name: "p3", value: orderByColumn }
489
537
  ]);
490
538
  if (validateResult.length === 0) {
491
539
  orderByColumn = "";
@@ -507,8 +555,8 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
507
555
  ${!topClause ? dialect.limitOffset(0, 1) : ""}
508
556
  `;
509
557
  const structureResult = await executeQuery(structureQuery, [
510
- { name: "schema", value: schema },
511
- { name: "table", value: table }
558
+ { name: "p1", value: schema },
559
+ { name: "p2", value: table }
512
560
  ]);
513
561
  if (structureResult.length > 0) {
514
562
  orderByColumn = structureResult[0].column_name || structureResult[0].COLUMN_NAME;
@@ -540,8 +588,8 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
540
588
  `;
541
589
  console.log("Fetching foreign keys with query:", fkQuery);
542
590
  const foreignKeys = await executeQuery(fkQuery, [
543
- { name: "schema", value: schema },
544
- { name: "table", value: table }
591
+ { name: "p1", value: schema },
592
+ { name: "p2", value: table }
545
593
  ]);
546
594
  console.log(`Found ${foreignKeys.length} foreign key(s)`);
547
595
  if (foreignKeys.length > 0) {
@@ -558,11 +606,11 @@ tableRoutes.get("/:schema/:table/data", async (req, res) => {
558
606
  WHERE ${tableConditions}
559
607
  ORDER BY table_schema, table_name, ordinal_position
560
608
  `;
561
- const batchParams = uniqueRefTables.flatMap((tableRef) => {
609
+ const batchParams = uniqueRefTables.flatMap((tableRef, idx) => {
562
610
  const [refSchema, refTable] = tableRef.split(".");
563
611
  return [
564
- { name: "refSchema", value: refSchema },
565
- { name: "refTable", value: refTable }
612
+ { name: `p${idx * 2 + 1}`, value: refSchema },
613
+ { name: `p${idx * 2 + 2}`, value: refTable }
566
614
  ];
567
615
  });
568
616
  console.log("Fetching referenced table columns with query:", batchColumnsQuery);
@@ -795,8 +843,8 @@ tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
795
843
  ORDER BY ordinal_position
796
844
  `;
797
845
  const columns = await executeQuery(columnsQuery, [
798
- { name: "refSchema", value: referencedSchema },
799
- { name: "refTable", value: referencedTable }
846
+ { name: "p1", value: referencedSchema },
847
+ { name: "p2", value: referencedTable }
800
848
  ]);
801
849
  const referencedColInfo = columns.find((col) => {
802
850
  const colName = col.column_name || col.COLUMN_NAME;
@@ -838,8 +886,8 @@ tableRoutes.post("/:schema/:table/related-data", async (req, res) => {
838
886
  FROM ${quotedRefSchema}.${quotedRefTable}
839
887
  WHERE ${quotedRefColumn} IN (${placeholders})
840
888
  `;
841
- const params = ids.map((id) => ({
842
- name: "id",
889
+ const params = ids.map((id, idx) => ({
890
+ name: `p${idx + 1}`,
843
891
  value: id
844
892
  }));
845
893
  const result = await executeQuery(dataQuery, params);
@@ -905,9 +953,9 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
905
953
  WHERE c.table_schema = ${dialect.param(1)} AND c.table_name = ${dialect.param(2)} AND c.column_name = ${dialect.param(3)}
906
954
  `;
907
955
  const columnResult = await executeQuery(columnQuery, [
908
- { name: "schema", value: schema },
909
- { name: "table", value: table },
910
- { name: "column", value: column }
956
+ { name: "p1", value: schema },
957
+ { name: "p2", value: table },
958
+ { name: "p3", value: column }
911
959
  ]);
912
960
  if (columnResult.length === 0) {
913
961
  return res.status(400).json({ error: "Column not found" });
@@ -934,7 +982,7 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
934
982
  if (searchQuery && searchQuery.trim()) {
935
983
  const searchCols = columnsToSelect.map((col) => `${dialect.quoteId(col)} LIKE ${dialect.param(1)}`).join(" OR ");
936
984
  query += ` WHERE (${searchCols}) AND ${quotedRefColumn} IS NOT NULL`;
937
- params.push({ name: "search", value: `%${searchQuery.trim()}%` });
985
+ params.push({ name: "p1", value: `%${searchQuery.trim()}%` });
938
986
  } else {
939
987
  query += ` WHERE ${quotedRefColumn} IS NOT NULL`;
940
988
  }
@@ -948,8 +996,8 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
948
996
  ORDER BY ordinal_position
949
997
  `;
950
998
  const refColumns = await executeQuery(refColumnsQuery, [
951
- { name: "refSchema", value: refSchema },
952
- { name: "refTable", value: refTable }
999
+ { name: "p1", value: refSchema },
1000
+ { name: "p2", value: refTable }
953
1001
  ]);
954
1002
  const preferredNames = ["name", "title", "description", "code"];
955
1003
  for (const preferredName of preferredNames) {
@@ -982,7 +1030,7 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
982
1030
  } else {
983
1031
  query += ` WHERE ${quotedRefColumn} LIKE ${dialect.param(1)}`;
984
1032
  }
985
- params.push({ name: "search", value: `%${searchQuery.trim()}%` });
1033
+ params.push({ name: "p1", value: `%${searchQuery.trim()}%` });
986
1034
  query += ` AND ${quotedRefColumn} IS NOT NULL`;
987
1035
  } else {
988
1036
  query += ` WHERE ${quotedRefColumn} IS NOT NULL`;
@@ -1003,7 +1051,7 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
1003
1051
  if (searchQuery && searchQuery.trim()) {
1004
1052
  const searchCols = quotedColumns.map((col) => `${dialect.tryCastToNVarChar(col)} LIKE ${dialect.param(1)}`).join(" OR ");
1005
1053
  query += ` WHERE (${searchCols}) AND ${keyColumn} IS NOT NULL`;
1006
- params.push({ name: "search", value: `%${searchQuery.trim()}%` });
1054
+ params.push({ name: "p1", value: `%${searchQuery.trim()}%` });
1007
1055
  } else {
1008
1056
  query += ` WHERE ${keyColumn} IS NOT NULL`;
1009
1057
  }
@@ -1013,7 +1061,7 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
1013
1061
  if (searchQuery && searchQuery.trim()) {
1014
1062
  if (["varchar", "nvarchar", "char", "nchar", "text", "ntext"].some((t) => dataType.includes(t))) {
1015
1063
  query += ` WHERE ${quotedColumn} LIKE ${dialect.param(1)}`;
1016
- params.push({ name: "search", value: `%${searchQuery.trim()}%` });
1064
+ params.push({ name: "p1", value: `%${searchQuery.trim()}%` });
1017
1065
  query += ` AND ${quotedColumn} IS NOT NULL`;
1018
1066
  } else {
1019
1067
  query += ` WHERE ${quotedColumn} IS NOT NULL`;
@@ -1036,6 +1084,131 @@ tableRoutes.get("/:schema/:table/distinct-values/:column", async (req, res) => {
1036
1084
  res.status(500).json({ error: error.message || "Failed to fetch distinct values" });
1037
1085
  }
1038
1086
  });
1087
+ tableRoutes.get("/:schema/:table/reverse-foreign-keys", async (req, res) => {
1088
+ try {
1089
+ const { schema, table } = req.params;
1090
+ const pool = getConnection();
1091
+ if (!pool) {
1092
+ return res.status(400).json({ error: "Not connected to database" });
1093
+ }
1094
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
1095
+ if (!isConnected) {
1096
+ return res.status(400).json({ error: "Not connected to database" });
1097
+ }
1098
+ const dialect = getDialect();
1099
+ const query = `
1100
+ SELECT
1101
+ kcu1.table_schema as "referencingSchema",
1102
+ kcu1.table_name as "referencingTable",
1103
+ kcu1.column_name as "fkColumnName",
1104
+ kcu2.column_name as "referencedColumn"
1105
+ FROM information_schema.referential_constraints rc
1106
+ INNER JOIN information_schema.key_column_usage kcu1
1107
+ ON rc.constraint_catalog = kcu1.constraint_catalog
1108
+ AND rc.constraint_schema = kcu1.constraint_schema
1109
+ AND rc.constraint_name = kcu1.constraint_name
1110
+ INNER JOIN information_schema.key_column_usage kcu2
1111
+ ON rc.unique_constraint_catalog = kcu2.constraint_catalog
1112
+ AND rc.unique_constraint_schema = kcu2.constraint_schema
1113
+ AND rc.unique_constraint_name = kcu2.constraint_name
1114
+ AND kcu1.ordinal_position = kcu2.ordinal_position
1115
+ WHERE kcu2.table_schema = ${dialect.param(1)}
1116
+ AND kcu2.table_name = ${dialect.param(2)}
1117
+ ORDER BY kcu1.table_schema, kcu1.table_name, kcu1.ordinal_position
1118
+ `;
1119
+ const result = await executeQuery(query, [
1120
+ { name: "p1", value: schema },
1121
+ { name: "p2", value: table }
1122
+ ]);
1123
+ const grouped = {};
1124
+ result.forEach((row) => {
1125
+ const referencingSchema = row.referencingSchema || row.referencing_schema;
1126
+ const referencingTable = row.referencingTable || row.referencing_table;
1127
+ const fkColumnName = row.fkColumnName || row.fk_column_name;
1128
+ const referencedColumn = row.referencedColumn || row.referenced_column;
1129
+ const key = `${referencingSchema}.${referencingTable}`;
1130
+ if (!grouped[key]) {
1131
+ grouped[key] = {
1132
+ referencingSchema,
1133
+ referencingTable,
1134
+ fkColumns: [],
1135
+ referencedColumns: []
1136
+ };
1137
+ }
1138
+ grouped[key].fkColumns.push(fkColumnName);
1139
+ grouped[key].referencedColumns.push(referencedColumn);
1140
+ });
1141
+ const reverseFks = Object.values(grouped);
1142
+ res.json(reverseFks);
1143
+ } catch (error) {
1144
+ console.error("Error fetching reverse foreign keys:", error);
1145
+ const errorMessage = error.message || "";
1146
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
1147
+ const { disconnect: disconnect2 } = await import("./db-CJPCGHL3.js");
1148
+ await disconnect2();
1149
+ }
1150
+ res.status(500).json({ error: error.message || "Failed to fetch reverse foreign keys" });
1151
+ }
1152
+ });
1153
+ tableRoutes.post("/:schema/:table/count-related", async (req, res) => {
1154
+ try {
1155
+ const { schema, table } = req.params;
1156
+ const { referencingSchema, referencingTable, fkColumns, referencedColumns, primaryKeyValues } = req.body;
1157
+ if (!referencingSchema || !referencingTable || !fkColumns || !Array.isArray(fkColumns) || !referencedColumns || !Array.isArray(referencedColumns) || !primaryKeyValues || !Array.isArray(primaryKeyValues)) {
1158
+ return res.status(400).json({ error: "Missing required parameters" });
1159
+ }
1160
+ if (fkColumns.length !== referencedColumns.length || fkColumns.length !== primaryKeyValues.length) {
1161
+ return res.status(400).json({ error: "FK columns, referenced columns, and primary key values arrays must have the same length" });
1162
+ }
1163
+ const pool = getConnection();
1164
+ if (!pool) {
1165
+ return res.status(400).json({ error: "Not connected to database" });
1166
+ }
1167
+ const isConnected = "connected" in pool ? pool.connected === true : !pool.ended;
1168
+ if (!isConnected) {
1169
+ return res.status(400).json({ error: "Not connected to database" });
1170
+ }
1171
+ const dialect = getDialect();
1172
+ const quotedSchema = dialect.quoteId(referencingSchema);
1173
+ const quotedTable = dialect.quoteId(referencingTable);
1174
+ const whereConditions = [];
1175
+ let paramIndex = 1;
1176
+ const params = [];
1177
+ fkColumns.forEach((fkCol, idx) => {
1178
+ const quotedCol = dialect.quoteId(fkCol);
1179
+ let pkValue = primaryKeyValues[idx];
1180
+ if (typeof pkValue === "number" && isNaN(pkValue)) {
1181
+ pkValue = null;
1182
+ }
1183
+ if (pkValue === "NaN" || pkValue === "nan") {
1184
+ pkValue = null;
1185
+ }
1186
+ if (pkValue === null || pkValue === void 0) {
1187
+ whereConditions.push(`${quotedCol} IS NULL`);
1188
+ } else {
1189
+ whereConditions.push(`${quotedCol} = ${dialect.param(paramIndex)}`);
1190
+ params.push({ name: `p${paramIndex}`, value: pkValue });
1191
+ paramIndex++;
1192
+ }
1193
+ });
1194
+ const query = `
1195
+ SELECT COUNT(*) as "count"
1196
+ FROM ${quotedSchema}.${quotedTable}
1197
+ WHERE ${whereConditions.join(" AND ")}
1198
+ `;
1199
+ const result = await executeQuery(query, params);
1200
+ const count = result[0]?.count || result[0]?.COUNT || 0;
1201
+ res.json({ count: Number(count) });
1202
+ } catch (error) {
1203
+ console.error("Error counting related rows:", error);
1204
+ const errorMessage = error.message || "";
1205
+ if (errorMessage.includes("Login failed") || errorMessage.includes("authentication") || errorMessage.includes("password authentication")) {
1206
+ const { disconnect: disconnect2 } = await import("./db-CJPCGHL3.js");
1207
+ await disconnect2();
1208
+ }
1209
+ res.status(500).json({ error: error.message || "Failed to count related rows" });
1210
+ }
1211
+ });
1039
1212
 
1040
1213
  // src/server/routes/query.ts
1041
1214
  import { Router as Router3 } from "express";