recce-nightly 1.5.0.20250527__py3-none-any.whl → 1.6.0.20250529__py3-none-any.whl
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.
Potentially problematic release.
This version of recce-nightly might be problematic. Click here for more details.
- recce/VERSION +1 -1
- recce/adapter/dbt_adapter/__init__.py +112 -91
- recce/data/404.html +2 -2
- recce/data/_next/static/chunks/711-5ac2c87eb5073191.js +1 -0
- recce/data/_next/static/chunks/app/layout-c76e6ca2ce9857e9.js +1 -0
- recce/data/_next/static/chunks/app/page-f0b9d7985eaf7eed.js +1 -0
- recce/data/index.html +2 -2
- recce/data/index.txt +3 -3
- recce/models/types.py +47 -1
- recce/server.py +4 -9
- recce/util/cll.py +110 -90
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250529.dist-info}/METADATA +1 -1
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250529.dist-info}/RECORD +21 -21
- tests/adapter/dbt_adapter/dbt_test_helper.py +1 -2
- tests/adapter/dbt_adapter/test_dbt_cll.py +95 -18
- recce/data/_next/static/chunks/711-9f4b13e082dc2f43.js +0 -1
- recce/data/_next/static/chunks/app/layout-84447815782d7e41.js +0 -1
- recce/data/_next/static/chunks/app/page-183db1bcb15b328f.js +0 -1
- /recce/data/_next/static/{_i7vGe6giBb07tZ0Ml9uG → 1rwJEkX_z2KfSDpX3cGQ1}/_buildManifest.js +0 -0
- /recce/data/_next/static/{_i7vGe6giBb07tZ0Ml9uG → 1rwJEkX_z2KfSDpX3cGQ1}/_ssgManifest.js +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250529.dist-info}/WHEEL +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250529.dist-info}/entry_points.txt +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250529.dist-info}/licenses/LICENSE +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250529.dist-info}/top_level.txt +0 -0
recce/data/index.html
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="/logo/recce-logo-white.png"/><link rel="stylesheet" href="/_next/static/css/1b121dc4d36aeb4d.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/951e2e0eea2d4a5b.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/17a96168e3a9db13.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/6d6961e0422ac782.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-8442f6926a7a34ce.js"/><script src="/_next/static/chunks/1f229bf6-d9fe92e56db8d93b.js" async=""></script><script src="/_next/static/chunks/700-3b65fc3666820d00.js" async=""></script><script src="/_next/static/chunks/main-app-0225a2255968e566.js" async=""></script><script src="/_next/static/chunks/9746af58-d74bef4d03eea6ab.js" async=""></script><script src="/_next/static/chunks/47d8844f-79a1b53c66a7d7ec.js" async=""></script><script src="/_next/static/chunks/a30376cd-ac45432855afb688.js" async=""></script><script src="/_next/static/chunks/6dc81886-c94b9b91bc2c3caf.js" async=""></script><script src="/_next/static/chunks/3a92ee20-3b5d922d4157af5e.js" async=""></script><script src="/_next/static/chunks/3998a672-eaad84bdd88cc73e.js" async=""></script><script src="/_next/static/chunks/e24bf851-0f8cbc99656833e7.js" async=""></script><script src="/_next/static/chunks/c1ceaa8b-a1e442154d23515e.js" async=""></script><script src="/_next/static/chunks/6ef81909-694dc38134099299.js" async=""></script><script src="/_next/static/chunks/ce84277d-f42c2c58049cea2d.js" async=""></script><script src="/_next/static/chunks/fee69bc6-f17d36c080742e74.js" async=""></script><script src="/_next/static/chunks/7a8a3e83-d7fa409d97b38b2b.js" async=""></script><script src="/_next/static/chunks/450c323b-1bb5db526e54435a.js" async=""></script><script src="/_next/static/chunks/36e1c10d-bb0210cbd6573a8d.js" async=""></script><script src="/_next/static/chunks/29e3cc0d-8c150e37dff9631b.js" async=""></script><script src="/_next/static/chunks/b63b1b3f-7395c74e11a14e95.js" async=""></script><script src="/_next/static/chunks/7f27ae6c-413f6b869a04183a.js" async=""></script><script src="/_next/static/chunks/c132bf7d-8102037f9ccf372a.js" async=""></script><script src="/_next/static/chunks/cd9f8d63-e020f408095ed77c.js" async=""></script><script src="/_next/static/chunks/316-9e0164c48e40d69b.js" async=""></script><script src="/_next/static/chunks/963-7cb453d3f159e7cc.js" async=""></script><script src="/_next/static/chunks/711-9f4b13e082dc2f43.js" async=""></script><script src="/_next/static/chunks/app/page-183db1bcb15b328f.js" async=""></script><script src="/_next/static/chunks/350-30608a95db2634b6.js" async=""></script><script src="/_next/static/chunks/app/layout-84447815782d7e41.js" async=""></script><title>recce</title><meta name="description" content="Recce: Data validation toolkit for comprehensive PR review"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="32x32"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><style data-emotion="css-global dh58eu">:host,:root,[data-theme]{--chakra-ring-inset:var(--chakra-empty,/*!*/ /*!*/);--chakra-ring-offset-width:0px;--chakra-ring-offset-color:#fff;--chakra-ring-color:rgba(66, 153, 225, 0.6);--chakra-ring-offset-shadow:0 0 #0000;--chakra-ring-shadow:0 0 #0000;--chakra-space-x-reverse:0;--chakra-space-y-reverse:0;--chakra-colors-transparent:transparent;--chakra-colors-current:currentColor;--chakra-colors-black:#000000;--chakra-colors-white:#FFFFFF;--chakra-colors-whiteAlpha-50:rgba(255, 255, 255, 0.04);--chakra-colors-whiteAlpha-100:rgba(255, 255, 255, 0.06);--chakra-colors-whiteAlpha-200:rgba(255, 255, 255, 0.08);--chakra-colors-whiteAlpha-300:rgba(255, 255, 255, 0.16);--chakra-colors-whiteAlpha-400:rgba(255, 255, 255, 0.24);--chakra-colors-whiteAlpha-500:rgba(255, 255, 255, 0.36);--chakra-colors-whiteAlpha-600:rgba(255, 255, 255, 0.48);--chakra-colors-whiteAlpha-700:rgba(255, 255, 255, 0.64);--chakra-colors-whiteAlpha-800:rgba(255, 255, 255, 0.80);--chakra-colors-whiteAlpha-900:rgba(255, 255, 255, 0.92);--chakra-colors-blackAlpha-50:rgba(0, 0, 0, 0.04);--chakra-colors-blackAlpha-100:rgba(0, 0, 0, 0.06);--chakra-colors-blackAlpha-200:rgba(0, 0, 0, 0.08);--chakra-colors-blackAlpha-300:rgba(0, 0, 0, 0.16);--chakra-colors-blackAlpha-400:rgba(0, 0, 0, 0.24);--chakra-colors-blackAlpha-500:rgba(0, 0, 0, 0.36);--chakra-colors-blackAlpha-600:rgba(0, 0, 0, 0.48);--chakra-colors-blackAlpha-700:rgba(0, 0, 0, 0.64);--chakra-colors-blackAlpha-800:rgba(0, 0, 0, 0.80);--chakra-colors-blackAlpha-900:rgba(0, 0, 0, 0.92);--chakra-colors-gray-50:#F7FAFC;--chakra-colors-gray-100:#EDF2F7;--chakra-colors-gray-200:#E2E8F0;--chakra-colors-gray-300:#CBD5E0;--chakra-colors-gray-400:#A0AEC0;--chakra-colors-gray-500:#718096;--chakra-colors-gray-600:#4A5568;--chakra-colors-gray-700:#2D3748;--chakra-colors-gray-800:#1A202C;--chakra-colors-gray-900:#171923;--chakra-colors-red-50:#FFF5F5;--chakra-colors-red-100:#FED7D7;--chakra-colors-red-200:#FEB2B2;--chakra-colors-red-300:#FC8181;--chakra-colors-red-400:#F56565;--chakra-colors-red-500:#E53E3E;--chakra-colors-red-600:#C53030;--chakra-colors-red-700:#9B2C2C;--chakra-colors-red-800:#822727;--chakra-colors-red-900:#63171B;--chakra-colors-orange-50:#FFFAF0;--chakra-colors-orange-100:#FEEBC8;--chakra-colors-orange-200:#FBD38D;--chakra-colors-orange-300:#F6AD55;--chakra-colors-orange-400:#ED8936;--chakra-colors-orange-500:#DD6B20;--chakra-colors-orange-600:#C05621;--chakra-colors-orange-700:#9C4221;--chakra-colors-orange-800:#7B341E;--chakra-colors-orange-900:#652B19;--chakra-colors-yellow-50:#FFFFF0;--chakra-colors-yellow-100:#FEFCBF;--chakra-colors-yellow-200:#FAF089;--chakra-colors-yellow-300:#F6E05E;--chakra-colors-yellow-400:#ECC94B;--chakra-colors-yellow-500:#D69E2E;--chakra-colors-yellow-600:#B7791F;--chakra-colors-yellow-700:#975A16;--chakra-colors-yellow-800:#744210;--chakra-colors-yellow-900:#5F370E;--chakra-colors-green-50:#F0FFF4;--chakra-colors-green-100:#C6F6D5;--chakra-colors-green-200:#9AE6B4;--chakra-colors-green-300:#68D391;--chakra-colors-green-400:#48BB78;--chakra-colors-green-500:#38A169;--chakra-colors-green-600:#2F855A;--chakra-colors-green-700:#276749;--chakra-colors-green-800:#22543D;--chakra-colors-green-900:#1C4532;--chakra-colors-teal-50:#E6FFFA;--chakra-colors-teal-100:#B2F5EA;--chakra-colors-teal-200:#81E6D9;--chakra-colors-teal-300:#4FD1C5;--chakra-colors-teal-400:#38B2AC;--chakra-colors-teal-500:#319795;--chakra-colors-teal-600:#2C7A7B;--chakra-colors-teal-700:#285E61;--chakra-colors-teal-800:#234E52;--chakra-colors-teal-900:#1D4044;--chakra-colors-blue-50:#ebf8ff;--chakra-colors-blue-100:#bee3f8;--chakra-colors-blue-200:#90cdf4;--chakra-colors-blue-300:#63b3ed;--chakra-colors-blue-400:#4299e1;--chakra-colors-blue-500:#3182ce;--chakra-colors-blue-600:#2b6cb0;--chakra-colors-blue-700:#2c5282;--chakra-colors-blue-800:#2a4365;--chakra-colors-blue-900:#1A365D;--chakra-colors-cyan-50:#EDFDFD;--chakra-colors-cyan-100:#C4F1F9;--chakra-colors-cyan-200:#9DECF9;--chakra-colors-cyan-300:#76E4F7;--chakra-colors-cyan-400:#0BC5EA;--chakra-colors-cyan-500:#00B5D8;--chakra-colors-cyan-600:#00A3C4;--chakra-colors-cyan-700:#0987A0;--chakra-colors-cyan-800:#086F83;--chakra-colors-cyan-900:#065666;--chakra-colors-purple-50:#FAF5FF;--chakra-colors-purple-100:#E9D8FD;--chakra-colors-purple-200:#D6BCFA;--chakra-colors-purple-300:#B794F4;--chakra-colors-purple-400:#9F7AEA;--chakra-colors-purple-500:#805AD5;--chakra-colors-purple-600:#6B46C1;--chakra-colors-purple-700:#553C9A;--chakra-colors-purple-800:#44337A;--chakra-colors-purple-900:#322659;--chakra-colors-pink-50:#FFF5F7;--chakra-colors-pink-100:#FED7E2;--chakra-colors-pink-200:#FBB6CE;--chakra-colors-pink-300:#F687B3;--chakra-colors-pink-400:#ED64A6;--chakra-colors-pink-500:#D53F8C;--chakra-colors-pink-600:#B83280;--chakra-colors-pink-700:#97266D;--chakra-colors-pink-800:#702459;--chakra-colors-pink-900:#521B41;--chakra-colors-brand-100:#fd683e;--chakra-colors-brand-200:#fd683e;--chakra-colors-brand-300:#fd683e;--chakra-colors-brand-400:#fd683e;--chakra-colors-brand-500:#fd683e;--chakra-colors-brand-600:#fd683e;--chakra-colors-brand-700:#fd683e;--chakra-colors-brand-800:#fd683e;--chakra-colors-brand-900:#fd683e;--chakra-borders-none:0;--chakra-borders-1px:1px solid;--chakra-borders-2px:2px solid;--chakra-borders-4px:4px solid;--chakra-borders-8px:8px solid;--chakra-fonts-heading:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--chakra-fonts-body:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--chakra-fonts-mono:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--chakra-fontSizes-3xs:0.45rem;--chakra-fontSizes-2xs:0.625rem;--chakra-fontSizes-xs:0.75rem;--chakra-fontSizes-sm:0.875rem;--chakra-fontSizes-md:1rem;--chakra-fontSizes-lg:1.125rem;--chakra-fontSizes-xl:1.25rem;--chakra-fontSizes-2xl:1.5rem;--chakra-fontSizes-3xl:1.875rem;--chakra-fontSizes-4xl:2.25rem;--chakra-fontSizes-5xl:3rem;--chakra-fontSizes-6xl:3.75rem;--chakra-fontSizes-7xl:4.5rem;--chakra-fontSizes-8xl:6rem;--chakra-fontSizes-9xl:8rem;--chakra-fontWeights-hairline:100;--chakra-fontWeights-thin:200;--chakra-fontWeights-light:300;--chakra-fontWeights-normal:400;--chakra-fontWeights-medium:500;--chakra-fontWeights-semibold:600;--chakra-fontWeights-bold:700;--chakra-fontWeights-extrabold:800;--chakra-fontWeights-black:900;--chakra-letterSpacings-tighter:-0.05em;--chakra-letterSpacings-tight:-0.025em;--chakra-letterSpacings-normal:0;--chakra-letterSpacings-wide:0.025em;--chakra-letterSpacings-wider:0.05em;--chakra-letterSpacings-widest:0.1em;--chakra-lineHeights-3:.75rem;--chakra-lineHeights-4:1rem;--chakra-lineHeights-5:1.25rem;--chakra-lineHeights-6:1.5rem;--chakra-lineHeights-7:1.75rem;--chakra-lineHeights-8:2rem;--chakra-lineHeights-9:2.25rem;--chakra-lineHeights-10:2.5rem;--chakra-lineHeights-normal:normal;--chakra-lineHeights-none:1;--chakra-lineHeights-shorter:1.25;--chakra-lineHeights-short:1.375;--chakra-lineHeights-base:1.5;--chakra-lineHeights-tall:1.625;--chakra-lineHeights-taller:2;--chakra-radii-none:0;--chakra-radii-sm:0.125rem;--chakra-radii-base:0.25rem;--chakra-radii-md:0.375rem;--chakra-radii-lg:0.5rem;--chakra-radii-xl:0.75rem;--chakra-radii-2xl:1rem;--chakra-radii-3xl:1.5rem;--chakra-radii-full:9999px;--chakra-space-1:0.25rem;--chakra-space-2:0.5rem;--chakra-space-3:0.75rem;--chakra-space-4:1rem;--chakra-space-5:1.25rem;--chakra-space-6:1.5rem;--chakra-space-7:1.75rem;--chakra-space-8:2rem;--chakra-space-9:2.25rem;--chakra-space-10:2.5rem;--chakra-space-12:3rem;--chakra-space-14:3.5rem;--chakra-space-16:4rem;--chakra-space-20:5rem;--chakra-space-24:6rem;--chakra-space-28:7rem;--chakra-space-32:8rem;--chakra-space-36:9rem;--chakra-space-40:10rem;--chakra-space-44:11rem;--chakra-space-48:12rem;--chakra-space-52:13rem;--chakra-space-56:14rem;--chakra-space-60:15rem;--chakra-space-64:16rem;--chakra-space-72:18rem;--chakra-space-80:20rem;--chakra-space-96:24rem;--chakra-space-px:1px;--chakra-space-0-5:0.125rem;--chakra-space-1-5:0.375rem;--chakra-space-2-5:0.625rem;--chakra-space-3-5:0.875rem;--chakra-shadows-xs:0 0 0 1px rgba(0, 0, 0, 0.05);--chakra-shadows-sm:0 1px 2px 0 rgba(0, 0, 0, 0.05);--chakra-shadows-base:0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);--chakra-shadows-md:0 4px 6px -1px rgba(0, 0, 0, 0.1),0 2px 4px -1px rgba(0, 0, 0, 0.06);--chakra-shadows-lg:0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);--chakra-shadows-xl:0 20px 25px -5px rgba(0, 0, 0, 0.1),0 10px 10px -5px rgba(0, 0, 0, 0.04);--chakra-shadows-2xl:0 25px 50px -12px rgba(0, 0, 0, 0.25);--chakra-shadows-outline:0 0 0 3px rgba(66, 153, 225, 0.6);--chakra-shadows-inner:inset 0 2px 4px 0 rgba(0,0,0,0.06);--chakra-shadows-none:none;--chakra-shadows-dark-lg:rgba(0, 0, 0, 0.1) 0px 0px 0px 1px,rgba(0, 0, 0, 0.2) 0px 5px 10px,rgba(0, 0, 0, 0.4) 0px 15px 40px;--chakra-sizes-1:0.25rem;--chakra-sizes-2:0.5rem;--chakra-sizes-3:0.75rem;--chakra-sizes-4:1rem;--chakra-sizes-5:1.25rem;--chakra-sizes-6:1.5rem;--chakra-sizes-7:1.75rem;--chakra-sizes-8:2rem;--chakra-sizes-9:2.25rem;--chakra-sizes-10:2.5rem;--chakra-sizes-12:3rem;--chakra-sizes-14:3.5rem;--chakra-sizes-16:4rem;--chakra-sizes-20:5rem;--chakra-sizes-24:6rem;--chakra-sizes-28:7rem;--chakra-sizes-32:8rem;--chakra-sizes-36:9rem;--chakra-sizes-40:10rem;--chakra-sizes-44:11rem;--chakra-sizes-48:12rem;--chakra-sizes-52:13rem;--chakra-sizes-56:14rem;--chakra-sizes-60:15rem;--chakra-sizes-64:16rem;--chakra-sizes-72:18rem;--chakra-sizes-80:20rem;--chakra-sizes-96:24rem;--chakra-sizes-px:1px;--chakra-sizes-0-5:0.125rem;--chakra-sizes-1-5:0.375rem;--chakra-sizes-2-5:0.625rem;--chakra-sizes-3-5:0.875rem;--chakra-sizes-max:max-content;--chakra-sizes-min:min-content;--chakra-sizes-full:100%;--chakra-sizes-3xs:14rem;--chakra-sizes-2xs:16rem;--chakra-sizes-xs:20rem;--chakra-sizes-sm:24rem;--chakra-sizes-md:28rem;--chakra-sizes-lg:32rem;--chakra-sizes-xl:36rem;--chakra-sizes-2xl:42rem;--chakra-sizes-3xl:48rem;--chakra-sizes-4xl:56rem;--chakra-sizes-5xl:64rem;--chakra-sizes-6xl:72rem;--chakra-sizes-7xl:80rem;--chakra-sizes-8xl:90rem;--chakra-sizes-prose:60ch;--chakra-sizes-container-sm:640px;--chakra-sizes-container-md:768px;--chakra-sizes-container-lg:1024px;--chakra-sizes-container-xl:1280px;--chakra-zIndices-hide:-1;--chakra-zIndices-auto:auto;--chakra-zIndices-base:0;--chakra-zIndices-docked:10;--chakra-zIndices-dropdown:1000;--chakra-zIndices-sticky:1100;--chakra-zIndices-banner:1200;--chakra-zIndices-overlay:1300;--chakra-zIndices-modal:1400;--chakra-zIndices-popover:1500;--chakra-zIndices-skipLink:1600;--chakra-zIndices-toast:1700;--chakra-zIndices-tooltip:1800;--chakra-transition-property-common:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;--chakra-transition-property-colors:background-color,border-color,color,fill,stroke;--chakra-transition-property-dimensions:width,height;--chakra-transition-property-position:left,right,top,bottom;--chakra-transition-property-background:background-color,background-image,background-position;--chakra-transition-easing-ease-in:cubic-bezier(0.4, 0, 1, 1);--chakra-transition-easing-ease-out:cubic-bezier(0, 0, 0.2, 1);--chakra-transition-easing-ease-in-out:cubic-bezier(0.4, 0, 0.2, 1);--chakra-transition-duration-ultra-fast:50ms;--chakra-transition-duration-faster:100ms;--chakra-transition-duration-fast:150ms;--chakra-transition-duration-normal:200ms;--chakra-transition-duration-slow:300ms;--chakra-transition-duration-slower:400ms;--chakra-transition-duration-ultra-slow:500ms;--chakra-blur-none:0;--chakra-blur-sm:4px;--chakra-blur-base:8px;--chakra-blur-md:12px;--chakra-blur-lg:16px;--chakra-blur-xl:24px;--chakra-blur-2xl:40px;--chakra-blur-3xl:64px;--chakra-breakpoints-base:0em;--chakra-breakpoints-sm:30em;--chakra-breakpoints-md:48em;--chakra-breakpoints-lg:62em;--chakra-breakpoints-xl:80em;--chakra-breakpoints-2xl:96em;}.chakra-ui-light :host:not([data-theme]),.chakra-ui-light :root:not([data-theme]),.chakra-ui-light [data-theme]:not([data-theme]),[data-theme=light] :host:not([data-theme]),[data-theme=light] :root:not([data-theme]),[data-theme=light] [data-theme]:not([data-theme]),:host[data-theme=light],:root[data-theme=light],[data-theme][data-theme=light]{--chakra-colors-chakra-body-text:var(--chakra-colors-gray-800);--chakra-colors-chakra-body-bg:var(--chakra-colors-white);--chakra-colors-chakra-border-color:var(--chakra-colors-gray-200);--chakra-colors-chakra-inverse-text:var(--chakra-colors-white);--chakra-colors-chakra-subtle-bg:var(--chakra-colors-gray-100);--chakra-colors-chakra-subtle-text:var(--chakra-colors-gray-600);--chakra-colors-chakra-placeholder-color:var(--chakra-colors-gray-500);}.chakra-ui-dark :host:not([data-theme]),.chakra-ui-dark :root:not([data-theme]),.chakra-ui-dark [data-theme]:not([data-theme]),[data-theme=dark] :host:not([data-theme]),[data-theme=dark] :root:not([data-theme]),[data-theme=dark] [data-theme]:not([data-theme]),:host[data-theme=dark],:root[data-theme=dark],[data-theme][data-theme=dark]{--chakra-colors-chakra-body-text:var(--chakra-colors-whiteAlpha-900);--chakra-colors-chakra-body-bg:var(--chakra-colors-gray-800);--chakra-colors-chakra-border-color:var(--chakra-colors-whiteAlpha-300);--chakra-colors-chakra-inverse-text:var(--chakra-colors-gray-800);--chakra-colors-chakra-subtle-bg:var(--chakra-colors-gray-700);--chakra-colors-chakra-subtle-text:var(--chakra-colors-gray-400);--chakra-colors-chakra-placeholder-color:var(--chakra-colors-whiteAlpha-400);}</style><style data-emotion="css-global fubdgu">html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:system-ui,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;touch-action:manipulation;}body{position:relative;min-height:100%;margin:0;font-feature-settings:"kern";}:where(*, *::before, *::after){border-width:0;border-style:solid;box-sizing:border-box;word-wrap:break-word;}main{display:block;}hr{border-top-width:1px;box-sizing:content-box;height:0;overflow:visible;}:where(pre, code, kbd,samp){font-family:SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:1em;}a{background-color:transparent;color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit;}abbr[title]{border-bottom:none;-webkit-text-decoration:underline;text-decoration:underline;-webkit-text-decoration:underline dotted;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;}:where(b, strong){font-weight:bold;}small{font-size:80%;}:where(sub,sup){font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sub{bottom:-0.25em;}sup{top:-0.5em;}img{border-style:none;}:where(button, input, optgroup, select, textarea){font-family:inherit;font-size:100%;line-height:1.15;margin:0;}:where(button, input){overflow:visible;}:where(button, select){text-transform:none;}:where(
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="/logo/recce-logo-white.png"/><link rel="stylesheet" href="/_next/static/css/1b121dc4d36aeb4d.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/951e2e0eea2d4a5b.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/17a96168e3a9db13.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/6d6961e0422ac782.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-8442f6926a7a34ce.js"/><script src="/_next/static/chunks/1f229bf6-d9fe92e56db8d93b.js" async=""></script><script src="/_next/static/chunks/700-3b65fc3666820d00.js" async=""></script><script src="/_next/static/chunks/main-app-0225a2255968e566.js" async=""></script><script src="/_next/static/chunks/9746af58-d74bef4d03eea6ab.js" async=""></script><script src="/_next/static/chunks/47d8844f-79a1b53c66a7d7ec.js" async=""></script><script src="/_next/static/chunks/a30376cd-ac45432855afb688.js" async=""></script><script src="/_next/static/chunks/6dc81886-c94b9b91bc2c3caf.js" async=""></script><script src="/_next/static/chunks/3a92ee20-3b5d922d4157af5e.js" async=""></script><script src="/_next/static/chunks/3998a672-eaad84bdd88cc73e.js" async=""></script><script src="/_next/static/chunks/e24bf851-0f8cbc99656833e7.js" async=""></script><script src="/_next/static/chunks/c1ceaa8b-a1e442154d23515e.js" async=""></script><script src="/_next/static/chunks/6ef81909-694dc38134099299.js" async=""></script><script src="/_next/static/chunks/ce84277d-f42c2c58049cea2d.js" async=""></script><script src="/_next/static/chunks/fee69bc6-f17d36c080742e74.js" async=""></script><script src="/_next/static/chunks/7a8a3e83-d7fa409d97b38b2b.js" async=""></script><script src="/_next/static/chunks/450c323b-1bb5db526e54435a.js" async=""></script><script src="/_next/static/chunks/36e1c10d-bb0210cbd6573a8d.js" async=""></script><script src="/_next/static/chunks/29e3cc0d-8c150e37dff9631b.js" async=""></script><script src="/_next/static/chunks/b63b1b3f-7395c74e11a14e95.js" async=""></script><script src="/_next/static/chunks/7f27ae6c-413f6b869a04183a.js" async=""></script><script src="/_next/static/chunks/c132bf7d-8102037f9ccf372a.js" async=""></script><script src="/_next/static/chunks/cd9f8d63-e020f408095ed77c.js" async=""></script><script src="/_next/static/chunks/316-9e0164c48e40d69b.js" async=""></script><script src="/_next/static/chunks/963-7cb453d3f159e7cc.js" async=""></script><script src="/_next/static/chunks/711-5ac2c87eb5073191.js" async=""></script><script src="/_next/static/chunks/app/page-f0b9d7985eaf7eed.js" async=""></script><script src="/_next/static/chunks/350-30608a95db2634b6.js" async=""></script><script src="/_next/static/chunks/app/layout-c76e6ca2ce9857e9.js" async=""></script><title>recce</title><meta name="description" content="Recce: Data validation toolkit for comprehensive PR review"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="32x32"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><style data-emotion="css-global dh58eu">:host,:root,[data-theme]{--chakra-ring-inset:var(--chakra-empty,/*!*/ /*!*/);--chakra-ring-offset-width:0px;--chakra-ring-offset-color:#fff;--chakra-ring-color:rgba(66, 153, 225, 0.6);--chakra-ring-offset-shadow:0 0 #0000;--chakra-ring-shadow:0 0 #0000;--chakra-space-x-reverse:0;--chakra-space-y-reverse:0;--chakra-colors-transparent:transparent;--chakra-colors-current:currentColor;--chakra-colors-black:#000000;--chakra-colors-white:#FFFFFF;--chakra-colors-whiteAlpha-50:rgba(255, 255, 255, 0.04);--chakra-colors-whiteAlpha-100:rgba(255, 255, 255, 0.06);--chakra-colors-whiteAlpha-200:rgba(255, 255, 255, 0.08);--chakra-colors-whiteAlpha-300:rgba(255, 255, 255, 0.16);--chakra-colors-whiteAlpha-400:rgba(255, 255, 255, 0.24);--chakra-colors-whiteAlpha-500:rgba(255, 255, 255, 0.36);--chakra-colors-whiteAlpha-600:rgba(255, 255, 255, 0.48);--chakra-colors-whiteAlpha-700:rgba(255, 255, 255, 0.64);--chakra-colors-whiteAlpha-800:rgba(255, 255, 255, 0.80);--chakra-colors-whiteAlpha-900:rgba(255, 255, 255, 0.92);--chakra-colors-blackAlpha-50:rgba(0, 0, 0, 0.04);--chakra-colors-blackAlpha-100:rgba(0, 0, 0, 0.06);--chakra-colors-blackAlpha-200:rgba(0, 0, 0, 0.08);--chakra-colors-blackAlpha-300:rgba(0, 0, 0, 0.16);--chakra-colors-blackAlpha-400:rgba(0, 0, 0, 0.24);--chakra-colors-blackAlpha-500:rgba(0, 0, 0, 0.36);--chakra-colors-blackAlpha-600:rgba(0, 0, 0, 0.48);--chakra-colors-blackAlpha-700:rgba(0, 0, 0, 0.64);--chakra-colors-blackAlpha-800:rgba(0, 0, 0, 0.80);--chakra-colors-blackAlpha-900:rgba(0, 0, 0, 0.92);--chakra-colors-gray-50:#F7FAFC;--chakra-colors-gray-100:#EDF2F7;--chakra-colors-gray-200:#E2E8F0;--chakra-colors-gray-300:#CBD5E0;--chakra-colors-gray-400:#A0AEC0;--chakra-colors-gray-500:#718096;--chakra-colors-gray-600:#4A5568;--chakra-colors-gray-700:#2D3748;--chakra-colors-gray-800:#1A202C;--chakra-colors-gray-900:#171923;--chakra-colors-red-50:#FFF5F5;--chakra-colors-red-100:#FED7D7;--chakra-colors-red-200:#FEB2B2;--chakra-colors-red-300:#FC8181;--chakra-colors-red-400:#F56565;--chakra-colors-red-500:#E53E3E;--chakra-colors-red-600:#C53030;--chakra-colors-red-700:#9B2C2C;--chakra-colors-red-800:#822727;--chakra-colors-red-900:#63171B;--chakra-colors-orange-50:#FFFAF0;--chakra-colors-orange-100:#FEEBC8;--chakra-colors-orange-200:#FBD38D;--chakra-colors-orange-300:#F6AD55;--chakra-colors-orange-400:#ED8936;--chakra-colors-orange-500:#DD6B20;--chakra-colors-orange-600:#C05621;--chakra-colors-orange-700:#9C4221;--chakra-colors-orange-800:#7B341E;--chakra-colors-orange-900:#652B19;--chakra-colors-yellow-50:#FFFFF0;--chakra-colors-yellow-100:#FEFCBF;--chakra-colors-yellow-200:#FAF089;--chakra-colors-yellow-300:#F6E05E;--chakra-colors-yellow-400:#ECC94B;--chakra-colors-yellow-500:#D69E2E;--chakra-colors-yellow-600:#B7791F;--chakra-colors-yellow-700:#975A16;--chakra-colors-yellow-800:#744210;--chakra-colors-yellow-900:#5F370E;--chakra-colors-green-50:#F0FFF4;--chakra-colors-green-100:#C6F6D5;--chakra-colors-green-200:#9AE6B4;--chakra-colors-green-300:#68D391;--chakra-colors-green-400:#48BB78;--chakra-colors-green-500:#38A169;--chakra-colors-green-600:#2F855A;--chakra-colors-green-700:#276749;--chakra-colors-green-800:#22543D;--chakra-colors-green-900:#1C4532;--chakra-colors-teal-50:#E6FFFA;--chakra-colors-teal-100:#B2F5EA;--chakra-colors-teal-200:#81E6D9;--chakra-colors-teal-300:#4FD1C5;--chakra-colors-teal-400:#38B2AC;--chakra-colors-teal-500:#319795;--chakra-colors-teal-600:#2C7A7B;--chakra-colors-teal-700:#285E61;--chakra-colors-teal-800:#234E52;--chakra-colors-teal-900:#1D4044;--chakra-colors-blue-50:#ebf8ff;--chakra-colors-blue-100:#bee3f8;--chakra-colors-blue-200:#90cdf4;--chakra-colors-blue-300:#63b3ed;--chakra-colors-blue-400:#4299e1;--chakra-colors-blue-500:#3182ce;--chakra-colors-blue-600:#2b6cb0;--chakra-colors-blue-700:#2c5282;--chakra-colors-blue-800:#2a4365;--chakra-colors-blue-900:#1A365D;--chakra-colors-cyan-50:#EDFDFD;--chakra-colors-cyan-100:#C4F1F9;--chakra-colors-cyan-200:#9DECF9;--chakra-colors-cyan-300:#76E4F7;--chakra-colors-cyan-400:#0BC5EA;--chakra-colors-cyan-500:#00B5D8;--chakra-colors-cyan-600:#00A3C4;--chakra-colors-cyan-700:#0987A0;--chakra-colors-cyan-800:#086F83;--chakra-colors-cyan-900:#065666;--chakra-colors-purple-50:#FAF5FF;--chakra-colors-purple-100:#E9D8FD;--chakra-colors-purple-200:#D6BCFA;--chakra-colors-purple-300:#B794F4;--chakra-colors-purple-400:#9F7AEA;--chakra-colors-purple-500:#805AD5;--chakra-colors-purple-600:#6B46C1;--chakra-colors-purple-700:#553C9A;--chakra-colors-purple-800:#44337A;--chakra-colors-purple-900:#322659;--chakra-colors-pink-50:#FFF5F7;--chakra-colors-pink-100:#FED7E2;--chakra-colors-pink-200:#FBB6CE;--chakra-colors-pink-300:#F687B3;--chakra-colors-pink-400:#ED64A6;--chakra-colors-pink-500:#D53F8C;--chakra-colors-pink-600:#B83280;--chakra-colors-pink-700:#97266D;--chakra-colors-pink-800:#702459;--chakra-colors-pink-900:#521B41;--chakra-colors-brand-100:#fd683e;--chakra-colors-brand-200:#fd683e;--chakra-colors-brand-300:#fd683e;--chakra-colors-brand-400:#fd683e;--chakra-colors-brand-500:#fd683e;--chakra-colors-brand-600:#fd683e;--chakra-colors-brand-700:#fd683e;--chakra-colors-brand-800:#fd683e;--chakra-colors-brand-900:#fd683e;--chakra-borders-none:0;--chakra-borders-1px:1px solid;--chakra-borders-2px:2px solid;--chakra-borders-4px:4px solid;--chakra-borders-8px:8px solid;--chakra-fonts-heading:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--chakra-fonts-body:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--chakra-fonts-mono:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--chakra-fontSizes-3xs:0.45rem;--chakra-fontSizes-2xs:0.625rem;--chakra-fontSizes-xs:0.75rem;--chakra-fontSizes-sm:0.875rem;--chakra-fontSizes-md:1rem;--chakra-fontSizes-lg:1.125rem;--chakra-fontSizes-xl:1.25rem;--chakra-fontSizes-2xl:1.5rem;--chakra-fontSizes-3xl:1.875rem;--chakra-fontSizes-4xl:2.25rem;--chakra-fontSizes-5xl:3rem;--chakra-fontSizes-6xl:3.75rem;--chakra-fontSizes-7xl:4.5rem;--chakra-fontSizes-8xl:6rem;--chakra-fontSizes-9xl:8rem;--chakra-fontWeights-hairline:100;--chakra-fontWeights-thin:200;--chakra-fontWeights-light:300;--chakra-fontWeights-normal:400;--chakra-fontWeights-medium:500;--chakra-fontWeights-semibold:600;--chakra-fontWeights-bold:700;--chakra-fontWeights-extrabold:800;--chakra-fontWeights-black:900;--chakra-letterSpacings-tighter:-0.05em;--chakra-letterSpacings-tight:-0.025em;--chakra-letterSpacings-normal:0;--chakra-letterSpacings-wide:0.025em;--chakra-letterSpacings-wider:0.05em;--chakra-letterSpacings-widest:0.1em;--chakra-lineHeights-3:.75rem;--chakra-lineHeights-4:1rem;--chakra-lineHeights-5:1.25rem;--chakra-lineHeights-6:1.5rem;--chakra-lineHeights-7:1.75rem;--chakra-lineHeights-8:2rem;--chakra-lineHeights-9:2.25rem;--chakra-lineHeights-10:2.5rem;--chakra-lineHeights-normal:normal;--chakra-lineHeights-none:1;--chakra-lineHeights-shorter:1.25;--chakra-lineHeights-short:1.375;--chakra-lineHeights-base:1.5;--chakra-lineHeights-tall:1.625;--chakra-lineHeights-taller:2;--chakra-radii-none:0;--chakra-radii-sm:0.125rem;--chakra-radii-base:0.25rem;--chakra-radii-md:0.375rem;--chakra-radii-lg:0.5rem;--chakra-radii-xl:0.75rem;--chakra-radii-2xl:1rem;--chakra-radii-3xl:1.5rem;--chakra-radii-full:9999px;--chakra-space-1:0.25rem;--chakra-space-2:0.5rem;--chakra-space-3:0.75rem;--chakra-space-4:1rem;--chakra-space-5:1.25rem;--chakra-space-6:1.5rem;--chakra-space-7:1.75rem;--chakra-space-8:2rem;--chakra-space-9:2.25rem;--chakra-space-10:2.5rem;--chakra-space-12:3rem;--chakra-space-14:3.5rem;--chakra-space-16:4rem;--chakra-space-20:5rem;--chakra-space-24:6rem;--chakra-space-28:7rem;--chakra-space-32:8rem;--chakra-space-36:9rem;--chakra-space-40:10rem;--chakra-space-44:11rem;--chakra-space-48:12rem;--chakra-space-52:13rem;--chakra-space-56:14rem;--chakra-space-60:15rem;--chakra-space-64:16rem;--chakra-space-72:18rem;--chakra-space-80:20rem;--chakra-space-96:24rem;--chakra-space-px:1px;--chakra-space-0-5:0.125rem;--chakra-space-1-5:0.375rem;--chakra-space-2-5:0.625rem;--chakra-space-3-5:0.875rem;--chakra-shadows-xs:0 0 0 1px rgba(0, 0, 0, 0.05);--chakra-shadows-sm:0 1px 2px 0 rgba(0, 0, 0, 0.05);--chakra-shadows-base:0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);--chakra-shadows-md:0 4px 6px -1px rgba(0, 0, 0, 0.1),0 2px 4px -1px rgba(0, 0, 0, 0.06);--chakra-shadows-lg:0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);--chakra-shadows-xl:0 20px 25px -5px rgba(0, 0, 0, 0.1),0 10px 10px -5px rgba(0, 0, 0, 0.04);--chakra-shadows-2xl:0 25px 50px -12px rgba(0, 0, 0, 0.25);--chakra-shadows-outline:0 0 0 3px rgba(66, 153, 225, 0.6);--chakra-shadows-inner:inset 0 2px 4px 0 rgba(0,0,0,0.06);--chakra-shadows-none:none;--chakra-shadows-dark-lg:rgba(0, 0, 0, 0.1) 0px 0px 0px 1px,rgba(0, 0, 0, 0.2) 0px 5px 10px,rgba(0, 0, 0, 0.4) 0px 15px 40px;--chakra-sizes-1:0.25rem;--chakra-sizes-2:0.5rem;--chakra-sizes-3:0.75rem;--chakra-sizes-4:1rem;--chakra-sizes-5:1.25rem;--chakra-sizes-6:1.5rem;--chakra-sizes-7:1.75rem;--chakra-sizes-8:2rem;--chakra-sizes-9:2.25rem;--chakra-sizes-10:2.5rem;--chakra-sizes-12:3rem;--chakra-sizes-14:3.5rem;--chakra-sizes-16:4rem;--chakra-sizes-20:5rem;--chakra-sizes-24:6rem;--chakra-sizes-28:7rem;--chakra-sizes-32:8rem;--chakra-sizes-36:9rem;--chakra-sizes-40:10rem;--chakra-sizes-44:11rem;--chakra-sizes-48:12rem;--chakra-sizes-52:13rem;--chakra-sizes-56:14rem;--chakra-sizes-60:15rem;--chakra-sizes-64:16rem;--chakra-sizes-72:18rem;--chakra-sizes-80:20rem;--chakra-sizes-96:24rem;--chakra-sizes-px:1px;--chakra-sizes-0-5:0.125rem;--chakra-sizes-1-5:0.375rem;--chakra-sizes-2-5:0.625rem;--chakra-sizes-3-5:0.875rem;--chakra-sizes-max:max-content;--chakra-sizes-min:min-content;--chakra-sizes-full:100%;--chakra-sizes-3xs:14rem;--chakra-sizes-2xs:16rem;--chakra-sizes-xs:20rem;--chakra-sizes-sm:24rem;--chakra-sizes-md:28rem;--chakra-sizes-lg:32rem;--chakra-sizes-xl:36rem;--chakra-sizes-2xl:42rem;--chakra-sizes-3xl:48rem;--chakra-sizes-4xl:56rem;--chakra-sizes-5xl:64rem;--chakra-sizes-6xl:72rem;--chakra-sizes-7xl:80rem;--chakra-sizes-8xl:90rem;--chakra-sizes-prose:60ch;--chakra-sizes-container-sm:640px;--chakra-sizes-container-md:768px;--chakra-sizes-container-lg:1024px;--chakra-sizes-container-xl:1280px;--chakra-zIndices-hide:-1;--chakra-zIndices-auto:auto;--chakra-zIndices-base:0;--chakra-zIndices-docked:10;--chakra-zIndices-dropdown:1000;--chakra-zIndices-sticky:1100;--chakra-zIndices-banner:1200;--chakra-zIndices-overlay:1300;--chakra-zIndices-modal:1400;--chakra-zIndices-popover:1500;--chakra-zIndices-skipLink:1600;--chakra-zIndices-toast:1700;--chakra-zIndices-tooltip:1800;--chakra-transition-property-common:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform;--chakra-transition-property-colors:background-color,border-color,color,fill,stroke;--chakra-transition-property-dimensions:width,height;--chakra-transition-property-position:left,right,top,bottom;--chakra-transition-property-background:background-color,background-image,background-position;--chakra-transition-easing-ease-in:cubic-bezier(0.4, 0, 1, 1);--chakra-transition-easing-ease-out:cubic-bezier(0, 0, 0.2, 1);--chakra-transition-easing-ease-in-out:cubic-bezier(0.4, 0, 0.2, 1);--chakra-transition-duration-ultra-fast:50ms;--chakra-transition-duration-faster:100ms;--chakra-transition-duration-fast:150ms;--chakra-transition-duration-normal:200ms;--chakra-transition-duration-slow:300ms;--chakra-transition-duration-slower:400ms;--chakra-transition-duration-ultra-slow:500ms;--chakra-blur-none:0;--chakra-blur-sm:4px;--chakra-blur-base:8px;--chakra-blur-md:12px;--chakra-blur-lg:16px;--chakra-blur-xl:24px;--chakra-blur-2xl:40px;--chakra-blur-3xl:64px;--chakra-breakpoints-base:0em;--chakra-breakpoints-sm:30em;--chakra-breakpoints-md:48em;--chakra-breakpoints-lg:62em;--chakra-breakpoints-xl:80em;--chakra-breakpoints-2xl:96em;}.chakra-ui-light :host:not([data-theme]),.chakra-ui-light :root:not([data-theme]),.chakra-ui-light [data-theme]:not([data-theme]),[data-theme=light] :host:not([data-theme]),[data-theme=light] :root:not([data-theme]),[data-theme=light] [data-theme]:not([data-theme]),:host[data-theme=light],:root[data-theme=light],[data-theme][data-theme=light]{--chakra-colors-chakra-body-text:var(--chakra-colors-gray-800);--chakra-colors-chakra-body-bg:var(--chakra-colors-white);--chakra-colors-chakra-border-color:var(--chakra-colors-gray-200);--chakra-colors-chakra-inverse-text:var(--chakra-colors-white);--chakra-colors-chakra-subtle-bg:var(--chakra-colors-gray-100);--chakra-colors-chakra-subtle-text:var(--chakra-colors-gray-600);--chakra-colors-chakra-placeholder-color:var(--chakra-colors-gray-500);}.chakra-ui-dark :host:not([data-theme]),.chakra-ui-dark :root:not([data-theme]),.chakra-ui-dark [data-theme]:not([data-theme]),[data-theme=dark] :host:not([data-theme]),[data-theme=dark] :root:not([data-theme]),[data-theme=dark] [data-theme]:not([data-theme]),:host[data-theme=dark],:root[data-theme=dark],[data-theme][data-theme=dark]{--chakra-colors-chakra-body-text:var(--chakra-colors-whiteAlpha-900);--chakra-colors-chakra-body-bg:var(--chakra-colors-gray-800);--chakra-colors-chakra-border-color:var(--chakra-colors-whiteAlpha-300);--chakra-colors-chakra-inverse-text:var(--chakra-colors-gray-800);--chakra-colors-chakra-subtle-bg:var(--chakra-colors-gray-700);--chakra-colors-chakra-subtle-text:var(--chakra-colors-gray-400);--chakra-colors-chakra-placeholder-color:var(--chakra-colors-whiteAlpha-400);}</style><style data-emotion="css-global fubdgu">html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:system-ui,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;touch-action:manipulation;}body{position:relative;min-height:100%;margin:0;font-feature-settings:"kern";}:where(*, *::before, *::after){border-width:0;border-style:solid;box-sizing:border-box;word-wrap:break-word;}main{display:block;}hr{border-top-width:1px;box-sizing:content-box;height:0;overflow:visible;}:where(pre, code, kbd,samp){font-family:SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:1em;}a{background-color:transparent;color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit;}abbr[title]{border-bottom:none;-webkit-text-decoration:underline;text-decoration:underline;-webkit-text-decoration:underline dotted;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;}:where(b, strong){font-weight:bold;}small{font-size:80%;}:where(sub,sup){font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sub{bottom:-0.25em;}sup{top:-0.5em;}img{border-style:none;}:where(button, input, optgroup, select, textarea){font-family:inherit;font-size:100%;line-height:1.15;margin:0;}:where(button, input){overflow:visible;}:where(button, select){text-transform:none;}:where(
|
|
2
2
|
button::-moz-focus-inner,
|
|
3
3
|
[type="button"]::-moz-focus-inner,
|
|
4
4
|
[type="reset"]::-moz-focus-inner,
|
|
@@ -24,4 +24,4 @@
|
|
|
24
24
|
transparent 0%,
|
|
25
25
|
#3182ce 50%,
|
|
26
26
|
transparent 100%
|
|
27
|
-
);position:absolute;will-change:left;min-width:50%;-webkit-animation:animation-11lmxjq 1s ease infinite normal none running;animation:animation-11lmxjq 1s ease infinite normal none running;}@-webkit-keyframes animation-11lmxjq{0%{left:-40%;}100%{left:100%;}}@keyframes animation-11lmxjq{0%{left:-40%;}100%{left:100%;}}</style><div style="width:0%" data-indeterminate="" aria-valuemax="100" aria-valuemin="0" role="progressbar" class="css-h5ends"></div></div></div><div class="css-0"></div></div></div
|
|
27
|
+
);position:absolute;will-change:left;min-width:50%;-webkit-animation:animation-11lmxjq 1s ease infinite normal none running;animation:animation-11lmxjq 1s ease infinite normal none running;}@-webkit-keyframes animation-11lmxjq{0%{left:-40%;}100%{left:100%;}}@keyframes animation-11lmxjq{0%{left:-40%;}100%{left:100%;}}</style><div style="width:0%" data-indeterminate="" aria-valuemax="100" aria-valuemin="0" role="progressbar" class="css-h5ends"></div></div></div><div class="css-0"></div></div></div></div><span></span><span id="__chakra_env" hidden=""></span><script src="/_next/static/chunks/webpack-8442f6926a7a34ce.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/1b121dc4d36aeb4d.css\",\"style\"]\n2:HL[\"/_next/static/css/951e2e0eea2d4a5b.css\",\"style\"]\n3:HL[\"/_next/static/css/17a96168e3a9db13.css\",\"style\"]\n4:HL[\"/_next/static/css/6d6961e0422ac782.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"5:I[37194,[],\"\"]\n7:I[86822,[],\"ClientPageRoot\"]\n8:I[2324,[\"509\",\"static/chunks/9746af58-d74bef4d03eea6ab.js\",\"989\",\"static/chunks/47d8844f-79a1b53c66a7d7ec.js\",\"147\",\"static/chunks/a30376cd-ac45432855afb688.js\",\"495\",\"static/chunks/6dc81886-c94b9b91bc2c3caf.js\",\"376\",\"static/chunks/3a92ee20-3b5d922d4157af5e.js\",\"678\",\"static/chunks/3998a672-eaad84bdd88cc73e.js\",\"266\",\"static/chunks/e24bf851-0f8cbc99656833e7.js\",\"517\",\"static/chunks/c1ceaa8b-a1e442154d23515e.js\",\"455\",\"static/chunks/6ef81909-694dc38134099299.js\",\"648\",\"static/chunks/ce84277d-f42c2c58049cea2d.js\",\"995\",\"static/chunks/fee69bc6-f17d36c080742e74.js\",\"739\",\"static/chunks/7a8a3e83-d7fa409d97b38b2b.js\",\"283\",\"static/chunks/450c323b-1bb5db526e54435a.js\",\"303\",\"static/chunks/36e1c10d-bb0210cbd6573a8d.js\",\"22\",\"static/chunks/29e3cc0d-8c150e37dff9631b.js\",\"25\",\"static/chunks/b63b1b3f-7395c74e11a14e95.js\",\"355\",\"static/chunks/7f27ae6c-413f6b869a04183a.js\",\"599\",\"static/chunks/c132bf7d-8102037f9ccf372a.js\",\"971\",\"static/chunks/cd9f8d63-e020f408095ed77c.js\",\"316\",\"static/chunks/316-9e0164c48e40d69b.js\",\"963\",\"static/chunks/963-7cb453d3f159e7cc.js\",\"711\",\"static/chunks/711-5ac2c87eb5073191.js\",\"931\",\"static/chunks/app/page-f0b9d7985eaf7eed.js\"],\"default\",1]\n9:I[84046,[\"509\",\"static/chunks/9746af58-d74bef4d03eea6ab.js\",\"989\",\"static/chunks/47d8844f-79a1b53c66a7d7ec.js\",\"147\",\"static/chunks/a30376cd-ac45432855afb688.js\",\"495\",\"static/chunks/6dc81886-c94b9b91bc2c3caf.js\",\"376\",\"static/chunks/3a92ee20-3b5d922d4157af5e.js\",\"678\",\"static/chunks/3998a672-eaad84bdd88cc73e.js\",\"266\",\"static/chunks/e24bf851-0f8cbc99656833e7.js\",\"517\",\"static/chunks/c1ceaa8b-a1e442154d23515e.js\",\"455\",\"static/chunks/6ef81909-694dc38134099299.js\",\"316\",\"static/chunks/316-9e0164c48e40d69b.js\",\"350\",\"static/chunks/350-30608a95db2634b6.js\",\"711\",\"static/chunks/711-5ac2c87eb5073191.js\",\"185\",\"static/chunks/app/layout-c76e6ca2ce9857e9.js\"],\"default\"]\na:I[79137,[],\"\"]\nb:I[63846,[],\"\"]\nd:I[67160,[],\"\"]\ne:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L5\",null,{\"buildId\":\"1rwJEkX_z2KfSDpX3cGQ1\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"\"],\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[[\"$L6\",[\"$\",\"$L7\",null,{\"props\":{\"params\":{},\"searchParams\":{}},\"Component\":\"$8\"}],[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/17a96168e3a9db13.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/6d6961e0422ac782.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]]],null],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/1b121dc4d36aeb4d.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/951e2e0eea2d4a5b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"suppressHydrationWarning\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$La\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$Lb\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$Lc\"],\"globalErrorComponent\":\"$d\",\"missingSlots\":\"$We\"}]\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"recce\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Recce: Data validation toolkit for comprehensive PR review\"}],[\"$\",\"link\",\"4\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"32x32\"}]]\n6:null\n"])</script></body></html>
|
recce/data/index.txt
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
2:I[86822,[],"ClientPageRoot"]
|
|
2
|
-
3:I[
|
|
3
|
-
4:I[84046,["509","static/chunks/9746af58-d74bef4d03eea6ab.js","989","static/chunks/47d8844f-79a1b53c66a7d7ec.js","147","static/chunks/a30376cd-ac45432855afb688.js","495","static/chunks/6dc81886-c94b9b91bc2c3caf.js","376","static/chunks/3a92ee20-3b5d922d4157af5e.js","678","static/chunks/3998a672-eaad84bdd88cc73e.js","266","static/chunks/e24bf851-0f8cbc99656833e7.js","517","static/chunks/c1ceaa8b-a1e442154d23515e.js","455","static/chunks/6ef81909-694dc38134099299.js","316","static/chunks/316-9e0164c48e40d69b.js","350","static/chunks/350-30608a95db2634b6.js","711","static/chunks/711-
|
|
2
|
+
3:I[2324,["509","static/chunks/9746af58-d74bef4d03eea6ab.js","989","static/chunks/47d8844f-79a1b53c66a7d7ec.js","147","static/chunks/a30376cd-ac45432855afb688.js","495","static/chunks/6dc81886-c94b9b91bc2c3caf.js","376","static/chunks/3a92ee20-3b5d922d4157af5e.js","678","static/chunks/3998a672-eaad84bdd88cc73e.js","266","static/chunks/e24bf851-0f8cbc99656833e7.js","517","static/chunks/c1ceaa8b-a1e442154d23515e.js","455","static/chunks/6ef81909-694dc38134099299.js","648","static/chunks/ce84277d-f42c2c58049cea2d.js","995","static/chunks/fee69bc6-f17d36c080742e74.js","739","static/chunks/7a8a3e83-d7fa409d97b38b2b.js","283","static/chunks/450c323b-1bb5db526e54435a.js","303","static/chunks/36e1c10d-bb0210cbd6573a8d.js","22","static/chunks/29e3cc0d-8c150e37dff9631b.js","25","static/chunks/b63b1b3f-7395c74e11a14e95.js","355","static/chunks/7f27ae6c-413f6b869a04183a.js","599","static/chunks/c132bf7d-8102037f9ccf372a.js","971","static/chunks/cd9f8d63-e020f408095ed77c.js","316","static/chunks/316-9e0164c48e40d69b.js","963","static/chunks/963-7cb453d3f159e7cc.js","711","static/chunks/711-5ac2c87eb5073191.js","931","static/chunks/app/page-f0b9d7985eaf7eed.js"],"default",1]
|
|
3
|
+
4:I[84046,["509","static/chunks/9746af58-d74bef4d03eea6ab.js","989","static/chunks/47d8844f-79a1b53c66a7d7ec.js","147","static/chunks/a30376cd-ac45432855afb688.js","495","static/chunks/6dc81886-c94b9b91bc2c3caf.js","376","static/chunks/3a92ee20-3b5d922d4157af5e.js","678","static/chunks/3998a672-eaad84bdd88cc73e.js","266","static/chunks/e24bf851-0f8cbc99656833e7.js","517","static/chunks/c1ceaa8b-a1e442154d23515e.js","455","static/chunks/6ef81909-694dc38134099299.js","316","static/chunks/316-9e0164c48e40d69b.js","350","static/chunks/350-30608a95db2634b6.js","711","static/chunks/711-5ac2c87eb5073191.js","185","static/chunks/app/layout-c76e6ca2ce9857e9.js"],"default"]
|
|
4
4
|
5:I[79137,[],""]
|
|
5
5
|
6:I[63846,[],""]
|
|
6
|
-
0:["
|
|
6
|
+
0:["1rwJEkX_z2KfSDpX3cGQ1",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/17a96168e3a9db13.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/6d6961e0422ac782.css","precedence":"next","crossOrigin":"$undefined"}]]],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/1b121dc4d36aeb4d.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/951e2e0eea2d4a5b.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"suppressHydrationWarning":true,"children":["$","$L4",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]}]],null],null],["$L7",null]]]]
|
|
7
7
|
7:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"recce"}],["$","meta","3",{"name":"description","content":"Recce: Data validation toolkit for comprehensive PR review"}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"32x32"}]]
|
|
8
8
|
1:null
|
recce/models/types.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Literal, Optional
|
|
4
|
+
from typing import Dict, List, Literal, Optional
|
|
5
5
|
|
|
6
6
|
from pydantic import UUID4, BaseModel, Field
|
|
7
7
|
|
|
@@ -110,3 +110,49 @@ class LineageDiff(BaseModel):
|
|
|
110
110
|
base: dict
|
|
111
111
|
current: dict
|
|
112
112
|
diff: dict[str, NodeDiff]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Column Level Linage
|
|
116
|
+
class CllColumnDep(BaseModel):
|
|
117
|
+
node: str
|
|
118
|
+
column: str
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CllColumn(BaseModel):
|
|
122
|
+
name: Optional[str]
|
|
123
|
+
|
|
124
|
+
# data type
|
|
125
|
+
type: Optional[str] = None
|
|
126
|
+
|
|
127
|
+
# transformation type
|
|
128
|
+
transformation_type: Literal["source", "passthrough", "renamed", "derived", "unknown"] = "unknown"
|
|
129
|
+
|
|
130
|
+
# column-to-column dependencies
|
|
131
|
+
depends_on: List[CllColumnDep] = Field(default_factory=list)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class CllNodeDependsOn(BaseModel):
|
|
135
|
+
# model-to-column dependencies
|
|
136
|
+
columns: List[CllColumnDep] = Field(default_factory=list)
|
|
137
|
+
|
|
138
|
+
# model-to-model dependencies
|
|
139
|
+
nodes: List[str] = Field(default_factory=list)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class CllNode(BaseModel):
|
|
143
|
+
id: str
|
|
144
|
+
name: str
|
|
145
|
+
package_name: str
|
|
146
|
+
resource_type: str
|
|
147
|
+
raw_code: Optional[str] = None
|
|
148
|
+
source_name: Optional[str] = None
|
|
149
|
+
|
|
150
|
+
# Model to column dependencies
|
|
151
|
+
depends_on: CllNodeDependsOn = Field(default_factory=CllNodeDependsOn)
|
|
152
|
+
|
|
153
|
+
# Column to column dependencies
|
|
154
|
+
columns: Dict[str, CllColumn] = Field(default_factory=dict)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class CllData(BaseModel):
|
|
158
|
+
nodes: Dict[str, CllNode] = Field(default_factory=dict)
|
recce/server.py
CHANGED
|
@@ -36,6 +36,7 @@ from .config import RecceConfig
|
|
|
36
36
|
from .core import RecceContext, default_context, load_context
|
|
37
37
|
from .event import log_api_event, log_single_env_event
|
|
38
38
|
from .exceptions import RecceException
|
|
39
|
+
from .models.types import CllData
|
|
39
40
|
from .run import load_preset_checks
|
|
40
41
|
from .state import RecceShareStateManager, RecceStateLoader
|
|
41
42
|
|
|
@@ -338,7 +339,7 @@ class CllIn(BaseModel):
|
|
|
338
339
|
|
|
339
340
|
|
|
340
341
|
class CllOutput(BaseModel):
|
|
341
|
-
current:
|
|
342
|
+
current: CllData
|
|
342
343
|
|
|
343
344
|
|
|
344
345
|
@app.post("/api/cll", response_model=CllOutput)
|
|
@@ -346,14 +347,8 @@ async def column_level_lineage_by_node(cll_input: CllIn):
|
|
|
346
347
|
from recce.adapter.dbt_adapter import DbtAdapter
|
|
347
348
|
|
|
348
349
|
dbt_adapter: DbtAdapter = default_context().adapter
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
# TODO: Add support for by the node and column
|
|
352
|
-
result = dbt_adapter.get_cll_by_node_id(cll_input.params.get("node_id"))
|
|
353
|
-
except Exception as e:
|
|
354
|
-
raise HTTPException(status_code=400, detail=str(e))
|
|
355
|
-
|
|
356
|
-
return CllOutput(current=result)
|
|
350
|
+
cll = dbt_adapter.get_cll_by_node_id(cll_input.params.get("node_id"))
|
|
351
|
+
return CllOutput(current=cll)
|
|
357
352
|
|
|
358
353
|
|
|
359
354
|
class SelectNodesInput(BaseModel):
|
recce/util/cll.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Dict, List,
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
4
|
|
|
5
5
|
import sqlglot.expressions as exp
|
|
6
6
|
from sqlglot import Dialect, parse_one
|
|
@@ -9,8 +9,14 @@ from sqlglot.optimizer import Scope, traverse_scope
|
|
|
9
9
|
from sqlglot.optimizer.qualify import qualify
|
|
10
10
|
|
|
11
11
|
from recce.exceptions import RecceException
|
|
12
|
+
from recce.models.types import CllColumn, CllColumnDep
|
|
12
13
|
from recce.util import SingletonMeta
|
|
13
14
|
|
|
15
|
+
CllResult = Tuple[
|
|
16
|
+
List[CllColumnDep], # Model to column dependencies
|
|
17
|
+
Dict[str, CllColumn], # Column to column dependencies
|
|
18
|
+
]
|
|
19
|
+
|
|
14
20
|
|
|
15
21
|
@dataclass
|
|
16
22
|
class CLLPerformanceTracking(metaclass=SingletonMeta):
|
|
@@ -68,32 +74,11 @@ class CLLPerformanceTracking(metaclass=SingletonMeta):
|
|
|
68
74
|
self.other_error_nodes = 0
|
|
69
75
|
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
class ColumnLevelDependsOn:
|
|
73
|
-
node: str
|
|
74
|
-
column: str
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@dataclass
|
|
78
|
-
class ColumnLevelDependencyColumn:
|
|
79
|
-
type: Literal["source", "passthrough", "renamed", "derived"]
|
|
80
|
-
depends_on: List[ColumnLevelDependsOn]
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
@dataclass()
|
|
84
|
-
class CllResult:
|
|
85
|
-
# Model to column dependencies
|
|
86
|
-
depends_on: List[ColumnLevelDependsOn]
|
|
87
|
-
|
|
88
|
-
# Column to column dependencies
|
|
89
|
-
columns: Dict[str, ColumnLevelDependencyColumn]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _cll_column(proj, table_alias_map) -> ColumnLevelDependencyColumn:
|
|
77
|
+
def _cll_column(proj, table_alias_map) -> CllColumn:
|
|
93
78
|
# given an expression, return the columns depends on
|
|
94
79
|
# [{node: table, column: column}, ...]
|
|
95
80
|
type = "source"
|
|
96
|
-
depends_on: List[
|
|
81
|
+
depends_on: List[CllColumnDep] = []
|
|
97
82
|
|
|
98
83
|
# instance of Column
|
|
99
84
|
if isinstance(proj, exp.Alias):
|
|
@@ -110,7 +95,7 @@ def _cll_column(proj, table_alias_map) -> ColumnLevelDependencyColumn:
|
|
|
110
95
|
table = next(iter(table_alias_map.values()))
|
|
111
96
|
else:
|
|
112
97
|
table = table_alias_map.get(alias, alias)
|
|
113
|
-
depends_on.append(
|
|
98
|
+
depends_on.append(CllColumnDep(table, column.name))
|
|
114
99
|
if type == "source":
|
|
115
100
|
type = "passthrough"
|
|
116
101
|
elif isinstance(expression, (exp.Paren, exp.Identifier)):
|
|
@@ -128,15 +113,10 @@ def _cll_column(proj, table_alias_map) -> ColumnLevelDependencyColumn:
|
|
|
128
113
|
if type == "passthrough" and depends_on[0].column != alias.alias_or_name:
|
|
129
114
|
type = "renamed"
|
|
130
115
|
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def cll_old(sql, schema=None, dialect=None) -> Dict[str, ColumnLevelDependencyColumn]:
|
|
135
|
-
result = cll(sql, schema=schema, dialect=dialect)
|
|
136
|
-
return result.columns
|
|
116
|
+
return CllColumn(type=type, depends_on=depends_on)
|
|
137
117
|
|
|
138
118
|
|
|
139
|
-
def _dedeup_depends_on(depends_on: List[
|
|
119
|
+
def _dedeup_depends_on(depends_on: List[CllColumnDep]) -> List[CllColumnDep]:
|
|
140
120
|
# deduplicate the depends_on list
|
|
141
121
|
dedup_set = set()
|
|
142
122
|
dedup_list = []
|
|
@@ -148,96 +128,118 @@ def _dedeup_depends_on(depends_on: List[ColumnLevelDependsOn]) -> List[ColumnLev
|
|
|
148
128
|
return dedup_list
|
|
149
129
|
|
|
150
130
|
|
|
151
|
-
def _dedeup_cll_result(cll_result: CllResult):
|
|
152
|
-
cll_result.depends_on = _dedeup_depends_on(cll_result.depends_on)
|
|
153
|
-
for column in cll_result.columns.values():
|
|
154
|
-
column.depends_on = _dedeup_depends_on(column.depends_on)
|
|
155
|
-
|
|
156
|
-
|
|
157
131
|
def _cll_set_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> CllResult:
|
|
158
|
-
|
|
159
|
-
|
|
132
|
+
# model-to-column
|
|
133
|
+
m2c: List[CllColumnDep] = []
|
|
134
|
+
# column-to-column
|
|
135
|
+
c2c_map: Dict[str, CllColumn] = {}
|
|
160
136
|
|
|
161
137
|
for union_scope in scope.union_scopes:
|
|
162
138
|
sub_scope_result = scope_cll_map.get(union_scope)
|
|
139
|
+
if sub_scope_result is None:
|
|
140
|
+
raise RecceException(f"Scope {union_scope} not found in scope_cll_map")
|
|
141
|
+
sub_m2c, sub_c2c_map = sub_scope_result
|
|
163
142
|
|
|
164
|
-
for k, v in
|
|
165
|
-
if k not in
|
|
166
|
-
|
|
143
|
+
for k, v in sub_c2c_map.items():
|
|
144
|
+
if k not in c2c_map:
|
|
145
|
+
c2c_map[k] = v
|
|
167
146
|
else:
|
|
168
|
-
|
|
169
|
-
|
|
147
|
+
c2c_map[k].depends_on.extend(v.depends_on)
|
|
148
|
+
c2c_map[k].transformation_type = "derived"
|
|
170
149
|
|
|
171
|
-
|
|
172
|
-
return
|
|
150
|
+
m2c.extend(sub_m2c)
|
|
151
|
+
return m2c, c2c_map
|
|
173
152
|
|
|
174
153
|
|
|
175
154
|
def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> CllResult:
|
|
176
155
|
assert scope.expression.key == "select"
|
|
177
156
|
|
|
178
|
-
|
|
179
|
-
|
|
157
|
+
# model-to-column
|
|
158
|
+
m2c: List[CllColumnDep] = []
|
|
159
|
+
# column-to-column
|
|
160
|
+
c2c_map: Dict[str, CllColumn] = {}
|
|
161
|
+
|
|
180
162
|
table_alias_map = {t.alias_or_name: t.name for t in scope.tables}
|
|
181
163
|
select = scope.expression
|
|
182
164
|
|
|
183
|
-
def source_column_dependency(ref_column: exp.Column) -> Optional[
|
|
165
|
+
def source_column_dependency(ref_column: exp.Column) -> Optional[CllColumn]:
|
|
184
166
|
column_name = ref_column.name
|
|
185
167
|
table_name = ref_column.table if ref_column.table != "" else next(iter(table_alias_map.values()))
|
|
186
|
-
source = scope.sources.get(table_name, None) #
|
|
168
|
+
source = scope.sources.get(table_name, None) # transformation_type: exp.Table | Scope
|
|
187
169
|
if isinstance(source, Scope):
|
|
188
|
-
|
|
189
|
-
if
|
|
170
|
+
ref_cll_result = scope_cll_map.get(source)
|
|
171
|
+
if ref_cll_result is None:
|
|
190
172
|
return None
|
|
191
|
-
|
|
173
|
+
_, sub_c2c_map = ref_cll_result
|
|
174
|
+
return sub_c2c_map.get(column_name)
|
|
192
175
|
elif isinstance(source, exp.Table):
|
|
193
|
-
return
|
|
194
|
-
|
|
176
|
+
return CllColumn(
|
|
177
|
+
name=column_name,
|
|
178
|
+
transformation_type="passthrough",
|
|
179
|
+
depends_on=[CllColumnDep(node=source.name, column=column_name)],
|
|
195
180
|
)
|
|
196
181
|
else:
|
|
197
182
|
return None
|
|
198
183
|
|
|
184
|
+
def subquery_cll(subquery: exp.Subquery) -> Optional[CllResult]:
|
|
185
|
+
select = subquery.find(exp.Select)
|
|
186
|
+
if select is None:
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
matched_scope = None
|
|
190
|
+
for sub_scope in scope.subquery_scopes:
|
|
191
|
+
if sub_scope.expression == select:
|
|
192
|
+
matched_scope = sub_scope
|
|
193
|
+
break
|
|
194
|
+
if matched_scope is None:
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
return scope_cll_map.get(matched_scope)
|
|
198
|
+
|
|
199
199
|
for proj in scope.expression.selects:
|
|
200
|
-
|
|
201
|
-
column_depends_on: List[
|
|
200
|
+
transformation_type = "source"
|
|
201
|
+
column_depends_on: List[CllColumnDep] = []
|
|
202
202
|
root = proj.this if isinstance(proj, exp.Alias) else proj
|
|
203
203
|
for expression in root.walk(bfs=False):
|
|
204
204
|
if isinstance(expression, exp.Column):
|
|
205
205
|
ref_column_dependency = source_column_dependency(expression)
|
|
206
206
|
if ref_column_dependency is not None:
|
|
207
207
|
column_depends_on.extend(ref_column_dependency.depends_on)
|
|
208
|
-
if ref_column_dependency.
|
|
209
|
-
|
|
210
|
-
elif ref_column_dependency.
|
|
211
|
-
if
|
|
212
|
-
|
|
213
|
-
elif ref_column_dependency.
|
|
214
|
-
if
|
|
215
|
-
|
|
208
|
+
if ref_column_dependency.transformation_type == "derived":
|
|
209
|
+
transformation_type = "derived"
|
|
210
|
+
elif ref_column_dependency.transformation_type == "renamed":
|
|
211
|
+
if transformation_type == "source" or transformation_type == "passthrough":
|
|
212
|
+
transformation_type = "renamed"
|
|
213
|
+
elif ref_column_dependency.transformation_type == "passthrough":
|
|
214
|
+
if transformation_type == "source":
|
|
215
|
+
transformation_type = "passthrough"
|
|
216
216
|
else:
|
|
217
|
-
column_depends_on.append(
|
|
218
|
-
if
|
|
219
|
-
|
|
217
|
+
column_depends_on.append(CllColumnDep(expression.table, expression.name))
|
|
218
|
+
if transformation_type == "source":
|
|
219
|
+
transformation_type = "passthrough"
|
|
220
220
|
|
|
221
221
|
elif isinstance(expression, (exp.Paren, exp.Identifier)):
|
|
222
222
|
pass
|
|
223
223
|
else:
|
|
224
|
-
|
|
224
|
+
transformation_type = "derived"
|
|
225
225
|
|
|
226
226
|
column_depends_on = _dedeup_depends_on(column_depends_on)
|
|
227
227
|
|
|
228
|
-
if len(column_depends_on) == 0 and
|
|
229
|
-
|
|
228
|
+
if len(column_depends_on) == 0 and transformation_type != "source":
|
|
229
|
+
transformation_type = "source"
|
|
230
230
|
|
|
231
231
|
if isinstance(proj, exp.Alias):
|
|
232
232
|
alias = proj
|
|
233
|
-
if
|
|
234
|
-
|
|
233
|
+
if transformation_type == "passthrough" and column_depends_on[0].column != alias.alias_or_name:
|
|
234
|
+
transformation_type = "renamed"
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
c2c_map[proj.alias_or_name] = CllColumn(
|
|
237
|
+
name=proj.alias_or_name, transformation_type=transformation_type, depends_on=column_depends_on
|
|
238
|
+
)
|
|
237
239
|
|
|
238
|
-
def selected_column_dependency(ref_column: exp.Column) -> Optional[
|
|
240
|
+
def selected_column_dependency(ref_column: exp.Column) -> Optional[CllColumn]:
|
|
239
241
|
column_name = ref_column.name
|
|
240
|
-
return
|
|
242
|
+
return c2c_map.get(column_name)
|
|
241
243
|
|
|
242
244
|
# joins clause: Reference the source columns
|
|
243
245
|
if select.args.get("joins"):
|
|
@@ -246,7 +248,7 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
246
248
|
if isinstance(join, exp.Join):
|
|
247
249
|
for ref_column in join.find_all(exp.Column):
|
|
248
250
|
if source_column_dependency(ref_column) is not None:
|
|
249
|
-
|
|
251
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
250
252
|
|
|
251
253
|
# where clauses: Reference the source columns
|
|
252
254
|
if select.args.get("where"):
|
|
@@ -254,7 +256,14 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
254
256
|
if isinstance(where, exp.Where):
|
|
255
257
|
for ref_column in where.find_all(exp.Column):
|
|
256
258
|
if source_column_dependency(ref_column) is not None:
|
|
257
|
-
|
|
259
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
260
|
+
for subquery in where.find_all(exp.Subquery):
|
|
261
|
+
sub_cll = subquery_cll(subquery)
|
|
262
|
+
if sub_cll is not None:
|
|
263
|
+
sub_m2c, sub_c2c_map = sub_cll
|
|
264
|
+
m2c.extend(sub_m2c)
|
|
265
|
+
for sub_c in sub_c2c_map.values():
|
|
266
|
+
m2c.extend(sub_c.depends_on)
|
|
258
267
|
|
|
259
268
|
# group by clause: Reference the source columns, column index
|
|
260
269
|
if select.args.get("group"):
|
|
@@ -262,7 +271,7 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
262
271
|
if isinstance(group, exp.Group):
|
|
263
272
|
for ref_column in group.find_all(exp.Column):
|
|
264
273
|
if source_column_dependency(ref_column) is not None:
|
|
265
|
-
|
|
274
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
266
275
|
|
|
267
276
|
# having clause: Reference the source columns, selected columns
|
|
268
277
|
if select.args.get("having"):
|
|
@@ -270,9 +279,16 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
270
279
|
if isinstance(having, exp.Having):
|
|
271
280
|
for ref_column in having.find_all(exp.Column):
|
|
272
281
|
if source_column_dependency(ref_column) is not None:
|
|
273
|
-
|
|
282
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
274
283
|
elif selected_column_dependency(ref_column) is not None:
|
|
275
|
-
|
|
284
|
+
m2c.extend(selected_column_dependency(ref_column).depends_on)
|
|
285
|
+
for subquery in having.find_all(exp.Subquery):
|
|
286
|
+
sub_cll = subquery_cll(subquery)
|
|
287
|
+
if sub_cll is not None:
|
|
288
|
+
sub_m2c, sub_c2c_map = sub_cll
|
|
289
|
+
m2c.extend(sub_m2c)
|
|
290
|
+
for sub_c in sub_c2c_map.values():
|
|
291
|
+
m2c.extend(sub_c.depends_on)
|
|
276
292
|
|
|
277
293
|
# order by clause: Reference the source columns, selected columns, column index
|
|
278
294
|
if select.args.get("order"):
|
|
@@ -280,18 +296,20 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
280
296
|
if isinstance(order, exp.Order):
|
|
281
297
|
for ref_column in order.find_all(exp.Column):
|
|
282
298
|
if source_column_dependency(ref_column) is not None:
|
|
283
|
-
|
|
299
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
284
300
|
elif selected_column_dependency(ref_column) is not None:
|
|
285
|
-
|
|
301
|
+
m2c.extend(selected_column_dependency(ref_column).depends_on)
|
|
286
302
|
|
|
287
303
|
for source in scope.sources.values():
|
|
288
|
-
|
|
289
|
-
if
|
|
290
|
-
|
|
304
|
+
scope_cll_result = scope_cll_map.get(source)
|
|
305
|
+
if scope_cll_result is None:
|
|
306
|
+
continue
|
|
307
|
+
sub_m2c, _ = scope_cll_result
|
|
308
|
+
m2c.extend(sub_m2c)
|
|
291
309
|
|
|
292
|
-
|
|
310
|
+
m2c = _dedeup_depends_on(m2c)
|
|
293
311
|
|
|
294
|
-
return
|
|
312
|
+
return m2c, c2c_map
|
|
295
313
|
|
|
296
314
|
|
|
297
315
|
def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
@@ -320,7 +338,7 @@ def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
|
320
338
|
except SqlglotError as e:
|
|
321
339
|
raise RecceException(f"Failed to qualify SQL: {str(e)}")
|
|
322
340
|
|
|
323
|
-
result =
|
|
341
|
+
result = None
|
|
324
342
|
scope_cll_map = {}
|
|
325
343
|
for scope in traverse_scope(expression):
|
|
326
344
|
scope_type = scope.expression.key
|
|
@@ -333,4 +351,6 @@ def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
|
333
351
|
|
|
334
352
|
scope_cll_map[scope] = result
|
|
335
353
|
|
|
354
|
+
if result is None:
|
|
355
|
+
raise RecceException("Failed to extract CLL from SQL")
|
|
336
356
|
return result
|