@sun-yryr/queot 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 sun-yryr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({dir: import.meta.url})
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-500:oklch(63.7% .237 25.331);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-700:oklch(55.5% .163 48.998);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-700:oklch(50.8% .118 165.612);--color-sky-500:oklch(68.5% .169 237.323);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-700:oklch(45.7% .24 277.023);--color-violet-500:oklch(60.6% .25 292.717);--color-rose-200:oklch(89.2% .058 10.001);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-700:oklch(51.4% .222 16.935);--color-rose-800:oklch(45.5% .188 13.697);--color-zinc-50:oklch(98.5% 0 0);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-white:#fff;--spacing:.25rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--font-weight-black:900;--leading-snug:1.375;--leading-relaxed:1.625;--radius-lg:.5rem;--radius-xl:.75rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:root{color-scheme:light dark}*{box-sizing:border-box}body{margin:calc(var(--spacing)*0);background-color:var(--color-white);color:var(--color-zinc-900)}@media(prefers-color-scheme:dark){body{background-color:var(--color-zinc-950);color:var(--color-zinc-100)}}body{font-family:var(--font-sans)}.planSection{margin-top:calc(var(--spacing)*5);border-top-style:var(--tw-border-style);border-color:#e4e4e7b3;border-top-width:1px}@supports (color:color-mix(in lab,red,red)){.planSection{border-color:color-mix(in oklab,var(--color-zinc-200)70%,transparent)}}.planSection{padding-top:calc(var(--spacing)*4)}@media(prefers-color-scheme:dark){.planSection{border-color:#27272a99}@supports (color:color-mix(in lab,red,red)){.planSection{border-color:color-mix(in oklab,var(--color-zinc-800)60%,transparent)}}}.planHeader{align-items:center}.planModeBadge{border-style:var(--tw-border-style);border-width:1px;border-color:#625fff4d;border-radius:3.40282e38px}@supports (color:color-mix(in lab,red,red)){.planModeBadge{border-color:color-mix(in oklab,var(--color-indigo-500)30%,transparent)}}.planModeBadge{background-color:#625fff26}@supports (color:color-mix(in lab,red,red)){.planModeBadge{background-color:color-mix(in oklab,var(--color-indigo-500)15%,transparent)}}.planModeBadge{padding-inline:calc(var(--spacing)*3);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-50{z-index:50}.m-0{margin:calc(var(--spacing)*0)}.mx-3{margin-inline:calc(var(--spacing)*3)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-5{margin-top:calc(var(--spacing)*5)}.mr-1{margin-right:calc(var(--spacing)*1)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-flex{display:inline-flex}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-\[calc\(100\%-56px\)\]{height:calc(100% - 56px)}.h-full{height:100%}.max-h-\[320px\]{max-height:320px}.max-h-\[540px\]{max-height:540px}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-\[6px\]{width:6px}.w-\[52px\]{width:52px}.w-\[380px\]{width:380px}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.max-w-\[92vw\]{max-width:92vw}.min-w-0{min-width:calc(var(--spacing)*0)}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,1\.6fr\)\]{grid-template-columns:minmax(0,1fr) minmax(0,1.6fr)}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.content-start{align-content:flex-start}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-x-3{column-gap:calc(var(--spacing)*3)}.gap-y-1{row-gap:calc(var(--spacing)*1)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500)30%,transparent)}}.border-emerald-500\/30{border-color:#00bb7f4d}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\/30{border-color:color-mix(in oklab,var(--color-emerald-500)30%,transparent)}}.border-indigo-500\/30{border-color:#625fff4d}@supports (color:color-mix(in lab,red,red)){.border-indigo-500\/30{border-color:color-mix(in oklab,var(--color-indigo-500)30%,transparent)}}.border-indigo-500\/60{border-color:#625fff99}@supports (color:color-mix(in lab,red,red)){.border-indigo-500\/60{border-color:color-mix(in oklab,var(--color-indigo-500)60%,transparent)}}.border-red-500\/40{border-color:#fb2c3666}@supports (color:color-mix(in lab,red,red)){.border-red-500\/40{border-color:color-mix(in oklab,var(--color-red-500)40%,transparent)}}.border-rose-500\/30{border-color:#ff23574d}@supports (color:color-mix(in lab,red,red)){.border-rose-500\/30{border-color:color-mix(in oklab,var(--color-rose-500)30%,transparent)}}.border-transparent{border-color:#0000}.border-zinc-200\/50{border-color:#e4e4e780}@supports (color:color-mix(in lab,red,red)){.border-zinc-200\/50{border-color:color-mix(in oklab,var(--color-zinc-200)50%,transparent)}}.border-zinc-200\/60{border-color:#e4e4e799}@supports (color:color-mix(in lab,red,red)){.border-zinc-200\/60{border-color:color-mix(in oklab,var(--color-zinc-200)60%,transparent)}}.border-zinc-200\/70{border-color:#e4e4e7b3}@supports (color:color-mix(in lab,red,red)){.border-zinc-200\/70{border-color:color-mix(in oklab,var(--color-zinc-200)70%,transparent)}}.border-zinc-300\/60{border-color:#d4d4d899}@supports (color:color-mix(in lab,red,red)){.border-zinc-300\/60{border-color:color-mix(in oklab,var(--color-zinc-300)60%,transparent)}}.border-zinc-300\/70{border-color:#d4d4d8b3}@supports (color:color-mix(in lab,red,red)){.border-zinc-300\/70{border-color:color-mix(in oklab,var(--color-zinc-300)70%,transparent)}}.border-zinc-400\/30{border-color:#9f9fa94d}@supports (color:color-mix(in lab,red,red)){.border-zinc-400\/30{border-color:color-mix(in oklab,var(--color-zinc-400)30%,transparent)}}.\!bg-emerald-500\/20{background-color:#00bb7f33!important}@supports (color:color-mix(in lab,red,red)){.\!bg-emerald-500\/20{background-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)!important}}.\!bg-red-500\/20{background-color:#fb2c3633!important}@supports (color:color-mix(in lab,red,red)){.\!bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)!important}}.bg-amber-400\/20{background-color:#fcbb0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-400\/20{background-color:color-mix(in oklab,var(--color-amber-400)20%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/15{background-color:#f99c0026}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/15{background-color:color-mix(in oklab,var(--color-amber-500)15%,transparent)}}.bg-amber-500\/70{background-color:#f99c00b3}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/70{background-color:color-mix(in oklab,var(--color-amber-500)70%,transparent)}}.bg-emerald-500\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/10{background-color:color-mix(in oklab,var(--color-emerald-500)10%,transparent)}}.bg-emerald-500\/15{background-color:#00bb7f26}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/15{background-color:color-mix(in oklab,var(--color-emerald-500)15%,transparent)}}.bg-emerald-500\/20{background-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/20{background-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.bg-emerald-500\/70{background-color:#00bb7fb3}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/70{background-color:color-mix(in oklab,var(--color-emerald-500)70%,transparent)}}.bg-indigo-500\/10{background-color:#625fff1a}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/10{background-color:color-mix(in oklab,var(--color-indigo-500)10%,transparent)}}.bg-indigo-500\/15{background-color:#625fff26}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/15{background-color:color-mix(in oklab,var(--color-indigo-500)15%,transparent)}}.bg-indigo-500\/70{background-color:#625fffb3}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/70{background-color:color-mix(in oklab,var(--color-indigo-500)70%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.bg-rose-500\/10{background-color:#ff23571a}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/10{background-color:color-mix(in oklab,var(--color-rose-500)10%,transparent)}}.bg-sky-500\/70{background-color:#00a5efb3}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/70{background-color:color-mix(in oklab,var(--color-sky-500)70%,transparent)}}.bg-violet-500\/70{background-color:#8d54ffb3}@supports (color:color-mix(in lab,red,red)){.bg-violet-500\/70{background-color:color-mix(in oklab,var(--color-violet-500)70%,transparent)}}.bg-white{background-color:var(--color-white)}.bg-white\/40{background-color:#fff6}@supports (color:color-mix(in lab,red,red)){.bg-white\/40{background-color:color-mix(in oklab,var(--color-white)40%,transparent)}}.bg-white\/50{background-color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.bg-white\/50{background-color:color-mix(in oklab,var(--color-white)50%,transparent)}}.bg-white\/60{background-color:#fff9}@supports (color:color-mix(in lab,red,red)){.bg-white\/60{background-color:color-mix(in oklab,var(--color-white)60%,transparent)}}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.bg-white\/70{background-color:color-mix(in oklab,var(--color-white)70%,transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white)90%,transparent)}}.bg-zinc-50\/60{background-color:#fafafa99}@supports (color:color-mix(in lab,red,red)){.bg-zinc-50\/60{background-color:color-mix(in oklab,var(--color-zinc-50)60%,transparent)}}.bg-zinc-50\/70{background-color:#fafafab3}@supports (color:color-mix(in lab,red,red)){.bg-zinc-50\/70{background-color:color-mix(in oklab,var(--color-zinc-50)70%,transparent)}}.bg-zinc-100\/40{background-color:#f4f4f566}@supports (color:color-mix(in lab,red,red)){.bg-zinc-100\/40{background-color:color-mix(in oklab,var(--color-zinc-100)40%,transparent)}}.bg-zinc-100\/50{background-color:#f4f4f580}@supports (color:color-mix(in lab,red,red)){.bg-zinc-100\/50{background-color:color-mix(in oklab,var(--color-zinc-100)50%,transparent)}}.bg-zinc-100\/70{background-color:#f4f4f5b3}@supports (color:color-mix(in lab,red,red)){.bg-zinc-100\/70{background-color:color-mix(in oklab,var(--color-zinc-100)70%,transparent)}}.bg-zinc-200\/40{background-color:#e4e4e766}@supports (color:color-mix(in lab,red,red)){.bg-zinc-200\/40{background-color:color-mix(in oklab,var(--color-zinc-200)40%,transparent)}}.bg-zinc-200\/60{background-color:#e4e4e799}@supports (color:color-mix(in lab,red,red)){.bg-zinc-200\/60{background-color:color-mix(in oklab,var(--color-zinc-200)60%,transparent)}}.bg-zinc-400\/10{background-color:#9f9fa91a}@supports (color:color-mix(in lab,red,red)){.bg-zinc-400\/10{background-color:color-mix(in oklab,var(--color-zinc-400)10%,transparent)}}.bg-zinc-500\/60{background-color:#71717b99}@supports (color:color-mix(in lab,red,red)){.bg-zinc-500\/60{background-color:color-mix(in oklab,var(--color-zinc-500)60%,transparent)}}.bg-zinc-950\/20{background-color:#09090b33}@supports (color:color-mix(in lab,red,red)){.bg-zinc-950\/20{background-color:color-mix(in oklab,var(--color-zinc-950)20%,transparent)}}.p-1{padding:calc(var(--spacing)*1)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-6{padding-block:calc(var(--spacing)*6)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.text-center{text-align:center}.text-left{text-align:left}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-black{--tw-font-weight:var(--font-weight-black);font-weight:var(--font-weight-black)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.break-words{overflow-wrap:break-word}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-emerald-700{color:var(--color-emerald-700)}.text-indigo-700{color:var(--color-indigo-700)}.text-rose-700{color:var(--color-rose-700)}.text-rose-800{color:var(--color-rose-800)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-500\/80{color:#71717bcc}@supports (color:color-mix(in lab,red,red)){.text-zinc-500\/80{color:color-mix(in oklab,var(--color-zinc-500)80%,transparent)}}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.italic{font-style:italic}.line-through{text-decoration-line:line-through}.opacity-0{opacity:0}.opacity-60{opacity:.6}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-indigo-500\/20{--tw-ring-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.ring-indigo-500\/20{--tw-ring-color:color-mix(in oklab,var(--color-indigo-500)20%,transparent)}}.backdrop-blur{--tw-backdrop-blur:blur(8px);-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,)}.backdrop-blur-\[1px\]{--tw-backdrop-blur:blur(1px);-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,)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.hover\:bg-zinc-50:hover{background-color:var(--color-zinc-50)}.hover\:bg-zinc-200\/40:hover{background-color:#e4e4e766}@supports (color:color-mix(in lab,red,red)){.hover\:bg-zinc-200\/40:hover{background-color:color-mix(in oklab,var(--color-zinc-200)40%,transparent)}}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-indigo-400\/50:focus{--tw-ring-color:#7d87ff80}@supports (color:color-mix(in lab,red,red)){.focus\:ring-indigo-400\/50:focus{--tw-ring-color:color-mix(in oklab,var(--color-indigo-400)50%,transparent)}}.active\:bg-zinc-100:active{background-color:var(--color-zinc-100)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-60:disabled{opacity:.6}@media(min-width:64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(prefers-color-scheme:dark){.dark\:border-zinc-700\/60{border-color:#3f3f4699}@supports (color:color-mix(in lab,red,red)){.dark\:border-zinc-700\/60{border-color:color-mix(in oklab,var(--color-zinc-700)60%,transparent)}}.dark\:border-zinc-700\/70{border-color:#3f3f46b3}@supports (color:color-mix(in lab,red,red)){.dark\:border-zinc-700\/70{border-color:color-mix(in oklab,var(--color-zinc-700)70%,transparent)}}.dark\:border-zinc-800\/50{border-color:#27272a80}@supports (color:color-mix(in lab,red,red)){.dark\:border-zinc-800\/50{border-color:color-mix(in oklab,var(--color-zinc-800)50%,transparent)}}.dark\:border-zinc-800\/60{border-color:#27272a99}@supports (color:color-mix(in lab,red,red)){.dark\:border-zinc-800\/60{border-color:color-mix(in oklab,var(--color-zinc-800)60%,transparent)}}.dark\:bg-zinc-800\/40{background-color:#27272a66}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-800\/40{background-color:color-mix(in oklab,var(--color-zinc-800)40%,transparent)}}.dark\:bg-zinc-800\/60{background-color:#27272a99}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-800\/60{background-color:color-mix(in oklab,var(--color-zinc-800)60%,transparent)}}.dark\:bg-zinc-900\/20{background-color:#18181b33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-900\/20{background-color:color-mix(in oklab,var(--color-zinc-900)20%,transparent)}}.dark\:bg-zinc-900\/40{background-color:#18181b66}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-900\/40{background-color:color-mix(in oklab,var(--color-zinc-900)40%,transparent)}}.dark\:bg-zinc-900\/60{background-color:#18181b99}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-900\/60{background-color:color-mix(in oklab,var(--color-zinc-900)60%,transparent)}}.dark\:bg-zinc-950\/10{background-color:#09090b1a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-950\/10{background-color:color-mix(in oklab,var(--color-zinc-950)10%,transparent)}}.dark\:bg-zinc-950\/20{background-color:#09090b33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-950\/20{background-color:color-mix(in oklab,var(--color-zinc-950)20%,transparent)}}.dark\:bg-zinc-950\/40{background-color:#09090b66}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-950\/40{background-color:color-mix(in oklab,var(--color-zinc-950)40%,transparent)}}.dark\:bg-zinc-950\/70{background-color:#09090bb3}@supports (color:color-mix(in lab,red,red)){.dark\:bg-zinc-950\/70{background-color:color-mix(in oklab,var(--color-zinc-950)70%,transparent)}}.dark\:text-amber-200{color:var(--color-amber-200)}.dark\:text-emerald-200{color:var(--color-emerald-200)}.dark\:text-indigo-200{color:var(--color-indigo-200)}.dark\:text-rose-200{color:var(--color-rose-200)}.dark\:text-zinc-100{color:var(--color-zinc-100)}.dark\:text-zinc-200{color:var(--color-zinc-200)}.dark\:text-zinc-300{color:var(--color-zinc-300)}.dark\:text-zinc-400{color:var(--color-zinc-400)}.dark\:text-zinc-400\/80{color:#9f9fa9cc}@supports (color:color-mix(in lab,red,red)){.dark\:text-zinc-400\/80{color:color-mix(in oklab,var(--color-zinc-400)80%,transparent)}}.dark\:opacity-40{opacity:.4}@media(hover:hover){.dark\:hover\:bg-zinc-800\/40:hover{background-color:#27272a66}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-zinc-800\/40:hover{background-color:color-mix(in oklab,var(--color-zinc-800)40%,transparent)}}.dark\:hover\:bg-zinc-800\/50:hover{background-color:#27272a80}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-zinc-800\/50:hover{background-color:color-mix(in oklab,var(--color-zinc-800)50%,transparent)}}.dark\:hover\:bg-zinc-900\/30:hover{background-color:#18181b4d}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-zinc-900\/30:hover{background-color:color-mix(in oklab,var(--color-zinc-900)30%,transparent)}}}.dark\:active\:bg-zinc-900\/50:active{background-color:#18181b80}@supports (color:color-mix(in lab,red,red)){.dark\:active\:bg-zinc-900\/50:active{background-color:color-mix(in oklab,var(--color-zinc-900)50%,transparent)}}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}.\[\&\>td\]\:bg-amber-400\/10>td{background-color:#fcbb001a}@supports (color:color-mix(in lab,red,red)){.\[\&\>td\]\:bg-amber-400\/10>td{background-color:color-mix(in oklab,var(--color-amber-400)10%,transparent)}}.\[\&\>td\]\:bg-emerald-500\/10>td{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.\[\&\>td\]\:bg-emerald-500\/10>td{background-color:color-mix(in oklab,var(--color-emerald-500)10%,transparent)}}.\[\&\>td\]\:bg-red-500\/10>td{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.\[\&\>td\]\:bg-red-500\/10>td{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.\[\&\>td\]\:text-zinc-500>td{color:var(--color-zinc-500)}@media(prefers-color-scheme:dark){.dark\:\[\&\>td\]\:text-zinc-400>td{color:var(--color-zinc-400)}}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}
@@ -0,0 +1,4 @@
1
+ (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))i(s);new MutationObserver(s=>{for(const o of s)if(o.type==="childList")for(const d of o.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&i(d)}).observe(document,{childList:!0,subtree:!0});function n(s){const o={};return s.integrity&&(o.integrity=s.integrity),s.referrerPolicy&&(o.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?o.credentials="include":s.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function i(s){if(s.ep)return;s.ep=!0;const o=n(s);fetch(s.href,o)}})();var Lt=Symbol("RENDERER"),he=Symbol("ERROR_HANDLER"),w=Symbol("STASH"),at=Symbol("INTERNAL"),Ot=Symbol("MEMO"),le={title:[],script:["src"],style:["data-href"],link:["href"],meta:["name","httpEquiv","charset","itemProp"]},_t={},de="data-precedence",Ht=e=>Array.isArray(e)?e:[e],Ft=new Map([["className","class"],["htmlFor","for"],["crossOrigin","crossorigin"],["httpEquiv","http-equiv"],["itemProp","itemprop"],["fetchPriority","fetchpriority"],["noModule","nomodule"],["formAction","formaction"]]),Fe=e=>Ft.get(e)||e,Kt=(e,t)=>{for(const[n,i]of Object.entries(e)){const s=n[0]==="-"||!/[A-Z]/.test(n)?n:n.replace(/[A-Z]/g,o=>`-${o.toLowerCase()}`);t(s,i==null?null:typeof i=="number"?s.match(/^(?:a|border-im|column(?:-c|s)|flex(?:$|-[^b])|grid-(?:ar|[^a])|font-w|li|or|sca|st|ta|wido|z)|ty$/)?`${i}`:`${i}px`:i)}},Ke=e=>(e[at]=!0,e),qt=e=>({value:t,children:n})=>{if(!n)return;const i={children:[{tag:Ke(()=>{e.push(t)}),props:{}}]};Array.isArray(n)?i.children.push(...n.flat()):i.children.push(n),i.children.push({tag:Ke(()=>{e.pop()}),props:{}});const s={tag:"",props:i,type:""};return s[he]=o=>{throw e.pop(),o},s},lt=e=>{const t=[e],n=qt(t);return n.values=t,n.Provider=n,be.push(n),n},be=[],dt=e=>e.values.at(-1),G="_hp",Wt={Change:"Input",DoubleClick:"DblClick"},Xt={svg:"2000/svg",math:"1998/Math/MathML"},L=[],xe=new WeakMap,F=void 0,Yt=()=>F,j=e=>"t"in e,ue={onClick:["click",!1]},qe=e=>{if(!e.startsWith("on"))return;if(ue[e])return ue[e];const t=e.match(/^on([A-Z][a-zA-Z]+?(?:PointerCapture)?)(Capture)?$/);if(t){const[,n,i]=t;return ue[e]=[(Wt[n]||n).toLowerCase(),!!i]}},We=(e,t)=>F&&e instanceof SVGElement&&/[A-Z]/.test(t)&&(t in e.style||t.match(/^(?:o|pai|str|u|ve)/))?t.replace(/([A-Z])/g,"-$1").toLowerCase():t,Gt=(e,t,n)=>{t||={};for(let i in t){const s=t[i];if(i!=="children"&&(!n||n[i]!==s)){i=Fe(i);const o=qe(i);if(o){if(n?.[i]!==s&&(n&&e.removeEventListener(o[0],n[i],o[1]),s!=null)){if(typeof s!="function")throw new Error(`Event handler for "${i}" is not a function`);e.addEventListener(o[0],s,o[1])}}else if(i==="dangerouslySetInnerHTML"&&s)e.innerHTML=s.__html;else if(i==="ref"){let d;typeof s=="function"?d=s(e)||(()=>s(null)):s&&"current"in s&&(s.current=e,d=()=>s.current=null),xe.set(e,d)}else if(i==="style"){const d=e.style;typeof s=="string"?d.cssText=s:(d.cssText="",s!=null&&Kt(s,d.setProperty.bind(d)))}else{if(i==="value"){const u=e.nodeName;if(u==="INPUT"||u==="TEXTAREA"||u==="SELECT"){if(e.value=s==null||s===!1?null:s,u==="TEXTAREA"){e.textContent=s;continue}else if(u==="SELECT"){e.selectedIndex===-1&&(e.selectedIndex=0);continue}}}else(i==="checked"&&e.nodeName==="INPUT"||i==="selected"&&e.nodeName==="OPTION")&&(e[i]=s);const d=We(e,i);s==null||s===!1?e.removeAttribute(d):s===!0?e.setAttribute(d,""):typeof s=="string"||typeof s=="number"?e.setAttribute(d,s):e.setAttribute(d,s.toString())}}}if(n)for(let i in n){const s=n[i];if(i!=="children"&&!(i in t)){i=Fe(i);const o=qe(i);o?e.removeEventListener(o[0],s,o[1]):i==="ref"?xe.get(e)?.():e.removeAttribute(We(e,i))}}},Qt=(e,t)=>{t[w][0]=0,L.push([e,t]);const n=t.tag[Lt]||t.tag,i=n.defaultProps?{...n.defaultProps,...t.props}:t.props;try{return[n.call(null,i)]}finally{L.pop()}},ut=(e,t,n,i,s)=>{e.vR?.length&&(i.push(...e.vR),delete e.vR),typeof e.tag=="function"&&e[w][1][ke]?.forEach(o=>s.push(o)),e.vC.forEach(o=>{if(j(o))n.push(o);else if(typeof o.tag=="function"||o.tag===""){o.c=t;const d=n.length;if(ut(o,t,n,i,s),o.s){for(let u=d;u<n.length;u++)n[u].s=!0;o.s=!1}}else n.push(o),o.vR?.length&&(i.push(...o.vR),delete o.vR)})},Ut=e=>{for(;;e=e.tag===G||!e.vC||!e.pP?e.nN:e.vC[0]){if(!e)return null;if(e.tag!==G&&e.e)return e.e}},ft=e=>{j(e)||(e[w]?.[1][ke]?.forEach(t=>t[2]?.()),xe.get(e.e)?.(),e.p===2&&e.vC?.forEach(t=>t.p=2),e.vC?.forEach(ft)),e.p||(e.e?.remove(),delete e.e),typeof e.tag=="function"&&(Y.delete(e),U.delete(e),delete e[w][3],e.a=!0)},ze=(e,t,n)=>{e.c=t,pt(e,t,n)},Xe=(e,t)=>{if(t){for(let n=0,i=e.length;n<i;n++)if(e[n]===t)return n}},Ye=Symbol(),pt=(e,t,n)=>{const i=[],s=[],o=[];ut(e,t,i,s,o),s.forEach(ft);const d=n?void 0:t.childNodes;let u,h=null;if(n)u=-1;else if(!d.length)u=0;else{const c=Xe(d,Ut(e.nN));c!==void 0?(h=d[c],u=c):u=Xe(d,i.find(l=>l.tag!==G&&l.e)?.e)??-1,u===-1&&(n=!0)}for(let c=0,l=i.length;c<l;c++,u++){const a=i[c];let f;if(a.s&&a.e)f=a.e,a.s=!1;else{const g=n||!a.e;j(a)?(a.e&&a.d&&(a.e.textContent=a.t),a.d=!1,f=a.e||=document.createTextNode(a.t)):(f=a.e||=a.n?document.createElementNS(a.n,a.tag):document.createElement(a.tag),Gt(f,a.props,a.pP),pt(a,f,g))}a.tag===G?u--:n?f.parentNode||t.appendChild(f):d[u]!==f&&d[u-1]!==f&&(d[u+1]===f?t.appendChild(d[u]):t.insertBefore(f,h||d[u]||null))}if(e.pP&&delete e.pP,o.length){const c=[],l=[];o.forEach(([,a,,f,g])=>{a&&c.push(a),f&&l.push(f),g?.()}),c.forEach(a=>a()),l.length&&requestAnimationFrame(()=>{l.forEach(a=>a())})}},Zt=(e,t)=>!!(e&&e.length===t.length&&e.every((n,i)=>n[1]===t[i][1])),U=new WeakMap,V=(e,t,n)=>{const i=!n&&t.pC;n&&(t.pC||=t.vC);let s;try{n||=typeof t.tag=="function"?Qt(e,t):Ht(t.props.children),n[0]?.tag===""&&n[0][he]&&(s=n[0][he],e[5].push([e,s,t]));const o=i?[...t.pC]:t.vC?[...t.vC]:void 0,d=[];let u;for(let h=0;h<n.length;h++){Array.isArray(n[h])&&n.splice(h,1,...n[h].flat());let c=ht(n[h]);if(c){typeof c.tag=="function"&&!c.tag[at]&&(be.length>0&&(c[w][2]=be.map(a=>[a,a.values.at(-1)])),e[5]?.length&&(c[w][3]=e[5].at(-1)));let l;if(o&&o.length){const a=o.findIndex(j(c)?f=>j(f):c.key!==void 0?f=>f.key===c.key&&f.tag===c.tag:f=>f.tag===c.tag);a!==-1&&(l=o[a],o.splice(a,1))}if(l)if(j(c))l.t!==c.t&&(l.t=c.t,l.d=!0),c=l;else{const a=l.pP=l.props;if(l.props=c.props,l.f||=c.f||t.f,typeof c.tag=="function"){const f=l[w][2];l[w][2]=c[w][2]||[],l[w][3]=c[w][3],!l.f&&((l.o||l)===c.o||l.tag[Ot]?.(a,l.props))&&Zt(f,l[w][2])&&(l.s=!0)}c=l}else if(!j(c)&&F){const a=dt(F);a&&(c.n=a)}if(!j(c)&&!c.s&&(V(e,c),delete c.f),d.push(c),u&&!u.s&&!c.s)for(let a=u;a&&!j(a);a=a.vC?.at(-1))a.nN=c;u=c}}t.vR=i?[...t.vC,...o||[]]:o||[],t.vC=d,i&&delete t.pC}catch(o){if(t.f=!0,o===Ye){if(s)return;throw o}const[d,u,h]=t[w]?.[3]||[];if(u){const c=()=>Z([0,!1,e[2]],h),l=U.get(h)||[];l.push(c),U.set(h,l);const a=u(o,()=>{const f=U.get(h);if(f){const g=f.indexOf(c);if(g!==-1)return f.splice(g,1),c()}});if(a){if(e[0]===1)e[1]=!0;else if(V(e,h,[a]),(u.length===1||e!==d)&&h.c){ze(h,h.c,!1);return}throw Ye}}throw o}finally{s&&e[5].pop()}},ht=e=>{if(!(e==null||typeof e=="boolean")){if(typeof e=="string"||typeof e=="number")return{t:e.toString(),d:!0};if("vR"in e&&(e={tag:e.tag,props:e.props,key:e.key,f:e.f,type:e.tag,ref:e.props.ref,o:e.o||e}),typeof e.tag=="function")e[w]=[0,[]];else{const t=Xt[e.tag];t&&(F||=lt(""),e.props.children=[{tag:F,props:{value:e.n=`http://www.w3.org/${t}`,children:e.props.children}}])}return e}},bt=(e,t,n)=>{e.c===t&&(e.c=n,e.vC.forEach(i=>bt(i,t,n)))},Ge=(e,t)=>{t[w][2]?.forEach(([n,i])=>{n.values.push(i)});try{V(e,t,void 0)}catch{return}if(t.a){delete t.a;return}t[w][2]?.forEach(([n])=>{n.values.pop()}),(e[0]!==1||!e[1])&&ze(t,t.c,!1)},Y=new WeakMap,Qe=[],Z=async(e,t)=>{e[5]||=[];const n=Y.get(t);n&&n[0](void 0);let i;const s=new Promise(o=>i=o);if(Y.set(t,[i,()=>{e[2]?e[2](e,t,o=>{Ge(o,t)}).then(()=>i(t)):(Ge(e,t),i(t))}]),Qe.length)Qe.at(-1).add(t);else{await Promise.resolve();const o=Y.get(t);o&&(Y.delete(t),o[1]())}return s},Jt=(e,t)=>{const n=[];n[5]=[],n[4]=!0,V(n,e,void 0),n[4]=!1;const i=document.createDocumentFragment();ze(e,i,!0),bt(e,i,t),t.replaceChildren(i)},Vt=(e,t)=>{Jt(ht({tag:"",props:{children:e}}),t)},er=(e,t,n)=>({tag:G,props:{children:e},key:n,e:t,p:1}),tr=0,ke=1,rr=2,nr=3,ir=4,fe=new WeakMap,we=(e,t)=>!e||!t||e.length!==t.length||t.some((n,i)=>n!==e[i]),sr=void 0,Ue=[],z=e=>{const t=()=>typeof e=="function"?e():e,n=L.at(-1);if(!n)return[t(),()=>{}];const[,i]=n,s=i[w][1][tr]||=[],o=i[w][0]++;return s[o]||=[t(),d=>{const u=sr,h=s[o];if(typeof d=="function"&&(d=d(h[0])),!Object.is(d,h[0]))if(h[0]=d,Ue.length){const[c,l]=Ue.at(-1);Promise.all([c===3?i:Z([c,!1,u],i),l]).then(([a])=>{if(!a||!(c===2||c===3))return;const f=a.vC;requestAnimationFrame(()=>{setTimeout(()=>{f===a.vC&&Z([c===3?1:0,!1,u],a)})})})}else Z([0,!1,u],i)}]},or=(e,t,n)=>{const i=L.at(-1);if(!i)return;const[,s]=i,o=s[w][1][ke]||=[],d=s[w][0]++,[u,,h]=o[d]||=[];if(we(u,n)){h&&h();const c=()=>{l[e]=void 0,l[2]=t()},l=[n,void 0,void 0,void 0,void 0];l[e]=c,o[d]=l}},ee=(e,t)=>or(3,e,t),Ae=(e,t)=>{const n=L.at(-1);if(!n)return e;const[,i]=n,s=i[w][1][rr]||=[],o=i[w][0]++,d=s[o];return we(d?.[1],t)?s[o]=[e,t]:e=s[o][0],e},H=e=>{const t=L.at(-1);if(!t)return{current:e};const[,n]=t,i=n[w][1][ir]||=[],s=n[w][0]++;return i[s]||={current:e}},cr=e=>{const t=fe.get(e);if(t){if(t.length===2)throw t[1];return t[0]}throw e.then(n=>fe.set(e,[n]),n=>fe.set(e,[void 0,n])),e},J=(e,t)=>{const n=L.at(-1);if(!n)return e();const[,i]=n,s=i[w][1][nr]||=[],o=i[w][0]++,d=s[o];return we(d?.[1],t)&&(s[o]=[e(),t]),s[o][0]},ar=lt({pending:!1,data:null,method:null,action:null}),Ze=new Set,lr=e=>{Ze.add(e),e.finally(()=>Ze.delete(e))},dr=()=>{ge=Object.create(null),me=Object.create(null)},te=(e,t)=>J(()=>n=>{let i;e&&(typeof e=="function"?i=e(n)||(()=>{e(null)}):e&&"current"in e&&(e.current=n,i=()=>{e.current=null}));const s=t(n);return()=>{s?.(),i?.()}},[e]),ge=Object.create(null),me=Object.create(null),Q=(e,t,n,i,s)=>{if(t?.itemProp)return{tag:e,props:t,type:e,ref:t.ref};const o=document.head;let{onLoad:d,onError:u,precedence:h,blocking:c,...l}=t,a=null,f=!1;const g=le[e];let v;if(g.length>0){const k=o.querySelectorAll(e);e:for(const b of k)for(const x of le[e])if(b.getAttribute(x)===t[x]){a=b;break e}if(!a){const b=g.reduce((x,y)=>t[y]===void 0?x:`${x}-${y}-${t[y]}`,e);f=!me[b],a=me[b]||=(()=>{const x=document.createElement(e);for(const y of g)t[y]!==void 0&&x.setAttribute(y,t[y]),t.rel&&x.setAttribute("rel",t.rel);return x})()}}else v=o.querySelectorAll(e);h=i?h??"":void 0,i&&(l[de]=h);const m=Ae(k=>{if(g.length>0){let b=!1;for(const x of o.querySelectorAll(e)){if(b&&x.getAttribute(de)!==h){o.insertBefore(k,x);return}x.getAttribute(de)===h&&(b=!0)}o.appendChild(k)}else if(v){let b=!1;for(const x of v)if(x===k){b=!0;break}b||o.insertBefore(k,o.contains(v[0])?v[0]:o.querySelector(e)),v=void 0}},[h]),A=te(t.ref,k=>{const b=g[0];if(n===2&&(k.innerHTML=""),(f||v)&&m(k),!u&&!d)return;let x=ge[k.getAttribute(b)]||=new Promise((y,C)=>{k.addEventListener("load",y),k.addEventListener("error",C)});d&&(x=x.then(d)),u&&(x=x.catch(u)),x.catch(()=>{})});if(s&&c==="render"){const k=le[e][0];if(t[k]){const b=t[k],x=ge[b]||=new Promise((y,C)=>{m(a),a.addEventListener("load",y),a.addEventListener("error",C)});cr(x)}}const P={tag:e,type:e,props:{...l,ref:A},ref:A};return P.p=n,a&&(P.e=a),er(P,o)},xt=e=>{const t=Yt();return(t&&dt(t))?.endsWith("svg")?{tag:"title",props:e,type:"title",ref:e.ref}:Q("title",e,void 0,!1,!1)},gt=e=>!e||["src","async"].some(t=>!e[t])?{tag:"script",props:e,type:"script",ref:e.ref}:Q("script",e,1,!1,!0),mt=e=>!e||!["href","precedence"].every(t=>t in e)?{tag:"style",props:e,type:"style",ref:e.ref}:(e["data-href"]=e.href,delete e.href,Q("style",e,2,!0,!0)),vt=e=>!e||["onLoad","onError"].some(t=>t in e)||e.rel==="stylesheet"&&(!("precedence"in e)||"disabled"in e)?{tag:"link",props:e,type:"link",ref:e.ref}:Q("link",e,1,"precedence"in e,!0),yt=e=>Q("meta",e,void 0,!1,!1),zt=Symbol(),kt=e=>{const{action:t,...n}=e;typeof t!="function"&&(n.action=t);const[i,s]=z([null,!1]),o=Ae(async c=>{const l=c.isTrusted?t:c.detail[zt];if(typeof l!="function")return;c.preventDefault();const a=new FormData(c.target);s([a,!0]);const f=l(a);f instanceof Promise&&(lr(f),await f),s([null,!0])},[]),d=te(e.ref,c=>(c.addEventListener("submit",o),()=>{c.removeEventListener("submit",o)})),[u,h]=i;return i[1]=!1,{tag:ar,props:{value:{pending:u!==null,data:u,method:u?"post":null,action:u?t:null},children:{tag:"form",props:{...n,ref:d},type:"form",ref:d}},f:h}},wt=(e,{formAction:t,...n})=>{if(typeof t=="function"){const i=Ae(s=>{s.preventDefault(),s.currentTarget.form.dispatchEvent(new CustomEvent("submit",{detail:{[zt]:t}}))},[]);n.ref=te(n.ref,s=>(s.addEventListener("click",i),()=>{s.removeEventListener("click",i)}))}return{tag:e,props:n,type:e,ref:n.ref}},At=e=>wt("input",e),Et=e=>wt("button",e);Object.assign(_t,{title:xt,script:gt,style:mt,link:vt,meta:yt,form:kt,input:At,button:Et});const Je=Object.freeze(Object.defineProperty({__proto__:null,button:Et,clearCache:dr,composeRef:te,form:kt,input:At,link:vt,meta:yt,script:gt,style:mt,title:xt},Symbol.toStringTag,{value:"Module"}));var r=(e,t,n)=>(typeof e=="string"&&Je[e]&&(e=Je[e]),{tag:e,type:e,props:t,key:n,ref:t.ref}),Ee=e=>r("",e,void 0);function I(e){if(e===null)return"null";if(e===void 0)return"";switch(typeof e){case"string":return e;case"number":case"bigint":case"boolean":return String(e);case"symbol":return e.toString();case"function":return"[function]";case"object":try{return JSON.stringify(e)}catch{return Object.prototype.toString.call(e)}default:return""}}function ur(e){const t=e.indexOf("->");return t<=0?null:{before:e.slice(0,t),after:e.slice(t+2)}}function Ve(e){return I(e?.[0])==="@@"}function et(e){return I(e?.[0])==="!"}function fr(e){return I(e?.[0])==="..."}function tt(e){return e==="+++"?"!bg-emerald-500/20":e==="---"?"!bg-red-500/20":e==="->"||e.includes("->")||e.startsWith("(")&&e.endsWith(")")?"bg-amber-400/20":""}const pr=({diff:e,hasBothResults:t,emptyMessage:n})=>{if(!t)return r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:n??"Diffには Query A と Query B 両方の結果が必要です。"});const i=e??[];if(!i.some(l=>{const a=I(l?.[0]);return a==="!"||a==="+++"||a==="---"||a==="->"}))return r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:"完全に一致(差分なし)"});const o=i.findIndex(l=>Ve(l)),d=o>=0,u=d?i.slice(0,o+1):[],h=d?i.slice(o+1):i,c=[];for(const l of u)if(et(l))for(let a=1;a<l.length;a++){const f=tt(I(l[a]));if(f&&f!=="diffColChanged"){if(f==="diffColRemoved"){c[a]="diffColRemoved";continue}c[a]!=="diffColRemoved"&&(c[a]||(c[a]=f))}}return r("div",{class:"mt-3 max-h-[540px] overflow-auto rounded-xl border border-zinc-200/60 bg-white/60 dark:border-zinc-800/60 dark:bg-zinc-950/20",children:[r("details",{class:"mb-3 overflow-hidden rounded-xl border border-zinc-200/60 bg-zinc-50/70 dark:border-zinc-800/60 dark:bg-zinc-900/20",children:[r("summary",{class:"flex cursor-pointer list-none items-baseline justify-between gap-3 select-none px-3 py-2 [&::-webkit-details-marker]:hidden",children:[r("span",{class:"text-sm font-bold text-zinc-800 dark:text-zinc-100",children:"Diffの見方"}),r("span",{class:"text-xs text-zinc-500 dark:text-zinc-400",children:"(クリックで開閉)"})]}),r("div",{class:"border-t border-zinc-200/50 px-3 pb-3 dark:border-zinc-800/50",children:r("div",{class:"mt-3 grid grid-cols-1 gap-2 lg:grid-cols-2",children:[r("div",{class:"flex items-start gap-2",children:[r("span",{class:"mt-0.5 h-3.5 w-3.5 rounded border border-zinc-300/60 bg-emerald-500/20 dark:border-zinc-700/60"}),r("span",{class:"text-sm text-zinc-700 dark:text-zinc-200",children:[r("b",{children:"追加"}),"(行: ",r("code",{children:"+++"}),")"]})]}),r("div",{class:"flex items-start gap-2",children:[r("span",{class:"mt-0.5 h-3.5 w-3.5 rounded border border-zinc-300/60 bg-red-500/20 dark:border-zinc-700/60"}),r("span",{class:"text-sm text-zinc-700 dark:text-zinc-200",children:[r("b",{children:"削除"}),"(行: ",r("code",{children:"---"}),")"]})]}),r("div",{class:"flex items-start gap-2",children:[r("span",{class:"mt-0.5 h-3.5 w-3.5 rounded border border-zinc-300/60 bg-amber-400/20 dark:border-zinc-700/60"}),r("span",{class:"text-sm text-zinc-700 dark:text-zinc-200",children:[r("b",{children:"変更"}),"(行: ",r("code",{children:"->"})," / セル内:"," ",r("code",{children:"a->b"}),")"]})]}),r("div",{class:"flex items-start gap-2",children:[r("span",{class:"mt-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded border border-zinc-300/60 bg-zinc-200/60 text-[11px] font-black text-zinc-700 dark:border-zinc-700/60 dark:bg-zinc-800/60 dark:text-zinc-200",children:"!"}),r("span",{class:"text-sm text-zinc-700 dark:text-zinc-200",children:[r("b",{children:"カラム変更"}),"(ヘッダ上部の ",r("code",{children:"!"})," 行)",r("span",{class:"ml-1 text-zinc-500 dark:text-zinc-400",children:"追加/削除/名称変更はヘッダ色で表現"})]})]}),r("div",{class:"flex items-start gap-2",children:[r("span",{class:"mt-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded border border-zinc-300/60 bg-zinc-200/60 text-[11px] font-black text-zinc-700 dark:border-zinc-700/60 dark:bg-zinc-800/60 dark:text-zinc-200",children:"…"}),r("span",{class:"text-sm text-zinc-700 dark:text-zinc-200",children:[r("b",{children:"省略"}),"(間の行が省略されていることがあります)"]})]}),r("div",{class:"flex items-start gap-2",children:r("span",{class:"text-sm text-zinc-700 dark:text-zinc-200",children:[r("b",{children:"優先順位"}),": カラム削除(赤)は行追加(緑)より優先して表示します。"]})})]})})]}),r("table",{class:"w-full border-collapse text-sm",children:[r("thead",{children:u.map((l,a)=>{const f=a*32;return r("tr",{children:l.map((g,v)=>{const m=I(g),A=et(l),P=Ve(l),x=["sticky","text-left","backdrop-blur","border-b","border-zinc-200/70","dark:border-zinc-800/60","px-2.5","py-2","font-bold",A?A?"bg-zinc-200/40 dark:bg-zinc-800/40":"":P?"bg-zinc-100/70 dark:bg-zinc-900/60":"bg-zinc-100/40 dark:bg-zinc-900/40",A?"text-zinc-700 dark:text-zinc-200":"",P?"font-extrabold":""].filter(Boolean).join(" "),y=A&&v>0?tt(m):"";return r("th",{class:[x,v===0?"w-[52px] text-center font-bold text-zinc-600 dark:text-zinc-300":"",y].filter(Boolean).join(" "),style:`top:${f}px`,children:m})})})})}),r("tbody",{children:h.map(l=>{const a=I(l?.[0]),f=a==="+++"?"[&>td]:bg-emerald-500/10":a==="---"?"[&>td]:bg-red-500/10":a==="->"?"[&>td]:bg-amber-400/10":fr(l)?"text-center italic [&>td]:text-zinc-500 dark:[&>td]:text-zinc-400":"";return r("tr",{class:f,children:l.map((g,v)=>{const m=I(g),A=a==="->"&&v>0?ur(m):null,P=v>0?c[v]:"";return r("td",{class:["border-b border-zinc-200/70 px-2.5 py-2 align-top dark:border-zinc-800/60",v===0?"w-[52px] text-center font-bold text-zinc-600 dark:text-zinc-300":"font-mono text-[12.5px]",P].filter(Boolean).join(" "),children:A?r("span",{class:"inline-flex flex-wrap items-baseline gap-1.5",children:[r("span",{class:"text-zinc-500 line-through dark:text-zinc-400",children:A.before}),r("span",{class:"text-zinc-500/80 dark:text-zinc-400/80",children:"→"}),r("span",{class:"font-bold",children:A.after})]}):m})})})})})]})]})};function hr(e){if(e===null)return"null";if(e===void 0)return"";switch(typeof e){case"string":return e;case"number":case"bigint":case"boolean":return String(e);case"symbol":return e.toString();case"function":return"[function]";case"object":try{return JSON.stringify(e)}catch{return Object.prototype.toString.call(e)}default:return""}}const rt=({result:e,error:t,emptyMessage:n})=>r(Ee,{children:[t?r("div",{class:"mt-3 whitespace-pre-wrap rounded-xl border border-red-500/40 bg-red-500/10 px-3 py-2 text-sm",children:t}):null,e?r("div",{class:"mt-3 max-h-[540px] overflow-auto rounded-xl border border-zinc-200/60 bg-white/60 dark:border-zinc-800/60 dark:bg-zinc-950/20",children:[r("table",{class:"w-full border-collapse text-sm",children:[r("thead",{children:r("tr",{children:e.fields.map(i=>r("th",{class:"sticky top-0 bg-zinc-100/70 px-2.5 py-2 text-left font-bold backdrop-blur dark:bg-zinc-900/60",children:i.name}))})}),r("tbody",{children:e.rows.map(i=>{const s=i;return r("tr",{children:e.fields.map(o=>r("td",{class:"border-b border-zinc-200/70 px-2.5 py-2 align-top font-mono text-[12.5px] dark:border-zinc-800/60",children:hr(s[o.name])}))})})})]}),r("div",{class:"mt-2 px-3 pb-3 text-xs text-zinc-500 dark:text-zinc-400",children:[e.rows.length," row(s)"]})]}):r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:n})]});function ve(e){return typeof e=="string"}function br(e){return typeof e=="number"&&Number.isFinite(e)}function M(e,t=3){return Number.isFinite(e)?e.toFixed(t).replace(/\.?0+$/,""):""}function D(e,t){const n=e[t];return ve(n)&&n.trim().length>0?n:void 0}function R(e,t){const n=e[t];return br(n)?n:void 0}function re(e){return e["Node Type"]}function xr(e){const t=re(e).toLowerCase();return t.includes("limit")?"limit":t.includes("unique")?"unique":t.includes("sort")?"sort":t.includes("aggregate")||t.includes("group")?"aggregate":t.includes("join")?"join":t.includes("scan")||t.includes("seek")?"scan":t.includes("cte")||t.includes("subquery")||t.includes("initplan")?"cte":"other"}const gr={scan:{leftKeys:["rel","idx","as","dir"],rightKeys:["rows","cost","actual","loops"]},join:{leftKeys:["join","rel","as"],rightKeys:["rows","cost","actual","loops"]},limit:{leftKeys:[],rightKeys:["rows","cost","actual","loops"]},sort:{leftKeys:[],rightKeys:["rows","cost","actual","loops"]},unique:{leftKeys:[],rightKeys:["rows","cost","actual","loops"]},aggregate:{leftKeys:[],rightKeys:["rows","cost","actual","loops"]},cte:{leftKeys:["cte","subplan","rel","as"],rightKeys:["rows","cost","actual","loops"]},other:{leftKeys:["rel","idx","as","join","dir"],rightKeys:["rows","cost","actual","loops"]}};function mr(e){const t=e.raw,n=D(t,"Relation Name"),i=D(t,"Index Name"),s=D(t,"Alias"),o=D(t,"Join Type"),d=D(t,"Scan Direction"),u=D(t,"CTE Name"),h=D(t,"Subplan Name"),c=[];return u&&c.push({k:"cte",v:u}),h&&c.push({k:"subplan",v:h}),n&&c.push({k:"rel",v:n}),i&&c.push({k:"idx",v:i}),s&&s!==n&&c.push({k:"as",v:s}),o&&c.push({k:"join",v:o}),d&&c.push({k:"dir",v:d}),c}function ye(e){const t=e.raw,n=[["Index Cond","Index Cond"],["Filter","Filter"],["Join Filter","Join Filter"],["Hash Cond","Hash Cond"],["Merge Cond","Merge Cond"],["Recheck Cond","Recheck Cond"],["Sort Key","Sort Key"],["Group Key","Group Key"]],i=[];for(const[s,o]of n){const d=t[o];ve(d)&&d.trim().length&&i.push({label:s,value:d}),Array.isArray(d)&&d.every(ve)&&d.length&&i.push({label:s,value:d.join(", ")})}return i}function Ct(e){const t=e.raw,n=re(e),i=R(t,"Startup Cost"),s=R(t,"Total Cost"),o=R(t,"Plan Rows"),d=R(t,"Actual Startup Time"),u=R(t,"Actual Total Time"),h=R(t,"Actual Rows"),c=R(t,"Actual Loops"),l=D(t,"Relation Name"),a=D(t,"Index Name"),f=D(t,"Alias"),g=D(t,"Join Type"),v=D(t,"Scan Direction"),m=[];return(i!==void 0||s!==void 0||o!==void 0)&&(m.push(`cost=${i!==void 0?M(i,2):"?"}..${s!==void 0?M(s,2):"?"}`),m.push(`rows=${o!==void 0?M(o,0):"?"}`)),(d!==void 0||u!==void 0||h!==void 0||c!==void 0)&&(m.push(`actual time=${d!==void 0?M(d):"?"}..${u!==void 0?M(u):"?"}`),m.push(`actual rows=${h!==void 0?M(h,0):"?"}`),m.push(`loops=${c!==void 0?M(c,0):"?"}`)),g&&m.push(`join=${g}`),l&&m.push(`rel=${l}`),a&&m.push(`idx=${a}`),f&&f!==l&&m.push(`as=${f}`),v&&m.push(`dir=${v}`),m.length?`${n} (${m.join(" ")})`:n}function vr(e){const t=[],n=i=>{const s=i.depth===0?"":`${" ".repeat(Math.max(0,i.depth-1))}-> `;t.push(`${s}${Ct(i)}`);const o=ye(i);for(const d of o)t.push(`${" ".repeat(i.depth)} ${d.label}: ${d.value}`);for(const d of i.children)n(d)};return n(e),t.join(`
2
+ `)}function pe(){const e=document.getElementById("execution-plan");e&&requestAnimationFrame(()=>{e.scrollIntoView({block:"start",behavior:"smooth"})})}function yr(e){const t=re(e).toLowerCase();return t.includes("join")?"join":t.includes("scan")||t.includes("seek")?"scan":t.includes("sort")?"sort":t.includes("aggregate")||t.includes("group")?"aggregate":t.includes("cte")||t.includes("subquery")||t.includes("initplan")?"cte":"other"}function zr(e){const t=e.raw,n=R(t,"Startup Cost"),i=R(t,"Total Cost"),s=R(t,"Plan Rows"),o=R(t,"Actual Startup Time"),d=R(t,"Actual Total Time"),u=R(t,"Actual Rows"),h=R(t,"Actual Loops"),c=[];if((n!==void 0||i!==void 0)&&c.push({k:"cost",v:`${n!==void 0?M(n,2):"?"}..${i!==void 0?M(i,2):"?"}`}),s!==void 0||u!==void 0){const l=s!==void 0?M(s,0):"?",a=u!==void 0?` (actual ${M(u,0)})`:"";c.push({k:"rows",v:`${l}${a}`})}return(o!==void 0||d!==void 0)&&c.push({k:"actual",v:`${o!==void 0?M(o):"?"}..${d!==void 0?M(d):"?"}ms`}),h!==void 0&&c.push({k:"loops",v:M(h,0)}),c}function kr(e){const t=new Map;for(const n of e)t.set(n.k,n);return t}function nt(e,t){const n=kr(e),i=[];for(const s of t){const o=n.get(s);o&&i.push(o)}return i}function it(e){const t=re(e),n=xr(e),i=gr[n],s=mr(e),o=zr(e),d=nt(s,i.leftKeys),u=nt(o,i.rightKeys),h=[t,...d.map(c=>`${c.k}: ${c.v}`),...u.map(c=>`${c.k}: ${c.v}`)].join(" — ");return{title:t,left:d,right:u,rawTitle:h,kind:yr(e),category:n}}function wr(e){const t=new Map,n=i=>{t.set(i.id,i);for(const s of i.children)n(s)};return n(e),t}function st(e){const t=e.raw,n=R(t,"Actual Rows"),i=R(t,"Plan Rows");return n??i}function Pt(e,t,n){return Math.min(n,Math.max(t,e))}function Ar(e,t){if(!e||e<=0||!Number.isFinite(e)||t<=0)return 1.5;const s=Math.log10(e+1)/Math.log10(t+1);return Pt(1.5+s*(7.5-1.5),1.5,7.5)}function Er(e,t,n){const d=t.length>0?Math.max(...t.map(l=>`${l.k} ${l.v}`.length)):0,u=n.length>0?Math.max(...n.map(l=>`${l.k} ${l.v}`.length)):0,h=e.length*(7.8+.6),c=d*7.8+12+u*(7.8*1.15);return Pt(Math.ceil(48+Math.max(h,c)),320,1200)}function Cr(e){const d=new Map,u=new Map,h=new Map,c=[],l=[],a=b=>16+b*162,f=b=>{const x=u.get(b.id);if(x!==void 0)return x;const y=h.get(b.id)??it(b);h.set(b.id,y);const C=Er(y.title,y.left,y.right);return u.set(b.id,C),C},g=b=>{const x=d.get(b.id);if(x!==void 0)return x;const y=f(b),C=b.children.map(S=>g(S)),B=C.length===0?0:C.reduce((S,O)=>S+O,0)+32*(C.length-1),$=Math.max(y,B);return d.set(b.id,$),$},v=(b,x)=>{const y=g(b),C=f(b),B=x+(y-C)/2,$=a(b.depth),S=h.get(b.id)??it(b);h.set(b.id,S),c.push({id:b.id,x:B,y:$,w:C,h:132,title:S.title,left:S.left,right:S.right,rawTitle:S.rawTitle,kind:S.kind});let O=x;for(const K of b.children){l.push({fromId:b.id,toId:K.id});const q=g(K);v(K,O),O+=q+32}};v(e,16);const m=c.reduce((b,x)=>Math.max(b,Math.round((x.y-16)/162)),0),A=16+g(e)+16,P=32+(m+1)*162-30,k=new Map(c.map(b=>[b.id,b]));return{nodes:c,edges:l,nodeById:k,width:A,height:P,constants:{NODE_H:132,GAP_X:32,GAP_Y:30,PAD_X:16,PAD_Y:16}}}const Pr=({root:e})=>{const[t,n]=z(void 0),i=J(()=>wr(e),[e]),s=t?i.get(t):void 0,o=J(()=>Cr(e),[e]),d=J(()=>{let c=0;for(const l of i.values()){const a=st(l);a!==void 0&&Number.isFinite(a)&&(c=Math.max(c,a))}return c},[i]),u=H(null);ee(()=>{const c=u.current;if(!c)return;const l=c.getContext("2d");if(!l)return;const a=typeof window<"u"?window.devicePixelRatio??1:1,f=Math.max(1,Math.ceil(o.width)),g=Math.max(1,Math.ceil(o.height));c.width=Math.ceil(f*a),c.height=Math.ceil(g*a),c.style.width=`${f}px`,c.style.height=`${g}px`,l.setTransform(a,0,0,a,0,0),l.clearRect(0,0,f,g),l.lineCap="round",l.lineJoin="round",l.strokeStyle="rgba(113,113,122,0.35)";for(const v of o.edges){const m=o.nodeById.get(v.fromId),A=o.nodeById.get(v.toId);if(!m||!A)continue;const P=m.x+m.w/2,k=m.y+m.h,b=A.x+A.w/2,x=A.y,y=(k+x)/2,C=i.get(v.toId),B=C?st(C):void 0,$=Ar(B,d);l.lineWidth=$,l.beginPath(),l.moveTo(P,k),l.lineTo(P,y),l.lineTo(b,y),l.lineTo(b,x),l.stroke()}},[i,o,d]);const h=c=>{switch(c){case"join":return"bg-violet-500/70";case"scan":return"bg-sky-500/70";case"sort":return"bg-amber-500/70";case"aggregate":return"bg-emerald-500/70";case"cte":return"bg-indigo-500/70";default:return"bg-zinc-500/60"}};return r("div",{class:"mx-3 overflow-hidden rounded-xl border border-zinc-200/60 bg-white/50 dark:border-zinc-800/60 dark:bg-zinc-950/20",children:[r("div",{class:"overflow-auto",children:r("div",{class:"relative",style:`width:${o.width}px;height:${Math.max(240,o.height)}px`,role:"img","aria-label":"Execution plan graph",children:[r("div",{class:"pointer-events-none absolute inset-0 opacity-60 dark:opacity-40",style:["background-image: linear-gradient(to right, rgba(120,120,120,0.10) 1px, transparent 1px),","linear-gradient(to bottom, rgba(120,120,120,0.10) 1px, transparent 1px);","background-size: 20px 20px;"].join("")}),r("canvas",{ref:u,class:"pointer-events-none absolute left-0 top-0"}),o.nodes.map(c=>{const l=c.x,a=c.y,f=c.id===t;return r("div",{class:["absolute cursor-pointer overflow-hidden rounded-xl border bg-white/60 shadow-sm","dark:bg-zinc-950/20",f?"border-indigo-500/60 ring-2 ring-indigo-500/20":"border-zinc-200/70 dark:border-zinc-800/60"].join(" "),style:`left:${l}px;top:${a}px;width:${c.w}px;height:${c.h}px`,title:c.rawTitle,onClick:()=>n(g=>g===c.id?void 0:c.id),children:[r("div",{class:["absolute left-0 top-0 h-full w-[6px]",h(c.kind)].join(" ")}),r("div",{class:"flex h-full flex-col gap-1 px-3 py-2",children:[r("div",{class:"whitespace-normal break-words text-[12px] font-extrabold leading-snug text-zinc-800 dark:text-zinc-100",children:c.title}),r("div",{class:"mt-0.5 grid grid-cols-[minmax(0,1fr)_minmax(0,1.6fr)] gap-x-3 gap-y-1 text-[10.5px] leading-snug",children:[r("div",{class:"grid content-start gap-1",children:c.left.map(g=>r("div",{class:"min-w-0 break-words",children:[r("span",{class:"mr-1 font-extrabold text-zinc-500 dark:text-zinc-400",children:g.k}),r("span",{class:"font-mono font-semibold text-zinc-700 dark:text-zinc-200",children:g.v})]}))}),r("div",{class:"grid content-start gap-1",children:c.right.map(g=>r("div",{class:"min-w-0 overflow-x-auto whitespace-nowrap",children:[r("span",{class:"mr-1 font-extrabold text-zinc-500 dark:text-zinc-400",children:g.k}),r("span",{class:"font-mono font-semibold text-zinc-700 dark:text-zinc-200",children:g.v})]}))})]})]})]})})]})}),r("div",{class:"border-t border-zinc-200/50 px-3 py-2 text-[11px] text-zinc-500 dark:border-zinc-800/50 dark:text-zinc-400",children:"edge width: rows / ヒント: 横/縦に大きい場合はスクロールできます。ノードクリックで詳細表示。"}),s?r("div",{class:"border-t border-zinc-200/50 px-3 py-3 dark:border-zinc-800/50",children:[r("div",{class:"text-xs font-extrabold text-zinc-800 dark:text-zinc-100",children:Ct(s)}),r("div",{class:"mt-2 grid gap-1.5",children:[ye(s).length?r("div",{class:"rounded-xl border border-zinc-200/60 bg-white/60 px-3 py-2 dark:border-zinc-800/60 dark:bg-zinc-950/20",children:[r("div",{class:"text-[11px] font-extrabold text-zinc-700 dark:text-zinc-200",children:"Conditions"}),r("div",{class:"mt-1 grid gap-1 text-[11px] text-zinc-600 dark:text-zinc-300",children:ye(s).map(c=>r("div",{class:"font-mono",children:[r("span",{class:"font-sans font-bold text-zinc-700 dark:text-zinc-200",children:c.label}),": ",c.value]}))})]}):null,r("details",{class:"rounded-xl border border-zinc-200/60 bg-white/60 px-3 py-2 dark:border-zinc-800/60 dark:bg-zinc-950/20",children:[r("summary",{class:"cursor-pointer select-none text-[11px] font-extrabold text-zinc-700 dark:text-zinc-200 [&::-webkit-details-marker]:hidden",children:"Raw JSON"}),r("pre",{class:"m-0 mt-2 max-h-[320px] overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-relaxed text-zinc-800 dark:text-zinc-100",children:JSON.stringify(s.raw,null,2)})]})]})]}):null]})},Mr=({root:e})=>{const t=vr(e);return r("div",{class:"mx-3 overflow-hidden rounded-xl border border-zinc-200/60 bg-white/60 dark:border-zinc-800/60 dark:bg-zinc-950/20",children:[r("div",{class:"flex items-center justify-between gap-2 border-b border-zinc-200/50 px-3 py-2 dark:border-zinc-800/50",children:[r("span",{class:"text-xs font-extrabold text-zinc-700 dark:text-zinc-200",children:"Plain text"}),r("button",{type:"button",class:"rounded-lg border border-zinc-300/70 bg-white px-2 py-1 text-[11px] font-bold text-zinc-700 hover:bg-zinc-50 active:bg-zinc-100 dark:border-zinc-700/70 dark:bg-zinc-900/40 dark:text-zinc-200 dark:hover:bg-zinc-800/50",onClick:()=>{navigator.clipboard?.writeText(t)},children:"Copy"})]}),r("pre",{class:"m-0 max-h-[540px] overflow-auto whitespace-pre px-3 py-3 font-mono text-[12.5px] leading-relaxed text-zinc-800 dark:text-zinc-100",children:t})]})},ot=({result:e,error:t})=>{if(!e)return r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:t??"Plan is not available"});const n=e.statements??[],[i,s]=z(0),[o,d]=z("graph");ee(()=>{n.length&&(i<0||i>=n.length)&&s(0)},[i,n.length]);const u=n.length>0?n[Math.min(n.length-1,Math.max(0,i))]:void 0,h=u?.meta.planningTimeMs,c=u?.meta.executionTimeMs,l=a=>["cursor-pointer select-none rounded-lg border border-transparent px-3 py-1.5 text-xs font-extrabold","text-zinc-700 hover:bg-zinc-200/40 dark:text-zinc-200 dark:hover:bg-zinc-800/40",a?"border-indigo-500/30 bg-indigo-500/15":""].filter(Boolean).join(" ");return r(Ee,{children:r("div",{class:"mt-3 grid gap-3",children:n.length&&u?r("div",{class:"overflow-hidden rounded-xl border border-zinc-200/70 bg-zinc-50/60 dark:border-zinc-800/60 dark:bg-zinc-900/20",children:[r("div",{class:"flex flex-wrap items-center justify-between gap-2 border-b border-zinc-200/50 px-3 py-2 dark:border-zinc-800/50",children:[r("div",{class:"flex flex-wrap items-center gap-2",children:[r("label",{class:"text-xs font-extrabold text-zinc-700 dark:text-zinc-200",children:"Statement"}),r("select",{class:"rounded-lg border border-zinc-300/70 bg-white px-2 py-1 text-xs font-bold text-zinc-700 dark:border-zinc-700/70 dark:bg-zinc-900/40 dark:text-zinc-200",value:String(i),disabled:n.length<=1,onChange:a=>{const f=Number(a.currentTarget.value);s(Number.isFinite(f)?f:0),pe()},children:n.map((a,f)=>r("option",{value:String(f),children:["Statement ",f+1]}))}),r("div",{class:"ml-2 inline-flex gap-3 text-[11px] font-bold text-zinc-500 dark:text-zinc-400",children:[h!==void 0?r("span",{children:["planning ",M(h),"ms"]}):null,c!==void 0?r("span",{children:["execution ",M(c),"ms"]}):null]})]}),r("div",{class:"inline-flex gap-1 rounded-xl border border-zinc-300/70 bg-zinc-100/50 p-1 dark:border-zinc-700/70 dark:bg-zinc-900/40",children:[r("button",{type:"button",class:l(o==="graph"),onClick:()=>{d("graph"),pe()},children:"Graph"}),r("button",{type:"button",class:l(o==="text"),onClick:()=>{d("text"),pe()},children:"Text"})]})]}),r("div",{class:"py-2",children:o==="graph"?r("section",{children:r(Pr,{root:u.root})}):r("section",{children:r(Mr,{root:u.root})})})]}):r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:t??"No plan statements"})})})};function Rr(e){const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString(void 0,{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})}function ct(e,t=120){const i=((e??"").split(/\r?\n/,1)[0]??"").trim();return i.length<=t?i:`${i.slice(0,Math.max(0,t-1))}…`}const Tr=({open:e,loading:t,error:n,items:i,onClose:s,onReload:o,onSelect:d})=>e?r("div",{class:"fixed inset-0 z-50",children:[r("div",{class:"absolute inset-0 bg-zinc-950/20 backdrop-blur-[1px]",onClick:s}),r("aside",{class:"absolute right-0 top-0 h-full w-[380px] max-w-[92vw] border-l border-zinc-200/60 bg-white/90 shadow-xl backdrop-blur dark:border-zinc-800/60 dark:bg-zinc-950/70",children:[r("div",{class:"flex items-center justify-between gap-2 border-b border-zinc-200/60 px-4 py-3 dark:border-zinc-800/60",children:[r("div",{children:[r("div",{class:"text-sm font-extrabold text-zinc-800 dark:text-zinc-100",children:"履歴"}),r("div",{class:"text-[11px] text-zinc-500 dark:text-zinc-400",children:"直近の実行結果を復元できます"})]}),r("div",{class:"flex items-center gap-2",children:[r("button",{type:"button",class:"rounded-lg border border-zinc-300/70 bg-white px-2 py-1 text-[11px] font-bold text-zinc-700 hover:bg-zinc-50 active:bg-zinc-100 dark:border-zinc-700/70 dark:bg-zinc-900/40 dark:text-zinc-200 dark:hover:bg-zinc-800/50",onClick:o,disabled:t,children:t?"更新中…":"更新"}),r("button",{type:"button",class:"rounded-lg border border-zinc-300/70 bg-white px-2 py-1 text-[11px] font-bold text-zinc-700 hover:bg-zinc-50 active:bg-zinc-100 dark:border-zinc-700/70 dark:bg-zinc-900/40 dark:text-zinc-200 dark:hover:bg-zinc-800/50",onClick:s,children:"閉じる"})]})]}),r("div",{class:"h-[calc(100%-56px)] overflow-auto px-4 py-3",children:[n?r("div",{class:"rounded-xl border border-rose-500/30 bg-rose-500/10 px-3 py-2 text-xs text-rose-800 dark:text-rose-200",children:n}):null,!t&&i.length===0?r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:"まだ履歴がありません。"}):null,r("div",{class:"mt-3 grid gap-2",children:i.map(u=>r("button",{type:"button",class:["w-full text-left rounded-xl border px-3 py-2 shadow-sm","border-zinc-200/70 bg-white/60 hover:bg-zinc-50 active:bg-zinc-100","dark:border-zinc-800/60 dark:bg-zinc-950/20 dark:hover:bg-zinc-900/30 dark:active:bg-zinc-900/50"].join(" "),onClick:()=>d(u.id),children:[r("div",{class:"flex items-baseline justify-between gap-2",children:[r("div",{class:"text-xs font-extrabold text-zinc-800 dark:text-zinc-100",children:Rr(u.timestamp)}),r("div",{class:"flex items-center gap-1",children:[u.hasError?r("span",{class:"rounded-full border border-rose-500/30 bg-rose-500/10 px-2 py-0.5 text-[10px] font-extrabold text-rose-700 dark:text-rose-200",children:"error"}):null,u.hasDiffChanges===!1?r("span",{class:"rounded-full border border-emerald-500/30 bg-emerald-500/10 px-2 py-0.5 text-[10px] font-extrabold text-emerald-700 dark:text-emerald-200",children:"same"}):r("span",{class:"rounded-full border border-amber-500/30 bg-amber-500/10 px-2 py-0.5 text-[10px] font-extrabold text-amber-700 dark:text-amber-200",children:"diff"}),r("span",{class:"rounded-full border border-indigo-500/30 bg-indigo-500/10 px-2 py-0.5 text-[10px] font-extrabold text-indigo-700 dark:text-indigo-200",children:u.planMode==="analyze"?"ANALYZE":"EXPLAIN"})]})]}),r("div",{class:"mt-1 text-[11px] text-zinc-600 dark:text-zinc-300",children:[r("div",{class:"font-mono",children:["A: ",ct(u.queryA)]}),r("div",{class:"font-mono",children:["B: ",ct(u.queryB)]})]})]}))})]})]})]}):null,Nr=`select
3
+ 1 as one,
4
+ now() as now`;function Sr(){const[e,t]=z(Nr),[n,i]=z(""),[s,o]=z("explain"),[d,u]=z(void 0),[h,c]=z(!1),[l,a]=z("diff"),[f,g]=z("a"),[v,m]=z(!0),[A,P]=z(void 0),[k,b]=z(void 0),[x,y]=z(void 0),[C,B]=z(void 0),[$,S]=z(void 0),[O,K]=z(void 0),[q,Ce]=z(void 0),[ne,Pe]=z(void 0),[ie,Me]=z(void 0),[se,Re]=z(void 0),[Rt,oe]=z(!1),[Tt,Te]=z(!1),[Nt,ce]=z(void 0),[St,Ne]=z([]),ae=!!(A||k||x||C),Dt=!!(ae||!!(q||ne||ie||se)),Se=H(null),De=H(null),$e=H(null),[je,$t]=z(0),Be=H(null),Ie=H(null),[Le,jt]=z(0),W=p=>p?"relative":"pointer-events-none absolute left-0 top-0 w-full opacity-0";ee(()=>{const p=[Se.current,De.current,$e.current].filter(Boolean);if(!p.length)return;const E=()=>{const T=Math.max(...p.map(_=>_.getBoundingClientRect().height));Number.isFinite(T)&&$t(Math.ceil(T))};if(E(),typeof ResizeObserver>"u")return;const N=new ResizeObserver(()=>E());for(const T of p)N.observe(T);return()=>N.disconnect()},[$,A,k,x,C,v]),ee(()=>{const p=[Be.current,Ie.current].filter(Boolean);if(!p.length)return;const E=()=>{const T=Math.max(...p.map(_=>_.getBoundingClientRect().height));Number.isFinite(T)&&jt(Math.ceil(T))};if(E(),typeof ResizeObserver>"u")return;const N=new ResizeObserver(()=>E());for(const T of p)N.observe(T);return()=>N.disconnect()},[q,ne,ie,se]);const X=p=>["cursor-pointer select-none rounded-lg border border-transparent px-3 py-1.5 text-xs font-bold","text-zinc-700 hover:bg-zinc-200/40 dark:text-zinc-200 dark:hover:bg-zinc-800/40",p?"border-indigo-500/30 bg-indigo-500/15":""].filter(Boolean).join(" ");function Oe(){const p=document.getElementById("execution-plan");p&&requestAnimationFrame(()=>{p.scrollIntoView({block:"start",behavior:"smooth"})})}function _e(p){P(p.resultA),b(p.resultB),y(p.errorA),B(p.errorB),S(p.diff),K(p.hasDiffChanges),Ce(p.planResultA),Pe(p.planResultB),Me(p.planErrorA),Re(p.planErrorB),u(p.planMode);const N=!!(p.resultA&&p.resultB)&&p.hasDiffChanges!==!1;m(N)}async function He(){Te(!0),ce(void 0);try{const p=await fetch("/api/histories?limit=40",{headers:{Accept:"application/json"}});if(!p.ok){const N=await p.text().catch(()=>"");throw new Error(`HTTP ${p.status}${N?`: ${N}`:""}`)}const E=await p.json();Ne(E.items??[])}catch(p){Ne([]),ce(p instanceof Error?p.message:String(p))}finally{Te(!1)}}async function Bt(p){try{const E=await fetch(`/api/histories/${encodeURIComponent(p)}`,{headers:{Accept:"application/json"}});if(!E.ok){const _=await E.text().catch(()=>"");throw new Error(`HTTP ${E.status}${_?`: ${_}`:""}`)}const T=(await E.json()).item;if(!T)throw new Error("履歴が見つかりませんでした。");t(T.request.queryA),i(T.request.queryB),o(T.request.planMode),_e(T.response),oe(!1)}catch(E){ce(E instanceof Error?E.message:String(E))}}async function It(){c(!0);try{const p=await fetch("/api/run",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({queryA:e,queryB:n,planMode:s})});if(!p.ok){const N=await p.text().catch(()=>"");throw new Error(`HTTP ${p.status}${N?`: ${N}`:""}`)}const E=await p.json();_e(E)}catch(p){const E=p instanceof Error?p.message:String(p);y(E),B(void 0),Me(void 0),Re(void 0),P(void 0),b(void 0),S(void 0),Ce(void 0),Pe(void 0),u(void 0),m(!0)}finally{c(!1)}}return r("div",{class:"mx-auto max-w-5xl px-6 py-6",children:[r("header",{class:"mb-4 flex items-start justify-between gap-4",children:[r("div",{children:r("h1",{class:"m-0 text-lg font-bold",children:"queot — Query Console"})}),r("div",{class:"flex items-center gap-2",children:r("button",{type:"button",class:"inline-flex items-center justify-center rounded-xl border border-zinc-300/70 bg-white px-3 py-2 text-sm font-semibold shadow-sm hover:bg-zinc-50 active:bg-zinc-100 dark:border-zinc-700/70 dark:bg-zinc-900/40 dark:hover:bg-zinc-800/50",onClick:()=>{oe(p=>{const E=!p;return E&&He(),E})},children:"履歴"})})]}),r("main",{class:"grid gap-4",children:[r("section",{class:"rounded-xl border border-zinc-200/70 bg-zinc-50/60 p-4 dark:border-zinc-800/60 dark:bg-zinc-900/20",children:r("details",{open:!0,children:[r("summary",{class:"flex cursor-pointer list-none items-baseline justify-between gap-3 select-none [&::-webkit-details-marker]:hidden",children:[r("span",{class:"text-sm font-bold text-zinc-800 dark:text-zinc-100",children:"SQL"}),r("span",{class:"text-xs text-zinc-500 dark:text-zinc-400",children:"(クリックで開閉)"})]}),r("div",{class:"mt-2",children:r("form",{onSubmit:p=>{p.preventDefault(),It()},children:[r("div",{class:"mt-2 grid grid-cols-1 gap-3 lg:grid-cols-2",children:[r("div",{class:"flex flex-col",children:[r("div",{class:"flex items-baseline justify-between",children:r("span",{class:"text-xs font-semibold text-zinc-600 dark:text-zinc-300",children:"Query A"})}),r("textarea",{id:"sqlA",class:"mt-2 w-full rounded-xl border border-zinc-300/70 bg-white/70 px-3 py-2 font-mono text-sm leading-snug shadow-sm outline-none focus:ring-2 focus:ring-indigo-400/50 dark:border-zinc-700/70 dark:bg-zinc-950/40",spellcheck:!1,rows:10,value:e,onInput:p=>t(p.currentTarget.value)})]}),r("div",{class:"flex flex-col",children:[r("div",{class:"flex items-baseline justify-between",children:r("span",{class:"text-xs font-semibold text-zinc-600 dark:text-zinc-300",children:"Query B"})}),r("textarea",{id:"sqlB",class:"mt-2 w-full rounded-xl border border-zinc-300/70 bg-white/70 px-3 py-2 font-mono text-sm leading-snug shadow-sm outline-none focus:ring-2 focus:ring-indigo-400/50 dark:border-zinc-700/70 dark:bg-zinc-950/40",spellcheck:!1,rows:10,value:n,onInput:p=>i(p.currentTarget.value)})]})]}),r("div",{class:"mt-3 flex flex-col gap-1.5",children:[r("div",{class:"flex items-baseline justify-between",children:[r("span",{class:"text-xs font-semibold text-zinc-600 dark:text-zinc-300",children:"実行計画"}),r("span",{class:"text-xs text-zinc-500 dark:text-zinc-400",children:"EXPLAIN / EXPLAIN ANALYZE を自動付与"})]}),r("div",{class:"flex flex-wrap items-center gap-3",children:[r("label",{class:"inline-flex items-center gap-2 text-xs text-zinc-700 dark:text-zinc-300",children:[r("input",{type:"radio",name:"planMode",value:"explain",checked:s==="explain",onChange:()=>o("explain")}),"EXPLAIN"]}),r("label",{class:"inline-flex items-center gap-2 text-xs text-zinc-700 dark:text-zinc-300",children:[r("input",{type:"radio",name:"planMode",value:"analyze",checked:s==="analyze",onChange:()=>o("analyze")}),"EXPLAIN ANALYZE"]})]})]}),r("div",{class:"mt-2 flex items-center gap-3",children:r("button",{type:"submit",class:"inline-flex items-center justify-center rounded-xl border border-zinc-300/70 bg-white px-3 py-2 text-sm font-semibold shadow-sm hover:bg-zinc-50 active:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-60 dark:border-zinc-700/70 dark:bg-zinc-900/40 dark:hover:bg-zinc-800/50",disabled:h,children:h?"Running...":"Run"})})]})})]})}),r("section",{class:"rounded-xl border border-zinc-200/70 bg-zinc-50/60 p-4 dark:border-zinc-800/60 dark:bg-zinc-900/20",children:[r("div",{class:"flex items-baseline justify-between gap-3",children:r("h2",{class:"m-0 text-sm font-bold text-zinc-700 dark:text-zinc-200",children:"出力"})}),Dt?r(Ee,{children:[ae?r("details",{class:"mt-3 overflow-hidden rounded-xl border border-zinc-200/70 bg-white/40 dark:border-zinc-800/60 dark:bg-zinc-950/10",open:v,onToggle:p=>m(p.currentTarget.open),children:[r("summary",{class:"flex cursor-pointer list-none items-baseline justify-between gap-3 select-none px-3 py-2 [&::-webkit-details-marker]:hidden",children:[r("div",{class:"flex flex-wrap items-baseline gap-2",children:[r("span",{class:"text-sm font-bold text-zinc-800 dark:text-zinc-100",children:"結果(Result)"}),A&&k?$?O?r("span",{class:"rounded-full border border-amber-500/30 bg-amber-500/15 px-2 py-0.5 text-[11px] font-extrabold text-amber-700 dark:text-amber-200",children:"差分あり"}):r("span",{class:"rounded-full border border-emerald-500/30 bg-emerald-500/15 px-2 py-0.5 text-[11px] font-extrabold text-emerald-700 dark:text-emerald-200",children:"差分なし"}):r("span",{class:"rounded-full border border-emerald-500/30 bg-emerald-500/15 px-2 py-0.5 text-[11px] font-extrabold text-emerald-700 dark:text-emerald-200",children:"Diff未取得"}):r("span",{class:"rounded-full border border-zinc-400/30 bg-zinc-400/10 px-2 py-0.5 text-[11px] font-extrabold text-zinc-600 dark:text-zinc-300",children:"片側のみ"})]}),r("span",{class:"text-xs text-zinc-500 dark:text-zinc-400",children:"(クリックで開閉)"})]}),r("div",{class:"border-t border-zinc-200/60 px-3 pb-3 dark:border-zinc-800/60",children:r("div",{class:"mt-3",children:[r("div",{class:"inline-flex gap-1 rounded-xl border border-zinc-300/70 bg-zinc-100/50 p-1 dark:border-zinc-700/70 dark:bg-zinc-900/40",children:[r("button",{type:"button",class:X(l==="diff"),onClick:()=>a("diff"),children:"Diff"}),r("button",{type:"button",class:X(l==="a"),onClick:()=>a("a"),children:"Query A"}),r("button",{type:"button",class:X(l==="b"),onClick:()=>a("b"),children:"Query B"})]}),r("div",{class:"relative mt-3",style:je>0?`min-height:${je}px`:void 0,children:[r("section",{ref:Se,class:W(l==="diff"),children:r(pr,{diff:$,hasBothResults:!!(A&&k)})}),r("section",{ref:De,class:W(l==="a"),children:r(rt,{result:A,error:x,emptyMessage:"Query Aは未実行/空です。"})}),r("section",{ref:$e,class:W(l==="b"),children:r(rt,{result:k,error:C,emptyMessage:"Query Bは未実行/空です。"})})]})]})})]}):null,r("section",{id:"execution-plan",class:[ae?"mt-5":"mt-3","rounded-xl border border-zinc-200/70 bg-white/40 p-3 dark:border-zinc-800/60 dark:bg-zinc-950/10"].join(" "),children:[(()=>{const p=d??s;return r("div",{class:"flex items-baseline justify-between gap-3",children:[r("div",{children:[r("h3",{class:"m-0 text-sm font-bold text-zinc-700 dark:text-zinc-200",children:"実行計画(Execution Plan)"}),r("div",{class:"text-xs text-zinc-500 dark:text-zinc-400",children:p==="analyze"?"EXPLAIN ANALYZE を付与して実行計画を取得しています。":"EXPLAIN を付与して実行計画を取得しています。"})]}),r("div",{class:"rounded-full border border-indigo-500/30 bg-indigo-500/15 px-3 py-1 text-xs font-extrabold",children:p==="analyze"?"EXPLAIN ANALYZE":"EXPLAIN"})]})})(),r("div",{class:"mt-3",children:[r("div",{class:"inline-flex gap-1 rounded-xl border border-zinc-300/70 bg-zinc-100/50 p-1 dark:border-zinc-700/70 dark:bg-zinc-900/40",children:[r("button",{type:"button",class:X(f==="a"),onClick:()=>{g("a"),Oe()},children:"Plan A"}),r("button",{type:"button",class:X(f==="b"),onClick:()=>{g("b"),Oe()},children:"Plan B"})]}),r("div",{class:"relative mt-3",style:Le>0?`min-height:${Le}px`:void 0,children:[r("section",{ref:Be,class:W(f==="a"),children:r(ot,{result:q,error:ie??"Query Aの実行計画は未実行/空です。"})}),r("section",{ref:Ie,class:W(f==="b"),children:r(ot,{result:ne,error:se??"Query Bの実行計画は未実行/空です。"})})]})]})]})]}):r("div",{class:"mt-2 text-xs text-zinc-500 dark:text-zinc-400",children:"まだ結果はありません。上のフォームから実行してください。"})]})]}),r(Tr,{open:Rt,loading:Tt,error:Nt,items:St,onClose:()=>oe(!1),onReload:()=>{He()},onSelect:p=>{Bt(p)}})]})}const Mt=document.getElementById("root");if(!Mt)throw new Error("Root element not found");Vt(r(Sr,{}),Mt);
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>queot — Query Console</title>
7
+ <script type="module" crossorigin src="/assets/index-n3G904qg.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BElNIk2M.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,109 @@
1
+ import { Command, Flags, settings } from "@oclif/core";
2
+ import { createApp } from "../server.js";
3
+ import { serve } from "@hono/node-server";
4
+ import { Context } from "effect";
5
+ import { RuntimeConfig, loadRuntimeConfigFromEnv } from "../infra/config.js";
6
+ import { HistoryStore, makeHistoryStore } from "../infra/history.js";
7
+ import { Client } from "pg";
8
+ import { makePgQueryable } from "../infra/postgres/queryable.js";
9
+ import { Queryable } from "../services/query.js";
10
+ import open from "open";
11
+ import { password } from "@inquirer/prompts";
12
+ function getPortFromServerAddress(addr) {
13
+ if (!addr)
14
+ return null;
15
+ if (typeof addr === "string")
16
+ return null;
17
+ return addr.port;
18
+ }
19
+ export default class Serve extends Command {
20
+ static args = {};
21
+ static summary = "PostgreSQL に接続し、queot のWeb UIサーバーを起動します。";
22
+ static description = `パスワードは起動時にプロンプトから入力します。 DBPASSWORD 環境変数で渡すことも可能です。`;
23
+ static examples = [
24
+ "$ <%= config.bin %> <%= command.id %>",
25
+ "$ <%= config.bin %> <%= command.id %> --db-name demo",
26
+ "$ DBPASSWORD=**** <%= config.bin %> <%= command.id %> --db-name demo",
27
+ "$ <%= config.bin %> <%= command.id %> --db-name demo --listen-port 3000",
28
+ ];
29
+ static flags = {
30
+ "db-host": Flags.string({
31
+ description: "host to connect to Database",
32
+ char: "h",
33
+ default: "localhost",
34
+ helpValue: "<hostname/IP address>",
35
+ }),
36
+ "db-port": Flags.integer({
37
+ char: "p",
38
+ description: "port to connect to Database",
39
+ default: 5432,
40
+ helpValue: "<port number>",
41
+ }),
42
+ "db-username": Flags.string({
43
+ char: "u",
44
+ description: "user to connect to Database",
45
+ default: "postgres",
46
+ helpValue: "<username>",
47
+ }),
48
+ "db-name": Flags.string({
49
+ char: "d",
50
+ description: "database to connect to Database",
51
+ default: "postgres",
52
+ helpValue: "<database name>",
53
+ }),
54
+ "listen-port": Flags.integer({
55
+ char: "l",
56
+ description: "port to listen on (default: auto select an available port)",
57
+ helpValue: "<port number>",
58
+ }),
59
+ };
60
+ async run() {
61
+ const { flags } = await this.parse(Serve);
62
+ // env は起動時に1回だけ読む(各モジュールで process.env を参照しない)
63
+ const cfg = await loadRuntimeConfigFromEnv(process.env);
64
+ const listenPort = flags["listen-port"];
65
+ const dbHost = flags["db-host"];
66
+ const dbPort = flags["db-port"];
67
+ const dbUser = flags["db-username"];
68
+ const dbDatabase = flags["db-name"];
69
+ let dbPassword = process.env.DBPASSWORD;
70
+ if (!dbPassword) {
71
+ dbPassword = await password({
72
+ message: "password > ",
73
+ mask: true,
74
+ });
75
+ }
76
+ const client = new Client({
77
+ host: dbHost,
78
+ port: dbPort,
79
+ user: dbUser,
80
+ password: dbPassword,
81
+ database: dbDatabase,
82
+ });
83
+ await client.connect();
84
+ const queryable = makePgQueryable(client);
85
+ const historyStore = makeHistoryStore(cfg);
86
+ const context = Context.empty().pipe(Context.add(RuntimeConfig, cfg), Context.add(Queryable, queryable), Context.add(HistoryStore, historyStore));
87
+ const app = createApp({
88
+ context,
89
+ isProduction: cfg.nodeEnv !== "development",
90
+ });
91
+ const server = serve({
92
+ fetch: app.fetch,
93
+ port: listenPort ?? 0,
94
+ });
95
+ const actualPort = getPortFromServerAddress(server.address()) ?? listenPort ?? 0;
96
+ process.on("SIGINT", () => {
97
+ server.close();
98
+ void client.end();
99
+ });
100
+ process.on("SIGTERM", () => {
101
+ server.close();
102
+ void client.end();
103
+ });
104
+ this.log(`サーバーはポート ${actualPort} で起動しています\n終了するには Ctrl+C を押してください`);
105
+ if (!settings.debug) {
106
+ await open(`http://localhost:${actualPort}`);
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,101 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ import { Context, Effect, Layer, Schema } from "effect";
5
+ import { Env } from "./env.js";
6
+ export class RuntimeConfig extends Context.Tag("RuntimeConfig")() {
7
+ }
8
+ const NonNegativeInt = Schema.Number.pipe(Schema.int(), Schema.nonNegative());
9
+ const QueotConfigFileSchema = Schema.Struct({
10
+ historyPath: Schema.optional(Schema.Trim.pipe(Schema.nonEmptyString())),
11
+ historyMaxEntries: Schema.optional(NonNegativeInt),
12
+ historyMaxBytes: Schema.optional(NonNegativeInt),
13
+ });
14
+ function clampInt(v, min, max) {
15
+ if (!Number.isFinite(v))
16
+ return min;
17
+ return Math.max(min, Math.min(max, Math.trunc(v)));
18
+ }
19
+ function getConfigHome(env) {
20
+ const xdg = env.get("XDG_CONFIG_HOME")?.trim();
21
+ if (xdg && xdg.length > 0)
22
+ return xdg;
23
+ return path.join(os.homedir(), ".config");
24
+ }
25
+ export function getQueotConfigPath(env) {
26
+ return path.join(getConfigHome(env), "queot", "config.json");
27
+ }
28
+ function loadQueotConfig(env) {
29
+ const p = getQueotConfigPath(env);
30
+ return Effect.tryPromise({
31
+ try: async () => {
32
+ const raw = await fs.readFile(p, "utf8");
33
+ const json = JSON.parse(raw);
34
+ const decoded = Schema.decodeUnknownEither(QueotConfigFileSchema)(json);
35
+ if (decoded._tag === "Left")
36
+ return {};
37
+ return decoded.right;
38
+ },
39
+ catch: (e) => (e instanceof Error ? e : new Error(String(e))),
40
+ }).pipe(Effect.catchAll(() => Effect.succeed({})));
41
+ }
42
+ function parseEnvNonNegativeInt(env, name) {
43
+ const raw = env.get(name);
44
+ if (!raw)
45
+ return undefined;
46
+ try {
47
+ return Schema.decodeUnknownSync(Schema.NumberFromString.pipe(Schema.int(), Schema.nonNegative()))(raw);
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ function parseEnvNonEmptyString(env, name) {
54
+ const raw = env.get(name);
55
+ if (!raw)
56
+ return undefined;
57
+ try {
58
+ return Schema.decodeUnknownSync(Schema.Trim.pipe(Schema.nonEmptyString()))(raw);
59
+ }
60
+ catch {
61
+ return undefined;
62
+ }
63
+ }
64
+ function resolveHistoryConfig(env, cfg) {
65
+ const historyPath = parseEnvNonEmptyString(env, "QUEOT_HISTORY_PATH") ??
66
+ cfg.historyPath ??
67
+ path.join(getConfigHome(env), "queot", "history.jsonl");
68
+ const historyMaxEntriesRaw = parseEnvNonNegativeInt(env, "QUEOT_HISTORY_MAX_ENTRIES") ??
69
+ cfg.historyMaxEntries ??
70
+ 500;
71
+ const historyMaxBytesRaw = parseEnvNonNegativeInt(env, "QUEOT_HISTORY_MAX_BYTES") ??
72
+ cfg.historyMaxBytes ??
73
+ 5 * 1024 * 1024;
74
+ return {
75
+ historyPath,
76
+ historyMaxEntries: clampInt(historyMaxEntriesRaw, 1, 50_000),
77
+ historyMaxBytes: clampInt(historyMaxBytesRaw, 1, 1024 * 1024 * 1024),
78
+ };
79
+ }
80
+ export const RuntimeConfigLive = Layer.effect(RuntimeConfig, Effect.gen(function* () {
81
+ const env = yield* Env;
82
+ const cfg = yield* loadQueotConfig(env);
83
+ return {
84
+ history: resolveHistoryConfig(env, cfg),
85
+ nodeEnv: env.get("NODE_ENV"),
86
+ };
87
+ }));
88
+ /**
89
+ * 起動時に1回だけ `process.env` を読む用途。
90
+ * - Effect/Layer を使わずに `RuntimeConfig` のサービス値だけ欲しいケース(CLI起動など)向け
91
+ */
92
+ export async function loadRuntimeConfigFromEnv(env) {
93
+ const envSvc = {
94
+ get: (name) => env[name],
95
+ };
96
+ const cfg = await Effect.runPromise(loadQueotConfig(envSvc));
97
+ return {
98
+ history: resolveHistoryConfig(envSvc, cfg),
99
+ nodeEnv: envSvc.get("NODE_ENV"),
100
+ };
101
+ }
@@ -0,0 +1,8 @@
1
+ import { Context, Layer } from "effect";
2
+ export class Env extends Context.Tag("Env")() {
3
+ }
4
+ export function EnvLive(env) {
5
+ return Layer.succeed(Env, {
6
+ get: (name) => env[name],
7
+ });
8
+ }
@@ -0,0 +1,201 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createReadStream } from "node:fs";
4
+ import readline from "node:readline";
5
+ import { randomUUID } from "node:crypto";
6
+ import { Context, Effect, Layer } from "effect";
7
+ import { diffHasChanges } from "../services/diff.js";
8
+ import { RuntimeConfig } from "./config.js";
9
+ export class HistoryStore extends Context.Tag("HistoryStore")() {
10
+ }
11
+ function clampInt(v, min, max) {
12
+ if (!Number.isFinite(v))
13
+ return min;
14
+ return Math.max(min, Math.min(max, Math.trunc(v)));
15
+ }
16
+ function toSummary(entry) {
17
+ const qA = entry.request.queryA ?? entry.response.queryA ?? "";
18
+ const qB = entry.request.queryB ?? entry.response.queryB ?? "";
19
+ const hasBothResults = Boolean(entry.response.resultA && entry.response.resultB);
20
+ // 期待仕様:
21
+ // - A/B片側しかなく diff が取れない場合は true(= 要確認)
22
+ // - diff がある場合は true
23
+ // - diff がない場合は false
24
+ const hasDiffChanges = typeof entry.response.hasDiffChanges === "boolean"
25
+ ? entry.response.hasDiffChanges
26
+ : hasBothResults
27
+ ? diffHasChanges(entry.response.diff ?? entry.response.planDiff)
28
+ : qA.trim().length > 0 || qB.trim().length > 0
29
+ ? true
30
+ : undefined;
31
+ return {
32
+ id: entry.id,
33
+ timestamp: entry.timestamp,
34
+ queryA: qA,
35
+ queryB: qB,
36
+ planMode: entry.request.planMode,
37
+ hasError: Boolean(entry.response.errorA || entry.response.errorB),
38
+ hasDiffChanges,
39
+ };
40
+ }
41
+ export function newHistoryEntry(args) {
42
+ return {
43
+ id: randomUUID(),
44
+ timestamp: new Date().toISOString(),
45
+ request: {
46
+ queryA: args.queryA,
47
+ queryB: args.queryB,
48
+ planMode: args.planMode,
49
+ },
50
+ response: args.response,
51
+ };
52
+ }
53
+ async function trimHistoryIfNeeded(p, retention) {
54
+ const { maxBytes, maxEntries } = retention;
55
+ const st = await fs.stat(p).catch(() => undefined);
56
+ if (!st)
57
+ return;
58
+ if (st.size <= maxBytes)
59
+ return;
60
+ // 最新maxEntries行だけ残す
61
+ const buf = [];
62
+ for await (const line of readLines(p)) {
63
+ const trimmed = line.trim();
64
+ if (!trimmed)
65
+ continue;
66
+ buf.push(trimmed);
67
+ if (buf.length > maxEntries)
68
+ buf.shift();
69
+ }
70
+ const tmp = `${p}.tmp-${randomUUID()}`;
71
+ await fs.writeFile(tmp, `${buf.join("\n")}\n`, "utf8");
72
+ await fs.rename(tmp, p);
73
+ }
74
+ export function makeHistoryStore(cfg) {
75
+ const retention = {
76
+ // 大きくなりすぎた時だけ compact して「最新N件」を残す(通常時は append のみで高速)
77
+ maxEntries: cfg.history.historyMaxEntries,
78
+ maxBytes: cfg.history.historyMaxBytes,
79
+ };
80
+ const historyPath = cfg.history.historyPath;
81
+ return {
82
+ getHistoryPath: Effect.succeed(historyPath),
83
+ appendHistory: (entry) => Effect.tryPromise({
84
+ try: async () => {
85
+ await fs.mkdir(path.dirname(historyPath), { recursive: true });
86
+ await fs.appendFile(historyPath, `${JSON.stringify(entry)}\n`, "utf8");
87
+ // best-effort: 履歴肥大化時に古いものから削除(失敗しても本処理は壊さない)
88
+ try {
89
+ await trimHistoryIfNeeded(historyPath, retention);
90
+ }
91
+ catch {
92
+ // ignore
93
+ }
94
+ },
95
+ catch: (e) => (e instanceof Error ? e : new Error(String(e))),
96
+ }).pipe(Effect.orDie),
97
+ readHistorySummaries: (opts) => Effect.tryPromise({
98
+ try: async () => {
99
+ const limit = clampInt(opts?.limit ?? 50, 1, 200);
100
+ const id = opts?.id?.trim() || undefined;
101
+ const timestamp = opts?.timestamp?.trim() || undefined;
102
+ await fs.stat(historyPath).catch(() => undefined);
103
+ // 最新N件だけ欲しいケースが多いので、リングバッファで保持する
104
+ const buf = [];
105
+ try {
106
+ for await (const line of readLines(historyPath)) {
107
+ const entry = parseEntry(line);
108
+ if (!entry)
109
+ continue;
110
+ if (id && entry.id !== id)
111
+ continue;
112
+ if (timestamp && entry.timestamp !== timestamp)
113
+ continue;
114
+ buf.push(toSummary(entry));
115
+ if (!id && !timestamp && buf.length > limit)
116
+ buf.shift();
117
+ }
118
+ }
119
+ catch {
120
+ // file not found / read error → empty
121
+ return [];
122
+ }
123
+ // 新しい順で返す
124
+ return buf.reverse().slice(0, limit);
125
+ },
126
+ catch: (e) => (e instanceof Error ? e : new Error(String(e))),
127
+ }).pipe(Effect.orDie),
128
+ readHistoryById: (id) => Effect.tryPromise({
129
+ try: async () => {
130
+ const needle = id.trim();
131
+ if (!needle)
132
+ return undefined;
133
+ await fs.stat(historyPath).catch(() => undefined);
134
+ let found = undefined;
135
+ try {
136
+ for await (const line of readLines(historyPath)) {
137
+ const entry = parseEntry(line);
138
+ if (!entry)
139
+ continue;
140
+ if (entry.id === needle)
141
+ found = entry;
142
+ }
143
+ }
144
+ catch {
145
+ return undefined;
146
+ }
147
+ return found;
148
+ },
149
+ catch: (e) => (e instanceof Error ? e : new Error(String(e))),
150
+ }).pipe(Effect.orDie),
151
+ };
152
+ }
153
+ export const HistoryStoreLive = Layer.effect(HistoryStore, Effect.gen(function* () {
154
+ const cfg = yield* RuntimeConfig;
155
+ return makeHistoryStore(cfg);
156
+ }));
157
+ async function* readLines(p) {
158
+ const stream = createReadStream(p, { encoding: "utf8" });
159
+ const rl = readline.createInterface({
160
+ input: stream,
161
+ crlfDelay: Infinity,
162
+ });
163
+ try {
164
+ for await (const line of rl) {
165
+ yield line;
166
+ }
167
+ }
168
+ finally {
169
+ rl.close();
170
+ stream.destroy();
171
+ }
172
+ }
173
+ function parseEntry(line) {
174
+ const trimmed = line.trim();
175
+ if (!trimmed)
176
+ return undefined;
177
+ try {
178
+ const v = JSON.parse(trimmed);
179
+ if (!v || typeof v !== "object")
180
+ return undefined;
181
+ if (typeof v.id !== "string")
182
+ return undefined;
183
+ if (typeof v.timestamp !== "string")
184
+ return undefined;
185
+ const req = (v.request ?? {});
186
+ const planMode = req.planMode === "analyze" ? "analyze" : "explain";
187
+ return {
188
+ id: v.id,
189
+ timestamp: v.timestamp,
190
+ request: {
191
+ queryA: typeof req.queryA === "string" ? req.queryA : "",
192
+ queryB: typeof req.queryB === "string" ? req.queryB : "",
193
+ planMode,
194
+ },
195
+ response: (v.response ?? {}),
196
+ };
197
+ }
198
+ catch {
199
+ return undefined;
200
+ }
201
+ }
@@ -0,0 +1,27 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { QueryResultSchema, } from "../../services/query.js";
3
+ function mapPgFields(fields) {
4
+ return fields.map((f) => ({
5
+ name: f.name,
6
+ // pgの型情報は dataTypeID(number) 等なので、ここでは文字列化して返す
7
+ type: String(f.dataTypeID),
8
+ }));
9
+ }
10
+ function mapPgResult(result) {
11
+ return Schema.decodeUnknownSync(QueryResultSchema)({
12
+ rows: result.rows,
13
+ fields: mapPgFields(result.fields),
14
+ });
15
+ }
16
+ /**
17
+ * `pg` の `Client` / `PoolClient` を、Effectの `Queryable` サービスに変換する。
18
+ * `createApp` へ注入して `provideService(Queryable, ...)` する想定。
19
+ */
20
+ export function makePgQueryable(pg) {
21
+ return {
22
+ execute: (query) => Effect.tryPromise({
23
+ try: async () => mapPgResult(await pg.query(query)),
24
+ catch: (e) => (e instanceof Error ? e : new Error(String(e))),
25
+ }),
26
+ };
27
+ }
@@ -0,0 +1,157 @@
1
+ import { Hono } from "hono";
2
+ import { Effect, Schema } from "effect";
3
+ import * as Either from "effect/Either";
4
+ import { executeQuery, Queryable, } from "../services/query.js";
5
+ import { createDiffSheet, diffHasChanges, } from "../services/diff.js";
6
+ import { parseExplainFromQueryResult } from "../services/plan.js";
7
+ import { newHistoryEntry, HistoryStore } from "../infra/history.js";
8
+ import { HistoriesQuerySchema, RunRequestSchema } from "./schemas.js";
9
+ /**
10
+ * 1つのクエリを実行する。
11
+ * @param query - 実行するクエリ
12
+ * @returns 実行結果のEffect
13
+ */
14
+ function runOne(query) {
15
+ const trimmed = query.trim();
16
+ if (trimmed.length === 0)
17
+ return Effect.succeed({
18
+ result: undefined,
19
+ error: "EMPTY_QUERY",
20
+ });
21
+ return executeQuery(trimmed).pipe(Effect.map((result) => ({
22
+ result,
23
+ error: undefined,
24
+ })), Effect.catchAll((e) => Effect.succeed({
25
+ result: undefined,
26
+ error: e instanceof Error ? e.message : String(e),
27
+ })));
28
+ }
29
+ function parsePlanSafe(planQueryResult) {
30
+ if (!planQueryResult)
31
+ return { plan: undefined, error: undefined };
32
+ try {
33
+ return {
34
+ plan: parseExplainFromQueryResult(planQueryResult),
35
+ error: undefined,
36
+ };
37
+ }
38
+ catch (e) {
39
+ return {
40
+ plan: undefined,
41
+ error: e instanceof Error ? e.message : String(e),
42
+ };
43
+ }
44
+ }
45
+ function computeHasDiffChanges(args) {
46
+ const hasBoth = Boolean(args.resultA && args.resultB);
47
+ if (hasBoth)
48
+ return diffHasChanges(args.diff);
49
+ // 片側しか結果が無く diff が取れない場合は「差分あり(= 要確認)」扱いにする
50
+ const hasAnyQuery = args.queryA.trim().length > 0 || args.queryB.trim().length > 0;
51
+ return hasAnyQuery ? true : undefined;
52
+ }
53
+ export function createApiRoute(deps) {
54
+ const api = new Hono();
55
+ api.get("/histories", async (c) => {
56
+ const decoded = Schema.decodeUnknownEither(HistoriesQuerySchema)({
57
+ limit: c.req.query("limit"),
58
+ id: c.req.query("id"),
59
+ timestamp: c.req.query("timestamp"),
60
+ });
61
+ if (Either.isLeft(decoded)) {
62
+ return c.json({ error: "BAD_REQUEST" }, 400);
63
+ }
64
+ const items = await Effect.runPromise(Effect.gen(function* () {
65
+ const history = yield* HistoryStore;
66
+ return yield* history.readHistorySummaries(decoded.right);
67
+ }).pipe(Effect.provide(deps.context)));
68
+ const res = { items };
69
+ return c.json(res);
70
+ });
71
+ api.get("/histories/:id", async (c) => {
72
+ const id = c.req.param("id");
73
+ const item = await Effect.runPromise(Effect.gen(function* () {
74
+ const history = yield* HistoryStore;
75
+ return yield* history.readHistoryById(id);
76
+ }).pipe(Effect.provide(deps.context)));
77
+ if (!item)
78
+ return c.json({ item: undefined }, 404);
79
+ return c.json({ item });
80
+ });
81
+ api.post("/run", async (c) => {
82
+ const rawBody = (await c.req.json().catch(() => ({})));
83
+ const decoded = Schema.decodeUnknownEither(RunRequestSchema)(rawBody);
84
+ if (Either.isLeft(decoded)) {
85
+ return c.json({ error: "BAD_REQUEST" }, 400);
86
+ }
87
+ const body = decoded.right;
88
+ const queryA = body.queryA ?? "";
89
+ const queryB = body.queryB ?? "";
90
+ const planMode = body.planMode ?? "explain";
91
+ let planPrefix = "EXPLAIN (FORMAT JSON";
92
+ if (planMode === "analyze") {
93
+ planPrefix += ", ANALYZE true";
94
+ }
95
+ planPrefix += ")";
96
+ // 1つのトランザクションで2つのクエリを実行する。
97
+ const program = Effect.gen(function* () {
98
+ const connection = yield* Queryable;
99
+ return yield* Effect.acquireUseRelease(connection.execute("BEGIN"), () => {
100
+ return Effect.gen(function* () {
101
+ const a = yield* runOne(queryA);
102
+ const aPlan = yield* runOne(`${planPrefix} ${queryA}`);
103
+ const b = yield* runOne(queryB);
104
+ const bPlan = yield* runOne(`${planPrefix} ${queryB}`);
105
+ return { a, aPlan, b, bPlan };
106
+ });
107
+ }, () => connection
108
+ .execute("ROLLBACK")
109
+ .pipe(Effect.catchAll(() => Effect.void)));
110
+ });
111
+ const { a, aPlan, b, bPlan } = await Effect.runPromise(program.pipe(Effect.provide(deps.context)));
112
+ const diff = a.result && b.result ? createDiffSheet(a.result, b.result) : undefined;
113
+ const aPlanParsed = parsePlanSafe(aPlan.result);
114
+ const bPlanParsed = parsePlanSafe(bPlan.result);
115
+ const res = {
116
+ queryA,
117
+ queryB,
118
+ planMode,
119
+ resultA: a.result,
120
+ resultB: b.result,
121
+ errorA: a.error,
122
+ errorB: b.error,
123
+ diff,
124
+ hasDiffChanges: computeHasDiffChanges({
125
+ queryA,
126
+ queryB,
127
+ resultA: a.result,
128
+ resultB: b.result,
129
+ diff,
130
+ }),
131
+ planQueryResultA: aPlan.result,
132
+ planQueryResultB: bPlan.result,
133
+ planResultA: aPlanParsed.plan,
134
+ planResultB: bPlanParsed.plan,
135
+ planErrorA: aPlanParsed.error ?? aPlan.error,
136
+ planErrorB: bPlanParsed.error ?? bPlan.error,
137
+ planDiff: undefined,
138
+ };
139
+ // 履歴は「失敗してもレスポンスを壊さない」方針で、best-effort で追記する
140
+ try {
141
+ await Effect.runPromise(Effect.gen(function* () {
142
+ const history = yield* HistoryStore;
143
+ yield* history.appendHistory(newHistoryEntry({
144
+ queryA,
145
+ queryB,
146
+ planMode,
147
+ response: res,
148
+ }));
149
+ }).pipe(Effect.provide(deps.context)));
150
+ }
151
+ catch {
152
+ // ignore
153
+ }
154
+ return c.json(res);
155
+ });
156
+ return api;
157
+ }
@@ -0,0 +1,12 @@
1
+ import { Schema } from "effect";
2
+ export const PlanModeSchema = Schema.Literal("explain", "analyze");
3
+ export const RunRequestSchema = Schema.Struct({
4
+ queryA: Schema.optional(Schema.String),
5
+ queryB: Schema.optional(Schema.String),
6
+ planMode: Schema.optional(PlanModeSchema),
7
+ });
8
+ export const HistoriesQuerySchema = Schema.Struct({
9
+ limit: Schema.optional(Schema.NumberFromString.pipe(Schema.int(), Schema.nonNegative(), Schema.clamp(1, 200))),
10
+ id: Schema.optional(Schema.Trim.pipe(Schema.nonEmptyString())),
11
+ timestamp: Schema.optional(Schema.Trim.pipe(Schema.nonEmptyString())),
12
+ });
@@ -0,0 +1 @@
1
+ export {};
package/dist/server.js ADDED
@@ -0,0 +1,19 @@
1
+ import { Hono } from "hono";
2
+ import { createApiRoute } from "./routes/api.js";
3
+ import { serveStatic } from "@hono/node-server/serve-static";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ export function createApp(deps) {
7
+ const app = new Hono();
8
+ app.route("/api", createApiRoute(deps));
9
+ if (deps.isProduction) {
10
+ const clientDistDir = fileURLToPath(new URL("../dist/client", import.meta.url));
11
+ // serveStatic の root は「起動時の cwd からの相対パス」前提なので、cwd依存を吸収する
12
+ const clientRoot = path.relative(process.cwd(), clientDistDir) || ".";
13
+ // まず実ファイル(/assets/* や /favicon.ico など)を配信
14
+ app.use("/*", serveStatic({ root: clientRoot, precompressed: true }));
15
+ // SPA fallback(存在しないパスは index.html)
16
+ app.get("*", serveStatic({ root: clientRoot, path: "index.html" }));
17
+ }
18
+ return app;
19
+ }
@@ -0,0 +1,60 @@
1
+ import daff from "daff";
2
+ function cellTextForDiff(v) {
3
+ if (v === null)
4
+ return "null";
5
+ if (v === undefined)
6
+ return "";
7
+ switch (typeof v) {
8
+ case "string":
9
+ return v;
10
+ case "number":
11
+ case "bigint":
12
+ case "boolean":
13
+ return String(v);
14
+ case "symbol":
15
+ return v.toString();
16
+ case "function":
17
+ return "[function]";
18
+ case "object": {
19
+ try {
20
+ return JSON.stringify(v);
21
+ }
22
+ catch {
23
+ return Object.prototype.toString.call(v);
24
+ }
25
+ }
26
+ default:
27
+ return "";
28
+ }
29
+ }
30
+ export function diffHasChanges(diff) {
31
+ const rows = diff ?? [];
32
+ // DiffView と同じ判定: row差分だけでなく、カラム差分("!" 行)も「差分あり」と扱う
33
+ return rows.some((row) => {
34
+ const marker = cellTextForDiff(row?.[0]);
35
+ return (marker === "!" || marker === "+++" || marker === "---" || marker === "->");
36
+ });
37
+ }
38
+ function resultToSheet(result) {
39
+ const headers = result.fields.map((f) => f.name);
40
+ const rows = result.rows.map((r) => {
41
+ const row = r;
42
+ return result.fields.map((f) => cellTextForDiff(row[f.name]));
43
+ });
44
+ return [headers, ...rows];
45
+ }
46
+ export function createDiffSheet(a, b) {
47
+ const sheetA = resultToSheet(a);
48
+ const sheetB = resultToSheet(b);
49
+ const viewA = new daff.TableView(sheetA);
50
+ const viewB = new daff.TableView(sheetB);
51
+ const alignment = daff.compareTables(viewA, viewB).align();
52
+ const flags = new daff.CompareFlags();
53
+ const highlighter = new daff.TableDiff(alignment, flags);
54
+ const out = new daff.TableView([]);
55
+ highlighter.hilite(out);
56
+ const json = daff.jsonify(out);
57
+ // daff.jsonify(out).h.sheet は (string|number|boolean|null|undefined)[][] になりうるが、
58
+ // 本アプリでは文字列として表示するため、必要に応じて JSX 側で string 化する。
59
+ return (json?.h?.sheet ?? []);
60
+ }
@@ -0,0 +1,35 @@
1
+ import { parseExplainAnalyzeJson, } from "@sun-yryr/queot-planparser";
2
+ function normKey(k) {
3
+ return k
4
+ .toLowerCase()
5
+ .replace(/[_\s]+/g, " ")
6
+ .trim();
7
+ }
8
+ function pickRowValue(row, fieldName) {
9
+ if (fieldName in row)
10
+ return row[fieldName];
11
+ const want = normKey(fieldName);
12
+ for (const k of Object.keys(row)) {
13
+ if (normKey(k) === want)
14
+ return row[k];
15
+ }
16
+ return undefined;
17
+ }
18
+ /**
19
+ * Postgres の EXPLAIN (FORMAT JSON ...) のクエリ結果(QueryResult) から、
20
+ * planparser 用の入力(JSON)を取り出して ExplainParseResult に変換する。
21
+ */
22
+ export function parseExplainFromQueryResult(result) {
23
+ if (!result.fields?.length || !result.rows?.length) {
24
+ return parseExplainAnalyzeJson([]);
25
+ }
26
+ const field = result.fields.find((f) => normKey(f.name) === "query plan") ??
27
+ result.fields.find((f) => ["json", "jsonb"].includes(f.type?.toLowerCase?.() ?? "")) ??
28
+ result.fields[0];
29
+ const row0 = result.rows[0];
30
+ const value = pickRowValue(row0, field.name);
31
+ if (value === undefined) {
32
+ throw new Error(`EXPLAIN JSON column not found (field: ${field.name})`);
33
+ }
34
+ return parseExplainAnalyzeJson(value);
35
+ }
@@ -0,0 +1,19 @@
1
+ import { Effect, Context, Schema } from "effect";
2
+ const FieldSchema = Schema.Struct({
3
+ name: Schema.String,
4
+ type: Schema.String,
5
+ });
6
+ const RowSchema = Schema.Record({
7
+ key: Schema.String,
8
+ value: Schema.Unknown,
9
+ });
10
+ export const QueryResultSchema = Schema.Struct({
11
+ rows: Schema.Array(RowSchema),
12
+ fields: Schema.Array(FieldSchema),
13
+ });
14
+ export class Queryable extends Context.Tag("Queryable")() {
15
+ }
16
+ export const executeQuery = (query) => Effect.gen(function* () {
17
+ const connection = yield* Queryable;
18
+ return yield* connection.execute(query);
19
+ });
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@sun-yryr/queot",
3
+ "version": "0.1.0",
4
+ "description": "Query Optimization Tool for SQL",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "keywords": [],
8
+ "author": "",
9
+ "license": "MIT",
10
+ "dependencies": {
11
+ "@hono/node-server": "^1.19.7",
12
+ "@inquirer/prompts": "^8.1.0",
13
+ "@oclif/core": "^4.8.0",
14
+ "@tailwindcss/vite": "^4.1.18",
15
+ "daff": "^1.4.2",
16
+ "effect": "^3.19.12",
17
+ "hono": "^4.10.8",
18
+ "open": "^11.0.0",
19
+ "pg": "^8.16.3",
20
+ "tailwindcss": "^4.1.18",
21
+ "@sun-yryr/queot-planparser": "0.1.0"
22
+ },
23
+ "devDependencies": {
24
+ "@eslint/js": "^9.39.2",
25
+ "@tsconfig/node24": "^24.0.3",
26
+ "@types/node": "^24.10.3",
27
+ "@types/pg": "^8.16.0",
28
+ "eslint": "^9.39.2",
29
+ "eslint-config-prettier": "^10.1.8",
30
+ "oclif": "^4.22.55",
31
+ "prettier": "^3.7.4",
32
+ "tsx": "^4.21.0",
33
+ "typescript": "^5.9.3",
34
+ "typescript-eslint": "^8.49.0",
35
+ "vite": "^7.3.0",
36
+ "vitest": "^4.0.16"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "bin/run.js",
41
+ "bin/run.cmd"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "registry": "https://registry.npmjs.org/"
46
+ },
47
+ "bin": {
48
+ "queot": "./bin/run.js"
49
+ },
50
+ "oclif": {
51
+ "bin": "queot",
52
+ "commands": "./dist/commands",
53
+ "dirname": "queot",
54
+ "helpOptions": {
55
+ "flagSortOrder": "none"
56
+ },
57
+ "topicSeparator": " "
58
+ },
59
+ "scripts": {
60
+ "dev": "pnpm -s dev:web & pnpm -s dev:api",
61
+ "dev:web": "vite --open",
62
+ "dev:api": "OCLIF_DEBUG=1 ./bin/dev.js serve -p 3000",
63
+ "build:server": "tsc --project tsconfig.build.json",
64
+ "build:client": "vite build",
65
+ "build": "pnpm build:server && pnpm build:client",
66
+ "start": "NODE_ENV=production node ./bin/run.js serve -p 3000",
67
+ "queot": "./bin/run.js",
68
+ "queot:dev": "./bin/dev.js",
69
+ "check": "pnpm \"/^check:.*/\"",
70
+ "check:prettier": "prettier . --check",
71
+ "check:eslint": "eslint",
72
+ "fix": "pnpm \"/^fix:.+$/\"",
73
+ "fix:prettier": "prettier . --write",
74
+ "fix:eslint": "eslint --fix",
75
+ "test": "vitest"
76
+ }
77
+ }