diff --git a/docs-site/components/product-mechanics.tsx b/docs-site/components/product-mechanics.tsx index 1c10e9aa..45baaa31 100644 --- a/docs-site/components/product-mechanics.tsx +++ b/docs-site/components/product-mechanics.tsx @@ -1,115 +1,502 @@ -import type { ReactNode } from "react"; +"use client"; -type SourceInput = { - name: string; - sources: string[]; - detail: string; - signal: string; +import { + Background, + BackgroundVariant, + type Edge, + type EdgeProps, + getSmoothStepPath, + Handle, + MarkerType, + type Node, + type NodeProps, + Position, + ReactFlow, +} from "@xyflow/react"; +import "@xyflow/react/dist/style.css"; +import { useEffect, useMemo, useState } from "react"; + +type SourceNodeData = { accent: string; + body: string; + items: string[]; + title: string; }; -const sourceInputs = [ - { - name: "Database structure", - sources: [ - "Postgres", - "Snowflake", - "BigQuery", - "and many others", - ], - detail: "tables, columns, types, constraints, row counts", - signal: "grounds definitions in live database structure", - accent: "border-fd-primary", - }, - { - name: "BI and usage evidence", - sources: ["Metabase", "Looker"], - detail: "historic SQL, questions, dashboards, usage patterns", - signal: "extracts joins, filters, grain, and trusted examples", - accent: "border-orange-500", - }, - { - name: "Semantic modeling", - sources: ["dbt", "MetricFlow", "LookML"], - detail: "models, metrics, dimensions, explores, joins", - signal: "maps existing modeling logic into semantic entities", - accent: "border-amber-500", - }, - { - name: "Company documentation", - sources: ["Notion"], - detail: "Notion pages, policies, caveats", - signal: "links business language back to semantic references", - accent: "border-slate-500 dark:border-cyan-200", - }, -] satisfies SourceInput[]; +type StageNodeData = { + body: string; + index: number; + title: string; +}; -const ingestSteps = [ +type OutputNodeData = { + accent: string; + body: string; + path: string; + tags: string[]; + title: string; +}; + +type SourceNode = Node; +type StageNode = Node; +type OutputNode = Node; +type FlowNode = SourceNode | StageNode | OutputNode; + +const SOURCE_W = 210; +const SOURCE_H = 200; +const STAGE_W = 280; +const STAGE_H = 120; +const OUTPUT_W = 340; +const OUTPUT_H = 232; + +const ROW_SOURCES_Y = 80; +const ROW_STAGE_START_Y = 360; +const STAGE_GAP = 30; +const ROW_OUTPUTS_Y = 1000; + +const STAGE_CENTER_X = 460; +const STAGE_X = STAGE_CENTER_X - STAGE_W / 2; + +const SOURCE_GAP_X = 24; +const SOURCES_TOTAL = SOURCE_W * 4 + SOURCE_GAP_X * 3; +const SOURCES_START_X = STAGE_CENTER_X - SOURCES_TOTAL / 2; + +const OUTPUT_GAP_X = 180; +const OUTPUTS_TOTAL = OUTPUT_W * 2 + OUTPUT_GAP_X; +const OUTPUTS_START_X = STAGE_CENTER_X - OUTPUTS_TOTAL / 2; + +const EDGE_STROKE = "#94a3b8"; + +const sourceData: SourceNodeData[] = [ { - title: "extract evidence", - body: "Pull structured facts from schemas, SQL, BI metadata, and docs.", + title: "Databases", + body: "Schemas, columns, keys, row counts, and query history.", + items: ["PostgreSQL", "Snowflake", "BigQuery", "SQLite"], + accent: "#3b82f6", }, { - title: "reconcile entities", - body: "Merge names, measures, joins, and caveats into one project model.", + title: "BI tools", + body: "Dashboards, questions, explores, usage, and trusted examples.", + items: ["Metabase", "Looker"], + accent: "#f97316", }, { - title: "validate references", - body: "Check semantic fields and joins against database context before agents use them.", + title: "Modeling code", + body: "Existing metrics, dimensions, models, joins, and entities.", + items: ["dbt", "LookML", "MetricFlow"], + accent: "#f59e0b", + }, + { + title: "Docs and notes", + body: "Policies, caveats, team definitions, and analyst context.", + items: ["Notion", "Any text"], + accent: "#10b981", }, ]; -const artifacts = [ +const stageData: Omit[] = [ { - path: "semantic-layer/*.yaml", - title: "Typed query model", - body: "sources, grain, joins, dimensions, measures, filters, segments", + title: "Source adapters", + body: "Read each configured system in its native shape.", }, { + title: "Context builder", + body: "Turn source evidence into proposed context updates.", + }, + { + title: "Reconciliation", + body: "Merge new evidence with the context that already exists.", + }, + { + title: "Validation", + body: "Check references and semantics before agents rely on them.", + }, +]; + +const outputData: OutputNodeData[] = [ + { + title: "Wiki", path: "wiki/*.md", - title: "Business context", - body: "rules and caveats with sl_refs back to semantic-layer entities", + tags: ["free-form", "auto-maintained"], + body: "Definitions, caveats, policies, analyst notes, and business language that agents can search.", + accent: "#10b981", }, { - path: "raw-sources/", - title: "Evidence trail", - body: "scan artifacts, extracted metadata, relationship evidence", - }, - { - path: ".ktx/", - title: "Local indexes", - body: "embeddings and search indexes, not the source of truth", + title: "Semantic layer", + path: "semantic-layer/*.yaml", + tags: ["structured", "executable", "auto-maintained"], + body: "Metrics, joins, tables, dimensions, filters, and segments that KTX can validate and compile into SQL.", + accent: "#3b82f6", }, ]; -const runtimeSteps = [ - { - title: "Search wiki", - body: "Find business rules, caveats, synonyms, and sl_refs.", - }, - { - title: "Resolve semantic refs", - body: "Map measure and dimension names to approved entities.", - }, - { - title: "Validate fields", - body: "Check source, columns, joins, grain, filters, and segments.", - }, - { - title: "Build query plan", - body: "Create a semantic query plan before SQL is generated.", - }, - { - title: "Compile dialect SQL", - body: "Generate warehouse-shaped SQL instead of copying examples.", - }, - { - title: "Execute with bounds", - body: "Optionally run with bounded rows and return provenance.", - }, +const nodes: FlowNode[] = [ + ...sourceData.map((source, index) => ({ + id: `source-${index}`, + type: "source", + position: { + x: SOURCES_START_X + index * (SOURCE_W + SOURCE_GAP_X), + y: ROW_SOURCES_Y, + }, + data: source, + draggable: false, + selectable: false, + })), + ...stageData.map((stage, index) => ({ + id: `stage-${index}`, + type: "stage", + position: { + x: STAGE_X, + y: ROW_STAGE_START_Y + index * (STAGE_H + STAGE_GAP), + }, + data: { ...stage, index: index + 1 }, + draggable: false, + selectable: false, + })), + ...outputData.map((output, index) => ({ + id: `output-${index}`, + type: "output", + position: { + x: OUTPUTS_START_X + index * (OUTPUT_W + OUTPUT_GAP_X), + y: ROW_OUTPUTS_Y, + }, + data: output, + draggable: false, + selectable: false, + })), ]; +const REF_EDGE_STROKE = "#64748b"; + +const flowEdges = [ + ...sourceData.map((_, index) => ({ + id: `e-source-${index}-stage-0`, + source: `source-${index}`, + target: "stage-0", + })), + ...stageData.slice(0, -1).map((_, index) => ({ + id: `e-stage-${index}-stage-${index + 1}`, + source: `stage-${index}`, + target: `stage-${index + 1}`, + })), + ...outputData.map((_, index) => ({ + id: `e-stage-3-output-${index}`, + source: "stage-3", + target: `output-${index}`, + })), +].map((edge) => ({ + ...edge, + type: "smoothstep" as const, + style: { stroke: EDGE_STROKE, strokeWidth: 1.5 }, + markerEnd: { + type: MarkerType.ArrowClosed, + color: EDGE_STROKE, + width: 16, + height: 16, + }, +})); + +const refsEdge = { + id: "e-output-refs", + source: "output-0", + sourceHandle: "right", + target: "output-1", + targetHandle: "left", + type: "straight" as const, + label: "references", + labelBgPadding: [6, 3] as [number, number], + labelBgBorderRadius: 4, + labelStyle: { + fontSize: 13, + fontWeight: 500, + fill: "var(--color-fd-muted-foreground)", + }, + labelBgStyle: { + fill: "var(--color-fd-background)", + stroke: "var(--color-fd-border)", + strokeWidth: 1, + }, + style: { + stroke: REF_EDGE_STROKE, + strokeWidth: 1.25, + strokeDasharray: "4 4", + }, + markerStart: { + type: MarkerType.ArrowClosed, + color: REF_EDGE_STROKE, + width: 14, + height: 14, + }, + markerEnd: { + type: MarkerType.ArrowClosed, + color: REF_EDGE_STROKE, + width: 14, + height: 14, + }, +}; + + +function SourceNodeView({ data }: NodeProps) { + return ( +
+ +

+ {data.title} +

+

+ {data.body} +

+
+ {data.items.map((item) => ( + + {item} + + ))} +
+ +
+ ); +} + +function StageNodeView({ data }: NodeProps) { + return ( +
+ + + {data.index} + +
+

+ {data.title} +

+

+ {data.body} +

+
+ +
+ ); +} + +function OutputNodeView({ data }: NodeProps) { + return ( +
+ + + +

+ {data.path} +

+

+ {data.title} +

+
+ {data.tags.map((tag) => ( + + {tag} + + ))} +
+

+ {data.body} +

+
+ ); +} + +const PARTICLES_PER_SOURCE = 2; +const PARTICLE_SPEED_PX_PER_SEC = 130; +const PARTICLE_MIN_DURATION_SEC = 4; + +const stageTopY = (i: number) => ROW_STAGE_START_Y + i * (STAGE_H + STAGE_GAP); +const stageBottomY = (i: number) => stageTopY(i) + STAGE_H; + +function buildParticlePath( + sourceIndex: number, + outputIndex: number, +): { d: string; length: number } { + const sourceCenterX = + SOURCES_START_X + sourceIndex * (SOURCE_W + SOURCE_GAP_X) + SOURCE_W / 2; + const sourceBottomYVal = ROW_SOURCES_Y + SOURCE_H; + const outputCenterX = + OUTPUTS_START_X + outputIndex * (OUTPUT_W + OUTPUT_GAP_X) + OUTPUT_W / 2; + + const legs: Array<[number, number, number, number]> = [ + [sourceCenterX, sourceBottomYVal, STAGE_CENTER_X, stageTopY(0)], + [STAGE_CENTER_X, stageBottomY(0), STAGE_CENTER_X, stageTopY(1)], + [STAGE_CENTER_X, stageBottomY(1), STAGE_CENTER_X, stageTopY(2)], + [STAGE_CENTER_X, stageBottomY(2), STAGE_CENTER_X, stageTopY(3)], + [STAGE_CENTER_X, stageBottomY(3), outputCenterX, ROW_OUTPUTS_Y], + ]; + + const segments = legs.map(([sx, sy, tx, ty]) => { + const [segment] = getSmoothStepPath({ + sourceX: sx, + sourceY: sy, + sourcePosition: Position.Bottom, + targetX: tx, + targetY: ty, + targetPosition: Position.Top, + }); + return segment; + }); + + let d = segments[0]; + for (let i = 1; i < segments.length; i += 1) { + d += ` ${segments[i].replace(/^M/, "L")}`; + } + + const length = legs.reduce( + (sum, [sx, sy, tx, ty]) => sum + Math.abs(tx - sx) + Math.abs(ty - sy), + 0, + ); + + return { d, length }; +} + +type ParticleEdgeData = { + d: string; + duration: number; + beginOffset: number; + color: string; +}; + +type ParticleEdge = Edge; + +function ParticleEdgeView({ id, data }: EdgeProps) { + if (!data) return null; + const pathId = `mechanics-particle-path-${id}`; + return ( + <> + + + + + + + + + + + ); +} + +const nodeTypes = { + source: SourceNodeView, + stage: StageNodeView, + output: OutputNodeView, +}; + +const edgeTypes = { + particle: ParticleEdgeView, +}; + +const staticEdges = [...flowEdges, refsEdge]; + +type ParticleSpec = { + id: string; + sourceIndex: number; + outputIndex: number; +}; + +function makeRandomParticles(perSource: number): ParticleSpec[] { + const specs: ParticleSpec[] = []; + for (let sourceIndex = 0; sourceIndex < sourceData.length; sourceIndex += 1) { + for (let n = 0; n < perSource; n += 1) { + specs.push({ + id: `particle-${sourceIndex}-${n}`, + sourceIndex, + outputIndex: Math.floor(Math.random() * outputData.length), + }); + } + } + return specs; +} + +function specToEdge(spec: ParticleSpec): { + id: string; + source: string; + target: string; + type: "particle"; + data: ParticleEdgeData; +} { + const { d, length } = buildParticlePath(spec.sourceIndex, spec.outputIndex); + const duration = Math.max( + PARTICLE_MIN_DURATION_SEC, + length / PARTICLE_SPEED_PX_PER_SEC, + ); + return { + id: spec.id, + source: `source-${spec.sourceIndex}`, + target: `output-${spec.outputIndex}`, + type: "particle", + data: { + d, + duration, + beginOffset: Math.random() * duration, + color: sourceData[spec.sourceIndex].accent, + }, + }; +} + export function ProductMechanics() { + const [particles, setParticles] = useState([]); + + useEffect(() => { + setParticles(makeRandomParticles(PARTICLES_PER_SOURCE)); + }, []); + + const edges = useMemo( + () => [...staticEdges, ...particles.map(specToEdge)], + [particles], + ); + return (
- How KTX works + How ingestion works

- KTX reads source evidence, writes local context files, and gives - agents semantic search, validation, SQL, and provenance. + KTX ingests source evidence, reconciles it with your existing project, + and produces durable context that agents can search, review, and + execute.

-
- - -
+
+
+

+ Ingestion flow +

+

+ From scattered source systems to agent-ready context +

+

+ The inputs can be structured systems or loose team knowledge. The + outputs are the two files agents need: a readable wiki and an + executable semantic layer. +

+
+ +
+ + + +
+
+
); } - -function IngestionDiagram() { - return ( -
- - -
-
- Inputs KTX reads -
- {sourceInputs.map((source) => ( -
-
-

- {source.name} -

-

- {source.detail} -

-

- {source.signal} -

-
- -
- ))} -
-
- -
- KTX transforms evidence -
-
-

- KTX builds the model -

-
    - {ingestSteps.map((step, index) => ( - - ))} -
-
- -
-

- Outputs KTX writes -

-
- {artifacts.map((artifact) => ( - - ))} -
-
-
-
-
-
- ); -} - -function RuntimeDiagram() { - return ( -
- - -
-
- Agent sends - -
connection: warehouse
-
measure: orders.total_revenue
-
dimension: customers.segment
-
filter: orders.created_date >= '2024-01-01'
-
-

- This is the API surface agents should use: compact semantic intent, - not hand-written warehouse SQL. -

-
- -
- KTX planning and execution -
    - {runtimeSteps.map((step, index) => ( - - ))} -
-
-
- -
-
- Semantic query plan -
-

- source:{" "} - orders joined to customers as many_to_one -

-

- measure:{" "} - total_revenue = sum(amount) with refund filter -

-

- grain: segment - group-by with date predicate -

-

- result: dialect - SQL, bounded rows, and provenance -

-
-
- -
- KTX returns - -
select
-
customers.segment,
-
sum(orders.amount) as total_revenue
-
from analytics.orders
-
join analytics.customers
-
on orders.customer_id = customers.id
-
where orders.status != 'refunded'
-
and orders.created_date >= '2024-01-01'
-
group by 1
-
-

- The output can be SQL-only or executed results with provenance, so - the agent can show where the answer came from. -

-
-
-
- ); -} - -function DiagramHeader({ - body, - eyebrow, - id, - title, -}: { - body: string; - eyebrow: string; - id: string; - title: string; -}) { - return ( -
-

- {eyebrow} -

-

- {title} -

-

- {body} -

-
- ); -} - -function Artifact({ - body, - path, - title, -}: { - body: string; - path: string; - title: string; -}) { - return ( -
-

- {path} -

-

{title}

-

- {body} -

-
- ); -} - -function PipelineStep({ - body, - dark = false, - index, - title, -}: { - body: string; - dark?: boolean; - index: number; - title: string; -}) { - return ( -
  • - - {index} - - - - {title} - - - {body} - - -
  • - ); -} - -function ColumnLabel({ children }: { children: ReactNode }) { - return ( -

    - {children} -

    - ); -} - -function SourceList({ - sources, -}: { - sources: string[]; -}) { - return ( -
    -

    - Sources -

    -
    - {sources.map((source) => - source === "and many others" ? ( - - {source} - - ) : ( - - {source} - - ), - )} -
    -
    - ); -} - -function CodeBox({ children }: { children: ReactNode }) { - return ( -
    -
    {children}
    -
    - ); -} diff --git a/docs-site/content/docs/getting-started/introduction.mdx b/docs-site/content/docs/getting-started/introduction.mdx index 499d25a0..d0ee126d 100644 --- a/docs-site/content/docs/getting-started/introduction.mdx +++ b/docs-site/content/docs/getting-started/introduction.mdx @@ -1,6 +1,6 @@ --- title: Introduction -description: What KTX is, how it works, and where to start. +description: KTX is an open-source, self-improving context layer for data agents. --- import { ProductMechanics } from "@/components/product-mechanics"; @@ -23,46 +23,67 @@ import { ProductMechanics } from "@/components/product-mechanics"; Make analytics context usable by agents

    - {'KTX turns warehouse metadata, semantic definitions, BI usage, and team knowledge into local files and runtime tools that database agents can trust.'} + {'KTX is an open-source context layer for database agents. It turns warehouse metadata, BI models, query history, docs, and approved metric definitions into reviewable files agents can search and execute.'}

    -## Why KTX +## Why KTX helps -- Schemas show columns, not business rules. -- Agents need trusted metrics, joins, filters, caveats, and provenance. -- KTX captures that context before agents write SQL, docs, or semantic edits. +KTX gives agents a shared context workspace before they write SQL, answer a +question, or update analytics definitions. -## What KTX creates +- **Context as code.** KTX writes wiki pages and semantic-layer definitions as + git-based files you can review, diff, and merge. +- **Self-improving ingest.** KTX reads warehouses, BI tools, modeling code, + query history, and notes, then reconciles new evidence with accepted context. +- **Executable semantics.** Agents can use approved measures, joins, filters, + dimensions, and segments instead of rebuilding canonical SQL from scratch. +- **Agent-native access.** CLI and MCP tools let agents search context, compile + semantic queries, run read-only SQL, and propose updates. -| Path | What it gives agents | -|------|----------------------| -| `semantic-layer/` | Measures, dimensions, joins, grain, filters, segments | -| `wiki/` | Business definitions, caveats, policies, analyst notes | -| `raw-sources/` | Extracted metadata, scan output, relationship evidence | -| `.ktx/` | Local indexes, embeddings, setup state, runtime data | +KTX complements existing semantic layers by pairing metric definitions with the +surrounding business knowledge, caveats, provenance, and review workflow agents +need for data work. + +## How KTX works + +KTX has two connected sides: it builds and maintains the context layer, then +serves that context to agents at runtime. + +| Side | What KTX does | +|------|---------------| +| **Ingest and auto-maintain knowledge** | Reads your data stack and company knowledge, reconciles new evidence with accepted context, and keeps changes to `semantic-layer/` plus `wiki/` as version-controlled diffs automatically. | +| **Serve agents at runtime** | Helps agents find the right wiki pages and semantic-layer entities, then compile or execute semantic queries through CLI and MCP tools. | ## Use it for -- **Generate SQL** from approved measures, dimensions, joins, and filters -- **Explain provenance** with wiki context and warehouse evidence -- **Repair context** through reviewable YAML and Markdown diffs -- **Work alongside** dbt, LookML, MetricFlow, Looker, Metabase, and warehouses +Use KTX when agents need more than raw database access. Agents can search wiki +context, find semantic-layer entities, compile trusted semantic queries, run +read-only SQL, and use the same tools through MCP. -Databases: SQLite, PostgreSQL, Snowflake, BigQuery, ClickHouse, MySQL, SQL -Server. +- Generate SQL from approved metrics, joins, filters, and dimensions. +- Explain metric provenance with wiki context and source evidence. +- Repair context through reviewable YAML and Markdown diffs. +- Work alongside dbt, MetricFlow, LookML, Looker, Metabase, Notion, and + supported databases. ## Start here +Choose the route that matches what you want to do next. The quickstart is the +best first step for users; contributor setup lives in the community docs. + - Set up KTX and build your first context in under 10 minutes. + Install KTX, run setup, build context, and connect an agent. - - Hands-on workflows for scanning, ingesting, writing, and serving. + + Understand why agents need more than schema access and raw SQL. + + + Refresh context from databases, BI tools, query history, and documents. Edit semantic-layer YAML and wiki Markdown safely. diff --git a/docs-site/package.json b/docs-site/package.json index 229af1e3..4cf896ff 100644 --- a/docs-site/package.json +++ b/docs-site/package.json @@ -10,6 +10,7 @@ "test": "node --test tests/*.test.mjs" }, "dependencies": { + "@xyflow/react": "^12.10.2", "fumadocs-core": "16.8.10", "fumadocs-mdx": "15.0.4", "fumadocs-ui": "16.8.10", @@ -18,11 +19,11 @@ "react-dom": "19.2.6" }, "devDependencies": { + "@tailwindcss/postcss": "^4", "@types/node": "^25.7.0", "@types/react": "^19", "@types/react-dom": "^19", - "typescript": "^6.0", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4" + "tailwindcss": "^4", + "typescript": "^6.0" } } diff --git a/docs-site/tests/product-mechanics-content.test.mjs b/docs-site/tests/product-mechanics-content.test.mjs index bf03abcc..81d716d3 100644 --- a/docs-site/tests/product-mechanics-content.test.mjs +++ b/docs-site/tests/product-mechanics-content.test.mjs @@ -23,7 +23,7 @@ test("docs introduction frames the concept before showing product mechanics", as const heroIndex = introduction.indexOf("Make analytics context"); const whyIndex = introduction.indexOf("## Why KTX"); - const createsIndex = introduction.indexOf("## What KTX creates"); + const worksIndex = introduction.indexOf("## How KTX works"); const mechanicsIndex = introduction.indexOf(""); const useCaseIndex = introduction.indexOf("## Use it for"); const heroSource = introduction.slice(0, mechanicsIndex); @@ -34,12 +34,12 @@ test("docs introduction frames the concept before showing product mechanics", as "problem framing should appear after the hero", ); assert.ok( - createsIndex > whyIndex, - "artifact summary should appear after problem framing", + worksIndex > whyIndex, + "mechanics bridge should appear after problem framing", ); assert.ok( - mechanicsIndex > createsIndex, - "mechanics component should appear after the artifact summary", + mechanicsIndex > worksIndex, + "mechanics component should appear after the mechanics bridge", ); assert.ok( mechanicsIndex < useCaseIndex, @@ -49,49 +49,47 @@ test("docs introduction frames the concept before showing product mechanics", as assert.doesNotMatch(heroSource, /The Context Layer/); assert.doesNotMatch(heroSource, /Building Context/); assert.doesNotMatch(heroSource, /flex flex-wrap gap-3/); + assert.doesNotMatch(introduction, /raw-sources/); + assert.doesNotMatch(introduction, /\.ktx/); }); -test("product mechanics component covers source-specific context and SQL expansion", async () => { +test("product mechanics component explains ingestion outputs", async () => { const component = await readDocsFile("components/product-mechanics.tsx"); for (const expectedText of [ - "How KTX works", - "Build context from source evidence", - "Run agent requests through the model", - "Ingestion", - "Runtime", - "wiki/", - "semantic-layer/", - "raw-sources/", - ".ktx/", - "sl_refs", - "Database structure", - "BI and usage evidence", - "Semantic modeling", - "Company documentation", - "Notion pages", - "Sources", - "KTX transforms evidence", - "KTX builds the model", - "Outputs KTX writes", - "Postgres", + "How ingestion works", + "Ingestion flow", + "From scattered source systems to agent-ready context", + "wiki/*.md", + "semantic-layer/*.yaml", + "Wiki", + "Semantic layer", + "Databases", + "BI tools", + "Modeling code", + "Docs and notes", + "Source adapters", + "Context builder", + "Reconciliation", + "Validation", + "PostgreSQL", "Snowflake", "BigQuery", - "and many others", "Metabase", "Looker", + "dbt", "MetricFlow", "LookML", - "extract evidence", - "reconcile entities", - "validate references", - "semantic query plan", - "dialect SQL", - "bounded rows", - "provenance", - "measure: orders.total_revenue", - "dimension: customers.segment", - "select", + "Notion", + "Any text", + "compile into SQL", + '"use client"', + "@xyflow/react", + "=17' + react-dom: '>=17' + + '@xyflow/system@0.0.76': + resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -3139,6 +3169,9 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -3325,6 +3358,44 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -5937,6 +6008,11 @@ packages: '@types/react': optional: true + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -6158,6 +6234,21 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -9061,6 +9152,27 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 + '@types/d3-color@3.1.3': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -9191,6 +9303,29 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + '@xyflow/react@12.10.2(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@xyflow/system': 0.0.76 + classcat: 5.0.5 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + zustand: 4.5.7(@types/react@19.2.14)(react@19.2.6) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.76': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -9459,6 +9594,8 @@ snapshots: dependencies: clsx: 2.1.1 + classcat@5.0.5: {} + clean-stack@2.2.0: {} clean-stack@5.3.0: @@ -9631,6 +9768,42 @@ snapshots: csstype@3.2.3: {} + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-ease@3.0.1: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-selection@3.0.0: {} + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + data-uri-to-buffer@4.0.1: {} debug@4.4.3: @@ -12730,6 +12903,10 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + use-sync-external-store@1.6.0(react@19.2.6): + dependencies: + react: 19.2.6 + util-deprecate@1.0.2: {} validate-npm-package-license@3.0.4: @@ -12907,4 +13084,11 @@ snapshots: zod@4.4.3: {} + zustand@4.5.7(@types/react@19.2.14)(react@19.2.6): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.6) + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.6 + zwitch@2.0.4: {}