recce-nightly 1.5.0.20250527__py3-none-any.whl → 1.6.0.20250528__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-7ee2d2f7634c0294.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 +81 -90
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250528.dist-info}/METADATA +1 -1
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250528.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 → -N8BYtJQWbixzZmjI30UF}/_buildManifest.js +0 -0
- /recce/data/_next/static/{_i7vGe6giBb07tZ0Ml9uG → -N8BYtJQWbixzZmjI30UF}/_ssgManifest.js +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250528.dist-info}/WHEEL +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250528.dist-info}/entry_points.txt +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250528.dist-info}/licenses/LICENSE +0 -0
- {recce_nightly-1.5.0.20250527.dist-info → recce_nightly-1.6.0.20250528.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-7ee2d2f7634c0294.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-7ee2d2f7634c0294.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\":\"-N8BYtJQWbixzZmjI30UF\",\"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-7ee2d2f7634c0294.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:["-N8BYtJQWbixzZmjI30UF",[[["",{"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,103 @@ 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
|
|
|
199
184
|
for proj in scope.expression.selects:
|
|
200
|
-
|
|
201
|
-
column_depends_on: List[
|
|
185
|
+
transformation_type = "source"
|
|
186
|
+
column_depends_on: List[CllColumnDep] = []
|
|
202
187
|
root = proj.this if isinstance(proj, exp.Alias) else proj
|
|
203
188
|
for expression in root.walk(bfs=False):
|
|
204
189
|
if isinstance(expression, exp.Column):
|
|
205
190
|
ref_column_dependency = source_column_dependency(expression)
|
|
206
191
|
if ref_column_dependency is not None:
|
|
207
192
|
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
|
-
|
|
193
|
+
if ref_column_dependency.transformation_type == "derived":
|
|
194
|
+
transformation_type = "derived"
|
|
195
|
+
elif ref_column_dependency.transformation_type == "renamed":
|
|
196
|
+
if transformation_type == "source" or transformation_type == "passthrough":
|
|
197
|
+
transformation_type = "renamed"
|
|
198
|
+
elif ref_column_dependency.transformation_type == "passthrough":
|
|
199
|
+
if transformation_type == "source":
|
|
200
|
+
transformation_type = "passthrough"
|
|
216
201
|
else:
|
|
217
|
-
column_depends_on.append(
|
|
218
|
-
if
|
|
219
|
-
|
|
202
|
+
column_depends_on.append(CllColumnDep(expression.table, expression.name))
|
|
203
|
+
if transformation_type == "source":
|
|
204
|
+
transformation_type = "passthrough"
|
|
220
205
|
|
|
221
206
|
elif isinstance(expression, (exp.Paren, exp.Identifier)):
|
|
222
207
|
pass
|
|
223
208
|
else:
|
|
224
|
-
|
|
209
|
+
transformation_type = "derived"
|
|
225
210
|
|
|
226
211
|
column_depends_on = _dedeup_depends_on(column_depends_on)
|
|
227
212
|
|
|
228
|
-
if len(column_depends_on) == 0 and
|
|
229
|
-
|
|
213
|
+
if len(column_depends_on) == 0 and transformation_type != "source":
|
|
214
|
+
transformation_type = "source"
|
|
230
215
|
|
|
231
216
|
if isinstance(proj, exp.Alias):
|
|
232
217
|
alias = proj
|
|
233
|
-
if
|
|
234
|
-
|
|
218
|
+
if transformation_type == "passthrough" and column_depends_on[0].column != alias.alias_or_name:
|
|
219
|
+
transformation_type = "renamed"
|
|
235
220
|
|
|
236
|
-
|
|
221
|
+
c2c_map[proj.alias_or_name] = CllColumn(
|
|
222
|
+
name=proj.alias_or_name, transformation_type=transformation_type, depends_on=column_depends_on
|
|
223
|
+
)
|
|
237
224
|
|
|
238
|
-
def selected_column_dependency(ref_column: exp.Column) -> Optional[
|
|
225
|
+
def selected_column_dependency(ref_column: exp.Column) -> Optional[CllColumn]:
|
|
239
226
|
column_name = ref_column.name
|
|
240
|
-
return
|
|
227
|
+
return c2c_map.get(column_name)
|
|
241
228
|
|
|
242
229
|
# joins clause: Reference the source columns
|
|
243
230
|
if select.args.get("joins"):
|
|
@@ -246,7 +233,7 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
246
233
|
if isinstance(join, exp.Join):
|
|
247
234
|
for ref_column in join.find_all(exp.Column):
|
|
248
235
|
if source_column_dependency(ref_column) is not None:
|
|
249
|
-
|
|
236
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
250
237
|
|
|
251
238
|
# where clauses: Reference the source columns
|
|
252
239
|
if select.args.get("where"):
|
|
@@ -254,7 +241,7 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
254
241
|
if isinstance(where, exp.Where):
|
|
255
242
|
for ref_column in where.find_all(exp.Column):
|
|
256
243
|
if source_column_dependency(ref_column) is not None:
|
|
257
|
-
|
|
244
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
258
245
|
|
|
259
246
|
# group by clause: Reference the source columns, column index
|
|
260
247
|
if select.args.get("group"):
|
|
@@ -262,7 +249,7 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
262
249
|
if isinstance(group, exp.Group):
|
|
263
250
|
for ref_column in group.find_all(exp.Column):
|
|
264
251
|
if source_column_dependency(ref_column) is not None:
|
|
265
|
-
|
|
252
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
266
253
|
|
|
267
254
|
# having clause: Reference the source columns, selected columns
|
|
268
255
|
if select.args.get("having"):
|
|
@@ -270,9 +257,9 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
270
257
|
if isinstance(having, exp.Having):
|
|
271
258
|
for ref_column in having.find_all(exp.Column):
|
|
272
259
|
if source_column_dependency(ref_column) is not None:
|
|
273
|
-
|
|
260
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
274
261
|
elif selected_column_dependency(ref_column) is not None:
|
|
275
|
-
|
|
262
|
+
m2c.extend(selected_column_dependency(ref_column).depends_on)
|
|
276
263
|
|
|
277
264
|
# order by clause: Reference the source columns, selected columns, column index
|
|
278
265
|
if select.args.get("order"):
|
|
@@ -280,18 +267,20 @@ def _cll_select_scope(scope: Scope, scope_cll_map: dict[Scope, CllResult]) -> Cl
|
|
|
280
267
|
if isinstance(order, exp.Order):
|
|
281
268
|
for ref_column in order.find_all(exp.Column):
|
|
282
269
|
if source_column_dependency(ref_column) is not None:
|
|
283
|
-
|
|
270
|
+
m2c.extend(source_column_dependency(ref_column).depends_on)
|
|
284
271
|
elif selected_column_dependency(ref_column) is not None:
|
|
285
|
-
|
|
272
|
+
m2c.extend(selected_column_dependency(ref_column).depends_on)
|
|
286
273
|
|
|
287
274
|
for source in scope.sources.values():
|
|
288
|
-
|
|
289
|
-
if
|
|
290
|
-
|
|
275
|
+
scope_cll_result = scope_cll_map.get(source)
|
|
276
|
+
if scope_cll_result is None:
|
|
277
|
+
continue
|
|
278
|
+
sub_m2c, _ = scope_cll_result
|
|
279
|
+
m2c.extend(sub_m2c)
|
|
291
280
|
|
|
292
|
-
|
|
281
|
+
m2c = _dedeup_depends_on(m2c)
|
|
293
282
|
|
|
294
|
-
return
|
|
283
|
+
return m2c, c2c_map
|
|
295
284
|
|
|
296
285
|
|
|
297
286
|
def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
@@ -320,7 +309,7 @@ def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
|
320
309
|
except SqlglotError as e:
|
|
321
310
|
raise RecceException(f"Failed to qualify SQL: {str(e)}")
|
|
322
311
|
|
|
323
|
-
result =
|
|
312
|
+
result = None
|
|
324
313
|
scope_cll_map = {}
|
|
325
314
|
for scope in traverse_scope(expression):
|
|
326
315
|
scope_type = scope.expression.key
|
|
@@ -333,4 +322,6 @@ def cll(sql, schema=None, dialect=None) -> CllResult:
|
|
|
333
322
|
|
|
334
323
|
scope_cll_map[scope] = result
|
|
335
324
|
|
|
325
|
+
if result is None:
|
|
326
|
+
raise RecceException("Failed to extract CLL from SQL")
|
|
336
327
|
return result
|