drizzle-cube 0.4.22 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +2 -2
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +2 -2
- package/dist/adapters/{handler-B-tEntiU.cjs → handler-C3hT7g2W.cjs} +1 -1
- package/dist/adapters/{handler-9Rdn7zM2.js → handler-t7Qd1IYi.js} +1 -1
- package/dist/adapters/hono/index.cjs +6 -6
- package/dist/adapters/hono/index.d.ts +13 -6
- package/dist/adapters/hono/index.js +65 -65
- package/dist/adapters/{mcp-transport-m1X1GtwG.js → mcp-transport-B6ZudTSk.js} +7 -0
- package/dist/adapters/{mcp-transport-8u9G5oNa.cjs → mcp-transport-DCiSGtp1.cjs} +1 -1
- package/dist/adapters/nextjs/index.cjs +2 -2
- package/dist/adapters/nextjs/index.js +2 -2
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +7 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("express"),H=require("cors"),c=require("../mcp-transport-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("express"),H=require("cors"),c=require("../mcp-transport-DCiSGtp1.cjs"),n=require("../utils.cjs");function $(C){const{cubes:v,drizzle:x,schema:I,extractSecurityContext:y,engineType:P,cors:S,basePath:f="/cubejs-api/v1",jsonLimit:Q="10mb",cache:O,mcp:E={enabled:!0},agent:h}=C;if(!v||v.length===0)throw new Error("At least one cube must be provided in the cubes array");const d=w.Router();S&&d.use(H(S)),d.use(w.json({limit:Q})),d.use(w.urlencoded({extended:!0,limit:Q}));const u=new c.SemanticLayerCompiler({drizzle:x,schema:I,engineType:P,cache:O});if(v.forEach(r=>{u.registerCube(r)}),d.post(`${f}/load`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=r.headers["x-cache-control"]==="no-cache",i=await u.executeMultiCubeQuery(e,o,{skipCache:a});t.json(n.formatCubeResponse(e,i,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.get(`${f}/load`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));let o;try{o=JSON.parse(e)}catch{return t.status(400).json(n.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=r.headers["x-cache-control"]==="no-cache",m=await u.executeMultiCubeQuery(o,s,{skipCache:i});t.json(n.formatCubeResponse(o,m,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.post(`${f}/batch`,async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).json(n.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).json(n.formatErrorResponse("Queries array cannot be empty",400));const o=await y(r,t),s=r.headers["x-cache-control"]==="no-cache",a=await n.handleBatchRequest(e,o,u,{skipCache:s});t.json(a)}catch(e){console.error("Batch execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),d.get(`${f}/meta`,(r,t)=>{try{const e=u.getMetadata();t.json(n.formatMetaResponse(e))}catch(e){console.error("Metadata error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),d.post(`${f}/sql`,async(r,t)=>{try{const e=r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=e.measures?.[0]||e.dimensions?.[0];if(!a)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const i=a.split(".")[0],m=await u.generateSQL(i,e,o);t.json(n.formatSqlResponse(e,m))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.get(`${f}/sql`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));const o=JSON.parse(e),s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=o.measures?.[0]||o.dimensions?.[0];if(!i)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const m=i.split(".")[0],R=await u.generateSQL(m,o,s);t.json(n.formatSqlResponse(o,R))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.post(`${f}/dry-run`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=await n.handleDryRun(e,o,u);t.json(s)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.get(`${f}/dry-run`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json({error:"Query parameter is required",valid:!1});const o=JSON.parse(e),s=await y(r,t),a=await n.handleDryRun(o,s,u);t.json(a)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.post(`${f}/explain`,async(r,t)=>{try{const e=r.body.query||r.body,o=r.body.options||{},s=await y(r,t),a=u.validateQuery(e);if(!a.isValid)return t.status(400).json({error:`Query validation failed: ${a.errors.join(", ")}`});const i=await u.explainQuery(e,s,o);t.json(i)}catch(e){console.error("Explain error:",e),t.status(500).json({error:e instanceof Error?e.message:"Explain query failed"})}}),h&&d.post(`${f}/agent/chat`,async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-C3hT7g2W.cjs")),{message:o,sessionId:s,history:a}=r.body;if(!o||typeof o!="string")return t.status(400).json({error:"message is required and must be a string"});let i=(h.apiKey||"").trim();if(h.allowClientApiKey){const p=r.headers["x-agent-api-key"];p&&(i=p.trim())}if(!i)return t.status(401).json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const m=h.allowClientApiKey?r.headers["x-agent-provider"]:void 0,R=h.allowClientApiKey?r.headers["x-agent-model"]:void 0,l=h.allowClientApiKey?r.headers["x-agent-provider-endpoint"]:void 0,g=await y(r,t),b=h.buildSystemContext?.(g);t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const p=e({message:o,sessionId:s,history:a,semanticLayer:u,securityContext:g,agentConfig:h,apiKey:i,systemContext:b,providerOverride:m,modelOverride:R,baseURLOverride:l});for await(const j of p)t.write(`data: ${JSON.stringify(j)}
|
|
2
2
|
|
|
3
3
|
`)}catch(p){const j={type:"error",data:{message:p instanceof Error?p.message:"Stream failed"}};t.write(`data: ${JSON.stringify(j)}
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import R, { Router as J } from "express";
|
|
2
2
|
import L from "cors";
|
|
3
|
-
import { S as K, v as D, b as x, a as _, n as V, p as z, w as T, d as B, i as I, M as U, c as F, e as Q, s as E } from "../mcp-transport-
|
|
3
|
+
import { S as K, v as D, b as x, a as _, n as V, p as z, w as T, d as B, i as I, M as U, c as F, e as Q, s as E } from "../mcp-transport-B6ZudTSk.js";
|
|
4
4
|
import { formatErrorResponse as u, formatCubeResponse as P, handleBatchRequest as X, formatMetaResponse as G, formatSqlResponse as H, handleDryRun as M } from "../utils.js";
|
|
5
5
|
function W(v) {
|
|
6
6
|
const {
|
|
@@ -198,7 +198,7 @@ function W(v) {
|
|
|
198
198
|
}
|
|
199
199
|
}), m && c.post(`${p}/agent/chat`, async (r, t) => {
|
|
200
200
|
try {
|
|
201
|
-
const { handleAgentChat: e } = await import("../handler-
|
|
201
|
+
const { handleAgentChat: e } = await import("../handler-t7Qd1IYi.js"), { message: o, sessionId: a, history: n } = r.body;
|
|
202
202
|
if (!o || typeof o != "string")
|
|
203
203
|
return t.status(400).json({ error: "message is required and must be a string" });
|
|
204
204
|
let s = (m.apiKey || "").trim();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var N=Object.create;var j=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var D=(l,n,v,S)=>{if(n&&typeof n=="object"||typeof n=="function")for(let f of J(n))!K.call(l,f)&&f!==v&&j(l,f,{get:()=>n[f],enumerable:!(S=k(n,f))||S.enumerable});return l};var H=(l,n,v)=>(v=l!=null?N(L(l)):{},D(n||!l||!l.__esModule?j(v,"default",{value:l,enumerable:!0}):v,l));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../mcp-transport-
|
|
1
|
+
"use strict";var N=Object.create;var j=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var D=(l,n,v,S)=>{if(n&&typeof n=="object"||typeof n=="function")for(let f of J(n))!K.call(l,f)&&f!==v&&j(l,f,{get:()=>n[f],enumerable:!(S=k(n,f))||S.enumerable});return l};var H=(l,n,v)=>(v=l!=null?N(L(l)):{},D(n||!l||!l.__esModule?j(v,"default",{value:l,enumerable:!0}):v,l));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("../mcp-transport-DCiSGtp1.cjs"),a=require("../utils.cjs"),Q=function(n,v,S){const{cubes:f,drizzle:A,schema:I,extractSecurityContext:m,engineType:O,cors:$,basePath:g="/cubejs-api/v1",bodyLimit:C=10485760,cache:M,mcp:q={enabled:!0},agent:b}=v;if(!f||f.length===0)return S(new Error("At least one cube must be provided in the cubes array"));$&&n.register(import("@fastify/cors"),$),n.addHook("onRequest",async(r,t)=>{r.method==="POST"&&(r.body=void 0)});const u=new d.SemanticLayerCompiler({drizzle:A,schema:I,engineType:O,cache:M});if(f.forEach(r=>{u.registerCube(r)}),n.post(`${g}/load`,{bodyLimit:C,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r),i=u.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",p=await u.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,p,u)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.get(`${g}/load`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query;let o;try{o=JSON.parse(e)}catch{return t.status(400).send(a.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await m(r),i=u.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",p=await u.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,p,u)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.post(`${g}/batch`,{bodyLimit:C,schema:{body:{type:"object",required:["queries"],properties:{queries:{type:"array",items:{type:"object"}}}}}},async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).send(a.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).send(a.formatErrorResponse("Queries array cannot be empty",400));const o=await m(r),s=r.headers["x-cache-control"]==="no-cache";return await a.handleBatchRequest(e,o,u,{skipCache:s})}catch(e){return r.log.error(e,"Batch execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),n.get(`${g}/meta`,async(r,t)=>{try{const e=u.getMetadata();return a.formatMetaResponse(e)}catch(e){return r.log.error(e,"Metadata error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),n.post(`${g}/sql`,{bodyLimit:C,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=await m(r),s=u.validateQuery(e);if(!s.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const i=e.measures?.[0]||e.dimensions?.[0];if(!i)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const c=i.split(".")[0],p=await u.generateSQL(c,e,o);return a.formatSqlResponse(e,p)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.get(`${g}/sql`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r),i=u.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=o.measures?.[0]||o.dimensions?.[0];if(!c)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const p=c.split(".")[0],P=await u.generateSQL(p,o,s);return a.formatSqlResponse(o,P)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.post(`${g}/dry-run`,{bodyLimit:C,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r);return await a.handleDryRun(o,s,u)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.get(`${g}/dry-run`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r);return await a.handleDryRun(o,s,u)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.post(`${g}/explain`,{bodyLimit:C,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=e.options||{},i=await m(r),c=u.validateQuery(o);return c.isValid?await u.explainQuery(o,i,s):t.status(400).send({error:`Query validation failed: ${c.errors.join(", ")}`})}catch(e){return r.log.error(e,"Explain error"),t.status(500).send({error:e instanceof Error?e.message:"Explain query failed"})}}),b&&n.post(`${g}/agent/chat`,{bodyLimit:C,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-C3hT7g2W.cjs")),o=r.body,{message:s,sessionId:i,history:c}=o;if(!s||typeof s!="string")return t.status(400).send({error:"message is required and must be a string"});let p=(b.apiKey||"").trim();if(b.allowClientApiKey){const h=r.headers["x-agent-api-key"];h&&(p=h.trim())}if(!p)return t.status(401).send({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const P=b.allowClientApiKey?r.headers["x-agent-provider"]:void 0,y=b.allowClientApiKey?r.headers["x-agent-model"]:void 0,E=b.allowClientApiKey?r.headers["x-agent-provider-endpoint"]:void 0,w=await m(r),R=b.buildSystemContext?.(w);t.raw.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const h=e({message:s,sessionId:i,history:c,semanticLayer:u,securityContext:w,agentConfig:b,apiKey:p,systemContext:R,providerOverride:P,modelOverride:y,baseURLOverride:E});for await(const x of h)t.raw.write(`data: ${JSON.stringify(x)}
|
|
2
2
|
|
|
3
3
|
`)}catch(h){const x={type:"error",data:{message:h instanceof Error?h.message:"Stream failed"}};t.raw.write(`data: ${JSON.stringify(x)}
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as H, v as D, b as E, a as T, n as V, p as z, w as _, d as B, i as j, M as F, c as U, e as P, s as Q } from "../mcp-transport-
|
|
1
|
+
import { S as H, v as D, b as E, a as T, n as V, p as z, w as _, d as B, i as j, M as F, c as U, e as P, s as Q } from "../mcp-transport-B6ZudTSk.js";
|
|
2
2
|
import { formatErrorResponse as d, formatCubeResponse as A, handleBatchRequest as X, formatMetaResponse as G, formatSqlResponse as I, handleDryRun as O } from "../utils.js";
|
|
3
3
|
const M = function(i, N, $) {
|
|
4
4
|
const {
|
|
@@ -262,7 +262,7 @@ const M = function(i, N, $) {
|
|
|
262
262
|
}
|
|
263
263
|
}, async (t, r) => {
|
|
264
264
|
try {
|
|
265
|
-
const { handleAgentChat: e } = await import("../handler-
|
|
265
|
+
const { handleAgentChat: e } = await import("../handler-t7Qd1IYi.js"), a = t.body, { message: n, sessionId: o, history: s } = a;
|
|
266
266
|
if (!n || typeof n != "string")
|
|
267
267
|
return r.status(400).send({ error: "message is required and must be a string" });
|
|
268
268
|
let u = (h.apiKey || "").trim();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("./mcp-transport-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("./mcp-transport-DCiSGtp1.cjs"),j=require("./utils.cjs");function Z(d){if(d.length===0)return"No cubes are currently available.";const s=["## Available Cubes",""];for(const a of d){if(s.push(`### ${a.name}`),a.description&&s.push(a.description),a.measures&&a.measures.length>0){s.push(""),s.push("**Measures:**");for(const o of a.measures){const y=o.description?` - ${o.description}`:"";s.push(`- \`${a.name}.${o.name}\` (${o.type})${y}`)}}if(a.dimensions&&a.dimensions.length>0){s.push(""),s.push("**Dimensions:**");for(const o of a.dimensions){const y=o.description?` - ${o.description}`:"";s.push(`- \`${a.name}.${o.name}\` (${o.type})${y}`)}}if(a.relationships&&a.relationships.length>0){s.push(""),s.push("**Joins:**");for(const o of a.relationships)s.push(`- → \`${o.targetCube}\` (${o.relationship})`)}a.meta?.eventStream&&(s.push(""),s.push("**Event Stream:** Yes (supports funnel, flow, retention queries)"),a.meta.eventStream.bindingKey&&s.push(`- Binding key: \`${a.name}.${a.meta.eventStream.bindingKey}\``),a.meta.eventStream.timeDimension&&s.push(`- Time dimension: \`${a.name}.${a.meta.eventStream.timeDimension}\``)),s.push("")}return s.join(`
|
|
2
2
|
`)}function M(d){return d.messages.map(s=>s.content.text).join(`
|
|
3
3
|
|
|
4
4
|
`)}function G(d){return["# Drizzle Cube Analytics Agent","","You are an analytics agent that helps users explore and visualize data.","You have access to a semantic layer with cubes (data models) that you can query.","","## Your Workflow","","For EACH insight, follow this cycle — do NOT batch all queries first:","","1. **Discover** available cubes using `discover_cubes` (once at the start)","2. **For each analysis point**, repeat this cycle:"," a. `execute_query` — get the data"," b. `add_markdown` — explain the results and insight"," c. `add_portlet` — visualize the results","","Call all three (query → markdown → portlet) in a single turn before moving on to the next analysis.","Do NOT run multiple queries first and add charts later — the user sees results in real-time.","","## Important Guidelines","","- ALWAYS discover cubes first before attempting queries","- Field names MUST be `CubeName.fieldName` with a DOT separator (e.g. `PullRequests.count`, `Teams.name`). NEVER use underscores, NEVER use just the cube name as a field — `PullRequests.PullRequests` and `Teams_count` are WRONG.","- Order keys MUST be one of the measures or dimensions already listed in that query. You CANNOT order by a field that is not in measures or dimensions — add it to measures first, or remove it from order.","- After EVERY `execute_query`, IMMEDIATELY call `add_markdown` and `add_portlet` in the SAME turn — never defer visualizations to a later turn","- Choose appropriate chart types: bar for categories, line for trends, table for detailed data","- If a query fails, explain the error and try an alternative approach","","## Output Format Rules","","### CRITICAL: Always think before acting","- EVERY single turn MUST begin with a text message (1-2 sentences) BEFORE any tool calls. This is your #1 rule — never violate it.","- This applies to EVERY turn, including turns where you are adding visualizations or explanations to the notebook.",`- Even when adding multiple charts in sequence, each turn must start with a brief status like "Now I'll chart the productivity breakdown." or "Next, let me show the department comparison."`,'- Example good turn: "Let me see what data is available." → discover_cubes',`- Example good turn: "I'll add a chart showing the top employees." → add_markdown → add_portlet`,"- Example bad turn: (no text) → add_portlet ← NEVER do this","","### Text vs Notebook","- ALL analysis, findings, methodology, and insights MUST go through `add_markdown` tool calls — never in your text responses","- Your text responses must be 1-2 short sentences (under 50 words) summarizing what you are about to do next — status updates only","- Never use markdown formatting (headers, bullets, bold, code blocks) in text responses — plain sentences only","- Write text responses as a friendly analyst would — use plain business language the user understands",'- NEVER mention internal terms like "cube", "query syntax", "field names", "measures", "dimensions", "portlet", "prefix format", or tool names in text responses','- Instead of "Let me correct the query syntax and retry" → "Let me fix that and try again"',`- Instead of "I'll query the PullRequests cube" → "I'll look at the pull request data"`,`- Instead of "Adding a portlet with the results" → "Here's a chart of the results"`,"","### Notebook content rules","- Before each `add_portlet`, ALWAYS call `add_markdown` first to explain WHY you are adding this visualization and what it shows","- Before calling `add_portlet`, verify the query is valid: all fields in `order` must also appear in `measures` or `dimensions`",'- Never put data tables in markdown blocks — use `add_portlet` with chartType "table" instead',"- Think out loud in the notebook: use `add_markdown` to share your reasoning at each step so users can follow along","- NEVER use emojis in text responses or markdown content — no 📊, 📈, ✅, 🔍, etc. Write in plain, professional language.","","## Chart Selection Guide","","Choose the chart type that best communicates the answer to the user's question. Think about what the data represents and what insight the user needs — do NOT default to the first option in this table. Consider the number of data points, whether values are categorical or temporal, and whether the user is comparing, trending, or summarizing.","","| Intent / Data Shape | Chart Type |","|---|---|","| Compare discrete categories or rankings | `bar` |","| Trend over time (one or few series) | `line` |","| Trend over time showing volume/magnitude | `area` |","| Part-of-whole breakdown | `pie` (≤7 slices) |","| Correlation between two measures | `scatter` |","| Correlation with size/color third dimension | `bubble` |","| Intensity across two categorical dimensions | `heatmap` |","| Multi-variable comparison across categories | `radar` |","| Distribution/spread of values | `boxPlot` |","| Detailed row-level data or many columns | `table` |","| Single headline number — ONLY when user explicitly asks for a KPI card or single number | `kpiNumber` |","| Headline metric with period-over-period change — ONLY when user asks about change in a single metric | `kpiDelta` |","","Analysis-mode-specific chart types (require the corresponding analysis mode):","","| Analysis Mode | Chart Type | Description |","|---|---|---|","| Funnel | `funnel` | Sequential step conversion bars with conversion rates |","| Flow | `sankey` | Flow diagram showing paths between states/steps |","| Flow | `sunburst` | Radial rings showing forward paths from a starting event |","| Retention | `retentionHeatmap` | Cohort × period retention matrix |","| Retention | `retentionCombined` | Retention with line chart, heatmap, or combined modes |","",'**Chart selection priorities:** Default to `bar` for categories, `line` for time series, `table` for exploratory data. Use `kpiNumber`/`kpiDelta` only as a last resort — they are appropriate only when the user explicitly asks for a single headline number or KPI card. If the query returns multiple rows or the user asks a general question like "show me revenue", prefer `bar` or `table` over `kpiNumber`.',"","## Chart Axis Configuration Rules","","**Bar charts MUST have an xAxis.** Put a dimension in `chartConfig.xAxis` so bars have category labels. If your query has no dimensions, add one or use `table` instead.","","**Never duplicate xAxis in series.** Putting the same dimension in both `xAxis` and `series` creates a sparse, broken-looking chart. The `series` field is ONLY for splitting bars into grouped/stacked sub-series by a SECOND dimension.","","Correct bar chart examples:",'- Categories only: `xAxis: ["Cube.category"], yAxis: ["Cube.count"]` — no series needed','- Grouped bars: `xAxis: ["Cube.category"], yAxis: ["Cube.count"], series: ["Cube.status"]` — series is a DIFFERENT dimension','- Multiple measures: `xAxis: ["Cube.category"], yAxis: ["Cube.count", "Cube.total"]` — each measure becomes a bar group',"","Wrong:",'- `xAxis: [], yAxis: ["Cube.avg1", "Cube.avg2"]` — missing xAxis, bars have no labels','- `xAxis: ["Cube.size"], series: ["Cube.size"]` — same field in both, creates sparse chart',"","## Analysis Mode Decision Tree","","The default mode is **query** (standard measures/dimensions). Switch to a special mode only when the user's question matches:","",'- **Funnel mode** — "What is the conversion rate from step A → B → C?"'," - Requires: an event-stream cube with `capabilities.funnel = true` from `discover_cubes`"," - Execute: `execute_query` with `funnel` param:",' `{ bindingKey: "Events.userId", timeDimension: "Events.timestamp", steps: [{ name: "Signup", filter: { member: "Events.eventName", operator: "equals", values: ["signup"] }}, { name: "Purchase", filter: { member: "Events.eventName", operator: "equals", values: ["purchase"] }}] }`',' - Visualize: `add_portlet` with `chartType: "funnel"` and `query` as JSON string containing `{ "funnel": { ... } }`',"",'- **Flow mode** — "What paths do users take after signup?"'," - Requires: `capabilities.flow = true` from `discover_cubes`"," - Execute: `execute_query` with `flow` param:",' `{ bindingKey: "Events.userId", timeDimension: "Events.timestamp", eventDimension: "Events.eventName", startingStep: { name: "Signup", filter: { member: "Events.eventName", operator: "equals", values: ["signup"] }}, stepsBefore: 0, stepsAfter: 3 }`',' - Visualize: `add_portlet` with `chartType: "sankey"` (or `"sunburst"`) and `query` as JSON string containing `{ "flow": { ... } }`',"",'- **Retention mode** — "What % of users come back after 7 days?"'," - Requires: `capabilities.retention = true` from `discover_cubes`"," - Execute: `execute_query` with `retention` param:",' `{ timeDimension: "Events.timestamp", bindingKey: "Events.userId", dateRange: { start: "2024-01-01", end: "2024-03-31" }, granularity: "week", periods: 8, retentionType: "classic" }`',' - Visualize: `add_portlet` with `chartType: "retentionCombined"` (or `"retentionHeatmap"`) and `query` as JSON string containing `{ "retention": { ... } }`',"","Before using funnel/flow/retention, check the `capabilities` object returned by `discover_cubes`. If the required capability is `false`, explain to the user that the data model does not support that analysis mode.","","Event-stream cubes are marked in the Available Cubes section below with **Event Stream: Yes** and list their binding key and time dimension.","","---","",M(I.MCP_GUIDE_PROMPT),"","---","",M(I.QUERY_RULES_PROMPT),"","---","",M(I.QUERY_BUILDING_PROMPT),"","---","",M(I.DATE_FILTERING_PROMPT),"","---","","## Save as Dashboard","","When the user asks to save, export, or convert the notebook into a dashboard, use the `save_as_dashboard` tool.","","### Layout Rules","- Dashboard grid is 12 columns wide","- KPI cards: w=3, h=3 — place at the top in a row of 4","- Overview charts (bar, line, area): w=6, h=4","- Wide charts (heatmap, table): w=12, h=5","- Section headers (markdown): w=12, h=1","","### Section Headers",'Use `chartType: "markdown"` portlets as section headers to organize the dashboard:',"```json","{",' "id": "header-overview",',' "title": "Overview",',' "chartType": "markdown",',' "displayConfig": {',' "content": "## Overview",',' "hideHeader": true,',' "transparentBackground": true,',' "autoHeight": true'," },",' "w": 12, "h": 1, "x": 0, "y": 0',"}","```","","### Dashboard Filters","- ALWAYS include a universal date filter with `isUniversalTime: true`","- Add dimension filters for key fields used across portlets (e.g., department, status, region)",'- Use human-readable labels (e.g., "Department" not "Employees.departmentName")',"- Map filters to portlets using `dashboardFilterMapping` — list the filter IDs that apply","- When promoting a hardcoded filter to a dashboard filter, REMOVE that filter from the portlet query","","### Analysis Types",'- Standard query portlets: `analysisType: "query"` (default)','- Funnel portlets: `analysisType: "funnel"`, query contains `{ "funnel": {...} }`, chartType `"funnel"`','- Flow portlets: `analysisType: "flow"`, query contains `{ "flow": {...} }`, chartType `"sankey"` or `"sunburst"`','- Retention portlets: `analysisType: "retention"`, query contains `{ "retention": {...} }`, chartType `"retentionHeatmap"` or `"retentionCombined"`',"","### CRITICAL: Only use portlets from the notebook","- ONLY include portlets that you already added to the notebook via `add_portlet` during this conversation","- Do NOT invent new queries or charts that were not part of the analysis — the dashboard is a direct conversion of the notebook","- Reuse the exact same queries, chart types, and chart configs from the notebook portlets","- Arrange the existing portlets in a sensible layout (KPIs at top, charts in middle, tables at bottom)","- You may add section header markdown portlets to organize the layout, but do not add new data portlets","","---","",Z(d)].join(`
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("hono"),a=require("../mcp-transport-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const M=require("hono"),a=require("../mcp-transport-DCiSGtp1.cjs"),R=require("../utils.cjs");var L=g=>{const y={...{origin:"*",allowMethods:["GET","HEAD","PUT","POST","DELETE","PATCH"],allowHeaders:[],exposeHeaders:[]},...g},T=(d=>typeof d=="string"?d==="*"?()=>d:c=>d===c?c:null:typeof d=="function"?d:c=>d.includes(c)?c:null)(y.origin),h=(d=>typeof d=="function"?d:Array.isArray(d)?()=>d:()=>[])(y.allowMethods);return async function(c,q){function b(f,i){c.res.headers.set(f,i)}const E=await T(c.req.header("origin")||"",c);if(E&&b("Access-Control-Allow-Origin",E),y.credentials&&b("Access-Control-Allow-Credentials","true"),y.exposeHeaders?.length&&b("Access-Control-Expose-Headers",y.exposeHeaders.join(",")),c.req.method==="OPTIONS"){y.origin!=="*"&&b("Vary","Origin"),y.maxAge!=null&&b("Access-Control-Max-Age",y.maxAge.toString());const f=await h(c.req.header("origin")||"",c);f.length&&b("Access-Control-Allow-Methods",f.join(","));let i=y.allowHeaders;if(!i?.length){const s=c.req.header("Access-Control-Request-Headers");s&&(i=s.split(/\s*,\s*/))}return i?.length&&(b("Access-Control-Allow-Headers",i.join(",")),c.res.headers.append("Vary","Access-Control-Request-Headers")),c.res.headers.delete("Content-Length"),c.res.headers.delete("Content-Type"),new Response(null,{headers:c.res.headers,status:204,statusText:"No Content"})}await q(),y.origin!=="*"&&c.header("Vary","Origin",{append:!0})}};function N(g){const{cubes:j,drizzle:y,schema:T,extractSecurityContext:h,engineType:d,cors:c,basePath:q="/cubejs-api/v1",cache:b,mcp:E={enabled:!0},agent:f}=g;if(!g.semanticLayer&&(!j||j.length===0))throw new Error("Either semanticLayer or a non-empty cubes array must be provided");const i=new M.Hono;c&&i.use("/*",L(c));const s=g.semanticLayer??new a.SemanticLayerCompiler({drizzle:y,schema:T,engineType:d,cache:b});if(!g.semanticLayer&&j&&j.forEach(e=>{s.registerCube(e)}),i.post(`${q}/load`,async e=>{try{const r=await e.req.json(),n=r.query||r,o=await h(e),t=s.validateQuery(n);if(!t.isValid)return e.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=e.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return e.json(R.formatCubeResponse(n,l,s))}catch(r){return console.error("Query execution error:",r),e.json({error:r instanceof Error?r.message:"Query execution failed"},500)}}),i.get(`${q}/load`,async e=>{try{const r=e.req.query("query");if(!r)return e.json({error:"Query parameter is required"},400);let n;try{n=JSON.parse(r)}catch{return e.json({error:"Invalid JSON in query parameter"},400)}const o=await h(e),t=s.validateQuery(n);if(!t.isValid)return e.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=e.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return e.json(R.formatCubeResponse(n,l,s))}catch(r){return console.error("Query execution error:",r),e.json({error:r instanceof Error?r.message:"Query execution failed"},500)}}),i.post(`${q}/batch`,async e=>{try{const r=await e.req.json(),{queries:n}=r;if(!n||!Array.isArray(n))return e.json({error:'Request body must contain a "queries" array'},400);if(n.length===0)return e.json({error:"Queries array cannot be empty"},400);const o=await h(e),t=e.req.header("x-cache-control")==="no-cache",u=await R.handleBatchRequest(n,o,s,{skipCache:t});return e.json(u)}catch(r){return console.error("Batch execution error:",r),e.json({error:r instanceof Error?r.message:"Batch execution failed"},500)}}),i.get(`${q}/meta`,e=>{try{const r=s.getMetadata();return e.json(R.formatMetaResponse(r))}catch(r){return console.error("Metadata error:",r),e.json({error:r instanceof Error?r.message:"Failed to fetch metadata"},500)}}),i.post(`${q}/sql`,async e=>{try{const r=await e.req.json(),n=await h(e),o=s.validateQuery(r);if(!o.isValid)return e.json({error:`Query validation failed: ${o.errors.join(", ")}`},400);const t=r.measures?.[0]||r.dimensions?.[0];if(!t)return e.json({error:"No measures or dimensions specified"},400);const u=t.split(".")[0],l=await s.generateSQL(u,r,n);return e.json(R.formatSqlResponse(r,l))}catch(r){return console.error("SQL generation error:",r),e.json({error:r instanceof Error?r.message:"SQL generation failed"},500)}}),i.get(`${q}/sql`,async e=>{try{const r=e.req.query("query");if(!r)return e.json({error:"Query parameter is required"},400);const n=JSON.parse(r),o=await h(e),t=s.validateQuery(n);if(!t.isValid)return e.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=n.measures?.[0]||n.dimensions?.[0];if(!u)return e.json({error:"No measures or dimensions specified"},400);const l=u.split(".")[0],v=await s.generateSQL(l,n,o);return e.json(R.formatSqlResponse(n,v))}catch(r){return console.error("SQL generation error:",r),e.json({error:r instanceof Error?r.message:"SQL generation failed"},500)}}),i.post(`${q}/dry-run`,async e=>{try{const r=await e.req.json(),n=r.query||r,o=await h(e),t=await R.handleDryRun(n,o,s);return e.json(t)}catch(r){return console.error("Dry-run error:",r),e.json({error:r instanceof Error?r.message:"Dry-run validation failed",valid:!1},400)}}),i.get(`${q}/dry-run`,async e=>{try{const r=e.req.query("query");if(!r)return e.json({error:"Query parameter is required",valid:!1},400);const n=JSON.parse(r),o=await h(e),t=await R.handleDryRun(n,o,s);return e.json(t)}catch(r){return console.error("Dry-run error:",r),e.json({error:r instanceof Error?r.message:"Dry-run validation failed",valid:!1},400)}}),i.post(`${q}/explain`,async e=>{try{const r=await e.req.json(),n=r.query||r,o=r.options||{},t=await h(e),u=s.validateQuery(n);if(!u.isValid)return e.json({error:`Query validation failed: ${u.errors.join(", ")}`},400);const l=await s.explainQuery(n,t,o);return e.json(l)}catch(r){return console.error("Explain error:",r),e.json({error:r instanceof Error?r.message:"Explain query failed"},500)}}),f&&i.post(`${q}/agent/chat`,async e=>{try{const{handleAgentChat:r}=await Promise.resolve().then(()=>require("../handler-C3hT7g2W.cjs")),n=await e.req.json(),{message:o,sessionId:t,history:u}=n;if(!o||typeof o!="string")return e.json({error:"message is required and must be a string"},400);let l=(f.apiKey||"").trim();if(f.allowClientApiKey){const C=e.req.header("x-agent-api-key");C&&(l=C.trim())}if(!l)return e.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},401);const v=f.allowClientApiKey?e.req.header("x-agent-provider"):void 0,Q=f.allowClientApiKey?e.req.header("x-agent-model"):void 0,m=f.allowClientApiKey?e.req.header("x-agent-provider-endpoint"):void 0,P=await h(e),H=f.buildSystemContext?.(P),p=new TextEncoder,A=new ReadableStream({async start(C){try{const w=r({message:o,sessionId:t,history:u,semanticLayer:s,securityContext:P,agentConfig:f,apiKey:l,systemContext:H,providerOverride:v,modelOverride:Q,baseURLOverride:m});for await(const x of w){const S=`data: ${JSON.stringify(x)}
|
|
2
2
|
|
|
3
|
-
`;
|
|
3
|
+
`;C.enqueue(p.encode(S))}}catch(w){const x={type:"error",data:{message:w instanceof Error?w.message:"Stream failed"}};C.enqueue(p.encode(`data: ${JSON.stringify(x)}
|
|
4
4
|
|
|
5
|
-
`))}finally{
|
|
5
|
+
`))}finally{C.close()}}});return new Response(A,{status:200,headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})}catch(r){return console.error("Agent chat error:",r),e.json({error:r instanceof Error?r.message:"Agent chat failed"},500)}}),E.enabled!==!1){const e={uri:"drizzle-cube://schema",name:"Cube Schema",description:"Current cube metadata as JSON",mimeType:"application/json",text:JSON.stringify(s.getMetadata(),null,2)},r=[...a.getDefaultResources(),e],n=a.getDefaultPrompts(),o=E.basePath??"/mcp";i.post(`${o}`,async t=>{const u=a.validateOriginHeader(t.req.header("origin"),E.allowedOrigins?{allowedOrigins:E.allowedOrigins}:{});if(!u.valid)return t.json(a.buildJsonRpcError(null,-32600,u.reason),403);const l=t.req.header("accept");if(!a.validateAcceptHeader(l))return t.json(a.buildJsonRpcError(null,-32600,"Accept header must include both application/json and text/event-stream"),400);const v=a.negotiateProtocol(t.req.header());if(!v.ok)return t.json({error:"Unsupported MCP protocol version",supported:v.supported},426);const Q=await t.req.json().catch(()=>null),m=a.parseJsonRpc(Q);if(!m)return t.json(a.buildJsonRpcError(null,-32600,"Invalid JSON-RPC 2.0 request"),400);const P=a.wantsEventStream(l),H=m.method==="initialize";try{const p=await a.dispatchMcpMethod(m.method,m.params,{semanticLayer:s,extractSecurityContext:h,rawRequest:t,rawResponse:null,negotiatedProtocol:v.negotiated,resources:r,prompts:n});if(a.isNotification(m))return t.body(null,202);const A=a.buildJsonRpcResult(m.id??null,p),C=H&&p&&typeof p=="object"&&"sessionId"in p?p.sessionId:void 0,w={};if(C&&(w[a.MCP_SESSION_ID_HEADER]=C),P){const x=new TextEncoder,S=a.primeEventId(),O=new ReadableStream({start($){$.enqueue(x.encode(`id: ${S}
|
|
6
6
|
|
|
7
|
-
`)),$.enqueue(
|
|
7
|
+
`)),$.enqueue(x.encode(a.serializeSseEvent(A,S))),$.close()}});return new Response(O,{status:200,headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive",...w}})}return t.json(A,200,w)}catch(p){if(a.isNotification(m))return console.error("MCP notification processing error:",p),t.body(null,202);console.error("MCP RPC error:",p);const A=p?.code??-32603,C=p?.data,w=p.message||"MCP request failed",x=a.buildJsonRpcError(m.id??null,A,w,C);if(P){const S=new TextEncoder,O=a.primeEventId(),$=new ReadableStream({start(I){I.enqueue(S.encode(`id: ${O}
|
|
8
8
|
|
|
9
|
-
`)),I.enqueue(S.encode(a.serializeSseEvent(
|
|
9
|
+
`)),I.enqueue(S.encode(a.serializeSseEvent(x,O))),I.close()}});return new Response($,{status:200,headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})}return t.json(x,200)}}),i.delete(`${o}`,t=>t.json({error:"Session termination not supported"},405)),i.get(`${o}`,t=>{const u=new TextEncoder,l=a.primeEventId();let v;const Q=new ReadableStream({start(m){m.enqueue(u.encode(a.serializeSseEvent({jsonrpc:"2.0",method:"mcp/ready",params:{protocol:"streamable-http"}},l,15e3))),v=setInterval(()=>{m.enqueue(u.encode(`: keep-alive
|
|
10
10
|
|
|
11
|
-
`))},15e3)},cancel(){clearInterval(
|
|
11
|
+
`))},15e3)},cancel(){clearInterval(v)}});return new Response(Q,{status:200,headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})})}return i}function J(g,j){const y=N(j);return g.route("/",y),g}function D(g){const j=new M.Hono;return J(j,g)}exports.createCubeApp=D;exports.createCubeRoutes=N;exports.mountCubeRoutes=J;
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { SemanticQuery, SecurityContext, DatabaseExecutor, DrizzleDatabase, Cube, CacheConfig } from '../../server';
|
|
3
3
|
import { AgentConfig } from '../../server/agent/types';
|
|
4
|
+
import { SemanticLayerCompiler } from '../../server/compiler';
|
|
4
5
|
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
5
6
|
import { MySql2Database } from 'drizzle-orm/mysql2';
|
|
6
7
|
import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
|
7
8
|
import { MCPOptions } from '../utils';
|
|
8
9
|
export interface HonoAdapterOptions {
|
|
9
10
|
/**
|
|
10
|
-
* Array of cube definitions to register
|
|
11
|
+
* Array of cube definitions to register.
|
|
12
|
+
* Optional when `semanticLayer` is provided (caller manages registration).
|
|
11
13
|
*/
|
|
12
|
-
cubes
|
|
14
|
+
cubes?: Cube[];
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Accepts PostgreSQL, MySQL, or SQLite database instances
|
|
16
|
+
* Pre-configured SemanticLayerCompiler instance.
|
|
17
|
+
* When provided, skips creating a new compiler and cube registration (caller manages it).
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
+
semanticLayer?: SemanticLayerCompiler;
|
|
20
|
+
/**
|
|
21
|
+
* Drizzle database instance.
|
|
22
|
+
* Required unless `semanticLayer` is provided.
|
|
23
|
+
* Accepts PostgreSQL, MySQL, or SQLite database instances.
|
|
24
|
+
*/
|
|
25
|
+
drizzle?: PostgresJsDatabase<any> | MySql2Database<any> | BetterSQLite3Database<any> | DrizzleDatabase;
|
|
19
26
|
/**
|
|
20
27
|
* Database schema for type inference (RECOMMENDED)
|
|
21
28
|
* Provides full type safety for cube definitions
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Hono as
|
|
2
|
-
import { S as K, v as V, b as $, a as B, n as z, p as _, w as U, d as F, g as G, f as X, i as I, c as W, M as Y, e as N, s as T } from "../mcp-transport-
|
|
3
|
-
import { formatCubeResponse as
|
|
4
|
-
var re = (
|
|
1
|
+
import { Hono as J } from "hono";
|
|
2
|
+
import { S as K, v as V, b as $, a as B, n as z, p as _, w as U, d as F, g as G, f as X, i as I, c as W, M as Y, e as N, s as T } from "../mcp-transport-B6ZudTSk.js";
|
|
3
|
+
import { formatCubeResponse as L, handleBatchRequest as Z, formatMetaResponse as ee, formatSqlResponse as k, handleDryRun as D } from "../utils.js";
|
|
4
|
+
var re = (g) => {
|
|
5
5
|
const d = {
|
|
6
6
|
...{
|
|
7
7
|
origin: "*",
|
|
@@ -9,23 +9,23 @@ var re = (j) => {
|
|
|
9
9
|
allowHeaders: [],
|
|
10
10
|
exposeHeaders: []
|
|
11
11
|
},
|
|
12
|
-
...
|
|
12
|
+
...g
|
|
13
13
|
}, M = /* @__PURE__ */ ((l) => typeof l == "string" ? l === "*" ? () => l : (i) => l === i ? i : null : typeof l == "function" ? l : (i) => l.includes(i) ? i : null)(d.origin), m = ((l) => typeof l == "function" ? l : Array.isArray(l) ? () => l : () => [])(d.allowMethods);
|
|
14
14
|
return async function(i, h) {
|
|
15
|
-
function
|
|
15
|
+
function j(p, a) {
|
|
16
16
|
i.res.headers.set(p, a);
|
|
17
17
|
}
|
|
18
18
|
const b = await M(i.req.header("origin") || "", i);
|
|
19
|
-
if (b &&
|
|
20
|
-
d.origin !== "*" &&
|
|
19
|
+
if (b && j("Access-Control-Allow-Origin", b), d.credentials && j("Access-Control-Allow-Credentials", "true"), d.exposeHeaders?.length && j("Access-Control-Expose-Headers", d.exposeHeaders.join(",")), i.req.method === "OPTIONS") {
|
|
20
|
+
d.origin !== "*" && j("Vary", "Origin"), d.maxAge != null && j("Access-Control-Max-Age", d.maxAge.toString());
|
|
21
21
|
const p = await m(i.req.header("origin") || "", i);
|
|
22
|
-
p.length &&
|
|
22
|
+
p.length && j("Access-Control-Allow-Methods", p.join(","));
|
|
23
23
|
let a = d.allowHeaders;
|
|
24
24
|
if (!a?.length) {
|
|
25
25
|
const s = i.req.header("Access-Control-Request-Headers");
|
|
26
26
|
s && (a = s.split(/\s*,\s*/));
|
|
27
27
|
}
|
|
28
|
-
return a?.length && (
|
|
28
|
+
return a?.length && (j("Access-Control-Allow-Headers", a.join(",")), i.res.headers.append("Vary", "Access-Control-Request-Headers")), i.res.headers.delete("Content-Length"), i.res.headers.delete("Content-Type"), new Response(null, {
|
|
29
29
|
headers: i.res.headers,
|
|
30
30
|
status: 204,
|
|
31
31
|
statusText: "No Content"
|
|
@@ -34,30 +34,30 @@ var re = (j) => {
|
|
|
34
34
|
await h(), d.origin !== "*" && i.header("Vary", "Origin", { append: !0 });
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
|
-
function te(
|
|
37
|
+
function te(g) {
|
|
38
38
|
const {
|
|
39
|
-
cubes:
|
|
39
|
+
cubes: v,
|
|
40
40
|
drizzle: d,
|
|
41
41
|
schema: M,
|
|
42
42
|
extractSecurityContext: m,
|
|
43
43
|
engineType: l,
|
|
44
44
|
cors: i,
|
|
45
45
|
basePath: h = "/cubejs-api/v1",
|
|
46
|
-
cache:
|
|
46
|
+
cache: j,
|
|
47
47
|
mcp: b = { enabled: !0 },
|
|
48
48
|
agent: p
|
|
49
|
-
} =
|
|
50
|
-
if (!
|
|
51
|
-
throw new Error("
|
|
52
|
-
const a = new
|
|
49
|
+
} = g;
|
|
50
|
+
if (!g.semanticLayer && (!v || v.length === 0))
|
|
51
|
+
throw new Error("Either semanticLayer or a non-empty cubes array must be provided");
|
|
52
|
+
const a = new J();
|
|
53
53
|
i && a.use("/*", re(i));
|
|
54
|
-
const s = new K({
|
|
54
|
+
const s = g.semanticLayer ?? new K({
|
|
55
55
|
drizzle: d,
|
|
56
56
|
schema: M,
|
|
57
57
|
engineType: l,
|
|
58
|
-
cache:
|
|
58
|
+
cache: j
|
|
59
59
|
});
|
|
60
|
-
if (
|
|
60
|
+
if (!g.semanticLayer && v && v.forEach((e) => {
|
|
61
61
|
s.registerCube(e);
|
|
62
62
|
}), a.post(`${h}/load`, async (e) => {
|
|
63
63
|
try {
|
|
@@ -67,7 +67,7 @@ function te(j) {
|
|
|
67
67
|
error: `Query validation failed: ${t.errors.join(", ")}`
|
|
68
68
|
}, 400);
|
|
69
69
|
const c = e.req.header("x-cache-control") === "no-cache", u = await s.executeMultiCubeQuery(n, o, { skipCache: c });
|
|
70
|
-
return e.json(
|
|
70
|
+
return e.json(L(n, u, s));
|
|
71
71
|
} catch (r) {
|
|
72
72
|
return console.error("Query execution error:", r), e.json({
|
|
73
73
|
error: r instanceof Error ? r.message : "Query execution failed"
|
|
@@ -94,7 +94,7 @@ function te(j) {
|
|
|
94
94
|
error: `Query validation failed: ${t.errors.join(", ")}`
|
|
95
95
|
}, 400);
|
|
96
96
|
const c = e.req.header("x-cache-control") === "no-cache", u = await s.executeMultiCubeQuery(n, o, { skipCache: c });
|
|
97
|
-
return e.json(
|
|
97
|
+
return e.json(L(n, u, s));
|
|
98
98
|
} catch (r) {
|
|
99
99
|
return console.error("Query execution error:", r), e.json({
|
|
100
100
|
error: r instanceof Error ? r.message : "Query execution failed"
|
|
@@ -140,7 +140,7 @@ function te(j) {
|
|
|
140
140
|
error: "No measures or dimensions specified"
|
|
141
141
|
}, 400);
|
|
142
142
|
const c = t.split(".")[0], u = await s.generateSQL(c, r, n);
|
|
143
|
-
return e.json(
|
|
143
|
+
return e.json(k(r, u));
|
|
144
144
|
} catch (r) {
|
|
145
145
|
return console.error("SQL generation error:", r), e.json({
|
|
146
146
|
error: r instanceof Error ? r.message : "SQL generation failed"
|
|
@@ -163,8 +163,8 @@ function te(j) {
|
|
|
163
163
|
return e.json({
|
|
164
164
|
error: "No measures or dimensions specified"
|
|
165
165
|
}, 400);
|
|
166
|
-
const u = c.split(".")[0],
|
|
167
|
-
return e.json(
|
|
166
|
+
const u = c.split(".")[0], w = await s.generateSQL(u, n, o);
|
|
167
|
+
return e.json(k(n, w));
|
|
168
168
|
} catch (r) {
|
|
169
169
|
return console.error("SQL generation error:", r), e.json({
|
|
170
170
|
error: r instanceof Error ? r.message : "SQL generation failed"
|
|
@@ -172,7 +172,7 @@ function te(j) {
|
|
|
172
172
|
}
|
|
173
173
|
}), a.post(`${h}/dry-run`, async (e) => {
|
|
174
174
|
try {
|
|
175
|
-
const r = await e.req.json(), n = r.query || r, o = await m(e), t = await
|
|
175
|
+
const r = await e.req.json(), n = r.query || r, o = await m(e), t = await D(n, o, s);
|
|
176
176
|
return e.json(t);
|
|
177
177
|
} catch (r) {
|
|
178
178
|
return console.error("Dry-run error:", r), e.json({
|
|
@@ -188,7 +188,7 @@ function te(j) {
|
|
|
188
188
|
error: "Query parameter is required",
|
|
189
189
|
valid: !1
|
|
190
190
|
}, 400);
|
|
191
|
-
const n = JSON.parse(r), o = await m(e), t = await
|
|
191
|
+
const n = JSON.parse(r), o = await m(e), t = await D(n, o, s);
|
|
192
192
|
return e.json(t);
|
|
193
193
|
} catch (r) {
|
|
194
194
|
return console.error("Dry-run error:", r), e.json({
|
|
@@ -212,22 +212,22 @@ function te(j) {
|
|
|
212
212
|
}
|
|
213
213
|
}), p && a.post(`${h}/agent/chat`, async (e) => {
|
|
214
214
|
try {
|
|
215
|
-
const { handleAgentChat: r } = await import("../handler-
|
|
215
|
+
const { handleAgentChat: r } = await import("../handler-t7Qd1IYi.js"), n = await e.req.json(), { message: o, sessionId: t, history: c } = n;
|
|
216
216
|
if (!o || typeof o != "string")
|
|
217
217
|
return e.json({ error: "message is required and must be a string" }, 400);
|
|
218
218
|
let u = (p.apiKey || "").trim();
|
|
219
219
|
if (p.allowClientApiKey) {
|
|
220
|
-
const
|
|
221
|
-
|
|
220
|
+
const q = e.req.header("x-agent-api-key");
|
|
221
|
+
q && (u = q.trim());
|
|
222
222
|
}
|
|
223
223
|
if (!u)
|
|
224
224
|
return e.json({
|
|
225
225
|
error: "No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."
|
|
226
226
|
}, 401);
|
|
227
|
-
const
|
|
228
|
-
async start(
|
|
227
|
+
const w = p.allowClientApiKey ? e.req.header("x-agent-provider") : void 0, A = p.allowClientApiKey ? e.req.header("x-agent-model") : void 0, f = p.allowClientApiKey ? e.req.header("x-agent-provider-endpoint") : void 0, E = await m(e), O = p.buildSystemContext?.(E), y = new TextEncoder(), S = new ReadableStream({
|
|
228
|
+
async start(q) {
|
|
229
229
|
try {
|
|
230
|
-
const
|
|
230
|
+
const C = r({
|
|
231
231
|
message: o,
|
|
232
232
|
sessionId: t,
|
|
233
233
|
history: c,
|
|
@@ -236,26 +236,26 @@ function te(j) {
|
|
|
236
236
|
agentConfig: p,
|
|
237
237
|
apiKey: u,
|
|
238
238
|
systemContext: O,
|
|
239
|
-
providerOverride:
|
|
239
|
+
providerOverride: w,
|
|
240
240
|
modelOverride: A,
|
|
241
241
|
baseURLOverride: f
|
|
242
242
|
});
|
|
243
|
-
for await (const
|
|
244
|
-
const R = `data: ${JSON.stringify(
|
|
243
|
+
for await (const x of C) {
|
|
244
|
+
const R = `data: ${JSON.stringify(x)}
|
|
245
245
|
|
|
246
246
|
`;
|
|
247
|
-
|
|
247
|
+
q.enqueue(y.encode(R));
|
|
248
248
|
}
|
|
249
|
-
} catch (
|
|
250
|
-
const
|
|
249
|
+
} catch (C) {
|
|
250
|
+
const x = {
|
|
251
251
|
type: "error",
|
|
252
|
-
data: { message:
|
|
252
|
+
data: { message: C instanceof Error ? C.message : "Stream failed" }
|
|
253
253
|
};
|
|
254
|
-
|
|
254
|
+
q.enqueue(y.encode(`data: ${JSON.stringify(x)}
|
|
255
255
|
|
|
256
256
|
`));
|
|
257
257
|
} finally {
|
|
258
|
-
|
|
258
|
+
q.close();
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
});
|
|
@@ -290,11 +290,11 @@ function te(j) {
|
|
|
290
290
|
const u = t.req.header("accept");
|
|
291
291
|
if (!B(u))
|
|
292
292
|
return t.json($(null, -32600, "Accept header must include both application/json and text/event-stream"), 400);
|
|
293
|
-
const
|
|
294
|
-
if (!
|
|
293
|
+
const w = z(t.req.header());
|
|
294
|
+
if (!w.ok)
|
|
295
295
|
return t.json({
|
|
296
296
|
error: "Unsupported MCP protocol version",
|
|
297
|
-
supported:
|
|
297
|
+
supported: w.supported
|
|
298
298
|
}, 426);
|
|
299
299
|
const A = await t.req.json().catch(() => null), f = _(A);
|
|
300
300
|
if (!f)
|
|
@@ -309,20 +309,20 @@ function te(j) {
|
|
|
309
309
|
extractSecurityContext: m,
|
|
310
310
|
rawRequest: t,
|
|
311
311
|
rawResponse: null,
|
|
312
|
-
negotiatedProtocol:
|
|
312
|
+
negotiatedProtocol: w.negotiated,
|
|
313
313
|
resources: r,
|
|
314
314
|
prompts: n
|
|
315
315
|
}
|
|
316
316
|
);
|
|
317
317
|
if (I(f))
|
|
318
318
|
return t.body(null, 202);
|
|
319
|
-
const S = W(f.id ?? null, y),
|
|
320
|
-
if (
|
|
321
|
-
const
|
|
319
|
+
const S = W(f.id ?? null, y), q = O && y && typeof y == "object" && "sessionId" in y ? y.sessionId : void 0, C = {};
|
|
320
|
+
if (q && (C[Y] = q), E) {
|
|
321
|
+
const x = new TextEncoder(), R = N(), P = new ReadableStream({
|
|
322
322
|
start(Q) {
|
|
323
|
-
Q.enqueue(
|
|
323
|
+
Q.enqueue(x.encode(`id: ${R}
|
|
324
324
|
|
|
325
|
-
`)), Q.enqueue(
|
|
325
|
+
`)), Q.enqueue(x.encode(T(S, R))), Q.close();
|
|
326
326
|
}
|
|
327
327
|
});
|
|
328
328
|
return new Response(P, {
|
|
@@ -331,22 +331,22 @@ function te(j) {
|
|
|
331
331
|
"Content-Type": "text/event-stream",
|
|
332
332
|
"Cache-Control": "no-cache",
|
|
333
333
|
Connection: "keep-alive",
|
|
334
|
-
...
|
|
334
|
+
...C
|
|
335
335
|
}
|
|
336
336
|
});
|
|
337
337
|
}
|
|
338
|
-
return t.json(S, 200,
|
|
338
|
+
return t.json(S, 200, C);
|
|
339
339
|
} catch (y) {
|
|
340
340
|
if (I(f))
|
|
341
341
|
return console.error("MCP notification processing error:", y), t.body(null, 202);
|
|
342
342
|
console.error("MCP RPC error:", y);
|
|
343
|
-
const S = y?.code ?? -32603,
|
|
343
|
+
const S = y?.code ?? -32603, q = y?.data, C = y.message || "MCP request failed", x = $(f.id ?? null, S, C, q);
|
|
344
344
|
if (E) {
|
|
345
345
|
const R = new TextEncoder(), P = N(), Q = new ReadableStream({
|
|
346
346
|
start(H) {
|
|
347
347
|
H.enqueue(R.encode(`id: ${P}
|
|
348
348
|
|
|
349
|
-
`)), H.enqueue(R.encode(T(
|
|
349
|
+
`)), H.enqueue(R.encode(T(x, P))), H.close();
|
|
350
350
|
}
|
|
351
351
|
});
|
|
352
352
|
return new Response(Q, {
|
|
@@ -358,25 +358,25 @@ function te(j) {
|
|
|
358
358
|
}
|
|
359
359
|
});
|
|
360
360
|
}
|
|
361
|
-
return t.json(
|
|
361
|
+
return t.json(x, 200);
|
|
362
362
|
}
|
|
363
363
|
}), a.delete(`${o}`, (t) => t.json({ error: "Session termination not supported" }, 405)), a.get(`${o}`, (t) => {
|
|
364
364
|
const c = new TextEncoder(), u = N();
|
|
365
|
-
let
|
|
365
|
+
let w;
|
|
366
366
|
const A = new ReadableStream({
|
|
367
367
|
start(f) {
|
|
368
368
|
f.enqueue(c.encode(T({
|
|
369
369
|
jsonrpc: "2.0",
|
|
370
370
|
method: "mcp/ready",
|
|
371
371
|
params: { protocol: "streamable-http" }
|
|
372
|
-
}, u, 15e3))),
|
|
372
|
+
}, u, 15e3))), w = setInterval(() => {
|
|
373
373
|
f.enqueue(c.encode(`: keep-alive
|
|
374
374
|
|
|
375
375
|
`));
|
|
376
376
|
}, 15e3);
|
|
377
377
|
},
|
|
378
378
|
cancel() {
|
|
379
|
-
clearInterval(
|
|
379
|
+
clearInterval(w);
|
|
380
380
|
}
|
|
381
381
|
});
|
|
382
382
|
return new Response(A, {
|
|
@@ -391,13 +391,13 @@ function te(j) {
|
|
|
391
391
|
}
|
|
392
392
|
return a;
|
|
393
393
|
}
|
|
394
|
-
function ne(
|
|
395
|
-
const d = te(
|
|
396
|
-
return
|
|
394
|
+
function ne(g, v) {
|
|
395
|
+
const d = te(v);
|
|
396
|
+
return g.route("/", d), g;
|
|
397
397
|
}
|
|
398
|
-
function ie(
|
|
399
|
-
const
|
|
400
|
-
return ne(
|
|
398
|
+
function ie(g) {
|
|
399
|
+
const v = new J();
|
|
400
|
+
return ne(v, g);
|
|
401
401
|
}
|
|
402
402
|
export {
|
|
403
403
|
ie as createCubeApp,
|
|
@@ -10374,6 +10374,13 @@ ${t.join(`
|
|
|
10374
10374
|
hasCube(e) {
|
|
10375
10375
|
return this.cubes.has(e);
|
|
10376
10376
|
}
|
|
10377
|
+
/**
|
|
10378
|
+
* Unregister a cube by name.
|
|
10379
|
+
* Returns true if the cube existed and was removed, false if not found.
|
|
10380
|
+
*/
|
|
10381
|
+
unregisterCube(e) {
|
|
10382
|
+
return this.removeCube(e);
|
|
10383
|
+
}
|
|
10377
10384
|
/**
|
|
10378
10385
|
* Remove a cube
|
|
10379
10386
|
*/
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
`)}}]},ht={name:"drizzle-cube-date-filtering",description:"CRITICAL: How to correctly filter by date vs group by time period - the #1 source of query mistakes",messages:[{role:"user",content:{type:"text",text:["# Date Filtering vs Time Grouping - CRITICAL GUIDE","","This is the most common mistake when building queries. These are TWO DIFFERENT operations.","","## Quick Decision Tree","","```","User wants data over a time period?",'├── Wants AGGREGATED TOTALS (e.g., "total sales last month")',"│ └── Use: filters with inDateRange operator","│",'└── Wants TIME SERIES breakdown (e.g., "daily sales last month")'," └── Use: timeDimensions with granularity","```","","## For Aggregated Totals (MOST COMMON)","",'When user says: "last 3 months", "over the past year", "in Q1", "since January"',"","```json","{",' "measures": ["Sales.totalRevenue"],',' "dimensions": ["Products.category"],',' "filters": [',' { "member": "Sales.date", "operator": "inDateRange", "values": ["last 3 months"] }'," ]","}","```","","Result: One row per category with TOTAL revenue over the 3-month period.","","## For Time Series (Trend Analysis)","",'When user says: "by month", "per week", "daily trend", "over time"',"","```json","{",' "measures": ["Sales.totalRevenue"],',' "timeDimensions": [',' { "dimension": "Sales.date", "dateRange": "last 3 months", "granularity": "month" }'," ]","}","```","","Result: Multiple rows, one per month.","","## WRONG: timeDimensions Without Granularity","","```json","// This returns ~90 rows (daily) instead of aggregates!","{",' "timeDimensions": [{ "dimension": "Sales.date", "dateRange": "last 3 months" }]',"}","```","","## Both: Filter AND Group","",'When user wants: "monthly breakdown for last quarter"',"","```json","{",' "measures": ["Sales.totalRevenue"],',' "filters": [',' { "member": "Sales.date", "operator": "inDateRange", "values": ["last quarter"] }'," ],",' "timeDimensions": [',' { "dimension": "Sales.date", "granularity": "month" }'," ]","}","```","","## Summary Table","","| User Request | Use | Example |","|-------------|-----|---------|",'| "total for last 3 months" | filters + inDateRange | { filters: [{ operator: "inDateRange", values: ["last 3 months"] }] } |','| "top 5 last quarter" | filters + inDateRange | Same as above + order + limit |','| "monthly trend" | timeDimensions + granularity | { timeDimensions: [{ granularity: "month" }] } |','| "daily breakdown last week" | timeDimensions | { timeDimensions: [{ dateRange: "last week", granularity: "day" }] } |'].join(`
|
|
198
198
|
`)}}]},Bn=[mt,ft,pt,ht];function kn(){return Bn}class Ie{cubes=new Map;dbExecutor;metadataCache;cacheConfig;constructor(e){e?.databaseExecutor?this.dbExecutor=e.databaseExecutor:e?.drizzle&&(this.dbExecutor=Ge(e.drizzle,e.schema,e.engineType)),this.cacheConfig=e?.cache}setDatabaseExecutor(e){this.dbExecutor=e}getEngineType(){return this.dbExecutor?.getEngineType()}setDrizzle(e,t,n){this.dbExecutor=Ge(e,t,n)}hasExecutor(){return!!this.dbExecutor}requireExecutor(){if(!this.dbExecutor)throw new Error("Database executor not configured");return this.dbExecutor}createQueryExecutor(e=!1){const t=this.requireExecutor();return new Pn(t,e?this.cacheConfig:void 0)}formatSqlResult(e){const t=this.requireExecutor().getEngineType();return{sql:Y.formatSqlString(e.sql,t),params:e.params}}registerCube(e){this.validateCalculatedMeasures(e),new X(this.cubes).populateDependencies(e),this.cubes.set(e.name,e),this.invalidateMetadataCache()}validateCalculatedMeasures(e){const t=[];for(const[n,i]of Object.entries(e.measures))if(i.type==="calculated"){if(!i.calculatedSql){t.push(`Calculated measure '${e.name}.${n}' must have calculatedSql property`);continue}const s=dn(i.calculatedSql);if(!s.isValid){t.push(`Invalid calculatedSql syntax in '${e.name}.${n}': ${s.errors.join(", ")}`);continue}const r=new Map(this.cubes);r.set(e.name,e);const a=new X(r);try{a.validateDependencies(e)}catch(u){t.push(u instanceof Error?u.message:String(u))}}if(t.length===0){const n=new Map(this.cubes);n.set(e.name,e);const i=new X(n);i.buildGraph(e);const s=i.detectCycle();s&&t.push(`Circular dependency detected in calculated measures: ${s.join(" -> ")}`)}if(t.length>0)throw new Error(`Calculated measure validation failed for cube '${e.name}':
|
|
199
199
|
${t.join(`
|
|
200
|
-
`)}`)}getCube(e){return this.cubes.get(e)}getAllCubes(){return Array.from(this.cubes.values())}getAllCubesMap(){return this.cubes}async execute(e,t,n){return this.createQueryExecutor(!0).execute(this.cubes,e,t,n)}async executeMultiCubeQuery(e,t,n){return this.execute(e,t,n)}async executeQuery(e,t,n){if(!this.cubes.get(e))throw new Error(`Cube '${e}' not found`);return this.execute(t,n)}getMetadata(){return this.metadataCache?this.metadataCache:(this.metadataCache=Array.from(this.cubes.values()).map(e=>this.generateCubeMetadata(e)),this.metadataCache)}getColumnName(e){if(e&&e.name||e&&e.columnType&&e.name)return e.name;if(typeof e=="string")return e;if(e&&typeof e=="object"){if(e._.name)return e._.name;if(e.name)return e.name;if(e.columnName)return e.columnName}return"unknown_column"}static DEFAULT_TIME_GRANULARITIES=["year","quarter","month","week","day","hour"];generateCubeMetadata(e){const t=Object.keys(e.measures),n=Object.keys(e.dimensions),i=new Array(t.length),s=new Array(n.length);for(let c=0;c<t.length;c++){const l=t[c],m=e.measures[l];let p;m.drillMembers&&m.drillMembers.length>0&&(p=m.drillMembers.map(f=>f.includes(".")?f:`${e.name}.${f}`)),i[c]={name:`${e.name}.${l}`,title:m.title||l,shortTitle:m.title||l,type:m.type,format:void 0,description:m.description,synonyms:m.synonyms,drillMembers:p}}for(let c=0;c<n.length;c++){const l=n[c],m=e.dimensions[l];let p;m.type==="time"&&(p=m.granularities||Ie.DEFAULT_TIME_GRANULARITIES),s[c]={name:`${e.name}.${l}`,title:m.title||l,shortTitle:m.title||l,type:m.type,format:void 0,description:m.description,synonyms:m.synonyms,granularities:p}}const r=[];if(e.joins)for(const[,c]of Object.entries(e.joins)){const l=typeof c.targetCube=="function"?c.targetCube():c.targetCube;r.push({targetCube:l.name,relationship:c.relationship,joinFields:c.on.map(m=>({sourceField:this.getColumnName(m.source),targetField:this.getColumnName(m.target)}))})}const a=[];if(e.hierarchies)for(const[,c]of Object.entries(e.hierarchies))a.push({name:c.name,title:c.title||c.name,cubeName:e.name,levels:c.levels.map(l=>l.includes(".")?l:`${e.name}.${l}`)});return{name:e.name,title:e.title||e.name,description:e.description,exampleQuestions:e.exampleQuestions,measures:i,dimensions:s,segments:[],relationships:r.length>0?r:void 0,hierarchies:a.length>0?a:void 0,meta:e.meta}}async generateSQL(e,t,n){const i=this.getCube(e);if(!i)throw new Error(`Cube '${e}' not found`);const r=await this.createQueryExecutor().generateSQL(i,t,n);return this.formatSqlResult(r)}async generateMultiCubeSQL(e,t){const i=await this.createQueryExecutor().generateMultiCubeSQL(this.cubes,e,t);return this.formatSqlResult(i)}async dryRun(e,t){const i=await this.createQueryExecutor().dryRunSQL(this.cubes,e,t);return this.formatSqlResult(i)}async dryRunFunnel(e,t){return this.dryRun(e,t)}async dryRunFlow(e,t){return this.dryRun(e,t)}async dryRunRetention(e,t){return this.dryRun(e,t)}async explainQuery(e,t,n){return this.createQueryExecutor().explainQuery(this.cubes,e,t,n)}hasCube(e){return this.cubes.has(e)}removeCube(e){const t=this.cubes.delete(e);return t&&this.invalidateMetadataCache(),t}clearCubes(){this.cubes.clear(),this.invalidateMetadataCache()}invalidateMetadataCache(){this.metadataCache=void 0}getCubeNames(){return Array.from(this.cubes.keys())}validateQuery(e){return gt(this.cubes,e)}analyzeQuery(e,t){return this.createQueryExecutor(!0).analyzeQuery(this.cubes,e,t)}}function Un(d){const e=[];return d.timeDimensions?.some(t=>t.compareDateRange&&t.compareDateRange.length>=2)&&e.push("comparison"),d.funnel!==void 0&&d.funnel.steps?.length>=2&&e.push("funnel"),d.flow!==void 0&&d.flow.startingStep!==void 0&&d.flow.eventDimension!==void 0&&e.push("flow"),d.retention!==void 0&&d.retention.timeDimension!=null&&d.retention.bindingKey!=null&&e.push("retention"),e.length===0?[]:e}function gt(d,e){const t=[],n=Un(e);if(n.length>1)return t.push(`Query contains multiple query modes: ${n.join(", ")}`),{isValid:!1,errors:t};const i={funnel:()=>{const r=e.funnel.bindingKey;if(typeof r=="string"){const[a]=r.split(".");a&&!d.has(a)&&t.push(`Funnel binding key cube not found: ${a}`)}else if(Array.isArray(r))for(const a of r)d.has(a.cube)||t.push(`Funnel binding key cube not found: ${a.cube}`)},flow:()=>{const r=e.flow.bindingKey;if(typeof r=="string"){const[a]=r.split(".");a&&!d.has(a)&&t.push(`Flow binding key cube not found: ${a}`)}},retention:()=>{const r=e.retention,a=xn(r.timeDimension);a&&!d.has(a)&&t.push(`Retention cube not found: ${a}`);const u=r.bindingKey;if(typeof u=="string"){const[c]=u.split(".");c&&!d.has(c)&&t.push(`Retention binding key cube not found: ${c}`)}else if(Array.isArray(u))for(const c of u)d.has(c.cube)||t.push(`Retention binding key cube not found: ${c.cube}`);if(r.breakdownDimensions&&Array.isArray(r.breakdownDimensions))for(const c of r.breakdownDimensions){const[l]=c.split(".");l&&!d.has(l)&&t.push(`Retention breakdown cube not found: ${l}`)}}};if(n.length===1&&n[0]!=="comparison"){const r=n[0];return i[r](),{isValid:t.length===0,errors:t}}const s=new Set;if(e.measures)for(const r of e.measures){const[a,u]=r.split(".");if(!a||!u){t.push(`Invalid measure format: ${r}. Expected format: 'CubeName.fieldName'`);continue}s.add(a);const c=d.get(a);if(!c){t.push(`Cube '${a}' not found (referenced in measure '${r}')`);continue}if(!c.measures[u]){const l=u===a?`. Did you mean one of: ${Object.keys(c.measures).slice(0,5).map(m=>`'${a}.${m}'`).join(", ")}?`:"";t.push(`Measure '${u}' not found on cube '${a}'${l}`)}}if(e.dimensions)for(const r of e.dimensions){const[a,u]=r.split(".");if(!a||!u){t.push(`Invalid dimension format: ${r}. Expected format: 'CubeName.fieldName'`);continue}s.add(a);const c=d.get(a);if(!c){t.push(`Cube '${a}' not found (referenced in dimension '${r}')`);continue}if(!c.dimensions[u]){const l=u===a?`. Did you mean one of: ${Object.keys(c.dimensions).slice(0,5).map(m=>`'${a}.${m}'`).join(", ")}?`:"";t.push(`Dimension '${u}' not found on cube '${a}'${l}`)}}if(e.timeDimensions)for(const r of e.timeDimensions){const[a,u]=r.dimension.split(".");if(!a||!u){t.push(`Invalid timeDimension format: ${r.dimension}. Expected format: 'CubeName.fieldName'`);continue}s.add(a);const c=d.get(a);if(!c){t.push(`Cube '${a}' not found (referenced in timeDimension '${r.dimension}')`);continue}c.dimensions[u]||t.push(`TimeDimension '${u}' not found on cube '${a}' (must be a dimension with time type)`)}if(e.filters)for(const r of e.filters)bt(r,d,t,s);return s.size===0&&t.push("Query must reference at least one cube through measures, dimensions, or filters"),{isValid:t.length===0,errors:t}}function bt(d,e,t,n){if("and"in d||"or"in d){const a=d.and||d.or||[];for(const u of a)bt(u,e,t,n);return}if(!("member"in d)){t.push("Filter must have a member field");return}const[i,s]=d.member.split(".");if(!i||!s){t.push(`Invalid filter member format: ${d.member}. Expected format: 'CubeName.fieldName'`);return}n.add(i);const r=e.get(i);if(!r){t.push(`Cube '${i}' not found (referenced in filter '${d.member}')`);return}if(!r.dimensions[s]&&!r.measures[s]){const a=s===i?`. Did you mean one of: ${[...Object.keys(r.dimensions),...Object.keys(r.measures)].slice(0,5).map(u=>`'${i}.${u}'`).join(", ")}?`:"";t.push(`Filter field '${s}' not found on cube '${i}' (must be a dimension or measure)${a}`)}}function xn(d){if(typeof d=="string"){const[e]=d.split(".");return e||null}return d.cube}const le=["2025-11-25","2025-06-18","2025-03-26"],yt="2025-11-25";function Qn(d){const t=Xn(d["mcp-protocol-version"])||yt;return{ok:le.includes(t),negotiated:le.includes(t)?t:null,supported:le}}function Kn(d){if(!d)return!1;const e=d.split(",").map(i=>i.trim().toLowerCase()),t=e.includes("text/event-stream"),n=e.includes("application/json");return t&&!n}const Wn="mcp-session-id";function Jn(d){if(!d)return!1;const e=d.split(",").map(i=>i.trim().toLowerCase().split(";")[0]),t=e.some(i=>i==="application/json"),n=e.some(i=>i==="text/event-stream");return t&&n}function zn(d,e={}){const{allowMissingOrigin:t=!0,allowedOrigins:n}=e;if(!d)return t?{valid:!0}:{valid:!1,reason:"Origin header is required"};if(!n||n.length===0)return{valid:!0};let i;try{i=new URL(d)}catch{return{valid:!1,reason:"Invalid Origin header format"}}return n.map(r=>{try{return new URL(r).origin}catch{return r}}).includes(i.origin)?{valid:!0}:{valid:!1,reason:"Origin not in allowed list"}}function qn(d,e,t){const n=[];return e&&n.push(`id: ${e}`),t&&t>0&&n.push(`retry: ${t}`),n.push("event: message"),n.push(`data: ${JSON.stringify(d)}`),n.push(""),n.join(`
|
|
200
|
+
`)}`)}getCube(e){return this.cubes.get(e)}getAllCubes(){return Array.from(this.cubes.values())}getAllCubesMap(){return this.cubes}async execute(e,t,n){return this.createQueryExecutor(!0).execute(this.cubes,e,t,n)}async executeMultiCubeQuery(e,t,n){return this.execute(e,t,n)}async executeQuery(e,t,n){if(!this.cubes.get(e))throw new Error(`Cube '${e}' not found`);return this.execute(t,n)}getMetadata(){return this.metadataCache?this.metadataCache:(this.metadataCache=Array.from(this.cubes.values()).map(e=>this.generateCubeMetadata(e)),this.metadataCache)}getColumnName(e){if(e&&e.name||e&&e.columnType&&e.name)return e.name;if(typeof e=="string")return e;if(e&&typeof e=="object"){if(e._.name)return e._.name;if(e.name)return e.name;if(e.columnName)return e.columnName}return"unknown_column"}static DEFAULT_TIME_GRANULARITIES=["year","quarter","month","week","day","hour"];generateCubeMetadata(e){const t=Object.keys(e.measures),n=Object.keys(e.dimensions),i=new Array(t.length),s=new Array(n.length);for(let c=0;c<t.length;c++){const l=t[c],m=e.measures[l];let p;m.drillMembers&&m.drillMembers.length>0&&(p=m.drillMembers.map(f=>f.includes(".")?f:`${e.name}.${f}`)),i[c]={name:`${e.name}.${l}`,title:m.title||l,shortTitle:m.title||l,type:m.type,format:void 0,description:m.description,synonyms:m.synonyms,drillMembers:p}}for(let c=0;c<n.length;c++){const l=n[c],m=e.dimensions[l];let p;m.type==="time"&&(p=m.granularities||Ie.DEFAULT_TIME_GRANULARITIES),s[c]={name:`${e.name}.${l}`,title:m.title||l,shortTitle:m.title||l,type:m.type,format:void 0,description:m.description,synonyms:m.synonyms,granularities:p}}const r=[];if(e.joins)for(const[,c]of Object.entries(e.joins)){const l=typeof c.targetCube=="function"?c.targetCube():c.targetCube;r.push({targetCube:l.name,relationship:c.relationship,joinFields:c.on.map(m=>({sourceField:this.getColumnName(m.source),targetField:this.getColumnName(m.target)}))})}const a=[];if(e.hierarchies)for(const[,c]of Object.entries(e.hierarchies))a.push({name:c.name,title:c.title||c.name,cubeName:e.name,levels:c.levels.map(l=>l.includes(".")?l:`${e.name}.${l}`)});return{name:e.name,title:e.title||e.name,description:e.description,exampleQuestions:e.exampleQuestions,measures:i,dimensions:s,segments:[],relationships:r.length>0?r:void 0,hierarchies:a.length>0?a:void 0,meta:e.meta}}async generateSQL(e,t,n){const i=this.getCube(e);if(!i)throw new Error(`Cube '${e}' not found`);const r=await this.createQueryExecutor().generateSQL(i,t,n);return this.formatSqlResult(r)}async generateMultiCubeSQL(e,t){const i=await this.createQueryExecutor().generateMultiCubeSQL(this.cubes,e,t);return this.formatSqlResult(i)}async dryRun(e,t){const i=await this.createQueryExecutor().dryRunSQL(this.cubes,e,t);return this.formatSqlResult(i)}async dryRunFunnel(e,t){return this.dryRun(e,t)}async dryRunFlow(e,t){return this.dryRun(e,t)}async dryRunRetention(e,t){return this.dryRun(e,t)}async explainQuery(e,t,n){return this.createQueryExecutor().explainQuery(this.cubes,e,t,n)}hasCube(e){return this.cubes.has(e)}unregisterCube(e){return this.removeCube(e)}removeCube(e){const t=this.cubes.delete(e);return t&&this.invalidateMetadataCache(),t}clearCubes(){this.cubes.clear(),this.invalidateMetadataCache()}invalidateMetadataCache(){this.metadataCache=void 0}getCubeNames(){return Array.from(this.cubes.keys())}validateQuery(e){return gt(this.cubes,e)}analyzeQuery(e,t){return this.createQueryExecutor(!0).analyzeQuery(this.cubes,e,t)}}function Un(d){const e=[];return d.timeDimensions?.some(t=>t.compareDateRange&&t.compareDateRange.length>=2)&&e.push("comparison"),d.funnel!==void 0&&d.funnel.steps?.length>=2&&e.push("funnel"),d.flow!==void 0&&d.flow.startingStep!==void 0&&d.flow.eventDimension!==void 0&&e.push("flow"),d.retention!==void 0&&d.retention.timeDimension!=null&&d.retention.bindingKey!=null&&e.push("retention"),e.length===0?[]:e}function gt(d,e){const t=[],n=Un(e);if(n.length>1)return t.push(`Query contains multiple query modes: ${n.join(", ")}`),{isValid:!1,errors:t};const i={funnel:()=>{const r=e.funnel.bindingKey;if(typeof r=="string"){const[a]=r.split(".");a&&!d.has(a)&&t.push(`Funnel binding key cube not found: ${a}`)}else if(Array.isArray(r))for(const a of r)d.has(a.cube)||t.push(`Funnel binding key cube not found: ${a.cube}`)},flow:()=>{const r=e.flow.bindingKey;if(typeof r=="string"){const[a]=r.split(".");a&&!d.has(a)&&t.push(`Flow binding key cube not found: ${a}`)}},retention:()=>{const r=e.retention,a=xn(r.timeDimension);a&&!d.has(a)&&t.push(`Retention cube not found: ${a}`);const u=r.bindingKey;if(typeof u=="string"){const[c]=u.split(".");c&&!d.has(c)&&t.push(`Retention binding key cube not found: ${c}`)}else if(Array.isArray(u))for(const c of u)d.has(c.cube)||t.push(`Retention binding key cube not found: ${c.cube}`);if(r.breakdownDimensions&&Array.isArray(r.breakdownDimensions))for(const c of r.breakdownDimensions){const[l]=c.split(".");l&&!d.has(l)&&t.push(`Retention breakdown cube not found: ${l}`)}}};if(n.length===1&&n[0]!=="comparison"){const r=n[0];return i[r](),{isValid:t.length===0,errors:t}}const s=new Set;if(e.measures)for(const r of e.measures){const[a,u]=r.split(".");if(!a||!u){t.push(`Invalid measure format: ${r}. Expected format: 'CubeName.fieldName'`);continue}s.add(a);const c=d.get(a);if(!c){t.push(`Cube '${a}' not found (referenced in measure '${r}')`);continue}if(!c.measures[u]){const l=u===a?`. Did you mean one of: ${Object.keys(c.measures).slice(0,5).map(m=>`'${a}.${m}'`).join(", ")}?`:"";t.push(`Measure '${u}' not found on cube '${a}'${l}`)}}if(e.dimensions)for(const r of e.dimensions){const[a,u]=r.split(".");if(!a||!u){t.push(`Invalid dimension format: ${r}. Expected format: 'CubeName.fieldName'`);continue}s.add(a);const c=d.get(a);if(!c){t.push(`Cube '${a}' not found (referenced in dimension '${r}')`);continue}if(!c.dimensions[u]){const l=u===a?`. Did you mean one of: ${Object.keys(c.dimensions).slice(0,5).map(m=>`'${a}.${m}'`).join(", ")}?`:"";t.push(`Dimension '${u}' not found on cube '${a}'${l}`)}}if(e.timeDimensions)for(const r of e.timeDimensions){const[a,u]=r.dimension.split(".");if(!a||!u){t.push(`Invalid timeDimension format: ${r.dimension}. Expected format: 'CubeName.fieldName'`);continue}s.add(a);const c=d.get(a);if(!c){t.push(`Cube '${a}' not found (referenced in timeDimension '${r.dimension}')`);continue}c.dimensions[u]||t.push(`TimeDimension '${u}' not found on cube '${a}' (must be a dimension with time type)`)}if(e.filters)for(const r of e.filters)bt(r,d,t,s);return s.size===0&&t.push("Query must reference at least one cube through measures, dimensions, or filters"),{isValid:t.length===0,errors:t}}function bt(d,e,t,n){if("and"in d||"or"in d){const a=d.and||d.or||[];for(const u of a)bt(u,e,t,n);return}if(!("member"in d)){t.push("Filter must have a member field");return}const[i,s]=d.member.split(".");if(!i||!s){t.push(`Invalid filter member format: ${d.member}. Expected format: 'CubeName.fieldName'`);return}n.add(i);const r=e.get(i);if(!r){t.push(`Cube '${i}' not found (referenced in filter '${d.member}')`);return}if(!r.dimensions[s]&&!r.measures[s]){const a=s===i?`. Did you mean one of: ${[...Object.keys(r.dimensions),...Object.keys(r.measures)].slice(0,5).map(u=>`'${i}.${u}'`).join(", ")}?`:"";t.push(`Filter field '${s}' not found on cube '${i}' (must be a dimension or measure)${a}`)}}function xn(d){if(typeof d=="string"){const[e]=d.split(".");return e||null}return d.cube}const le=["2025-11-25","2025-06-18","2025-03-26"],yt="2025-11-25";function Qn(d){const t=Xn(d["mcp-protocol-version"])||yt;return{ok:le.includes(t),negotiated:le.includes(t)?t:null,supported:le}}function Kn(d){if(!d)return!1;const e=d.split(",").map(i=>i.trim().toLowerCase()),t=e.includes("text/event-stream"),n=e.includes("application/json");return t&&!n}const Wn="mcp-session-id";function Jn(d){if(!d)return!1;const e=d.split(",").map(i=>i.trim().toLowerCase().split(";")[0]),t=e.some(i=>i==="application/json"),n=e.some(i=>i==="text/event-stream");return t&&n}function zn(d,e={}){const{allowMissingOrigin:t=!0,allowedOrigins:n}=e;if(!d)return t?{valid:!0}:{valid:!1,reason:"Origin header is required"};if(!n||n.length===0)return{valid:!0};let i;try{i=new URL(d)}catch{return{valid:!1,reason:"Invalid Origin header format"}}return n.map(r=>{try{return new URL(r).origin}catch{return r}}).includes(i.origin)?{valid:!0}:{valid:!1,reason:"Origin not in allowed list"}}function qn(d,e,t){const n=[];return e&&n.push(`id: ${e}`),t&&t>0&&n.push(`retry: ${t}`),n.push("event: message"),n.push(`data: ${JSON.stringify(d)}`),n.push(""),n.join(`
|
|
201
201
|
`)}function Vn(d,e,t,n){return{jsonrpc:"2.0",id:d??null,error:{code:e,message:t,...n!==void 0?{data:n}:{}}}}function Gn(d,e){return{jsonrpc:"2.0",id:d??null,result:e}}function Hn(d){if(!d||typeof d!="object")return null;const e=d;return e.jsonrpc!=="2.0"||typeof e.method!="string"?null:{jsonrpc:"2.0",method:e.method,id:e.id,params:e.params}}async function Yn(d,e,t){const{semanticLayer:n,extractSecurityContext:i,rawRequest:s,rawResponse:r}=t,a=t.prompts??wt,u=t.resources??$t;switch(d){case"initialize":{const c=e?.protocolVersion;let l;return c&&le.includes(c)?l=c:l=yt,{protocolVersion:l,capabilities:{tools:{listChanged:!1},resources:{listChanged:!1},prompts:{listChanged:!1},sampling:{}},sessionId:Ct(),serverInfo:{name:"drizzle-cube",version:typeof process<"u"?process.env?.npm_package_version||"dev":"worker"}}}case"list_tools":case"tools/list":return{tools:ei(),nextCursor:""};case"call_tool":case"tools/call":return ti(e,t);case"resources/list":return{resources:u.map(({uri:c,name:l,description:m,mimeType:p})=>({uri:c,name:l,description:m,mimeType:p})),nextCursor:""};case"resources/templates/list":return{resourceTemplates:[],nextCursor:""};case"resources/read":{const c=e?.uri,l=u.find(m=>m.uri===c)||u[0];if(!l)throw G(-32602,"resource not found");return{contents:[{uri:l.uri,mimeType:l.mimeType,text:l.text}]}}case"prompts/list":return{prompts:a.map(({name:c,description:l})=>({name:c,description:l})),nextCursor:""};case"ping":return{};case"notifications/initialized":return{};case"prompts/get":{const c=e?.name,l=a.find(m=>m.name===c)||a[0];if(!l)throw G(-32602,"prompt not found");return{name:l.name,description:l.description,messages:l.messages}}case"discover":return Y.handleDiscover(n,e||{});case"validate":{const c=e||{};if(!c.query)throw G(-32602,"query is required");return Y.handleValidate(n,c)}case"load":{const c=e||{};if(!c.query)throw G(-32602,"query is required");const l=await i(s,r);return Y.handleLoad(n,l,c)}default:throw G(-32601,`Unknown MCP method: ${d}`)}}function G(d,e,t){const n=new Error(e);return n.code=d,n}function Xn(d){return d?Array.isArray(d)?d[0]||null:d:null}function Zn(d){return d.id===void 0||d.id===null}function Ct(){return`evt-${Y.generateRequestId()}`}function ei(){return[{name:"discover",description:`Find relevant cubes based on topic or intent. Call this FIRST to understand available data.
|
|
202
202
|
|
|
203
203
|
Returns cubes with:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("next/server"),f=require("../mcp-transport-8u9G5oNa.cjs"),i=require("../utils.cjs");function v(n){const{cubes:o,drizzle:s,schema:c,engineType:d,cache:a}=n;if(!o||o.length===0)throw new Error("At least one cube must be provided in the cubes array");const t=new f.SemanticLayerCompiler({drizzle:s,schema:c,engineType:d,cache:a});return o.forEach(e=>{t.registerCube(e)}),t}function h(n,o){const s=n.headers.get("origin"),c={};return o.origin&&(typeof o.origin=="string"?c["Access-Control-Allow-Origin"]=o.origin:Array.isArray(o.origin)?s&&o.origin.includes(s)&&(c["Access-Control-Allow-Origin"]=s):typeof o.origin=="function"&&s&&o.origin(s)&&(c["Access-Control-Allow-Origin"]=s)),o.methods&&(c["Access-Control-Allow-Methods"]=o.methods.join(", ")),o.allowedHeaders&&(c["Access-Control-Allow-Headers"]=o.allowedHeaders.join(", ")),o.credentials&&(c["Access-Control-Allow-Credentials"]="true"),c}function J(n){return async function(s){const c=h(s,n);return new Response(null,{status:200,headers:c})}}function T(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{let e;if(a.method==="POST"){const N=await a.json();e=N.query||N}else if(a.method==="GET"){const N=a.nextUrl.searchParams.get("query");if(!N)return r.NextResponse.json(i.formatErrorResponse("Query parameter is required",400),{status:400});try{e=JSON.parse(N)}catch{return r.NextResponse.json(i.formatErrorResponse("Invalid JSON in query parameter",400),{status:400})}}else return r.NextResponse.json(i.formatErrorResponse("Method not allowed",405),{status:405});const l=await o(a,t),y=c.validateQuery(e);if(!y.isValid)return r.NextResponse.json(i.formatErrorResponse(`Query validation failed: ${y.errors.join(", ")}`,400),{status:400});const u=a.headers.get("x-cache-control")==="no-cache",p=await c.executeMultiCubeQuery(e,l,{skipCache:u}),w=i.formatCubeResponse(e,p,c);return r.NextResponse.json(w,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js load handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500),{status:500})}}}function A(n){const{cors:o}=n,s=v(n);return async function(d,a){try{const t=s.getMetadata(),e=i.formatMetaResponse(t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js meta handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Failed to fetch metadata",500),{status:500})}}}function M(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{let e;if(a.method==="POST"){const C=await a.json();e=C.query||C}else if(a.method==="GET"){const C=a.nextUrl.searchParams.get("query");if(!C)return r.NextResponse.json(i.formatErrorResponse("Query parameter is required",400),{status:400});try{e=JSON.parse(C)}catch{return r.NextResponse.json(i.formatErrorResponse("Invalid JSON in query parameter",400),{status:400})}}else return r.NextResponse.json(i.formatErrorResponse("Method not allowed",405),{status:405});const l=await o(a,t),y=c.validateQuery(e);if(!y.isValid)return r.NextResponse.json(i.formatErrorResponse(`Query validation failed: ${y.errors.join(", ")}`,400),{status:400});const u=e.measures?.[0]||e.dimensions?.[0];if(!u)return r.NextResponse.json(i.formatErrorResponse("No measures or dimensions specified",400),{status:400});const p=u.split(".")[0],w=await c.generateSQL(p,e,l),N=i.formatSqlResponse(e,w);return r.NextResponse.json(N,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js SQL handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500),{status:500})}}}function L(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{let e;if(a.method==="POST"){const u=await a.json();e=u.query||u}else if(a.method==="GET"){const u=a.nextUrl.searchParams.get("query");if(!u)return r.NextResponse.json({error:"Query parameter is required",valid:!1},{status:400});try{e=JSON.parse(u)}catch{return r.NextResponse.json({error:"Invalid JSON in query parameter",valid:!1},{status:400})}}else return r.NextResponse.json({error:"Method not allowed",valid:!1},{status:405});const l=await o(a,t),y=await i.handleDryRun(e,l,c);return r.NextResponse.json(y,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js dry-run handler error:",e),r.NextResponse.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},{status:400})}}}function D(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{if(a.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const e=await a.json(),{queries:l}=e;if(!l||!Array.isArray(l))return r.NextResponse.json(i.formatErrorResponse('Request body must contain a "queries" array',400),{status:400});if(l.length===0)return r.NextResponse.json(i.formatErrorResponse("Queries array cannot be empty",400),{status:400});const y=await o(a,t),u=a.headers.get("x-cache-control")==="no-cache",p=await i.handleBatchRequest(l,y,c,{skipCache:u});return r.NextResponse.json(p,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js batch handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500),{status:500})}}}function V(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{if(a.method!=="POST")return r.NextResponse.json({error:"Method not allowed"},{status:405});const e=await a.json(),l=e.query||e,y=e.options||{},u=await o(a,t),p=c.validateQuery(l);if(!p.isValid)return r.NextResponse.json({error:`Query validation failed: ${p.errors.join(", ")}`},{status:400});const w=await c.explainQuery(l,u,y);return r.NextResponse.json(w,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js explain handler error:",e),r.NextResponse.json({error:e instanceof Error?e.message:"Explain query failed"},{status:500})}}}function k(n){const{cors:o}=n,s=v(n);return async function(d,a){try{if(d.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const t=await d.json(),e=await i.handleDiscover(s,t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js discover handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Discovery failed",500),{status:500})}}}function K(n){const{cors:o}=n,s=v(n);return async function(d,a){try{if(d.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const t=await d.json();if(!t.naturalLanguage)return r.NextResponse.json(i.formatErrorResponse("naturalLanguage field is required",400),{status:400});const e=await i.handleSuggest(s,t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js suggest handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Query suggestion failed",500),{status:500})}}}function z(n){const{cors:o}=n,s=v(n);return async function(d,a){try{if(d.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const t=await d.json();if(!t.query)return r.NextResponse.json(i.formatErrorResponse("query field is required",400),{status:400});const e=await i.handleValidate(s,t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js validate handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Query validation failed",500),{status:500})}}}function $(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{if(a.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const e=await a.json();if(!e.query)return r.NextResponse.json(i.formatErrorResponse("query field is required",400),{status:400});const l=await o(a,t),y=await i.handleLoad(c,l,e);return r.NextResponse.json(y,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js MCP load handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500),{status:500})}}}function _(n){const{extractSecurityContext:o,cors:s,mcp:c={enabled:!0}}=n,d=v(n);return async function(t){if(t.method==="DELETE")return r.NextResponse.json({error:"Session termination not supported"},{status:405});if(t.method==="GET"){const m=new TextEncoder,S=f.primeEventId(),j=new ReadableStream({start(R){R.enqueue(m.encode(f.serializeSseEvent({jsonrpc:"2.0",method:"mcp/ready",params:{protocol:"streamable-http"}},S,15e3)))}}),H=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const R=h(t,s);Object.entries(R).forEach(([b,O])=>H.set(b,O))}return new r.NextResponse(j,{status:200,headers:H})}if(t.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const e=f.validateOriginHeader(t.headers.get("origin"),c.allowedOrigins?{allowedOrigins:c.allowedOrigins}:{});if(!e.valid)return r.NextResponse.json(f.buildJsonRpcError(null,-32600,e.reason),{status:403});const l=t.headers.get("accept");if(!f.validateAcceptHeader(l))return r.NextResponse.json(f.buildJsonRpcError(null,-32600,"Accept header must include both application/json and text/event-stream"),{status:400});const y=f.negotiateProtocol(Object.fromEntries(t.headers.entries()));if(!y.ok)return r.NextResponse.json({error:"Unsupported MCP protocol version",supported:y.supported},{status:426});let u;try{u=await t.json()}catch{u=null}const p=f.parseJsonRpc(u);if(!p)return r.NextResponse.json(f.buildJsonRpcError(null,-32600,"Invalid JSON-RPC 2.0 request"),{status:400});const w=f.wantsEventStream(l),N=p.method==="initialize",C=(m,S=200,j={})=>r.NextResponse.json(m,{status:S,headers:{...s?h(t,s):{},...j}});try{const m=await f.dispatchMcpMethod(p.method,p.params,{semanticLayer:d,extractSecurityContext:R=>o(R),rawRequest:t,rawResponse:null});if(f.isNotification(p))return new r.NextResponse(null,{status:202});const S=N&&m&&typeof m=="object"&&"sessionId"in m?m.sessionId:void 0,j={};S&&(j[f.MCP_SESSION_ID_HEADER]=S);const H=f.buildJsonRpcResult(p.id??null,m);if(w){const R=new TextEncoder,b=f.primeEventId(),O=new ReadableStream({start(x){x.enqueue(R.encode(`id: ${b}
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("next/server"),f=require("../mcp-transport-DCiSGtp1.cjs"),i=require("../utils.cjs");function v(n){const{cubes:o,drizzle:s,schema:c,engineType:d,cache:a}=n;if(!o||o.length===0)throw new Error("At least one cube must be provided in the cubes array");const t=new f.SemanticLayerCompiler({drizzle:s,schema:c,engineType:d,cache:a});return o.forEach(e=>{t.registerCube(e)}),t}function h(n,o){const s=n.headers.get("origin"),c={};return o.origin&&(typeof o.origin=="string"?c["Access-Control-Allow-Origin"]=o.origin:Array.isArray(o.origin)?s&&o.origin.includes(s)&&(c["Access-Control-Allow-Origin"]=s):typeof o.origin=="function"&&s&&o.origin(s)&&(c["Access-Control-Allow-Origin"]=s)),o.methods&&(c["Access-Control-Allow-Methods"]=o.methods.join(", ")),o.allowedHeaders&&(c["Access-Control-Allow-Headers"]=o.allowedHeaders.join(", ")),o.credentials&&(c["Access-Control-Allow-Credentials"]="true"),c}function J(n){return async function(s){const c=h(s,n);return new Response(null,{status:200,headers:c})}}function T(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{let e;if(a.method==="POST"){const N=await a.json();e=N.query||N}else if(a.method==="GET"){const N=a.nextUrl.searchParams.get("query");if(!N)return r.NextResponse.json(i.formatErrorResponse("Query parameter is required",400),{status:400});try{e=JSON.parse(N)}catch{return r.NextResponse.json(i.formatErrorResponse("Invalid JSON in query parameter",400),{status:400})}}else return r.NextResponse.json(i.formatErrorResponse("Method not allowed",405),{status:405});const l=await o(a,t),y=c.validateQuery(e);if(!y.isValid)return r.NextResponse.json(i.formatErrorResponse(`Query validation failed: ${y.errors.join(", ")}`,400),{status:400});const u=a.headers.get("x-cache-control")==="no-cache",p=await c.executeMultiCubeQuery(e,l,{skipCache:u}),w=i.formatCubeResponse(e,p,c);return r.NextResponse.json(w,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js load handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500),{status:500})}}}function A(n){const{cors:o}=n,s=v(n);return async function(d,a){try{const t=s.getMetadata(),e=i.formatMetaResponse(t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js meta handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Failed to fetch metadata",500),{status:500})}}}function M(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{let e;if(a.method==="POST"){const C=await a.json();e=C.query||C}else if(a.method==="GET"){const C=a.nextUrl.searchParams.get("query");if(!C)return r.NextResponse.json(i.formatErrorResponse("Query parameter is required",400),{status:400});try{e=JSON.parse(C)}catch{return r.NextResponse.json(i.formatErrorResponse("Invalid JSON in query parameter",400),{status:400})}}else return r.NextResponse.json(i.formatErrorResponse("Method not allowed",405),{status:405});const l=await o(a,t),y=c.validateQuery(e);if(!y.isValid)return r.NextResponse.json(i.formatErrorResponse(`Query validation failed: ${y.errors.join(", ")}`,400),{status:400});const u=e.measures?.[0]||e.dimensions?.[0];if(!u)return r.NextResponse.json(i.formatErrorResponse("No measures or dimensions specified",400),{status:400});const p=u.split(".")[0],w=await c.generateSQL(p,e,l),N=i.formatSqlResponse(e,w);return r.NextResponse.json(N,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js SQL handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500),{status:500})}}}function L(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{let e;if(a.method==="POST"){const u=await a.json();e=u.query||u}else if(a.method==="GET"){const u=a.nextUrl.searchParams.get("query");if(!u)return r.NextResponse.json({error:"Query parameter is required",valid:!1},{status:400});try{e=JSON.parse(u)}catch{return r.NextResponse.json({error:"Invalid JSON in query parameter",valid:!1},{status:400})}}else return r.NextResponse.json({error:"Method not allowed",valid:!1},{status:405});const l=await o(a,t),y=await i.handleDryRun(e,l,c);return r.NextResponse.json(y,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js dry-run handler error:",e),r.NextResponse.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},{status:400})}}}function D(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{if(a.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const e=await a.json(),{queries:l}=e;if(!l||!Array.isArray(l))return r.NextResponse.json(i.formatErrorResponse('Request body must contain a "queries" array',400),{status:400});if(l.length===0)return r.NextResponse.json(i.formatErrorResponse("Queries array cannot be empty",400),{status:400});const y=await o(a,t),u=a.headers.get("x-cache-control")==="no-cache",p=await i.handleBatchRequest(l,y,c,{skipCache:u});return r.NextResponse.json(p,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js batch handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500),{status:500})}}}function V(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{if(a.method!=="POST")return r.NextResponse.json({error:"Method not allowed"},{status:405});const e=await a.json(),l=e.query||e,y=e.options||{},u=await o(a,t),p=c.validateQuery(l);if(!p.isValid)return r.NextResponse.json({error:`Query validation failed: ${p.errors.join(", ")}`},{status:400});const w=await c.explainQuery(l,u,y);return r.NextResponse.json(w,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js explain handler error:",e),r.NextResponse.json({error:e instanceof Error?e.message:"Explain query failed"},{status:500})}}}function k(n){const{cors:o}=n,s=v(n);return async function(d,a){try{if(d.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const t=await d.json(),e=await i.handleDiscover(s,t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js discover handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Discovery failed",500),{status:500})}}}function K(n){const{cors:o}=n,s=v(n);return async function(d,a){try{if(d.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const t=await d.json();if(!t.naturalLanguage)return r.NextResponse.json(i.formatErrorResponse("naturalLanguage field is required",400),{status:400});const e=await i.handleSuggest(s,t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js suggest handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Query suggestion failed",500),{status:500})}}}function z(n){const{cors:o}=n,s=v(n);return async function(d,a){try{if(d.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const t=await d.json();if(!t.query)return r.NextResponse.json(i.formatErrorResponse("query field is required",400),{status:400});const e=await i.handleValidate(s,t);return r.NextResponse.json(e,{headers:o?h(d,o):{}})}catch(t){return process.env.NODE_ENV!=="test"&&console.error("Next.js validate handler error:",t),r.NextResponse.json(i.formatErrorResponse(t instanceof Error?t.message:"Query validation failed",500),{status:500})}}}function $(n){const{extractSecurityContext:o,cors:s}=n,c=v(n);return async function(a,t){try{if(a.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const e=await a.json();if(!e.query)return r.NextResponse.json(i.formatErrorResponse("query field is required",400),{status:400});const l=await o(a,t),y=await i.handleLoad(c,l,e);return r.NextResponse.json(y,{headers:s?h(a,s):{}})}catch(e){return process.env.NODE_ENV!=="test"&&console.error("Next.js MCP load handler error:",e),r.NextResponse.json(i.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500),{status:500})}}}function _(n){const{extractSecurityContext:o,cors:s,mcp:c={enabled:!0}}=n,d=v(n);return async function(t){if(t.method==="DELETE")return r.NextResponse.json({error:"Session termination not supported"},{status:405});if(t.method==="GET"){const m=new TextEncoder,S=f.primeEventId(),j=new ReadableStream({start(R){R.enqueue(m.encode(f.serializeSseEvent({jsonrpc:"2.0",method:"mcp/ready",params:{protocol:"streamable-http"}},S,15e3)))}}),H=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const R=h(t,s);Object.entries(R).forEach(([b,O])=>H.set(b,O))}return new r.NextResponse(j,{status:200,headers:H})}if(t.method!=="POST")return r.NextResponse.json(i.formatErrorResponse("Method not allowed - use POST",405),{status:405});const e=f.validateOriginHeader(t.headers.get("origin"),c.allowedOrigins?{allowedOrigins:c.allowedOrigins}:{});if(!e.valid)return r.NextResponse.json(f.buildJsonRpcError(null,-32600,e.reason),{status:403});const l=t.headers.get("accept");if(!f.validateAcceptHeader(l))return r.NextResponse.json(f.buildJsonRpcError(null,-32600,"Accept header must include both application/json and text/event-stream"),{status:400});const y=f.negotiateProtocol(Object.fromEntries(t.headers.entries()));if(!y.ok)return r.NextResponse.json({error:"Unsupported MCP protocol version",supported:y.supported},{status:426});let u;try{u=await t.json()}catch{u=null}const p=f.parseJsonRpc(u);if(!p)return r.NextResponse.json(f.buildJsonRpcError(null,-32600,"Invalid JSON-RPC 2.0 request"),{status:400});const w=f.wantsEventStream(l),N=p.method==="initialize",C=(m,S=200,j={})=>r.NextResponse.json(m,{status:S,headers:{...s?h(t,s):{},...j}});try{const m=await f.dispatchMcpMethod(p.method,p.params,{semanticLayer:d,extractSecurityContext:R=>o(R),rawRequest:t,rawResponse:null});if(f.isNotification(p))return new r.NextResponse(null,{status:202});const S=N&&m&&typeof m=="object"&&"sessionId"in m?m.sessionId:void 0,j={};S&&(j[f.MCP_SESSION_ID_HEADER]=S);const H=f.buildJsonRpcResult(p.id??null,m);if(w){const R=new TextEncoder,b=f.primeEventId(),O=new ReadableStream({start(x){x.enqueue(R.encode(`id: ${b}
|
|
2
2
|
|
|
3
3
|
`)),x.enqueue(R.encode(f.serializeSseEvent(H,b))),x.close()}}),E=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive",...j});if(s){const x=h(t,s);Object.entries(x).forEach(([g,P])=>E.set(g,P))}return new r.NextResponse(O,{status:200,headers:E})}return C(H,200,j)}catch(m){if(f.isNotification(p))return process.env.NODE_ENV!=="test"&&console.error("Next.js MCP notification processing error:",m),new r.NextResponse(null,{status:202});process.env.NODE_ENV!=="test"&&console.error("Next.js MCP RPC handler error:",m);const S=m?.code??-32603,j=m?.data,H=m.message||"MCP request failed",R=f.buildJsonRpcError(p.id??null,S,H,j);if(w){const b=new TextEncoder,O=f.primeEventId(),E=new ReadableStream({start(g){g.enqueue(b.encode(`id: ${O}
|
|
4
4
|
|
|
5
|
-
`)),g.enqueue(b.encode(f.serializeSseEvent(R,O))),g.close()}}),x=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const g=h(t,s);Object.entries(g).forEach(([P,I])=>x.set(P,I))}return new r.NextResponse(E,{status:200,headers:x})}return C(R,200)}}}function Q(n){const{extractSecurityContext:o,cors:s,agent:c}=n;if(!c)throw new Error("agent config is required for createAgentChatHandler");const d=v(n);return async function(t,e){try{if(t.method!=="POST")return r.NextResponse.json({error:"Method not allowed - use POST"},{status:405});const{handleAgentChat:l}=await Promise.resolve().then(()=>require("../handler-
|
|
5
|
+
`)),g.enqueue(b.encode(f.serializeSseEvent(R,O))),g.close()}}),x=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const g=h(t,s);Object.entries(g).forEach(([P,I])=>x.set(P,I))}return new r.NextResponse(E,{status:200,headers:x})}return C(R,200)}}}function Q(n){const{extractSecurityContext:o,cors:s,agent:c}=n;if(!c)throw new Error("agent config is required for createAgentChatHandler");const d=v(n);return async function(t,e){try{if(t.method!=="POST")return r.NextResponse.json({error:"Method not allowed - use POST"},{status:405});const{handleAgentChat:l}=await Promise.resolve().then(()=>require("../handler-C3hT7g2W.cjs")),y=await t.json(),{message:u,sessionId:p,history:w}=y;if(!u||typeof u!="string")return r.NextResponse.json({error:"message is required and must be a string"},{status:400});let N=(c.apiKey||"").trim();if(c.allowClientApiKey){const E=t.headers.get("x-agent-api-key");E&&(N=E.trim())}if(!N)return r.NextResponse.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},{status:401});const C=c.allowClientApiKey&&t.headers.get("x-agent-provider")||void 0,m=c.allowClientApiKey&&t.headers.get("x-agent-model")||void 0,S=c.allowClientApiKey&&t.headers.get("x-agent-provider-endpoint")||void 0,j=await o(t,e),H=c.buildSystemContext?.(j),R=new TextEncoder,b=new ReadableStream({async start(E){try{const x=l({message:u,sessionId:p,history:w,semanticLayer:d,securityContext:j,agentConfig:c,apiKey:N,systemContext:H,providerOverride:C,modelOverride:m,baseURLOverride:S});for await(const g of x){const P=`data: ${JSON.stringify(g)}
|
|
6
6
|
|
|
7
7
|
`;E.enqueue(R.encode(P))}}catch(x){const g={type:"error",data:{message:x instanceof Error?x.message:"Stream failed"}};E.enqueue(R.encode(`data: ${JSON.stringify(g)}
|
|
8
8
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse as r } from "next/server";
|
|
2
|
-
import { e as T, s as A, v as D, b as R, a as _, n as Q, p as V, w as I, d as J, i as M, M as k, c as K, S as $ } from "../mcp-transport-
|
|
2
|
+
import { e as T, s as A, v as D, b as R, a as _, n as Q, p as V, w as I, d as J, i as M, M as k, c as K, S as $ } from "../mcp-transport-B6ZudTSk.js";
|
|
3
3
|
import { formatErrorResponse as i, formatCubeResponse as z, formatMetaResponse as U, formatSqlResponse as G, handleDryRun as B, handleBatchRequest as F, handleDiscover as X, handleSuggest as q, handleValidate as W, handleLoad as Y } from "../utils.js";
|
|
4
4
|
function E(n) {
|
|
5
5
|
const { cubes: a, drizzle: s, schema: c, engineType: d, cache: o } = n;
|
|
@@ -519,7 +519,7 @@ function oe(n) {
|
|
|
519
519
|
{ error: "Method not allowed - use POST" },
|
|
520
520
|
{ status: 405 }
|
|
521
521
|
);
|
|
522
|
-
const { handleAgentChat: l } = await import("../handler-
|
|
522
|
+
const { handleAgentChat: l } = await import("../handler-t7Qd1IYi.js"), y = await t.json(), { message: u, sessionId: f, history: C } = y;
|
|
523
523
|
if (!u || typeof u != "string")
|
|
524
524
|
return r.json(
|
|
525
525
|
{ error: "message is required and must be a string" },
|
package/dist/server/index.cjs
CHANGED
|
@@ -211,7 +211,7 @@ ${JSON.stringify(E,void 0,2)}`)}}}var L;(function(i){i[i.SPACE=0]="SPACE",i[i.NO
|
|
|
211
211
|
`;case L.SINGLE_INDENT:return this.indentation.getSingleIndent();default:return e}}}const Do=i=>i===L.SPACE||i===L.SINGLE_INDENT,fo=i=>i===L.SPACE||i===L.SINGLE_INDENT||i===L.NEWLINE;function ns(i,e){if(e==="standard")return i;let t=[];return i.length>=10&&i.includes(" ")&&([i,...t]=i.split(" ")),e==="tabularLeft"?i=i.padEnd(9," "):i=i.padStart(9," "),i+["",...t].join(" ")}function is(i){return Qn(i)||i===_.RESERVED_CLAUSE||i===_.RESERVED_SELECT||i===_.RESERVED_SET_OPERATION||i===_.RESERVED_JOIN||i===_.LIMIT}const it="top-level",ho="block-level";class $s{constructor(e){this.indent=e,this.indentTypes=[]}getSingleIndent(){return this.indent}getLevel(){return this.indentTypes.length}increaseTopLevel(){this.indentTypes.push(it)}increaseBlockLevel(){this.indentTypes.push(ho)}decreaseTopLevel(){this.indentTypes.length>0&&Ne(this.indentTypes)===it&&this.indentTypes.pop()}decreaseBlockLevel(){for(;this.indentTypes.length>0&&this.indentTypes.pop()===it;);}}class Po extends vs{constructor(e){super(new $s("")),this.expressionWidth=e,this.length=0,this.trailingSpace=!1}add(...e){if(e.forEach(t=>this.addToLength(t)),this.length>this.expressionWidth)throw new St;super.add(...e)}addToLength(e){if(typeof e=="string")this.length+=e.length,this.trailingSpace=!1;else{if(e===L.MANDATORY_NEWLINE||e===L.NEWLINE)throw new St;e===L.INDENT||e===L.SINGLE_INDENT||e===L.SPACE?this.trailingSpace||(this.length++,this.trailingSpace=!0):(e===L.NO_NEWLINE||e===L.NO_SPACE)&&this.trailingSpace&&(this.trailingSpace=!1,this.length--)}}}class St extends Error{}class fe{constructor({cfg:e,dialectCfg:t,params:s,layout:n,inline:r=!1}){this.inline=!1,this.nodes=[],this.index=-1,this.cfg=e,this.dialectCfg=t,this.inline=r,this.params=s,this.layout=n}format(e){for(this.nodes=e,this.index=0;this.index<this.nodes.length;this.index++)this.formatNode(this.nodes[this.index]);return this.layout}formatNode(e){this.formatComments(e.leadingComments),this.formatNodeWithoutComments(e),this.formatComments(e.trailingComments)}formatNodeWithoutComments(e){switch(e.type){case M.function_call:return this.formatFunctionCall(e);case M.parameterized_data_type:return this.formatParameterizedDataType(e);case M.array_subscript:return this.formatArraySubscript(e);case M.property_access:return this.formatPropertyAccess(e);case M.parenthesis:return this.formatParenthesis(e);case M.between_predicate:return this.formatBetweenPredicate(e);case M.case_expression:return this.formatCaseExpression(e);case M.case_when:return this.formatCaseWhen(e);case M.case_else:return this.formatCaseElse(e);case M.clause:return this.formatClause(e);case M.set_operation:return this.formatSetOperation(e);case M.limit_clause:return this.formatLimitClause(e);case M.all_columns_asterisk:return this.formatAllColumnsAsterisk(e);case M.literal:return this.formatLiteral(e);case M.identifier:return this.formatIdentifier(e);case M.parameter:return this.formatParameter(e);case M.operator:return this.formatOperator(e);case M.comma:return this.formatComma(e);case M.line_comment:return this.formatLineComment(e);case M.block_comment:return this.formatBlockComment(e);case M.disable_comment:return this.formatBlockComment(e);case M.data_type:return this.formatDataType(e);case M.keyword:return this.formatKeywordNode(e)}}formatFunctionCall(e){this.withComments(e.nameKw,()=>{this.layout.add(this.showFunctionKw(e.nameKw))}),this.formatNode(e.parenthesis)}formatParameterizedDataType(e){this.withComments(e.dataType,()=>{this.layout.add(this.showDataType(e.dataType))}),this.formatNode(e.parenthesis)}formatArraySubscript(e){let t;switch(e.array.type){case M.data_type:t=this.showDataType(e.array);break;case M.keyword:t=this.showKw(e.array);break;default:t=this.showIdentifier(e.array);break}this.withComments(e.array,()=>{this.layout.add(t)}),this.formatNode(e.parenthesis)}formatPropertyAccess(e){this.formatNode(e.object),this.layout.add(L.NO_SPACE,e.operator),this.formatNode(e.property)}formatParenthesis(e){const t=this.formatInlineExpression(e.children);t?(this.layout.add(e.openParen),this.layout.add(...t.getLayoutItems()),this.layout.add(L.NO_SPACE,e.closeParen,L.SPACE)):(this.layout.add(e.openParen,L.NEWLINE),Te(this.cfg)?(this.layout.add(L.INDENT),this.layout=this.formatSubExpression(e.children)):(this.layout.indentation.increaseBlockLevel(),this.layout.add(L.INDENT),this.layout=this.formatSubExpression(e.children),this.layout.indentation.decreaseBlockLevel()),this.layout.add(L.NEWLINE,L.INDENT,e.closeParen,L.SPACE))}formatBetweenPredicate(e){this.layout.add(this.showKw(e.betweenKw),L.SPACE),this.layout=this.formatSubExpression(e.expr1),this.layout.add(L.NO_SPACE,L.SPACE,this.showNonTabularKw(e.andKw),L.SPACE),this.layout=this.formatSubExpression(e.expr2),this.layout.add(L.SPACE)}formatCaseExpression(e){this.formatNode(e.caseKw),this.layout.indentation.increaseBlockLevel(),this.layout=this.formatSubExpression(e.expr),this.layout=this.formatSubExpression(e.clauses),this.layout.indentation.decreaseBlockLevel(),this.layout.add(L.NEWLINE,L.INDENT),this.formatNode(e.endKw)}formatCaseWhen(e){this.layout.add(L.NEWLINE,L.INDENT),this.formatNode(e.whenKw),this.layout=this.formatSubExpression(e.condition),this.formatNode(e.thenKw),this.layout=this.formatSubExpression(e.result)}formatCaseElse(e){this.layout.add(L.NEWLINE,L.INDENT),this.formatNode(e.elseKw),this.layout=this.formatSubExpression(e.result)}formatClause(e){this.isOnelineClause(e)?this.formatClauseInOnelineStyle(e):Te(this.cfg)?this.formatClauseInTabularStyle(e):this.formatClauseInIndentedStyle(e)}isOnelineClause(e){return Te(this.cfg)?this.dialectCfg.tabularOnelineClauses[e.nameKw.text]:this.dialectCfg.onelineClauses[e.nameKw.text]}formatClauseInIndentedStyle(e){this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e.nameKw),L.NEWLINE),this.layout.indentation.increaseTopLevel(),this.layout.add(L.INDENT),this.layout=this.formatSubExpression(e.children),this.layout.indentation.decreaseTopLevel()}formatClauseInOnelineStyle(e){this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e.nameKw),L.SPACE),this.layout=this.formatSubExpression(e.children)}formatClauseInTabularStyle(e){this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e.nameKw),L.SPACE),this.layout.indentation.increaseTopLevel(),this.layout=this.formatSubExpression(e.children),this.layout.indentation.decreaseTopLevel()}formatSetOperation(e){this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e.nameKw),L.NEWLINE),this.layout.add(L.INDENT),this.layout=this.formatSubExpression(e.children)}formatLimitClause(e){this.withComments(e.limitKw,()=>{this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e.limitKw))}),this.layout.indentation.increaseTopLevel(),Te(this.cfg)?this.layout.add(L.SPACE):this.layout.add(L.NEWLINE,L.INDENT),e.offset?(this.layout=this.formatSubExpression(e.offset),this.layout.add(L.NO_SPACE,",",L.SPACE),this.layout=this.formatSubExpression(e.count)):this.layout=this.formatSubExpression(e.count),this.layout.indentation.decreaseTopLevel()}formatAllColumnsAsterisk(e){this.layout.add("*",L.SPACE)}formatLiteral(e){this.layout.add(e.text,L.SPACE)}formatIdentifier(e){this.layout.add(this.showIdentifier(e),L.SPACE)}formatParameter(e){this.layout.add(this.params.get(e),L.SPACE)}formatOperator({text:e}){this.cfg.denseOperators||this.dialectCfg.alwaysDenseOperators.includes(e)?this.layout.add(L.NO_SPACE,e):e===":"?this.layout.add(L.NO_SPACE,e,L.SPACE):this.layout.add(e,L.SPACE)}formatComma(e){this.inline?this.layout.add(L.NO_SPACE,",",L.SPACE):this.layout.add(L.NO_SPACE,",",L.NEWLINE,L.INDENT)}withComments(e,t){this.formatComments(e.leadingComments),t(),this.formatComments(e.trailingComments)}formatComments(e){e&&e.forEach(t=>{t.type===M.line_comment?this.formatLineComment(t):this.formatBlockComment(t)})}formatLineComment(e){tt(e.precedingWhitespace||"")?this.layout.add(L.NEWLINE,L.INDENT,e.text,L.MANDATORY_NEWLINE,L.INDENT):this.layout.getLayoutItems().length>0?this.layout.add(L.NO_NEWLINE,L.SPACE,e.text,L.MANDATORY_NEWLINE,L.INDENT):this.layout.add(e.text,L.MANDATORY_NEWLINE,L.INDENT)}formatBlockComment(e){e.type===M.block_comment&&this.isMultilineBlockComment(e)?(this.splitBlockComment(e.text).forEach(t=>{this.layout.add(L.NEWLINE,L.INDENT,t)}),this.layout.add(L.NEWLINE,L.INDENT)):this.layout.add(e.text,L.SPACE)}isMultilineBlockComment(e){return tt(e.text)||tt(e.precedingWhitespace||"")}isDocComment(e){const t=e.split(/\n/);return/^\/\*\*?$/.test(t[0])&&t.slice(1,t.length-1).every(s=>/^\s*\*/.test(s))&&/^\s*\*\/$/.test(Ne(t))}splitBlockComment(e){return this.isDocComment(e)?e.split(/\n/).map(t=>/^\s*\*/.test(t)?" "+t.replace(/^\s*/,""):t):e.split(/\n/).map(t=>t.replace(/^\s*/,""))}formatSubExpression(e){return new fe({cfg:this.cfg,dialectCfg:this.dialectCfg,params:this.params,layout:this.layout,inline:this.inline}).format(e)}formatInlineExpression(e){const t=this.params.getPositionalParameterIndex();try{return new fe({cfg:this.cfg,dialectCfg:this.dialectCfg,params:this.params,layout:new Po(this.cfg.expressionWidth),inline:!0}).format(e)}catch(s){if(s instanceof St){this.params.setPositionalParameterIndex(t);return}else throw s}}formatKeywordNode(e){switch(e.tokenType){case _.RESERVED_JOIN:return this.formatJoin(e);case _.AND:case _.OR:case _.XOR:return this.formatLogicalOperator(e);default:return this.formatKeyword(e)}}formatJoin(e){Te(this.cfg)?(this.layout.indentation.decreaseTopLevel(),this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e),L.SPACE),this.layout.indentation.increaseTopLevel()):this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e),L.SPACE)}formatKeyword(e){this.layout.add(this.showKw(e),L.SPACE)}formatLogicalOperator(e){this.cfg.logicalOperatorNewline==="before"?Te(this.cfg)?(this.layout.indentation.decreaseTopLevel(),this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e),L.SPACE),this.layout.indentation.increaseTopLevel()):this.layout.add(L.NEWLINE,L.INDENT,this.showKw(e),L.SPACE):this.layout.add(this.showKw(e),L.NEWLINE,L.INDENT)}formatDataType(e){this.layout.add(this.showDataType(e),L.SPACE)}showKw(e){return is(e.tokenType)?ns(this.showNonTabularKw(e),this.cfg.indentStyle):this.showNonTabularKw(e)}showNonTabularKw(e){switch(this.cfg.keywordCase){case"preserve":return Ce(e.raw);case"upper":return e.text;case"lower":return e.text.toLowerCase()}}showFunctionKw(e){return is(e.tokenType)?ns(this.showNonTabularFunctionKw(e),this.cfg.indentStyle):this.showNonTabularFunctionKw(e)}showNonTabularFunctionKw(e){switch(this.cfg.functionCase){case"preserve":return Ce(e.raw);case"upper":return e.text;case"lower":return e.text.toLowerCase()}}showIdentifier(e){if(e.quoted)return e.text;switch(this.cfg.identifierCase){case"preserve":return e.text;case"upper":return e.text.toUpperCase();case"lower":return e.text.toLowerCase()}}showDataType(e){switch(this.cfg.dataTypeCase){case"preserve":return Ce(e.raw);case"upper":return e.text;case"lower":return e.text.toLowerCase()}}}class Mo{constructor(e,t){this.dialect=e,this.cfg=t,this.params=new ro(this.cfg.params)}format(e){const t=this.parse(e);return this.formatAst(t).trimEnd()}parse(e){return po(this.dialect.tokenizer).parse(e,this.cfg.paramTypes||{})}formatAst(e){return e.map(t=>this.formatStatement(t)).join(`
|
|
212
212
|
`.repeat(this.cfg.linesBetweenQueries+1))}formatStatement(e){const t=new fe({cfg:this.cfg,dialectCfg:this.dialect.formatOptions,params:this.params,layout:new vs(new $s(io(this.cfg)))}).format(e.children);return e.hasSemicolon&&(this.cfg.newlineBeforeSemicolon?t.add(L.NEWLINE,";"):t.add(L.NO_NEWLINE,";")),t.toString()}}class Le extends Error{}function Uo(i){const e=["multilineLists","newlineBeforeOpenParen","newlineBeforeCloseParen","aliasAs","commaPosition","tabulateAlias"];for(const t of e)if(t in i)throw new Le(`${t} config is no more supported.`);if(i.expressionWidth<=0)throw new Le(`expressionWidth config must be positive number. Received ${i.expressionWidth} instead.`);if(i.params&&!yo(i.params)&&console.warn('WARNING: All "params" option values should be strings.'),i.paramTypes&&!go(i.paramTypes))throw new Le("Empty regex given in custom paramTypes. That would result in matching infinite amount of parameters.");return i}function yo(i){return(i instanceof Array?i:Object.values(i)).every(t=>typeof t=="string")}function go(i){return i.custom&&Array.isArray(i.custom)?i.custom.every(e=>e.regex!==""):!0}var bo=function(i,e){var t={};for(var s in i)Object.prototype.hasOwnProperty.call(i,s)&&e.indexOf(s)<0&&(t[s]=i[s]);if(i!=null&&typeof Object.getOwnPropertySymbols=="function")for(var n=0,s=Object.getOwnPropertySymbols(i);n<s.length;n++)e.indexOf(s[n])<0&&Object.prototype.propertyIsEnumerable.call(i,s[n])&&(t[s[n]]=i[s[n]]);return t};const Vs={bigquery:"bigquery",clickhouse:"clickhouse",db2:"db2",db2i:"db2i",duckdb:"duckdb",hive:"hive",mariadb:"mariadb",mysql:"mysql",n1ql:"n1ql",plsql:"plsql",postgresql:"postgresql",redshift:"redshift",spark:"spark",sqlite:"sqlite",sql:"sql",tidb:"tidb",trino:"trino",transactsql:"transactsql",tsql:"transactsql",singlestoredb:"singlestoredb",snowflake:"snowflake"},Go=Object.keys(Vs),Fo={tabWidth:2,useTabs:!1,keywordCase:"preserve",identifierCase:"preserve",dataTypeCase:"preserve",functionCase:"preserve",indentStyle:"standard",logicalOperatorNewline:"before",expressionWidth:50,linesBetweenQueries:1,denseOperators:!1,newlineBeforeSemicolon:!1},Bo=(i,e={})=>{if(typeof e.language=="string"&&!Go.includes(e.language))throw new Le(`Unsupported SQL dialect: ${e.language}`);const t=Vs[e.language||"sql"];return Ho(i,Object.assign(Object.assign({},e),{dialect:$a[t]}))},Ho=(i,e)=>{var{dialect:t}=e,s=bo(e,["dialect"]);if(typeof i!="string")throw new Error("Invalid query argument. Expected string, instead got "+typeof i);const n=Uo(Object.assign(Object.assign({},Fo),s));return new Mo(to(t),n).format(i)};function Yo(i,e){try{return Bo(i,{language:{postgres:"postgresql",mysql:"mysql",sqlite:"sqlite",singlestore:"mysql",duckdb:"postgresql"}[e],tabWidth:2,keywordCase:"upper",indentStyle:"standard"})}catch(t){return console.warn("SQL formatting failed:",t),i}}async function wo(i,e){const t=i.getMetadata();return{cubes:dt(t,{topic:e.topic,intent:e.intent,limit:e.limit,minScore:e.minScore})}}async function vo(i,e,t){const s=t.query,n=i.validateQuery(s);if(!n.isValid)throw new Error(`Query validation failed: ${n.errors.join(", ")}`);const r=await i.executeMultiCubeQuery(s,e);return{data:r.data,annotation:r.annotation,query:s}}class ge{cubes=new Map;dbExecutor;metadataCache;cacheConfig;constructor(e){e?.databaseExecutor?this.dbExecutor=e.databaseExecutor:e?.drizzle&&(this.dbExecutor=Tt(e.drizzle,e.schema,e.engineType)),this.cacheConfig=e?.cache}setDatabaseExecutor(e){this.dbExecutor=e}getEngineType(){return this.dbExecutor?.getEngineType()}setDrizzle(e,t,s){this.dbExecutor=Tt(e,t,s)}hasExecutor(){return!!this.dbExecutor}requireExecutor(){if(!this.dbExecutor)throw new Error("Database executor not configured");return this.dbExecutor}createQueryExecutor(e=!1){const t=this.requireExecutor();return new Ms(t,e?this.cacheConfig:void 0)}formatSqlResult(e){const t=this.requireExecutor().getEngineType();return{sql:Yo(e.sql,t),params:e.params}}registerCube(e){this.validateCalculatedMeasures(e),new te(this.cubes).populateDependencies(e),this.cubes.set(e.name,e),this.invalidateMetadataCache()}validateCalculatedMeasures(e){const t=[];for(const[s,n]of Object.entries(e.measures))if(n.type==="calculated"){if(!n.calculatedSql){t.push(`Calculated measure '${e.name}.${s}' must have calculatedSql property`);continue}const r=Dn(n.calculatedSql);if(!r.isValid){t.push(`Invalid calculatedSql syntax in '${e.name}.${s}': ${r.errors.join(", ")}`);continue}const E=new Map(this.cubes);E.set(e.name,e);const a=new te(E);try{a.validateDependencies(e)}catch(A){t.push(A instanceof Error?A.message:String(A))}}if(t.length===0){const s=new Map(this.cubes);s.set(e.name,e);const n=new te(s);n.buildGraph(e);const r=n.detectCycle();r&&t.push(`Circular dependency detected in calculated measures: ${r.join(" -> ")}`)}if(t.length>0)throw new Error(`Calculated measure validation failed for cube '${e.name}':
|
|
213
213
|
${t.join(`
|
|
214
|
-
`)}`)}getCube(e){return this.cubes.get(e)}getAllCubes(){return Array.from(this.cubes.values())}getAllCubesMap(){return this.cubes}async execute(e,t,s){return this.createQueryExecutor(!0).execute(this.cubes,e,t,s)}async executeMultiCubeQuery(e,t,s){return this.execute(e,t,s)}async executeQuery(e,t,s){if(!this.cubes.get(e))throw new Error(`Cube '${e}' not found`);return this.execute(t,s)}getMetadata(){return this.metadataCache?this.metadataCache:(this.metadataCache=Array.from(this.cubes.values()).map(e=>this.generateCubeMetadata(e)),this.metadataCache)}getColumnName(e){if(e&&e.name||e&&e.columnType&&e.name)return e.name;if(typeof e=="string")return e;if(e&&typeof e=="object"){if(e._.name)return e._.name;if(e.name)return e.name;if(e.columnName)return e.columnName}return"unknown_column"}static DEFAULT_TIME_GRANULARITIES=["year","quarter","month","week","day","hour"];generateCubeMetadata(e){const t=Object.keys(e.measures),s=Object.keys(e.dimensions),n=new Array(t.length),r=new Array(s.length);for(let T=0;T<t.length;T++){const R=t[T],l=e.measures[R];let S;l.drillMembers&&l.drillMembers.length>0&&(S=l.drillMembers.map(N=>N.includes(".")?N:`${e.name}.${N}`)),n[T]={name:`${e.name}.${R}`,title:l.title||R,shortTitle:l.title||R,type:l.type,format:void 0,description:l.description,synonyms:l.synonyms,drillMembers:S}}for(let T=0;T<s.length;T++){const R=s[T],l=e.dimensions[R];let S;l.type==="time"&&(S=l.granularities||ge.DEFAULT_TIME_GRANULARITIES),r[T]={name:`${e.name}.${R}`,title:l.title||R,shortTitle:l.title||R,type:l.type,format:void 0,description:l.description,synonyms:l.synonyms,granularities:S}}const E=[];if(e.joins)for(const[,T]of Object.entries(e.joins)){const R=typeof T.targetCube=="function"?T.targetCube():T.targetCube;E.push({targetCube:R.name,relationship:T.relationship,joinFields:T.on.map(l=>({sourceField:this.getColumnName(l.source),targetField:this.getColumnName(l.target)}))})}const a=[];if(e.hierarchies)for(const[,T]of Object.entries(e.hierarchies))a.push({name:T.name,title:T.title||T.name,cubeName:e.name,levels:T.levels.map(R=>R.includes(".")?R:`${e.name}.${R}`)});return{name:e.name,title:e.title||e.name,description:e.description,exampleQuestions:e.exampleQuestions,measures:n,dimensions:r,segments:[],relationships:E.length>0?E:void 0,hierarchies:a.length>0?a:void 0,meta:e.meta}}async generateSQL(e,t,s){const n=this.getCube(e);if(!n)throw new Error(`Cube '${e}' not found`);const E=await this.createQueryExecutor().generateSQL(n,t,s);return this.formatSqlResult(E)}async generateMultiCubeSQL(e,t){const n=await this.createQueryExecutor().generateMultiCubeSQL(this.cubes,e,t);return this.formatSqlResult(n)}async dryRun(e,t){const n=await this.createQueryExecutor().dryRunSQL(this.cubes,e,t);return this.formatSqlResult(n)}async dryRunFunnel(e,t){return this.dryRun(e,t)}async dryRunFlow(e,t){return this.dryRun(e,t)}async dryRunRetention(e,t){return this.dryRun(e,t)}async explainQuery(e,t,s){return this.createQueryExecutor().explainQuery(this.cubes,e,t,s)}hasCube(e){return this.cubes.has(e)}removeCube(e){const t=this.cubes.delete(e);return t&&this.invalidateMetadataCache(),t}clearCubes(){this.cubes.clear(),this.invalidateMetadataCache()}invalidateMetadataCache(){this.metadataCache=void 0}getCubeNames(){return Array.from(this.cubes.keys())}validateQuery(e){return Ws(this.cubes,e)}analyzeQuery(e,t){return this.createQueryExecutor(!0).analyzeQuery(this.cubes,e,t)}}function $o(i){const e=[];return i.timeDimensions?.some(t=>t.compareDateRange&&t.compareDateRange.length>=2)&&e.push("comparison"),i.funnel!==void 0&&i.funnel.steps?.length>=2&&e.push("funnel"),i.flow!==void 0&&i.flow.startingStep!==void 0&&i.flow.eventDimension!==void 0&&e.push("flow"),i.retention!==void 0&&i.retention.timeDimension!=null&&i.retention.bindingKey!=null&&e.push("retention"),e.length===0?[]:e}function Ws(i,e){const t=[],s=$o(e);if(s.length>1)return t.push(`Query contains multiple query modes: ${s.join(", ")}`),{isValid:!1,errors:t};const n={funnel:()=>{const E=e.funnel.bindingKey;if(typeof E=="string"){const[a]=E.split(".");a&&!i.has(a)&&t.push(`Funnel binding key cube not found: ${a}`)}else if(Array.isArray(E))for(const a of E)i.has(a.cube)||t.push(`Funnel binding key cube not found: ${a.cube}`)},flow:()=>{const E=e.flow.bindingKey;if(typeof E=="string"){const[a]=E.split(".");a&&!i.has(a)&&t.push(`Flow binding key cube not found: ${a}`)}},retention:()=>{const E=e.retention,a=Vo(E.timeDimension);a&&!i.has(a)&&t.push(`Retention cube not found: ${a}`);const A=E.bindingKey;if(typeof A=="string"){const[T]=A.split(".");T&&!i.has(T)&&t.push(`Retention binding key cube not found: ${T}`)}else if(Array.isArray(A))for(const T of A)i.has(T.cube)||t.push(`Retention binding key cube not found: ${T.cube}`);if(E.breakdownDimensions&&Array.isArray(E.breakdownDimensions))for(const T of E.breakdownDimensions){const[R]=T.split(".");R&&!i.has(R)&&t.push(`Retention breakdown cube not found: ${R}`)}}};if(s.length===1&&s[0]!=="comparison"){const E=s[0];return n[E](),{isValid:t.length===0,errors:t}}const r=new Set;if(e.measures)for(const E of e.measures){const[a,A]=E.split(".");if(!a||!A){t.push(`Invalid measure format: ${E}. Expected format: 'CubeName.fieldName'`);continue}r.add(a);const T=i.get(a);if(!T){t.push(`Cube '${a}' not found (referenced in measure '${E}')`);continue}if(!T.measures[A]){const R=A===a?`. Did you mean one of: ${Object.keys(T.measures).slice(0,5).map(l=>`'${a}.${l}'`).join(", ")}?`:"";t.push(`Measure '${A}' not found on cube '${a}'${R}`)}}if(e.dimensions)for(const E of e.dimensions){const[a,A]=E.split(".");if(!a||!A){t.push(`Invalid dimension format: ${E}. Expected format: 'CubeName.fieldName'`);continue}r.add(a);const T=i.get(a);if(!T){t.push(`Cube '${a}' not found (referenced in dimension '${E}')`);continue}if(!T.dimensions[A]){const R=A===a?`. Did you mean one of: ${Object.keys(T.dimensions).slice(0,5).map(l=>`'${a}.${l}'`).join(", ")}?`:"";t.push(`Dimension '${A}' not found on cube '${a}'${R}`)}}if(e.timeDimensions)for(const E of e.timeDimensions){const[a,A]=E.dimension.split(".");if(!a||!A){t.push(`Invalid timeDimension format: ${E.dimension}. Expected format: 'CubeName.fieldName'`);continue}r.add(a);const T=i.get(a);if(!T){t.push(`Cube '${a}' not found (referenced in timeDimension '${E.dimension}')`);continue}T.dimensions[A]||t.push(`TimeDimension '${A}' not found on cube '${a}' (must be a dimension with time type)`)}if(e.filters)for(const E of e.filters)xs(E,i,t,r);return r.size===0&&t.push("Query must reference at least one cube through measures, dimensions, or filters"),{isValid:t.length===0,errors:t}}function xs(i,e,t,s){if("and"in i||"or"in i){const a=i.and||i.or||[];for(const A of a)xs(A,e,t,s);return}if(!("member"in i)){t.push("Filter must have a member field");return}const[n,r]=i.member.split(".");if(!n||!r){t.push(`Invalid filter member format: ${i.member}. Expected format: 'CubeName.fieldName'`);return}s.add(n);const E=e.get(n);if(!E){t.push(`Cube '${n}' not found (referenced in filter '${i.member}')`);return}if(!E.dimensions[r]&&!E.measures[r]){const a=r===n?`. Did you mean one of: ${[...Object.keys(E.dimensions),...Object.keys(E.measures)].slice(0,5).map(A=>`'${n}.${A}'`).join(", ")}?`:"";t.push(`Filter field '${r}' not found on cube '${n}' (must be a dimension or measure)${a}`)}}function Vo(i){if(typeof i=="string"){const[e]=i.split(".");return e||null}return i.cube}class Wo{cache=new Map;defaultTtlMs;maxEntries;cleanupIntervalId;accessOrder=[];constructor(e={}){this.defaultTtlMs=e.defaultTtlMs??3e5,this.maxEntries=e.maxEntries;const t=e.cleanupIntervalMs??6e4;t>0&&(this.cleanupIntervalId=setInterval(()=>{this.cleanup()},t),typeof this.cleanupIntervalId=="object"&&"unref"in this.cleanupIntervalId&&this.cleanupIntervalId.unref())}async get(e){const t=this.cache.get(e);if(!t)return null;const s=Date.now();return s>t.expiresAt?(this.cache.delete(e),this.removeFromAccessOrder(e),null):(this.touchAccessOrder(e),{value:t.value,metadata:{cachedAt:t.cachedAt,ttlMs:t.ttlMs,ttlRemainingMs:t.expiresAt-s}})}async set(e,t,s){const n=s??this.defaultTtlMs,r=Date.now();this.maxEntries&&this.cache.size>=this.maxEntries&&!this.cache.has(e)&&this.evictOldest(),this.cache.set(e,{value:t,cachedAt:r,ttlMs:n,expiresAt:r+n}),this.touchAccessOrder(e)}async delete(e){const t=this.cache.delete(e);return t&&this.removeFromAccessOrder(e),t}async deletePattern(e){let t=0;if(e.endsWith("*")){const s=e.slice(0,-1);for(const n of this.cache.keys())n.startsWith(s)&&(this.cache.delete(n),this.removeFromAccessOrder(n),t++)}else if(e.startsWith("*")){const s=e.slice(1);for(const n of this.cache.keys())n.endsWith(s)&&(this.cache.delete(n),this.removeFromAccessOrder(n),t++)}else if(e.includes("*")){const[s,n]=e.split("*");for(const r of this.cache.keys())r.startsWith(s)&&r.endsWith(n)&&(this.cache.delete(r),this.removeFromAccessOrder(r),t++)}else this.cache.delete(e)&&(this.removeFromAccessOrder(e),t++);return t}async has(e){const t=this.cache.get(e);return t?Date.now()>t.expiresAt?(this.cache.delete(e),this.removeFromAccessOrder(e),!1):!0:!1}async close(){this.cleanupIntervalId&&(clearInterval(this.cleanupIntervalId),this.cleanupIntervalId=void 0),this.cache.clear(),this.accessOrder=[]}cleanup(){const e=Date.now();let t=0;for(const[s,n]of this.cache.entries())e>n.expiresAt&&(this.cache.delete(s),this.removeFromAccessOrder(s),t++);return t}size(){return this.cache.size}clear(){this.cache.clear(),this.accessOrder=[]}stats(){return{size:this.cache.size,maxEntries:this.maxEntries,defaultTtlMs:this.defaultTtlMs}}touchAccessOrder(e){this.removeFromAccessOrder(e),this.accessOrder.push(e)}removeFromAccessOrder(e){const t=this.accessOrder.indexOf(e);t>-1&&this.accessOrder.splice(t,1)}evictOldest(){for(;this.accessOrder.length>0&&this.maxEntries&&this.cache.size>=this.maxEntries;){const e=this.accessOrder.shift();e&&this.cache.delete(e)}}}const qs=`You are a security validator for a data analytics system. Your ONLY job is to determine if a user's input is a valid data analysis request.
|
|
214
|
+
`)}`)}getCube(e){return this.cubes.get(e)}getAllCubes(){return Array.from(this.cubes.values())}getAllCubesMap(){return this.cubes}async execute(e,t,s){return this.createQueryExecutor(!0).execute(this.cubes,e,t,s)}async executeMultiCubeQuery(e,t,s){return this.execute(e,t,s)}async executeQuery(e,t,s){if(!this.cubes.get(e))throw new Error(`Cube '${e}' not found`);return this.execute(t,s)}getMetadata(){return this.metadataCache?this.metadataCache:(this.metadataCache=Array.from(this.cubes.values()).map(e=>this.generateCubeMetadata(e)),this.metadataCache)}getColumnName(e){if(e&&e.name||e&&e.columnType&&e.name)return e.name;if(typeof e=="string")return e;if(e&&typeof e=="object"){if(e._.name)return e._.name;if(e.name)return e.name;if(e.columnName)return e.columnName}return"unknown_column"}static DEFAULT_TIME_GRANULARITIES=["year","quarter","month","week","day","hour"];generateCubeMetadata(e){const t=Object.keys(e.measures),s=Object.keys(e.dimensions),n=new Array(t.length),r=new Array(s.length);for(let T=0;T<t.length;T++){const R=t[T],l=e.measures[R];let S;l.drillMembers&&l.drillMembers.length>0&&(S=l.drillMembers.map(N=>N.includes(".")?N:`${e.name}.${N}`)),n[T]={name:`${e.name}.${R}`,title:l.title||R,shortTitle:l.title||R,type:l.type,format:void 0,description:l.description,synonyms:l.synonyms,drillMembers:S}}for(let T=0;T<s.length;T++){const R=s[T],l=e.dimensions[R];let S;l.type==="time"&&(S=l.granularities||ge.DEFAULT_TIME_GRANULARITIES),r[T]={name:`${e.name}.${R}`,title:l.title||R,shortTitle:l.title||R,type:l.type,format:void 0,description:l.description,synonyms:l.synonyms,granularities:S}}const E=[];if(e.joins)for(const[,T]of Object.entries(e.joins)){const R=typeof T.targetCube=="function"?T.targetCube():T.targetCube;E.push({targetCube:R.name,relationship:T.relationship,joinFields:T.on.map(l=>({sourceField:this.getColumnName(l.source),targetField:this.getColumnName(l.target)}))})}const a=[];if(e.hierarchies)for(const[,T]of Object.entries(e.hierarchies))a.push({name:T.name,title:T.title||T.name,cubeName:e.name,levels:T.levels.map(R=>R.includes(".")?R:`${e.name}.${R}`)});return{name:e.name,title:e.title||e.name,description:e.description,exampleQuestions:e.exampleQuestions,measures:n,dimensions:r,segments:[],relationships:E.length>0?E:void 0,hierarchies:a.length>0?a:void 0,meta:e.meta}}async generateSQL(e,t,s){const n=this.getCube(e);if(!n)throw new Error(`Cube '${e}' not found`);const E=await this.createQueryExecutor().generateSQL(n,t,s);return this.formatSqlResult(E)}async generateMultiCubeSQL(e,t){const n=await this.createQueryExecutor().generateMultiCubeSQL(this.cubes,e,t);return this.formatSqlResult(n)}async dryRun(e,t){const n=await this.createQueryExecutor().dryRunSQL(this.cubes,e,t);return this.formatSqlResult(n)}async dryRunFunnel(e,t){return this.dryRun(e,t)}async dryRunFlow(e,t){return this.dryRun(e,t)}async dryRunRetention(e,t){return this.dryRun(e,t)}async explainQuery(e,t,s){return this.createQueryExecutor().explainQuery(this.cubes,e,t,s)}hasCube(e){return this.cubes.has(e)}unregisterCube(e){return this.removeCube(e)}removeCube(e){const t=this.cubes.delete(e);return t&&this.invalidateMetadataCache(),t}clearCubes(){this.cubes.clear(),this.invalidateMetadataCache()}invalidateMetadataCache(){this.metadataCache=void 0}getCubeNames(){return Array.from(this.cubes.keys())}validateQuery(e){return Ws(this.cubes,e)}analyzeQuery(e,t){return this.createQueryExecutor(!0).analyzeQuery(this.cubes,e,t)}}function $o(i){const e=[];return i.timeDimensions?.some(t=>t.compareDateRange&&t.compareDateRange.length>=2)&&e.push("comparison"),i.funnel!==void 0&&i.funnel.steps?.length>=2&&e.push("funnel"),i.flow!==void 0&&i.flow.startingStep!==void 0&&i.flow.eventDimension!==void 0&&e.push("flow"),i.retention!==void 0&&i.retention.timeDimension!=null&&i.retention.bindingKey!=null&&e.push("retention"),e.length===0?[]:e}function Ws(i,e){const t=[],s=$o(e);if(s.length>1)return t.push(`Query contains multiple query modes: ${s.join(", ")}`),{isValid:!1,errors:t};const n={funnel:()=>{const E=e.funnel.bindingKey;if(typeof E=="string"){const[a]=E.split(".");a&&!i.has(a)&&t.push(`Funnel binding key cube not found: ${a}`)}else if(Array.isArray(E))for(const a of E)i.has(a.cube)||t.push(`Funnel binding key cube not found: ${a.cube}`)},flow:()=>{const E=e.flow.bindingKey;if(typeof E=="string"){const[a]=E.split(".");a&&!i.has(a)&&t.push(`Flow binding key cube not found: ${a}`)}},retention:()=>{const E=e.retention,a=Vo(E.timeDimension);a&&!i.has(a)&&t.push(`Retention cube not found: ${a}`);const A=E.bindingKey;if(typeof A=="string"){const[T]=A.split(".");T&&!i.has(T)&&t.push(`Retention binding key cube not found: ${T}`)}else if(Array.isArray(A))for(const T of A)i.has(T.cube)||t.push(`Retention binding key cube not found: ${T.cube}`);if(E.breakdownDimensions&&Array.isArray(E.breakdownDimensions))for(const T of E.breakdownDimensions){const[R]=T.split(".");R&&!i.has(R)&&t.push(`Retention breakdown cube not found: ${R}`)}}};if(s.length===1&&s[0]!=="comparison"){const E=s[0];return n[E](),{isValid:t.length===0,errors:t}}const r=new Set;if(e.measures)for(const E of e.measures){const[a,A]=E.split(".");if(!a||!A){t.push(`Invalid measure format: ${E}. Expected format: 'CubeName.fieldName'`);continue}r.add(a);const T=i.get(a);if(!T){t.push(`Cube '${a}' not found (referenced in measure '${E}')`);continue}if(!T.measures[A]){const R=A===a?`. Did you mean one of: ${Object.keys(T.measures).slice(0,5).map(l=>`'${a}.${l}'`).join(", ")}?`:"";t.push(`Measure '${A}' not found on cube '${a}'${R}`)}}if(e.dimensions)for(const E of e.dimensions){const[a,A]=E.split(".");if(!a||!A){t.push(`Invalid dimension format: ${E}. Expected format: 'CubeName.fieldName'`);continue}r.add(a);const T=i.get(a);if(!T){t.push(`Cube '${a}' not found (referenced in dimension '${E}')`);continue}if(!T.dimensions[A]){const R=A===a?`. Did you mean one of: ${Object.keys(T.dimensions).slice(0,5).map(l=>`'${a}.${l}'`).join(", ")}?`:"";t.push(`Dimension '${A}' not found on cube '${a}'${R}`)}}if(e.timeDimensions)for(const E of e.timeDimensions){const[a,A]=E.dimension.split(".");if(!a||!A){t.push(`Invalid timeDimension format: ${E.dimension}. Expected format: 'CubeName.fieldName'`);continue}r.add(a);const T=i.get(a);if(!T){t.push(`Cube '${a}' not found (referenced in timeDimension '${E.dimension}')`);continue}T.dimensions[A]||t.push(`TimeDimension '${A}' not found on cube '${a}' (must be a dimension with time type)`)}if(e.filters)for(const E of e.filters)xs(E,i,t,r);return r.size===0&&t.push("Query must reference at least one cube through measures, dimensions, or filters"),{isValid:t.length===0,errors:t}}function xs(i,e,t,s){if("and"in i||"or"in i){const a=i.and||i.or||[];for(const A of a)xs(A,e,t,s);return}if(!("member"in i)){t.push("Filter must have a member field");return}const[n,r]=i.member.split(".");if(!n||!r){t.push(`Invalid filter member format: ${i.member}. Expected format: 'CubeName.fieldName'`);return}s.add(n);const E=e.get(n);if(!E){t.push(`Cube '${n}' not found (referenced in filter '${i.member}')`);return}if(!E.dimensions[r]&&!E.measures[r]){const a=r===n?`. Did you mean one of: ${[...Object.keys(E.dimensions),...Object.keys(E.measures)].slice(0,5).map(A=>`'${n}.${A}'`).join(", ")}?`:"";t.push(`Filter field '${r}' not found on cube '${n}' (must be a dimension or measure)${a}`)}}function Vo(i){if(typeof i=="string"){const[e]=i.split(".");return e||null}return i.cube}class Wo{cache=new Map;defaultTtlMs;maxEntries;cleanupIntervalId;accessOrder=[];constructor(e={}){this.defaultTtlMs=e.defaultTtlMs??3e5,this.maxEntries=e.maxEntries;const t=e.cleanupIntervalMs??6e4;t>0&&(this.cleanupIntervalId=setInterval(()=>{this.cleanup()},t),typeof this.cleanupIntervalId=="object"&&"unref"in this.cleanupIntervalId&&this.cleanupIntervalId.unref())}async get(e){const t=this.cache.get(e);if(!t)return null;const s=Date.now();return s>t.expiresAt?(this.cache.delete(e),this.removeFromAccessOrder(e),null):(this.touchAccessOrder(e),{value:t.value,metadata:{cachedAt:t.cachedAt,ttlMs:t.ttlMs,ttlRemainingMs:t.expiresAt-s}})}async set(e,t,s){const n=s??this.defaultTtlMs,r=Date.now();this.maxEntries&&this.cache.size>=this.maxEntries&&!this.cache.has(e)&&this.evictOldest(),this.cache.set(e,{value:t,cachedAt:r,ttlMs:n,expiresAt:r+n}),this.touchAccessOrder(e)}async delete(e){const t=this.cache.delete(e);return t&&this.removeFromAccessOrder(e),t}async deletePattern(e){let t=0;if(e.endsWith("*")){const s=e.slice(0,-1);for(const n of this.cache.keys())n.startsWith(s)&&(this.cache.delete(n),this.removeFromAccessOrder(n),t++)}else if(e.startsWith("*")){const s=e.slice(1);for(const n of this.cache.keys())n.endsWith(s)&&(this.cache.delete(n),this.removeFromAccessOrder(n),t++)}else if(e.includes("*")){const[s,n]=e.split("*");for(const r of this.cache.keys())r.startsWith(s)&&r.endsWith(n)&&(this.cache.delete(r),this.removeFromAccessOrder(r),t++)}else this.cache.delete(e)&&(this.removeFromAccessOrder(e),t++);return t}async has(e){const t=this.cache.get(e);return t?Date.now()>t.expiresAt?(this.cache.delete(e),this.removeFromAccessOrder(e),!1):!0:!1}async close(){this.cleanupIntervalId&&(clearInterval(this.cleanupIntervalId),this.cleanupIntervalId=void 0),this.cache.clear(),this.accessOrder=[]}cleanup(){const e=Date.now();let t=0;for(const[s,n]of this.cache.entries())e>n.expiresAt&&(this.cache.delete(s),this.removeFromAccessOrder(s),t++);return t}size(){return this.cache.size}clear(){this.cache.clear(),this.accessOrder=[]}stats(){return{size:this.cache.size,maxEntries:this.maxEntries,defaultTtlMs:this.defaultTtlMs}}touchAccessOrder(e){this.removeFromAccessOrder(e),this.accessOrder.push(e)}removeFromAccessOrder(e){const t=this.accessOrder.indexOf(e);t>-1&&this.accessOrder.splice(t,1)}evictOldest(){for(;this.accessOrder.length>0&&this.maxEntries&&this.cache.size>=this.maxEntries;){const e=this.accessOrder.shift();e&&this.cache.delete(e)}}}const qs=`You are a security validator for a data analytics system. Your ONLY job is to determine if a user's input is a valid data analysis request.
|
|
215
215
|
|
|
216
216
|
USER INPUT TO VALIDATE:
|
|
217
217
|
{USER_PROMPT}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -4522,6 +4522,11 @@ export declare class SemanticLayerCompiler {
|
|
|
4522
4522
|
* Check if a cube exists
|
|
4523
4523
|
*/
|
|
4524
4524
|
hasCube(name: string): boolean;
|
|
4525
|
+
/**
|
|
4526
|
+
* Unregister a cube by name.
|
|
4527
|
+
* Returns true if the cube existed and was removed, false if not found.
|
|
4528
|
+
*/
|
|
4529
|
+
unregisterCube(name: string): boolean;
|
|
4525
4530
|
/**
|
|
4526
4531
|
* Remove a cube
|
|
4527
4532
|
*/
|
package/dist/server/index.js
CHANGED
|
@@ -29492,6 +29492,13 @@ ${t.join(`
|
|
|
29492
29492
|
hasCube(e) {
|
|
29493
29493
|
return this.cubes.has(e);
|
|
29494
29494
|
}
|
|
29495
|
+
/**
|
|
29496
|
+
* Unregister a cube by name.
|
|
29497
|
+
* Returns true if the cube existed and was removed, false if not found.
|
|
29498
|
+
*/
|
|
29499
|
+
unregisterCube(e) {
|
|
29500
|
+
return this.removeCube(e);
|
|
29501
|
+
}
|
|
29495
29502
|
/**
|
|
29496
29503
|
* Remove a cube
|
|
29497
29504
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-cube",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.23",
|
|
4
4
|
"description": "Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.",
|
|
5
5
|
"main": "./dist/server/index.js",
|
|
6
6
|
"types": "./dist/server/index.d.ts",
|