@bluemarble/bi 0.6.2 → 0.6.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/server/compile.ts","../../src/server/defineModel.ts","../../src/server/modelFile.ts","../../src/server/normalize.ts","../../src/server/devtools.ts","../../src/server/runQuery.ts"],"sourcesContent":["import { type AggregateMeasure, aggregateSql, isAggregate } from '../model'\nimport { type Edge, joinPlan, measureHome, modelEdges, reachFrom, referencedTables } from '../star'\nimport type { DashboardQuery } from '../types'\nimport type { ModelDef } from './defineModel'\n\nexport class DashboardQueryError extends Error {}\n\n/** Pedaço da consulta, pronto para o devtools medir: devolve uma linha com `n` (linhas do pedaço). */\nexport type CompiledPart = { name: string; sql: string; params: (string | number)[] }\n\nexport type CompiledQuery = {\n sql: string\n params: (string | number)[]\n /** Pedaços do SQL, para o devtools medir cada um: `fato:<tabela>` (a agregação de cada tabela). */\n parts: CompiledPart[]\n}\n\nexport type CompileOptions = {\n /** `LIMIT` em cada tabela: o devtools valida o SQL do modelo com poucas linhas, sem custo. */\n sampleLimit?: number\n}\n\nconst MAX_FILTER_VALUES = 1000\n\nconst quote = (identifier: string) => `\\`${identifier}\\``\n\n/**\n * Expressão de uma medida. `leaf` diz como sai cada agregação (`sum`, `count`, `avg`...): a coluna\n * já agregada pela tabela de origem.\n */\nfunction measureSql(\n model: ModelDef,\n name: string,\n leaf: (name: string, measure: AggregateMeasure) => string,\n stack: string[] = []\n): string {\n if (stack.includes(name))\n throw new DashboardQueryError(`Medida circular: ${[...stack, name].join(' -> ')}`)\n const measure = model.measures[name]\n if (!measure) throw new DashboardQueryError(`Medida desconhecida: \"${name}\"`)\n if (isAggregate(measure)) return leaf(name, measure)\n const next = [...stack, name]\n const ref = (other: string) => measureSql(model, other, leaf, next)\n if (measure.type === 'ratio')\n return `(${ref(measure.numerator)}) / NULLIF(${ref(measure.denominator)}, 0)`\n return `(${measure.expression.replace(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, other) => `(${ref(other)})`)})`\n}\n\n/** Agregações de que as medidas pedidas dependem (razões e fórmulas abertas). */\nfunction leafMeasures(model: ModelDef, names: string[]): string[] {\n const leaves = new Set<string>()\n for (const name of names)\n measureSql(model, name, (leaf, measure) => {\n leaves.add(leaf)\n return measure.sql\n })\n return [...leaves]\n}\n\ntype Filter = { dimension: string; clause: string; params: (string | number)[] }\n\nfunction compileFilters(model: ModelDef, query: DashboardQuery): Filter[] {\n const filters: Filter[] = []\n for (const [name, values] of Object.entries(query.filters ?? {})) {\n const dimension = model.dimensions[name]\n if (!dimension) throw new DashboardQueryError(`Filtro em dimensão desconhecida: \"${name}\"`)\n if (!Array.isArray(values) || !values.length) continue\n if (values.length > MAX_FILTER_VALUES)\n throw new DashboardQueryError(`Valores demais no filtro \"${name}\"`)\n for (const value of values) {\n const valid = dimension.type === 'number' ? Number.isFinite(value) : typeof value === 'string'\n if (!valid) throw new DashboardQueryError(`Valor inválido no filtro \"${name}\": ${value}`)\n }\n filters.push({\n dimension: name,\n clause: `${dimension.sql} IN (${values.map(() => '?').join(', ')})`,\n params: values\n })\n }\n return filters\n}\n\n/**\n * Transforma uma consulta (dimensões + medidas + filtros) em SQL parametrizado (ver `compileStar`).\n *\n * Nomes de dimensões/medidas só são aceitos se existirem no modelo; valores de filtro vão sempre\n * como parâmetro — nada vindo da request é concatenado no SQL.\n */\nexport function compileQuery(\n model: ModelDef,\n query: DashboardQuery,\n options: CompileOptions = {}\n): CompiledQuery {\n const dimensions = query.dimensions ?? []\n const measures = query.measures ?? []\n if (!dimensions.length && !measures.length)\n throw new DashboardQueryError('Informe ao menos uma dimensão ou medida')\n\n for (const name of dimensions) {\n if (!model.dimensions[name]) throw new DashboardQueryError(`Dimensão desconhecida: \"${name}\"`)\n }\n\n const filters = compileFilters(model, query)\n return compileStar(model, dimensions, measures, filters, options)\n}\n\nconst range = (count: number) => Array.from({ length: count }, (_, index) => index + 1).join(', ')\n\n/**\n * Cada tabela de origem (a das medidas pedidas) vira uma agregação no próprio\n * grão, com LEFT JOIN só nas tabelas que levam às dimensões e filtros usados:\n *\n * SELECT <dimensões>, SUM(<medida>) FROM fPesagem\n * LEFT JOIN dOperacao ON dOperacao.id = fPesagem.navio_operacao_id\n * WHERE <filtros> GROUP BY <dimensões>\n *\n * Com medidas de mais de uma fato, cada uma agrega separada e o resultado é somado por dimensão\n * (`UNION ALL` + `GROUP BY`) — juntar duas fatos antes de somar multiplicaria os valores. Razões e\n * fórmulas são calculadas no fim, sobre os totais.\n *\n * Filtro numa dimensão que a fato não alcança é ignorado por ela (como no Power BI, a medida não é\n * filtrada); agrupar por uma dimensão que ela não alcança é erro.\n */\nfunction compileStar(\n model: ModelDef,\n dimensions: string[],\n measures: string[],\n filters: Filter[],\n { sampleLimit }: CompileOptions\n): CompiledQuery {\n const { tables } = model\n const tableNames = Object.keys(tables)\n const edges = modelEdges(model)\n const tableOf = (sql: string) => referencedTables(sql, tableNames)\n const dimensionTable = (name: string) => tableOf(model.dimensions[name].sql)[0]\n\n // Agregações agrupadas pela tabela onde agregam\n const groups = new Map<string, string[]>()\n const leafOf = (name: string) => model.measures[name] as AggregateMeasure\n for (const leaf of leafMeasures(model, measures)) {\n const measure = model.measures[leaf]\n if (!isAggregate(measure)) continue\n const home = measureHome(tableOf(measure.sql), edges)\n if (!home) throw new DashboardQueryError(`Medida \"${leaf}\" sem tabela de origem`)\n groups.set(home, [...(groups.get(home) ?? []), leaf])\n }\n if (!groups.size) groups.set(dimensionHome(model, dimensions, filters, edges), [])\n\n const source = (name: string) => {\n const sql = tables[name].sql\n return sampleLimit === undefined\n ? `(${sql})`\n : `(SELECT * FROM (${sql}) t LIMIT ${Math.max(0, Math.trunc(sampleLimit))})`\n }\n\n const aggregate = (home: string, leaves: string[]) => {\n const { paths } = reachFrom(home, edges)\n for (const name of dimensions)\n if (!paths.has(dimensionTable(name)))\n throw new DashboardQueryError(\n leaves.length\n ? `A dimensão \"${name}\" (${dimensionTable(name)}) não se relaciona com ${home}, de onde vêm ${leaves.join(', ')}`\n : `As dimensões ${dimensions.join(', ')} não se relacionam entre si`\n )\n const applied = filters.filter((filter) => paths.has(dimensionTable(filter.dimension)))\n const needed = new Set([\n ...dimensions.map(dimensionTable),\n ...applied.map((filter) => dimensionTable(filter.dimension)),\n ...leaves.flatMap((leaf) => tableOf(leafOf(leaf).sql))\n ])\n const joins = joinPlan(home, needed, edges).map(\n ({ table, edge }) =>\n `${edge.inner ? 'JOIN' : 'LEFT JOIN'} ${source(table)} ${quote(table)}\\n ON ${quote(table)}.${quote(edge.to.column)} = ${quote(edge.from.table)}.${quote(edge.from.column)}`\n )\n const select = [\n ...dimensions.map((name) => `${model.dimensions[name].sql} AS ${quote(name)}`),\n ...leaves.map((leaf) => `${aggregateSql(leafOf(leaf))} AS ${quote(leaf)}`)\n ]\n const sql = [\n `SELECT ${select.join(',\\n ')}`,\n `FROM ${source(home)} ${quote(home)}`,\n ...joins,\n applied.length ? `WHERE ${applied.map((filter) => filter.clause).join(' AND ')}` : '',\n dimensions.length ? `GROUP BY ${range(dimensions.length)}` : ''\n ]\n .filter(Boolean)\n .join('\\n')\n return { home, sql, params: applied.flatMap((filter) => filter.params) }\n }\n\n const aggregates = [...groups].map(([home, leaves]) => aggregate(home, leaves))\n const parts = aggregates.map(({ home, sql, params }) => ({\n name: `fato:${home}`,\n sql: `SELECT COUNT(*) AS n FROM (${sql}) g`,\n params\n }))\n const orderBy = dimensions.length ? `\\nORDER BY ${range(dimensions.length)}` : ''\n\n // Uma tabela só: as medidas saem direto da agregação\n if (aggregates.length === 1) {\n const [only] = aggregates\n if (!measures.length || measures.every((name) => isAggregate(model.measures[name])))\n return { sql: `${only.sql}${orderBy}`, params: only.params, parts }\n }\n\n // Várias (ou razões/fórmulas): junta as colunas já agregadas e calcula as medidas por cima. Cada\n // fato tem uma linha por grupo e as colunas das outras vêm NULL, então o SUM de fora só pega o\n // valor da própria fato — vale para qualquer agregação (média, contagem distinta...), sem reagregar\n const columns = [...dimensions, ...[...groups.values()].flat()]\n const union = aggregates\n .map(({ home }, index) => {\n const own = new Set(groups.get(home))\n const select = columns.map((column) =>\n dimensions.includes(column) || own.has(column) ? quote(column) : `NULL AS ${quote(column)}`\n )\n return `SELECT ${select.join(', ')} FROM ${quote(`__fato${index}`)}`\n })\n .join('\\n UNION ALL ')\n const leaf = (name: string) => `SUM(${quote('__u')}.${quote(name)})`\n const select = [\n ...dimensions.map((name) => `${quote('__u')}.${quote(name)} AS ${quote(name)}`),\n ...measures.map((name) => `${measureSql(model, name, leaf)} AS ${quote(name)}`)\n ]\n const sql = [\n `WITH ${aggregates.map(({ sql }, index) => `${quote(`__fato${index}`)} AS (\\n${sql}\\n)`).join(',\\n')}`,\n `SELECT ${select.join(',\\n ')}`,\n `FROM (\\n ${union}\\n) ${quote('__u')}`,\n dimensions.length ? `GROUP BY ${range(dimensions.length)}${orderBy}` : ''\n ]\n .filter(Boolean)\n .join('\\n')\n return { sql, params: aggregates.flatMap((item) => item.params), parts }\n}\n\n/**\n * Consulta só de dimensões (ex.: opções de um filtro): parte da tabela que alcança todas as\n * dimensões pedidas e o maior número de filtros ativos — com \"Mês = Janeiro\", a lista de navios sai\n * da fato que liga navio e calendário, e só mostra quem tem lançamento em janeiro. Sem filtro, a\n * própria tabela da dimensão (lista tudo, mesmo sem lançamento).\n */\nfunction dimensionHome(\n model: ModelDef,\n dimensions: string[],\n filters: Filter[],\n edges: Edge[]\n): string {\n const tableNames = Object.keys(model.tables)\n const tableOf = (name: string) => referencedTables(model.dimensions[name].sql, tableNames)[0]\n const own = tableOf(dimensions[0])\n const targets = dimensions.map(tableOf)\n const filterTables = filters.map((filter) => tableOf(filter.dimension))\n let best: { table: string; score: number } | null = null\n // A tabela da própria dimensão primeiro: em empate, ela ganha\n for (const table of [own, ...tableNames.filter((name) => name !== own)]) {\n const { paths } = reachFrom(table, edges)\n if (!targets.every((target) => paths.has(target))) continue\n const score = filterTables.filter((target) => paths.has(target)).length\n if (!best || score > best.score) best = { table, score }\n }\n return best?.table ?? own\n}\n","/**\n * Modelo semântico de um dashboard: o equivalente ao modelo do Power BI (tabelas, relacionamentos e\n * medidas DAX), só que versionado em código.\n *\n * - `tables`: tabelas fato (lançamentos, lado N) e dimensão (cadastros, lado 1). O SQL é a consulta\n * de origem — tabela, view ou SELECT, sem agregar.\n * - `relationships`: N:1 (`from: 'fato.coluna'`, `to: 'dimensao.coluna'`); o filtro vai do lado 1\n * para o lado N. Cada medida agrega no grão da própria tabela, então juntar fatos não duplica\n * valores (o problema de \"fan-out\").\n * - `dimensions`: por onde se agrupa e filtra, citando `tabela.coluna` de uma tabela.\n * - `measures`: agregações (`sum`, `count`, `countDistinct`, `avg`, `min`, `max`) sobre\n * `tabela.coluna`, razões entre medidas (`ratio`) ou fórmulas (`formula`).\n *\n * Dialeto: MySQL/MariaDB.\n */\n\nimport {\n type AggregateMeasure,\n type Aggregation,\n type FormulaMeasure,\n type MeasureMeta,\n type ModelDef,\n type RatioMeasure,\n type SumMeasure,\n validateModel\n} from '../model'\n\nexport type {\n AggregateMeasure,\n Aggregation,\n DimensionDef,\n FieldMeta,\n FormulaMeasure,\n MeasureDef,\n MeasureMeta,\n ModelDef,\n RatioMeasure,\n RelationshipDef,\n SumMeasure,\n TableDef\n} from '../model'\n\nexport const sum = (sql: string, meta?: MeasureMeta): SumMeasure => ({ ...meta, type: 'sum', sql })\n\nconst aggregate =\n <T extends Aggregation>(type: T) =>\n (sql: string, meta?: MeasureMeta): AggregateMeasure & { type: T } => ({ ...meta, type, sql })\n\n/** `COUNT(sql)`: valores não nulos. Para contar linhas, use uma coluna que nunca é nula (a chave). */\nexport const count = aggregate('count')\n/** `COUNT(DISTINCT sql)`: ex. quantas operações têm lançamento (`fVeiculo.navio_operacao_id`). */\nexport const countDistinct = aggregate('countDistinct')\n/** `AVG(sql)`: média das linhas (no estrela, das linhas da própria tabela, não de totais). */\nexport const avg = aggregate('avg')\nexport const min = aggregate('min')\nexport const max = aggregate('max')\n\n/** `numerator / denominator`, ambos nomes de medidas do mesmo modelo (nulo quando o divisor é 0). */\nexport const ratio = (\n numerator: string,\n denominator: string,\n meta?: MeasureMeta\n): RatioMeasure => ({\n ...meta,\n type: 'ratio',\n numerator,\n denominator\n})\n\n/**\n * Combina medidas já agregadas: `{nome}` vira o valor da medida. Ex.:\n * `formula('COALESCE({custoMmo}, 0) + COALESCE({custoAppa}, 0)')` — o jeito de somar medidas de\n * fatos diferentes no modelo estrela.\n */\nexport const formula = (expression: string, meta?: MeasureMeta): FormulaMeasure => ({\n ...meta,\n type: 'formula',\n expression\n})\n\n/** Valida o modelo na carga do módulo: erro de definição aparece no deploy, não na primeira query. */\nexport function defineModel<const M extends ModelDef>(model: M): M {\n const errors = validateModel(model)\n if (errors.length) throw new Error(`[${model.name}] ${errors.join('; ')}`)\n return model\n}\n","import { modelImports, modelObjectCode } from '../codegen'\nimport { AGGREGATIONS, type ModelDef } from '../model'\n\n/**\n * Troca, dentro do arquivo do modelo, só o objeto passado ao `defineModel(...)` e o import do\n * `@bluemarble/bi/server`. O resto do arquivo (JSDoc do modelo, outros exports) fica como está.\n */\n\nexport class ModelFileError extends Error {}\n\n/** Índice do `)` que fecha o `(` em `open`, pulando strings, templates e comentários. */\nfunction findClosingParen(source: string, open: number) {\n let depth = 0\n let comments = false\n // Pilha de contexto: '(' / '{' de código, '`' de template, '${' de interpolação\n const stack: string[] = []\n for (let i = open; i < source.length; i++) {\n const char = source[i]\n const next = source[i + 1]\n const inTemplate = stack[stack.length - 1] === '`'\n\n if (inTemplate) {\n if (char === '\\\\') i++\n else if (char === '`') stack.pop()\n else if (char === '$' && next === '{') {\n stack.push('${')\n i++\n }\n continue\n }\n if (char === \"'\" || char === '\"') {\n for (i++; i < source.length && source[i] !== char; i++) if (source[i] === '\\\\') i++\n continue\n }\n if (char === '/' && next === '/') {\n comments = true\n while (i < source.length && source[i] !== '\\n') i++\n continue\n }\n if (char === '/' && next === '*') {\n comments = true\n i = source.indexOf('*/', i + 2) + 1\n if (i === 0) break\n continue\n }\n if (char === '`') stack.push('`')\n else if (char === '{') stack.push('{')\n else if (char === '}')\n stack.pop() // fecha '{' ou '${'\n else if (char === '(') depth++\n else if (char === ')' && --depth === 0) return { close: i, comments }\n }\n throw new ModelFileError('Não encontrei o fim da chamada `defineModel(...)`')\n}\n\nexport function replaceModelInFile(source: string, model: ModelDef) {\n const warnings: string[] = []\n const call = new RegExp(`export\\\\s+const\\\\s+${model.name}\\\\s*=\\\\s*defineModel(?:<[^>]*>)?\\\\s*\\\\(`)\n const match = call.exec(source)\n if (!match)\n throw new ModelFileError(`O arquivo não tem \\`export const ${model.name} = defineModel(...)\\``)\n\n const open = match.index + match[0].length - 1\n const { close, comments } = findClosingParen(source, open)\n if (comments)\n warnings.push('Comentários dentro do defineModel(...) serão perdidos (os de fora ficam).')\n\n let content = `${source.slice(0, open + 1)}${modelObjectCode(model)}${source.slice(close)}`\n\n // Import: mantém o que mais vier do package e acerta os helpers de medida conforme o uso\n const importRegex = /import\\s*\\{([^}]*)\\}\\s*from\\s*'@bluemarble\\/bi\\/server'/\n const current = importRegex.exec(content)\n const helpers = new Set(['defineModel', 'formula', 'ratio', ...AGGREGATIONS])\n const others = (current?.[1] ?? '')\n .split(',')\n .map((name) => name.trim())\n .filter((name) => name && !helpers.has(name))\n const names = [...others, ...modelImports(model)].sort((a, b) =>\n a.localeCompare(b, 'en', { sensitivity: 'base' })\n )\n const line = `import { ${names.join(', ')} } from '@bluemarble/bi/server'`\n content = current ? content.replace(importRegex, line) : `${line}\\n${content}`\n\n const outside = source.slice(0, match.index) + source.slice(close + 1)\n const measureHelpers = [...helpers].filter((name) => name !== 'defineModel')\n if (new RegExp(`\\\\b(${measureHelpers.join('|')})\\\\(`).test(outside))\n warnings.push('O arquivo usa os helpers de medida fora do modelo; confira o import.')\n\n return { content, warnings }\n}\n","import type { DashboardRow } from '../types'\n\n/** Decimal do Prisma, bigint e afins viram number; datas viram ISO. */\nexport function normalizeValue(value: unknown): string | number | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string' || typeof value === 'number') return value\n if (typeof value === 'bigint') return Number(value)\n if (value instanceof Date) return value.toISOString()\n const number = Number(value)\n return Number.isNaN(number) ? String(value) : number\n}\n\nexport function normalizeRow(row: Record<string, unknown>): DashboardRow {\n const normalized: DashboardRow = {}\n for (const [key, value] of Object.entries(row)) normalized[key] = normalizeValue(value)\n return normalized\n}\n","import {\n type ChartsLayout,\n type ChartTypeSpec,\n layoutFileContent,\n layoutModelWarnings,\n resolveChartTypes,\n validateLayout\n} from '../layout'\nimport { isAggregate, modelMeta, validateModel } from '../model'\nimport { parseColumnRef, referencedTables } from '../star'\nimport type {\n DashboardDevtoolsRequest,\n DashboardQuery,\n DashboardResult,\n DashboardRow,\n DashboardSnapshot,\n ModelColumns,\n ModelValidation,\n ProfileStep,\n SchemaTable\n} from '../types'\nimport { compileQuery, DashboardQueryError } from './compile'\nimport type { ModelDef } from './defineModel'\nimport { ModelFileError, replaceModelInFile } from './modelFile'\nimport { normalizeValue } from './normalize'\n\n/**\n * Pedidos do devtools (`query.devtools`) que não são uma consulta comum. Só chega aqui com\n * `runQuery(..., { devtools })` ligado — tudo isso executa SQL vindo do browser ou escreve em disco.\n */\n\nexport type DevtoolsServerOptions = {\n /**\n * Pasta dos arquivos de modelo (`<pasta>/<nome>.ts`, caminho absoluto). Habilita \"Salvar no\n * arquivo\"; só arquivos que já existem são reescritos.\n */\n modelsDir?: string\n /** Pasta dos snapshots de regressão (`<pasta>/<modelo>/<nome>.json`, caminho absoluto). */\n snapshotsDir?: string\n /**\n * Pasta dos layouts de gráficos (`<pasta>/<id>.json`, caminho absoluto). Habilita \"Salvar no\n * arquivo\" na aba Layout; só arquivos que já existem são reescritos.\n */\n layoutsDir?: string\n /**\n * Tipos de gráfico do app (os mesmos `ChartTypeSpec` do `<DashboardProvider charts>`, sem o\n * componente). Sem eles, salvar um layout com esses tipos é recusado.\n */\n chartTypes?: Record<string, ChartTypeSpec>\n /**\n * Formata o arquivo do modelo antes do preview/gravação (ex.: Biome), para o diff mostrar só o\n * que mudou de fato. Sem isso, o arquivo sai com a formatação do gerador.\n */\n format?: (code: string, path: string) => Promise<string>\n}\n\ntype Executor = (sql: string, params: (string | number)[]) => Promise<Record<string, unknown>[]>\n\ntype Context = {\n registered: ModelDef\n /** Rascunho, se houver; senão o registrado. */\n model: ModelDef\n query: DashboardQuery\n request: DashboardDevtoolsRequest\n execute: Executor\n normalize: (row: Record<string, unknown>) => DashboardRow\n options: DevtoolsServerOptions\n /** Executa a consulta normal (com cache) e devolve as linhas. */\n runRows: (model: ModelDef) => Promise<DashboardRow[]>\n}\n\nconst fs = () => import('node:fs/promises')\n\n/** Mensagem do banco sem o embrulho do Prisma (`Raw query failed. Code: ... Message: ...`). */\nexport function dbMessage(error: unknown) {\n const err = error as { meta?: { message?: string }; message?: string }\n const message = err?.meta?.message ?? err?.message ?? String(error)\n return message.match(/Message: `?([\\s\\S]*?)`?$/)?.[1] ?? message\n}\n\nconst timed = async <T>(load: () => Promise<T>) => {\n const startedAt = performance.now()\n const result = await load()\n return { result, durationMs: Math.round(performance.now() - startedAt) }\n}\n\nconst SLUG = /^[A-Za-z0-9_-]{1,80}$/\n\n/** `<pasta>/<arquivo>`, aceitando pasta com `/` ou `\\` no fim (`path.join` no Windows). */\nconst inDir = (dir: string, file: string) => `${dir.replace(/[\\\\/]+$/, '')}/${file}`\n\n/**\n * Conteúdo final do arquivo: formatado pelo app (se der erro, sai sem formatar) e com o fim de\n * linha do arquivo atual — no Windows o checkout costuma vir com CRLF, e o gerador/formatador\n * devolvem LF, o que faria o diff marcar o arquivo inteiro.\n */\nasync function fileContent(\n raw: string,\n path: string,\n previous: string,\n options: DevtoolsServerOptions\n) {\n const { format } = options\n const formatted = format\n ? await Promise.resolve()\n .then(() => format(raw, path))\n .catch((error) => {\n console.warn('[@bluemarble/bi] format falhou; o arquivo sai sem formatar:', error)\n return raw\n })\n : raw\n const content = formatted.replace(/\\r\\n/g, '\\n')\n return previous.includes('\\r\\n') ? content.replace(/\\n/g, '\\r\\n') : content\n}\n\nexport async function handleDevtoolsRequest(context: Context): Promise<DashboardResult | null> {\n const { request } = context\n if (request.describe) return describe(context)\n if (request.schema) return { rows: [], devtools: { schema: await loadSchema(context.execute) } }\n if (request.columns) {\n const { columns, errors } = await describeColumns(context)\n return { rows: [], devtools: { columns, columnErrors: errors } }\n }\n if (request.validate) return { rows: [], devtools: { validation: await validateSql(context) } }\n if (request.save) return { rows: [], devtools: { save: await saveModel(context) } }\n if (request.saveLayout) return { rows: [], devtools: { save: await saveLayout(context) } }\n if (request.snapshots) return { rows: [], devtools: { snapshots: await listSnapshots(context) } }\n if (request.snapshot) return { rows: [], devtools: { snapshot: await saveSnapshot(context) } }\n if (request.compile) {\n const { sql, params } = compileQuery(context.model, context.query)\n const draft = context.model !== context.registered\n return { rows: [], devtools: { debug: { sql, params, durationMs: 0, cached: false, draft } } }\n }\n if (request.explain) {\n const { sql, params } = compileQuery(context.model, context.query)\n const rows = await context.execute(`EXPLAIN ${sql}`, params)\n return { rows: [], devtools: { explain: rows.map(context.normalize) } }\n }\n if (request.profile) return { rows: [], devtools: { profile: await profile(context) } }\n return null\n}\n\nfunction describe({ registered, options }: Context): DashboardResult {\n return {\n rows: [],\n meta: modelMeta(registered),\n devtools: {\n model: registered,\n capabilities: {\n saveModel: !!options.modelsDir,\n snapshots: !!options.snapshotsDir,\n saveLayout: !!options.layoutsDir,\n chartTypes: Object.keys(options.chartTypes ?? {})\n }\n }\n }\n}\n\nasync function profile({ model, query, execute }: Context): Promise<ProfileStep[]> {\n const { sql, params, parts } = compileQuery(model, query)\n // Em sequência: medições em paralelo disputariam o banco e os tempos não diriam nada\n const steps: ProfileStep[] = []\n for (const part of parts) {\n const { result, durationMs } = await timed(() => execute(part.sql, part.params))\n steps.push({ name: part.name, durationMs, rows: Number(result[0]?.n ?? 0) })\n }\n const total = await timed(() => execute(sql, params))\n steps.push({ name: 'consulta', durationMs: total.durationMs, rows: total.result.length })\n return steps\n}\n\nasync function loadSchema(execute: Executor): Promise<SchemaTable[]> {\n const rows = await execute(\n `SELECT c.TABLE_NAME AS tableName, t.TABLE_TYPE AS tableType,\n c.COLUMN_NAME AS columnName, c.DATA_TYPE AS dataType\n FROM information_schema.COLUMNS c\n JOIN information_schema.TABLES t\n ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.TABLE_NAME = c.TABLE_NAME\n WHERE c.TABLE_SCHEMA = DATABASE()\n ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION`,\n []\n )\n const tables = new Map<string, SchemaTable>()\n for (const row of rows) {\n const name = String(row.tableName)\n let table = tables.get(name)\n if (!table) {\n table = { name, type: String(row.tableType).includes('VIEW') ? 'view' : 'table', columns: [] }\n tables.set(name, table)\n }\n table.columns.push({ name: String(row.columnName), type: String(row.dataType) })\n }\n return [...tables.values()]\n}\n\n/** Poucas chaves bastam para o banco acusar erro de SQL e para descobrir as colunas. */\nconst SAMPLE_KEYS = 20\n\nconst emptyColumns = (): ModelColumns => ({ tables: {}, numeric: [] })\n\n/** Colunas numéricas da primeira linha (Decimal do Prisma e bigint contam). */\nfunction numericColumns(node: string, row: Record<string, unknown> | undefined) {\n return Object.entries(row ?? {})\n .filter(([, value]) => typeof normalizeValue(value) === 'number')\n .map(([column]) => `${node}.${column}`)\n}\n\n/**\n * Colunas de cada tabela do modelo (uma linha de cada). `errors` traz o que o banco recusou, com as\n * chaves da validação (`table:<nome>`).\n */\nasync function loadColumns(model: ModelDef, execute: Executor) {\n const columns = emptyColumns()\n const errors: Record<string, string> = {}\n const attempt = async (key: string, sql: string) => {\n try {\n return await execute(sql, [])\n } catch (error) {\n errors[key] = dbMessage(error)\n return null\n }\n }\n for (const [name, table] of Object.entries(model.tables)) {\n const rows = await attempt(`table:${name}`, `SELECT * FROM (${table.sql}) t LIMIT 1`)\n if (!rows) continue\n columns.tables[name] = Object.keys(rows[0] ?? {})\n columns.numeric.push(...numericColumns(name, rows[0]))\n }\n return { columns, errors }\n}\n\nasync function describeColumns({ model, execute }: Context) {\n const errors = validateModel(model)\n if (errors.length) throw new DashboardQueryError(`Modelo inválido: ${errors.join('; ')}`)\n return loadColumns(model, execute)\n}\n\nasync function validateSql({ model, execute }: Context): Promise<ModelValidation> {\n const validation: ModelValidation = { errors: {}, warnings: {}, columns: emptyColumns() }\n const modelErrors = validateModel(model)\n if (modelErrors.length) {\n validation.errors.model = modelErrors.join('; ')\n return validation\n }\n\n const attempt = async (key: string, sql: string) => {\n try {\n return await execute(sql, [])\n } catch (error) {\n validation.errors[key] = dbMessage(error)\n return null\n }\n }\n\n const loaded = await loadColumns(model, execute)\n validation.columns = loaded.columns\n Object.assign(validation.errors, loaded.errors)\n const tableNames = Object.keys(model.tables)\n\n for (const [index, relationship] of (model.relationships ?? []).entries()) {\n const key = `relationship:${index}`\n const from = parseColumnRef(relationship.from)\n const to = parseColumnRef(relationship.to)\n if (!from || !to) continue\n const missing = [from, to].find(\n (ref) =>\n validation.columns.tables[ref.table]?.length &&\n !validation.columns.tables[ref.table].includes(ref.column)\n )\n if (missing) {\n validation.errors[key] = `a tabela \"${missing.table}\" não tem a coluna \"${missing.column}\"`\n continue\n }\n // O lado 1 precisa ser único, senão a fato multiplica linhas ao juntar\n const toSql = model.tables[to.table]?.sql\n if (!toSql || validation.errors[`table:${to.table}`]) continue\n const repeated = await attempt(\n key,\n `SELECT \\`${to.column}\\` AS v FROM (${toSql}) t WHERE \\`${to.column}\\` IS NOT NULL GROUP BY 1 HAVING COUNT(*) > 1 LIMIT 1`\n )\n if (repeated?.length)\n validation.errors[key] =\n `o lado 1 (${relationship.to}) tem valor repetido: ${String(normalizeValue(repeated[0].v))}`\n }\n\n for (const [name, dimension] of Object.entries(model.dimensions)) {\n const table = referencedTables(dimension.sql, tableNames)[0]\n if (validation.errors[`table:${table}`]) continue\n const sql = `SELECT ${dimension.sql} AS v FROM (SELECT * FROM (${model.tables[table]?.sql}) t LIMIT ${SAMPLE_KEYS}) \\`${table}\\` WHERE ${dimension.sql} IS NOT NULL LIMIT 1`\n const rows = await attempt(`dimension:${name}`, sql)\n const value = rows?.[0]?.v\n if (value === undefined || value === null) continue\n const isNumber = typeof value === 'number' || typeof value === 'bigint'\n if (dimension.type === 'number' && !isNumber && Number.isNaN(Number(value)))\n validation.warnings[`dimension:${name}`] = `tipo number, mas o banco devolveu \"${value}\"`\n if (dimension.type === 'string' && isNumber)\n validation.warnings[`dimension:${name}`] =\n 'tipo string, mas o banco devolveu número (filtros vão chegar como texto)'\n }\n\n for (const [name, measure] of Object.entries(model.measures)) {\n if (!isAggregate(measure)) continue // razão e fórmula só combinam outras medidas\n const failed = referencedTables(measure.sql, tableNames).some(\n (table) => validation.errors[`table:${table}`]\n )\n if (failed) continue\n const { sql, params } = compileQuery(\n model,\n { model: model.name, measures: [name] },\n { sampleLimit: SAMPLE_KEYS }\n )\n try {\n await execute(sql, params)\n } catch (error) {\n validation.errors[`measure:${name}`] = dbMessage(error)\n }\n }\n\n return validation\n}\n\nasync function saveModel({ registered, model, request, options }: Context) {\n if (!options.modelsDir) throw new DashboardQueryError('O endpoint não configurou `modelsDir`')\n if (!request.model) throw new DashboardQueryError('Nada para salvar: não há rascunho')\n if (model.name !== registered.name)\n throw new DashboardQueryError('O rascunho não pode trocar o nome do modelo')\n\n const path = inDir(options.modelsDir, `${registered.name}.ts`)\n const { readFile, writeFile } = await fs()\n let previous: string\n try {\n previous = await readFile(path, 'utf8')\n } catch {\n throw new DashboardQueryError(`Arquivo do modelo não encontrado: ${path}`)\n }\n\n try {\n const replaced = replaceModelInFile(previous, model)\n const { warnings } = replaced\n const content = await fileContent(replaced.content, path, previous, options)\n const written = request.save === 'write' && content !== previous\n if (written) await writeFile(path, content, 'utf8')\n return { path, previous, content, written, warnings }\n } catch (error) {\n if (error instanceof ModelFileError) throw new DashboardQueryError(error.message)\n throw error\n }\n}\n\nasync function saveLayout({ model, request, options }: Context) {\n if (!options.layoutsDir) throw new DashboardQueryError('O endpoint não configurou `layoutsDir`')\n const { layout, mode } = request.saveLayout ?? ({} as { layout?: ChartsLayout; mode?: string })\n const types = resolveChartTypes(options.chartTypes)\n const errors = layout ? validateLayout(layout, types) : ['Nada para salvar: não há layout']\n if (errors.length) throw new DashboardQueryError(`Layout inválido: ${errors.join('; ')}`)\n const valid = layout as ChartsLayout\n\n const path = inDir(options.layoutsDir, `${valid.id}.json`)\n const { readFile, writeFile } = await fs()\n let previous: string\n try {\n previous = await readFile(path, 'utf8')\n } catch {\n throw new DashboardQueryError(`Arquivo do layout não encontrado: ${path}`)\n }\n\n const content = await fileContent(layoutFileContent(valid), path, previous, options)\n const written = mode === 'write' && content !== previous\n if (written) await writeFile(path, content, 'utf8')\n // Contra o modelo em vigor na página (o rascunho, se houver)\n return { path, previous, content, written, warnings: layoutModelWarnings(valid, model, types) }\n}\n\nconst snapshotDir = (options: DevtoolsServerOptions, model: string) => {\n if (!options.snapshotsDir)\n throw new DashboardQueryError('O endpoint não configurou `snapshotsDir`')\n return inDir(options.snapshotsDir, model)\n}\n\nasync function saveSnapshot({ registered, query, request, options, runRows }: Context) {\n const { name, overwrite } = request.snapshot ?? { name: '' }\n if (!SLUG.test(name))\n throw new DashboardQueryError('Nome do snapshot: letras, números, \"-\" e \"_\" (até 80)')\n const dir = snapshotDir(options, registered.name)\n const path = `${dir}/${name}.json`\n const { mkdir, readFile, writeFile } = await fs()\n\n if (!overwrite) {\n const exists = await readFile(path, 'utf8').then(\n () => true,\n () => false\n )\n if (exists) throw new DashboardQueryError(`Já existe um snapshot \"${name}\"`)\n }\n\n // Sempre o modelo registrado: o snapshot é a referência que o rascunho tem que bater\n const snapshot: DashboardSnapshot = {\n name,\n createdAt: new Date().toISOString(),\n query: {\n model: registered.name,\n dimensions: query.dimensions ?? [],\n measures: query.measures ?? [],\n filters: query.filters ?? {}\n },\n rows: await runRows(registered)\n }\n await mkdir(dir, { recursive: true })\n await writeFile(path, `${JSON.stringify(snapshot, null, 2)}\\n`, 'utf8')\n return { path }\n}\n\nasync function listSnapshots({ registered, options }: Context): Promise<DashboardSnapshot[]> {\n const dir = snapshotDir(options, registered.name)\n const { readdir, readFile } = await fs()\n const files = await readdir(dir).catch(() => [] as string[])\n const snapshots = await Promise.all(\n files\n .filter((file) => file.endsWith('.json'))\n .sort()\n .map(\n async (file) => JSON.parse(await readFile(`${dir}/${file}`, 'utf8')) as DashboardSnapshot\n )\n )\n return snapshots\n}\n","import { modelMeta, validateModel } from '../model'\nimport type { DashboardQuery, DashboardResult, DashboardRow } from '../types'\nimport { compileQuery, DashboardQueryError } from './compile'\nimport type { ModelDef } from './defineModel'\nimport { type DevtoolsServerOptions, dbMessage, handleDevtoolsRequest } from './devtools'\nimport { normalizeRow } from './normalize'\n\n/** Executa o SQL no banco do app (ex.: `(sql, params) => prisma.$queryRawUnsafe(sql, ...params)`). */\nexport type QueryExecutor = (sql: string, params: (string | number)[]) => Promise<unknown[]>\n\nexport type RunQueryOptions = {\n /**\n * Por quanto tempo reaproveitar o resultado de uma mesma consulta (mesmo SQL + parâmetros).\n * Faz o papel do \"refresh\" do Power BI: alternar filtros não recalcula tudo de novo. 0 = sem cache.\n */\n cacheTtlMs?: number\n /**\n * Aceita os pedidos do devtools (`query.devtools`): debug, rascunho de modelo, EXPLAIN, validação\n * de SQL etc. O rascunho é SQL vindo do browser — ligue SÓ em desenvolvimento. Passe as pastas\n * (`modelsDir`, `snapshotsDir`) para habilitar salvar o modelo e os snapshots.\n */\n devtools?: boolean | DevtoolsServerOptions\n}\n\ntype Execution = { rows: DashboardRow[]; durationMs: number }\n\nconst MAX_CACHE_ENTRIES = 500\nconst cache = new Map<string, { expiresAt: number; execution: Promise<Execution> }>()\n\nfunction fromCache(key: string, ttl: number, load: () => Promise<Execution>) {\n const now = Date.now()\n const hit = cache.get(key)\n if (hit && hit.expiresAt > now) return { execution: hit.execution, cached: true }\n\n if (cache.size >= MAX_CACHE_ENTRIES) {\n for (const [k, entry] of cache) if (entry.expiresAt <= now) cache.delete(k)\n if (cache.size >= MAX_CACHE_ENTRIES) cache.delete(cache.keys().next().value as string)\n }\n // Guarda a promise: requests simultâneas da mesma consulta esperam a mesma execução\n const execution = load()\n cache.set(key, { expiresAt: now + ttl, execution })\n execution.catch(() => cache.delete(key))\n return { execution, cached: false }\n}\n\nexport async function runQuery(\n models: Record<string, ModelDef>,\n query: DashboardQuery,\n execute: QueryExecutor,\n options: RunQueryOptions = {}\n): Promise<DashboardResult> {\n // `debug` desligado é só ignorado (a página continua funcionando com um endpoint sem devtools);\n // o resto expõe ou altera o modelo e é recusado\n const { debug, ...privileged } = query?.devtools ?? {}\n const devtools = options.devtools ? query?.devtools : undefined\n if (!options.devtools && Object.values(privileged).some(Boolean))\n throw new DashboardQueryError('Devtools desabilitado neste ambiente')\n\n const registered = models[query?.model]\n if (!registered) throw new DashboardQueryError(`Modelo desconhecido: \"${query?.model}\"`)\n\n const draft = devtools?.model\n if (draft) {\n const errors = validateModel(draft)\n if (errors.length) throw new DashboardQueryError(`Rascunho inválido: ${errors.join('; ')}`)\n }\n const model = draft ?? registered\n\n // Rótulos/formatos (sem SQL): liberado em produção, os componentes usam como padrão\n if (query.meta) return { rows: [], meta: modelMeta(model) }\n\n const ttl = options.cacheTtlMs ?? 0\n const run = async (target: ModelDef) => {\n const { sql, params } = compileQuery(target, query)\n const load = async (): Promise<Execution> => {\n const startedAt = performance.now()\n const rows = (await execute(sql, params)) as Record<string, unknown>[]\n return { durationMs: Math.round(performance.now() - startedAt), rows: rows.map(normalizeRow) }\n }\n // O rascunho muda o SQL, então nunca colide com o cache do modelo registrado\n const { execution, cached } =\n ttl > 0\n ? fromCache(JSON.stringify([sql, params]), ttl, load)\n : { execution: load(), cached: false }\n return { sql, params, cached, ...(await execution) }\n }\n\n if (devtools) {\n if (devtools.clearCache) cache.clear()\n try {\n const handled = await handleDevtoolsRequest({\n registered,\n model,\n query,\n request: devtools,\n execute: (sql, params) => execute(sql, params) as Promise<Record<string, unknown>[]>,\n normalize: normalizeRow,\n options: typeof options.devtools === 'object' ? options.devtools : {},\n runRows: async (target) => (await run(target)).rows\n })\n if (handled) return handled\n } catch (error) {\n // Erro de SQL no devtools é do rascunho/consulta, não do servidor: vira 400 com a mensagem do banco\n if (error instanceof DashboardQueryError) throw error\n throw new DashboardQueryError(dbMessage(error))\n }\n }\n\n const { rows, sql, params, durationMs, cached } = await run(model).catch((error) => {\n // Com o devtools, o erro do banco volta como 400 com a mensagem dele (o painel mostra o SQL)\n if (!devtools || error instanceof DashboardQueryError) throw error\n throw new DashboardQueryError(dbMessage(error))\n })\n if (!devtools?.debug) return { rows }\n return { rows, devtools: { debug: { sql, params, durationMs, cached, draft: !!draft } } }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/server/compile.ts","../../src/server/defineModel.ts","../../src/server/batch.ts","../../src/server/modelFile.ts","../../src/server/normalize.ts","../../src/server/devtools.ts","../../src/server/runQuery.ts"],"sourcesContent":["import { type AggregateMeasure, aggregateSql, isAggregate } from '../model'\nimport { type Edge, joinPlan, measureHome, modelEdges, reachFrom, referencedTables } from '../star'\nimport type { DashboardQuery } from '../types'\nimport type { ModelDef } from './defineModel'\n\nexport class DashboardQueryError extends Error {}\n\n/** Pedaço da consulta, pronto para o devtools medir: devolve uma linha com `n` (linhas do pedaço). */\nexport type CompiledPart = { name: string; sql: string; params: (string | number)[] }\n\nexport type CompiledQuery = {\n sql: string\n params: (string | number)[]\n /** Pedaços do SQL, para o devtools medir cada um: `fato:<tabela>` (a agregação de cada tabela). */\n parts: CompiledPart[]\n}\n\nexport type CompileOptions = {\n /** `LIMIT` em cada tabela: o devtools valida o SQL do modelo com poucas linhas, sem custo. */\n sampleLimit?: number\n}\n\nconst MAX_FILTER_VALUES = 1000\n\nconst quote = (identifier: string) => `\\`${identifier}\\``\n\n/**\n * Expressão de uma medida. `leaf` diz como sai cada agregação (`sum`, `count`, `avg`...): a coluna\n * já agregada pela tabela de origem.\n */\nfunction measureSql(\n model: ModelDef,\n name: string,\n leaf: (name: string, measure: AggregateMeasure) => string,\n stack: string[] = []\n): string {\n if (stack.includes(name))\n throw new DashboardQueryError(`Medida circular: ${[...stack, name].join(' -> ')}`)\n const measure = model.measures[name]\n if (!measure) throw new DashboardQueryError(`Medida desconhecida: \"${name}\"`)\n if (isAggregate(measure)) return leaf(name, measure)\n const next = [...stack, name]\n const ref = (other: string) => measureSql(model, other, leaf, next)\n if (measure.type === 'ratio')\n return `(${ref(measure.numerator)}) / NULLIF(${ref(measure.denominator)}, 0)`\n return `(${measure.expression.replace(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, other) => `(${ref(other)})`)})`\n}\n\n/** Agregações de que as medidas pedidas dependem (razões e fórmulas abertas). */\nfunction leafMeasures(model: ModelDef, names: string[]): string[] {\n const leaves = new Set<string>()\n for (const name of names)\n measureSql(model, name, (leaf, measure) => {\n leaves.add(leaf)\n return measure.sql\n })\n return [...leaves]\n}\n\n/**\n * De onde saem as medidas: cada tabela onde agregam (a fato) e as tabelas que as agregações dela\n * citam. Duas consultas com as mesmas dimensões, filtros e origens dão as mesmas linhas, e cada\n * medida o mesmo valor, juntas ou separadas — é o que o lote do `runQuery` usa para juntá-las.\n */\nexport function measureSources(model: ModelDef, measures: string[]): Record<string, string[]> {\n const tableNames = Object.keys(model.tables)\n const edges = modelEdges(model)\n const sources: Record<string, Set<string>> = {}\n for (const leaf of leafMeasures(model, measures)) {\n const measure = model.measures[leaf]\n if (!isAggregate(measure)) continue\n const tables = referencedTables(measure.sql, tableNames)\n const home = measureHome(tables, edges)\n if (!home) throw new DashboardQueryError(`Medida \"${leaf}\" sem tabela de origem`)\n sources[home] ??= new Set()\n for (const table of tables) sources[home].add(table)\n }\n return Object.fromEntries(\n Object.entries(sources)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([home, tables]) => [home, [...tables].sort()])\n )\n}\n\ntype Filter = { dimension: string; clause: string; params: (string | number)[] }\n\nfunction compileFilters(model: ModelDef, query: DashboardQuery): Filter[] {\n const filters: Filter[] = []\n for (const [name, values] of Object.entries(query.filters ?? {})) {\n const dimension = model.dimensions[name]\n if (!dimension) throw new DashboardQueryError(`Filtro em dimensão desconhecida: \"${name}\"`)\n if (!Array.isArray(values) || !values.length) continue\n if (values.length > MAX_FILTER_VALUES)\n throw new DashboardQueryError(`Valores demais no filtro \"${name}\"`)\n for (const value of values) {\n const valid = dimension.type === 'number' ? Number.isFinite(value) : typeof value === 'string'\n if (!valid) throw new DashboardQueryError(`Valor inválido no filtro \"${name}\": ${value}`)\n }\n filters.push({\n dimension: name,\n clause: `${dimension.sql} IN (${values.map(() => '?').join(', ')})`,\n params: values\n })\n }\n return filters\n}\n\n/**\n * Transforma uma consulta (dimensões + medidas + filtros) em SQL parametrizado (ver `compileStar`).\n *\n * Nomes de dimensões/medidas só são aceitos se existirem no modelo; valores de filtro vão sempre\n * como parâmetro — nada vindo da request é concatenado no SQL.\n */\nexport function compileQuery(\n model: ModelDef,\n query: DashboardQuery,\n options: CompileOptions = {}\n): CompiledQuery {\n const dimensions = query.dimensions ?? []\n const measures = query.measures ?? []\n if (!dimensions.length && !measures.length)\n throw new DashboardQueryError('Informe ao menos uma dimensão ou medida')\n\n for (const name of dimensions) {\n if (!model.dimensions[name]) throw new DashboardQueryError(`Dimensão desconhecida: \"${name}\"`)\n }\n\n const filters = compileFilters(model, query)\n return compileStar(model, dimensions, measures, filters, options)\n}\n\nconst range = (count: number) => Array.from({ length: count }, (_, index) => index + 1).join(', ')\n\n/**\n * Cada tabela de origem (a das medidas pedidas) vira uma agregação no próprio\n * grão, com LEFT JOIN só nas tabelas que levam às dimensões e filtros usados:\n *\n * SELECT <dimensões>, SUM(<medida>) FROM fPesagem\n * LEFT JOIN dOperacao ON dOperacao.id = fPesagem.navio_operacao_id\n * WHERE <filtros> GROUP BY <dimensões>\n *\n * Com medidas de mais de uma fato, cada uma agrega separada e o resultado é somado por dimensão\n * (`UNION ALL` + `GROUP BY`) — juntar duas fatos antes de somar multiplicaria os valores. Razões e\n * fórmulas são calculadas no fim, sobre os totais.\n *\n * Filtro numa dimensão que a fato não alcança é ignorado por ela (como no Power BI, a medida não é\n * filtrada); agrupar por uma dimensão que ela não alcança é erro.\n */\nfunction compileStar(\n model: ModelDef,\n dimensions: string[],\n measures: string[],\n filters: Filter[],\n { sampleLimit }: CompileOptions\n): CompiledQuery {\n const { tables } = model\n const tableNames = Object.keys(tables)\n const edges = modelEdges(model)\n const tableOf = (sql: string) => referencedTables(sql, tableNames)\n const dimensionTable = (name: string) => tableOf(model.dimensions[name].sql)[0]\n\n // Agregações agrupadas pela tabela onde agregam\n const groups = new Map<string, string[]>()\n const leafOf = (name: string) => model.measures[name] as AggregateMeasure\n for (const leaf of leafMeasures(model, measures)) {\n const measure = model.measures[leaf]\n if (!isAggregate(measure)) continue\n const home = measureHome(tableOf(measure.sql), edges)\n if (!home) throw new DashboardQueryError(`Medida \"${leaf}\" sem tabela de origem`)\n groups.set(home, [...(groups.get(home) ?? []), leaf])\n }\n if (!groups.size) groups.set(dimensionHome(model, dimensions, filters, edges), [])\n\n const source = (name: string) => {\n const sql = tables[name].sql\n return sampleLimit === undefined\n ? `(${sql})`\n : `(SELECT * FROM (${sql}) t LIMIT ${Math.max(0, Math.trunc(sampleLimit))})`\n }\n\n const aggregate = (home: string, leaves: string[]) => {\n const { paths } = reachFrom(home, edges)\n for (const name of dimensions)\n if (!paths.has(dimensionTable(name)))\n throw new DashboardQueryError(\n leaves.length\n ? `A dimensão \"${name}\" (${dimensionTable(name)}) não se relaciona com ${home}, de onde vêm ${leaves.join(', ')}`\n : `As dimensões ${dimensions.join(', ')} não se relacionam entre si`\n )\n const applied = filters.filter((filter) => paths.has(dimensionTable(filter.dimension)))\n const needed = new Set([\n ...dimensions.map(dimensionTable),\n ...applied.map((filter) => dimensionTable(filter.dimension)),\n ...leaves.flatMap((leaf) => tableOf(leafOf(leaf).sql))\n ])\n const joins = joinPlan(home, needed, edges).map(\n ({ table, edge }) =>\n `${edge.inner ? 'JOIN' : 'LEFT JOIN'} ${source(table)} ${quote(table)}\\n ON ${quote(table)}.${quote(edge.to.column)} = ${quote(edge.from.table)}.${quote(edge.from.column)}`\n )\n const select = [\n ...dimensions.map((name) => `${model.dimensions[name].sql} AS ${quote(name)}`),\n ...leaves.map((leaf) => `${aggregateSql(leafOf(leaf))} AS ${quote(leaf)}`)\n ]\n const sql = [\n `SELECT ${select.join(',\\n ')}`,\n `FROM ${source(home)} ${quote(home)}`,\n ...joins,\n applied.length ? `WHERE ${applied.map((filter) => filter.clause).join(' AND ')}` : '',\n dimensions.length ? `GROUP BY ${range(dimensions.length)}` : ''\n ]\n .filter(Boolean)\n .join('\\n')\n return { home, sql, params: applied.flatMap((filter) => filter.params) }\n }\n\n const aggregates = [...groups].map(([home, leaves]) => aggregate(home, leaves))\n const parts = aggregates.map(({ home, sql, params }) => ({\n name: `fato:${home}`,\n sql: `SELECT COUNT(*) AS n FROM (${sql}) g`,\n params\n }))\n const orderBy = dimensions.length ? `\\nORDER BY ${range(dimensions.length)}` : ''\n\n // Uma tabela só: as medidas saem direto da agregação\n if (aggregates.length === 1) {\n const [only] = aggregates\n if (!measures.length || measures.every((name) => isAggregate(model.measures[name])))\n return { sql: `${only.sql}${orderBy}`, params: only.params, parts }\n }\n\n // Várias (ou razões/fórmulas): junta as colunas já agregadas e calcula as medidas por cima. Cada\n // fato tem uma linha por grupo e as colunas das outras vêm NULL, então o SUM de fora só pega o\n // valor da própria fato — vale para qualquer agregação (média, contagem distinta...), sem reagregar\n const columns = [...dimensions, ...[...groups.values()].flat()]\n const union = aggregates\n .map(({ home }, index) => {\n const own = new Set(groups.get(home))\n const select = columns.map((column) =>\n dimensions.includes(column) || own.has(column) ? quote(column) : `NULL AS ${quote(column)}`\n )\n return `SELECT ${select.join(', ')} FROM ${quote(`__fato${index}`)}`\n })\n .join('\\n UNION ALL ')\n const leaf = (name: string) => `SUM(${quote('__u')}.${quote(name)})`\n const select = [\n ...dimensions.map((name) => `${quote('__u')}.${quote(name)} AS ${quote(name)}`),\n ...measures.map((name) => `${measureSql(model, name, leaf)} AS ${quote(name)}`)\n ]\n const sql = [\n `WITH ${aggregates.map(({ sql }, index) => `${quote(`__fato${index}`)} AS (\\n${sql}\\n)`).join(',\\n')}`,\n `SELECT ${select.join(',\\n ')}`,\n `FROM (\\n ${union}\\n) ${quote('__u')}`,\n dimensions.length ? `GROUP BY ${range(dimensions.length)}${orderBy}` : ''\n ]\n .filter(Boolean)\n .join('\\n')\n return { sql, params: aggregates.flatMap((item) => item.params), parts }\n}\n\n/**\n * Consulta só de dimensões (ex.: opções de um filtro): parte da tabela que alcança todas as\n * dimensões pedidas e o maior número de filtros ativos — com \"Mês = Janeiro\", a lista de navios sai\n * da fato que liga navio e calendário, e só mostra quem tem lançamento em janeiro. Sem filtro, a\n * própria tabela da dimensão (lista tudo, mesmo sem lançamento).\n */\nfunction dimensionHome(\n model: ModelDef,\n dimensions: string[],\n filters: Filter[],\n edges: Edge[]\n): string {\n const tableNames = Object.keys(model.tables)\n const tableOf = (name: string) => referencedTables(model.dimensions[name].sql, tableNames)[0]\n const own = tableOf(dimensions[0])\n const targets = dimensions.map(tableOf)\n const filterTables = filters.map((filter) => tableOf(filter.dimension))\n let best: { table: string; score: number } | null = null\n // A tabela da própria dimensão primeiro: em empate, ela ganha\n for (const table of [own, ...tableNames.filter((name) => name !== own)]) {\n const { paths } = reachFrom(table, edges)\n if (!targets.every((target) => paths.has(target))) continue\n const score = filterTables.filter((target) => paths.has(target)).length\n if (!best || score > best.score) best = { table, score }\n }\n return best?.table ?? own\n}\n","/**\n * Modelo semântico de um dashboard: o equivalente ao modelo do Power BI (tabelas, relacionamentos e\n * medidas DAX), só que versionado em código.\n *\n * - `tables`: tabelas fato (lançamentos, lado N) e dimensão (cadastros, lado 1). O SQL é a consulta\n * de origem — tabela, view ou SELECT, sem agregar.\n * - `relationships`: N:1 (`from: 'fato.coluna'`, `to: 'dimensao.coluna'`); o filtro vai do lado 1\n * para o lado N. Cada medida agrega no grão da própria tabela, então juntar fatos não duplica\n * valores (o problema de \"fan-out\").\n * - `dimensions`: por onde se agrupa e filtra, citando `tabela.coluna` de uma tabela.\n * - `measures`: agregações (`sum`, `count`, `countDistinct`, `avg`, `min`, `max`) sobre\n * `tabela.coluna`, razões entre medidas (`ratio`) ou fórmulas (`formula`).\n *\n * Dialeto: MySQL/MariaDB.\n */\n\nimport {\n type AggregateMeasure,\n type Aggregation,\n type FormulaMeasure,\n type MeasureMeta,\n type ModelDef,\n type RatioMeasure,\n type SumMeasure,\n validateModel\n} from '../model'\n\nexport type {\n AggregateMeasure,\n Aggregation,\n DimensionDef,\n FieldMeta,\n FormulaMeasure,\n MeasureDef,\n MeasureMeta,\n ModelDef,\n RatioMeasure,\n RelationshipDef,\n SumMeasure,\n TableDef\n} from '../model'\n\nexport const sum = (sql: string, meta?: MeasureMeta): SumMeasure => ({ ...meta, type: 'sum', sql })\n\nconst aggregate =\n <T extends Aggregation>(type: T) =>\n (sql: string, meta?: MeasureMeta): AggregateMeasure & { type: T } => ({ ...meta, type, sql })\n\n/** `COUNT(sql)`: valores não nulos. Para contar linhas, use uma coluna que nunca é nula (a chave). */\nexport const count = aggregate('count')\n/** `COUNT(DISTINCT sql)`: ex. quantas operações têm lançamento (`fVeiculo.navio_operacao_id`). */\nexport const countDistinct = aggregate('countDistinct')\n/** `AVG(sql)`: média das linhas (no estrela, das linhas da própria tabela, não de totais). */\nexport const avg = aggregate('avg')\nexport const min = aggregate('min')\nexport const max = aggregate('max')\n\n/** `numerator / denominator`, ambos nomes de medidas do mesmo modelo (nulo quando o divisor é 0). */\nexport const ratio = (\n numerator: string,\n denominator: string,\n meta?: MeasureMeta\n): RatioMeasure => ({\n ...meta,\n type: 'ratio',\n numerator,\n denominator\n})\n\n/**\n * Combina medidas já agregadas: `{nome}` vira o valor da medida. Ex.:\n * `formula('COALESCE({custoMmo}, 0) + COALESCE({custoAppa}, 0)')` — o jeito de somar medidas de\n * fatos diferentes no modelo estrela.\n */\nexport const formula = (expression: string, meta?: MeasureMeta): FormulaMeasure => ({\n ...meta,\n type: 'formula',\n expression\n})\n\n/** Valida o modelo na carga do módulo: erro de definição aparece no deploy, não na primeira query. */\nexport function defineModel<const M extends ModelDef>(model: M): M {\n const errors = validateModel(model)\n if (errors.length) throw new Error(`[${model.name}] ${errors.join('; ')}`)\n return model\n}\n","import type {\n DashboardBatchQuery,\n DashboardBatchResult,\n DashboardFilters,\n DashboardQueryDebug,\n DashboardRow\n} from '../types'\nimport { DashboardQueryError, measureSources } from './compile'\nimport type { ModelDef } from './defineModel'\n\n/** Consulta que vai ao banco por um grupo do lote. */\nexport type BatchGroup = {\n query: Required<DashboardBatchQuery>\n /** Índices, no lote, das consultas que saem dela. */\n members: number[]\n}\n\ntype Sources = Record<string, string[]>\n\ntype Candidate = BatchGroup & { key: string; sources: Sources | null }\n\nconst cleanFilters = (filters: DashboardFilters = {}): DashboardFilters =>\n Object.fromEntries(\n Object.keys(filters)\n .filter((name) => Array.isArray(filters[name]) && filters[name].length)\n .sort()\n .map((name) => [name, filters[name]])\n )\n\n/**\n * Pode juntar? Com dimensões, as origens têm que ser as mesmas: uma fato a mais traria grupos que só\n * existem nela. Sem dimensões (totais) sai sempre uma linha, então basta que cada fato em comum\n * agregue com as mesmas tabelas (uma tabela a mais no JOIN de uma fato poderia mudar o valor dela).\n */\nfunction compatible(a: Sources, b: Sources, totals: boolean) {\n if (!totals) return JSON.stringify(a) === JSON.stringify(b)\n return Object.keys(a).every((home) => !b[home] || a[home].join() === b[home].join())\n}\n\n/**\n * Agrupa as consultas do lote: mesmas dimensões e filtros e origens compatíveis viram uma consulta\n * com todas as medidas. Consulta só de dimensões (opções de filtro) só junta com outra igual.\n * Erro de uma consulta (medida desconhecida) fica em `errors`, sem derrubar as outras.\n */\nexport function planBatch(model: ModelDef, queries: DashboardBatchQuery[]) {\n const candidates: Candidate[] = []\n const errors = new Map<number, string>()\n\n queries.forEach((raw, index) => {\n const query = {\n dimensions: raw?.dimensions ?? [],\n measures: [...new Set(raw?.measures ?? [])],\n filters: cleanFilters(raw?.filters)\n }\n const key = JSON.stringify([query.dimensions, query.filters, !query.measures.length])\n let sources: Sources | null = null\n try {\n if (query.measures.length) sources = measureSources(model, query.measures)\n } catch (error) {\n if (!(error instanceof DashboardQueryError)) throw error\n errors.set(index, error.message)\n return\n }\n\n const totals = !query.dimensions.length\n const group = candidates.find(\n (candidate) =>\n candidate.key === key &&\n (sources && candidate.sources\n ? compatible(candidate.sources, sources, totals)\n : JSON.stringify(candidate.query.measures) === JSON.stringify(query.measures))\n )\n if (!group) {\n candidates.push({ key, sources, query, members: [index] })\n return\n }\n group.members.push(index)\n group.query.measures = [...new Set([...group.query.measures, ...query.measures])]\n if (group.sources && sources) group.sources = { ...group.sources, ...sources }\n })\n\n const groups: BatchGroup[] = candidates.map(({ query, members }) => ({\n query: { ...query, measures: [...query.measures].sort() },\n members\n }))\n return { groups, errors }\n}\n\nexport type BatchExecution = { rows: DashboardRow[]; debug: DashboardQueryDebug }\n\n/**\n * Executa o lote: uma consulta por grupo (em paralelo) e as linhas de cada consulta só com as\n * colunas que ela pediu. `toQueryError` diz se um erro do grupo é da consulta (vai para as\n * respostas dela) ou do servidor (`null`: derruba o request, como numa consulta sozinha).\n */\nexport async function runBatch(\n model: ModelDef,\n queries: DashboardBatchQuery[],\n run: (query: Required<DashboardBatchQuery>) => Promise<BatchExecution>,\n options: { debug: boolean; toQueryError: (error: unknown) => DashboardQueryError | null }\n): Promise<DashboardBatchResult[]> {\n if (!Array.isArray(queries)) throw new DashboardQueryError('`queries` deve ser uma lista')\n const { groups, errors } = planBatch(model, queries)\n const results: DashboardBatchResult[] = queries.map((_, index) => {\n const error = errors.get(index)\n return error ? { rows: [], error } : { rows: [] }\n })\n\n await Promise.all(\n groups.map(async ({ query, members }) => {\n let execution: BatchExecution\n try {\n execution = await run(query)\n } catch (error) {\n const queryError = options.toQueryError(error)\n if (!queryError) throw error\n for (const index of members) results[index] = { rows: [], error: queryError.message }\n return\n }\n for (const index of members) {\n const own = queries[index]\n const columns = [...(own.dimensions ?? []), ...(own.measures ?? [])]\n results[index] = {\n rows: execution.rows.map((row) =>\n Object.fromEntries(columns.map((column) => [column, row[column] ?? null]))\n ),\n ...(members.length > 1 && { batched: members.length }),\n ...(options.debug && { devtools: { debug: execution.debug } })\n }\n }\n })\n )\n return results\n}\n","import { modelImports, modelObjectCode } from '../codegen'\nimport { AGGREGATIONS, type ModelDef } from '../model'\n\n/**\n * Troca, dentro do arquivo do modelo, só o objeto passado ao `defineModel(...)` e o import do\n * `@bluemarble/bi/server`. O resto do arquivo (JSDoc do modelo, outros exports) fica como está.\n */\n\nexport class ModelFileError extends Error {}\n\n/** Índice do `)` que fecha o `(` em `open`, pulando strings, templates e comentários. */\nfunction findClosingParen(source: string, open: number) {\n let depth = 0\n let comments = false\n // Pilha de contexto: '(' / '{' de código, '`' de template, '${' de interpolação\n const stack: string[] = []\n for (let i = open; i < source.length; i++) {\n const char = source[i]\n const next = source[i + 1]\n const inTemplate = stack[stack.length - 1] === '`'\n\n if (inTemplate) {\n if (char === '\\\\') i++\n else if (char === '`') stack.pop()\n else if (char === '$' && next === '{') {\n stack.push('${')\n i++\n }\n continue\n }\n if (char === \"'\" || char === '\"') {\n for (i++; i < source.length && source[i] !== char; i++) if (source[i] === '\\\\') i++\n continue\n }\n if (char === '/' && next === '/') {\n comments = true\n while (i < source.length && source[i] !== '\\n') i++\n continue\n }\n if (char === '/' && next === '*') {\n comments = true\n i = source.indexOf('*/', i + 2) + 1\n if (i === 0) break\n continue\n }\n if (char === '`') stack.push('`')\n else if (char === '{') stack.push('{')\n else if (char === '}')\n stack.pop() // fecha '{' ou '${'\n else if (char === '(') depth++\n else if (char === ')' && --depth === 0) return { close: i, comments }\n }\n throw new ModelFileError('Não encontrei o fim da chamada `defineModel(...)`')\n}\n\nexport function replaceModelInFile(source: string, model: ModelDef) {\n const warnings: string[] = []\n const call = new RegExp(`export\\\\s+const\\\\s+${model.name}\\\\s*=\\\\s*defineModel(?:<[^>]*>)?\\\\s*\\\\(`)\n const match = call.exec(source)\n if (!match)\n throw new ModelFileError(`O arquivo não tem \\`export const ${model.name} = defineModel(...)\\``)\n\n const open = match.index + match[0].length - 1\n const { close, comments } = findClosingParen(source, open)\n if (comments)\n warnings.push('Comentários dentro do defineModel(...) serão perdidos (os de fora ficam).')\n\n let content = `${source.slice(0, open + 1)}${modelObjectCode(model)}${source.slice(close)}`\n\n // Import: mantém o que mais vier do package e acerta os helpers de medida conforme o uso\n const importRegex = /import\\s*\\{([^}]*)\\}\\s*from\\s*'@bluemarble\\/bi\\/server'/\n const current = importRegex.exec(content)\n const helpers = new Set(['defineModel', 'formula', 'ratio', ...AGGREGATIONS])\n const others = (current?.[1] ?? '')\n .split(',')\n .map((name) => name.trim())\n .filter((name) => name && !helpers.has(name))\n const names = [...others, ...modelImports(model)].sort((a, b) =>\n a.localeCompare(b, 'en', { sensitivity: 'base' })\n )\n const line = `import { ${names.join(', ')} } from '@bluemarble/bi/server'`\n content = current ? content.replace(importRegex, line) : `${line}\\n${content}`\n\n const outside = source.slice(0, match.index) + source.slice(close + 1)\n const measureHelpers = [...helpers].filter((name) => name !== 'defineModel')\n if (new RegExp(`\\\\b(${measureHelpers.join('|')})\\\\(`).test(outside))\n warnings.push('O arquivo usa os helpers de medida fora do modelo; confira o import.')\n\n return { content, warnings }\n}\n","import type { DashboardRow } from '../types'\n\n/** Decimal do Prisma, bigint e afins viram number; datas viram ISO. */\nexport function normalizeValue(value: unknown): string | number | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string' || typeof value === 'number') return value\n if (typeof value === 'bigint') return Number(value)\n if (value instanceof Date) return value.toISOString()\n const number = Number(value)\n return Number.isNaN(number) ? String(value) : number\n}\n\nexport function normalizeRow(row: Record<string, unknown>): DashboardRow {\n const normalized: DashboardRow = {}\n for (const [key, value] of Object.entries(row)) normalized[key] = normalizeValue(value)\n return normalized\n}\n","import {\n type ChartsLayout,\n type ChartTypeSpec,\n layoutFileContent,\n layoutModelWarnings,\n resolveChartTypes,\n validateLayout\n} from '../layout'\nimport { isAggregate, modelMeta, validateModel } from '../model'\nimport { parseColumnRef, referencedTables } from '../star'\nimport type {\n DashboardDevtoolsRequest,\n DashboardQuery,\n DashboardResult,\n DashboardRow,\n DashboardSnapshot,\n ModelColumns,\n ModelValidation,\n ProfileStep,\n SchemaTable\n} from '../types'\nimport { compileQuery, DashboardQueryError } from './compile'\nimport type { ModelDef } from './defineModel'\nimport { ModelFileError, replaceModelInFile } from './modelFile'\nimport { normalizeValue } from './normalize'\n\n/**\n * Pedidos do devtools (`query.devtools`) que não são uma consulta comum. Só chega aqui com\n * `runQuery(..., { devtools })` ligado — tudo isso executa SQL vindo do browser ou escreve em disco.\n */\n\nexport type DevtoolsServerOptions = {\n /**\n * Pasta dos arquivos de modelo (`<pasta>/<nome>.ts`, caminho absoluto). Habilita \"Salvar no\n * arquivo\"; só arquivos que já existem são reescritos.\n */\n modelsDir?: string\n /** Pasta dos snapshots de regressão (`<pasta>/<modelo>/<nome>.json`, caminho absoluto). */\n snapshotsDir?: string\n /**\n * Pasta dos layouts de gráficos (`<pasta>/<id>.json`, caminho absoluto). Habilita \"Salvar no\n * arquivo\" na aba Layout; só arquivos que já existem são reescritos.\n */\n layoutsDir?: string\n /**\n * Tipos de gráfico do app (os mesmos `ChartTypeSpec` do `<DashboardProvider charts>`, sem o\n * componente). Sem eles, salvar um layout com esses tipos é recusado.\n */\n chartTypes?: Record<string, ChartTypeSpec>\n /**\n * Formata o arquivo do modelo antes do preview/gravação (ex.: Biome), para o diff mostrar só o\n * que mudou de fato. Sem isso, o arquivo sai com a formatação do gerador.\n */\n format?: (code: string, path: string) => Promise<string>\n}\n\ntype Executor = (sql: string, params: (string | number)[]) => Promise<Record<string, unknown>[]>\n\ntype Context = {\n registered: ModelDef\n /** Rascunho, se houver; senão o registrado. */\n model: ModelDef\n query: DashboardQuery\n request: DashboardDevtoolsRequest\n execute: Executor\n normalize: (row: Record<string, unknown>) => DashboardRow\n options: DevtoolsServerOptions\n /** Executa a consulta normal (com cache) e devolve as linhas. */\n runRows: (model: ModelDef) => Promise<DashboardRow[]>\n}\n\nconst fs = () => import('node:fs/promises')\n\n/** Mensagem do banco sem o embrulho do Prisma (`Raw query failed. Code: ... Message: ...`). */\nexport function dbMessage(error: unknown) {\n const err = error as { meta?: { message?: string }; message?: string }\n const message = err?.meta?.message ?? err?.message ?? String(error)\n return message.match(/Message: `?([\\s\\S]*?)`?$/)?.[1] ?? message\n}\n\nconst timed = async <T>(load: () => Promise<T>) => {\n const startedAt = performance.now()\n const result = await load()\n return { result, durationMs: Math.round(performance.now() - startedAt) }\n}\n\nconst SLUG = /^[A-Za-z0-9_-]{1,80}$/\n\n/** `<pasta>/<arquivo>`, aceitando pasta com `/` ou `\\` no fim (`path.join` no Windows). */\nconst inDir = (dir: string, file: string) => `${dir.replace(/[\\\\/]+$/, '')}/${file}`\n\n/**\n * Conteúdo final do arquivo: formatado pelo app (se der erro, sai sem formatar) e com o fim de\n * linha do arquivo atual — no Windows o checkout costuma vir com CRLF, e o gerador/formatador\n * devolvem LF, o que faria o diff marcar o arquivo inteiro.\n */\nasync function fileContent(\n raw: string,\n path: string,\n previous: string,\n options: DevtoolsServerOptions\n) {\n const { format } = options\n const formatted = format\n ? await Promise.resolve()\n .then(() => format(raw, path))\n .catch((error) => {\n console.warn('[@bluemarble/bi] format falhou; o arquivo sai sem formatar:', error)\n return raw\n })\n : raw\n const content = formatted.replace(/\\r\\n/g, '\\n')\n return previous.includes('\\r\\n') ? content.replace(/\\n/g, '\\r\\n') : content\n}\n\nexport async function handleDevtoolsRequest(context: Context): Promise<DashboardResult | null> {\n const { request } = context\n if (request.describe) return describe(context)\n if (request.schema) return { rows: [], devtools: { schema: await loadSchema(context.execute) } }\n if (request.columns) {\n const { columns, errors } = await describeColumns(context)\n return { rows: [], devtools: { columns, columnErrors: errors } }\n }\n if (request.validate) return { rows: [], devtools: { validation: await validateSql(context) } }\n if (request.save) return { rows: [], devtools: { save: await saveModel(context) } }\n if (request.saveLayout) return { rows: [], devtools: { save: await saveLayout(context) } }\n if (request.snapshots) return { rows: [], devtools: { snapshots: await listSnapshots(context) } }\n if (request.snapshot) return { rows: [], devtools: { snapshot: await saveSnapshot(context) } }\n if (request.compile) {\n const { sql, params } = compileQuery(context.model, context.query)\n const draft = context.model !== context.registered\n return { rows: [], devtools: { debug: { sql, params, durationMs: 0, cached: false, draft } } }\n }\n if (request.explain) {\n const { sql, params } = compileQuery(context.model, context.query)\n const rows = await context.execute(`EXPLAIN ${sql}`, params)\n return { rows: [], devtools: { explain: rows.map(context.normalize) } }\n }\n if (request.profile) return { rows: [], devtools: { profile: await profile(context) } }\n return null\n}\n\nfunction describe({ registered, options }: Context): DashboardResult {\n return {\n rows: [],\n meta: modelMeta(registered),\n devtools: {\n model: registered,\n capabilities: {\n saveModel: !!options.modelsDir,\n snapshots: !!options.snapshotsDir,\n saveLayout: !!options.layoutsDir,\n chartTypes: Object.keys(options.chartTypes ?? {})\n }\n }\n }\n}\n\nasync function profile({ model, query, execute }: Context): Promise<ProfileStep[]> {\n const { sql, params, parts } = compileQuery(model, query)\n // Em sequência: medições em paralelo disputariam o banco e os tempos não diriam nada\n const steps: ProfileStep[] = []\n for (const part of parts) {\n const { result, durationMs } = await timed(() => execute(part.sql, part.params))\n steps.push({ name: part.name, durationMs, rows: Number(result[0]?.n ?? 0) })\n }\n const total = await timed(() => execute(sql, params))\n steps.push({ name: 'consulta', durationMs: total.durationMs, rows: total.result.length })\n return steps\n}\n\nasync function loadSchema(execute: Executor): Promise<SchemaTable[]> {\n const rows = await execute(\n `SELECT c.TABLE_NAME AS tableName, t.TABLE_TYPE AS tableType,\n c.COLUMN_NAME AS columnName, c.DATA_TYPE AS dataType\n FROM information_schema.COLUMNS c\n JOIN information_schema.TABLES t\n ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.TABLE_NAME = c.TABLE_NAME\n WHERE c.TABLE_SCHEMA = DATABASE()\n ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION`,\n []\n )\n const tables = new Map<string, SchemaTable>()\n for (const row of rows) {\n const name = String(row.tableName)\n let table = tables.get(name)\n if (!table) {\n table = { name, type: String(row.tableType).includes('VIEW') ? 'view' : 'table', columns: [] }\n tables.set(name, table)\n }\n table.columns.push({ name: String(row.columnName), type: String(row.dataType) })\n }\n return [...tables.values()]\n}\n\n/** Poucas chaves bastam para o banco acusar erro de SQL e para descobrir as colunas. */\nconst SAMPLE_KEYS = 20\n\nconst emptyColumns = (): ModelColumns => ({ tables: {}, numeric: [] })\n\n/** Colunas numéricas da primeira linha (Decimal do Prisma e bigint contam). */\nfunction numericColumns(node: string, row: Record<string, unknown> | undefined) {\n return Object.entries(row ?? {})\n .filter(([, value]) => typeof normalizeValue(value) === 'number')\n .map(([column]) => `${node}.${column}`)\n}\n\n/**\n * Colunas de cada tabela do modelo (uma linha de cada). `errors` traz o que o banco recusou, com as\n * chaves da validação (`table:<nome>`).\n */\nasync function loadColumns(model: ModelDef, execute: Executor) {\n const columns = emptyColumns()\n const errors: Record<string, string> = {}\n const attempt = async (key: string, sql: string) => {\n try {\n return await execute(sql, [])\n } catch (error) {\n errors[key] = dbMessage(error)\n return null\n }\n }\n for (const [name, table] of Object.entries(model.tables)) {\n const rows = await attempt(`table:${name}`, `SELECT * FROM (${table.sql}) t LIMIT 1`)\n if (!rows) continue\n columns.tables[name] = Object.keys(rows[0] ?? {})\n columns.numeric.push(...numericColumns(name, rows[0]))\n }\n return { columns, errors }\n}\n\nasync function describeColumns({ model, execute }: Context) {\n const errors = validateModel(model)\n if (errors.length) throw new DashboardQueryError(`Modelo inválido: ${errors.join('; ')}`)\n return loadColumns(model, execute)\n}\n\nasync function validateSql({ model, execute }: Context): Promise<ModelValidation> {\n const validation: ModelValidation = { errors: {}, warnings: {}, columns: emptyColumns() }\n const modelErrors = validateModel(model)\n if (modelErrors.length) {\n validation.errors.model = modelErrors.join('; ')\n return validation\n }\n\n const attempt = async (key: string, sql: string) => {\n try {\n return await execute(sql, [])\n } catch (error) {\n validation.errors[key] = dbMessage(error)\n return null\n }\n }\n\n const loaded = await loadColumns(model, execute)\n validation.columns = loaded.columns\n Object.assign(validation.errors, loaded.errors)\n const tableNames = Object.keys(model.tables)\n\n for (const [index, relationship] of (model.relationships ?? []).entries()) {\n const key = `relationship:${index}`\n const from = parseColumnRef(relationship.from)\n const to = parseColumnRef(relationship.to)\n if (!from || !to) continue\n const missing = [from, to].find(\n (ref) =>\n validation.columns.tables[ref.table]?.length &&\n !validation.columns.tables[ref.table].includes(ref.column)\n )\n if (missing) {\n validation.errors[key] = `a tabela \"${missing.table}\" não tem a coluna \"${missing.column}\"`\n continue\n }\n // O lado 1 precisa ser único, senão a fato multiplica linhas ao juntar\n const toSql = model.tables[to.table]?.sql\n if (!toSql || validation.errors[`table:${to.table}`]) continue\n const repeated = await attempt(\n key,\n `SELECT \\`${to.column}\\` AS v FROM (${toSql}) t WHERE \\`${to.column}\\` IS NOT NULL GROUP BY 1 HAVING COUNT(*) > 1 LIMIT 1`\n )\n if (repeated?.length)\n validation.errors[key] =\n `o lado 1 (${relationship.to}) tem valor repetido: ${String(normalizeValue(repeated[0].v))}`\n }\n\n for (const [name, dimension] of Object.entries(model.dimensions)) {\n const table = referencedTables(dimension.sql, tableNames)[0]\n if (validation.errors[`table:${table}`]) continue\n const sql = `SELECT ${dimension.sql} AS v FROM (SELECT * FROM (${model.tables[table]?.sql}) t LIMIT ${SAMPLE_KEYS}) \\`${table}\\` WHERE ${dimension.sql} IS NOT NULL LIMIT 1`\n const rows = await attempt(`dimension:${name}`, sql)\n const value = rows?.[0]?.v\n if (value === undefined || value === null) continue\n const isNumber = typeof value === 'number' || typeof value === 'bigint'\n if (dimension.type === 'number' && !isNumber && Number.isNaN(Number(value)))\n validation.warnings[`dimension:${name}`] = `tipo number, mas o banco devolveu \"${value}\"`\n if (dimension.type === 'string' && isNumber)\n validation.warnings[`dimension:${name}`] =\n 'tipo string, mas o banco devolveu número (filtros vão chegar como texto)'\n }\n\n for (const [name, measure] of Object.entries(model.measures)) {\n if (!isAggregate(measure)) continue // razão e fórmula só combinam outras medidas\n const failed = referencedTables(measure.sql, tableNames).some(\n (table) => validation.errors[`table:${table}`]\n )\n if (failed) continue\n const { sql, params } = compileQuery(\n model,\n { model: model.name, measures: [name] },\n { sampleLimit: SAMPLE_KEYS }\n )\n try {\n await execute(sql, params)\n } catch (error) {\n validation.errors[`measure:${name}`] = dbMessage(error)\n }\n }\n\n return validation\n}\n\nasync function saveModel({ registered, model, request, options }: Context) {\n if (!options.modelsDir) throw new DashboardQueryError('O endpoint não configurou `modelsDir`')\n if (!request.model) throw new DashboardQueryError('Nada para salvar: não há rascunho')\n if (model.name !== registered.name)\n throw new DashboardQueryError('O rascunho não pode trocar o nome do modelo')\n\n const path = inDir(options.modelsDir, `${registered.name}.ts`)\n const { readFile, writeFile } = await fs()\n let previous: string\n try {\n previous = await readFile(path, 'utf8')\n } catch {\n throw new DashboardQueryError(`Arquivo do modelo não encontrado: ${path}`)\n }\n\n try {\n const replaced = replaceModelInFile(previous, model)\n const { warnings } = replaced\n const content = await fileContent(replaced.content, path, previous, options)\n const written = request.save === 'write' && content !== previous\n if (written) await writeFile(path, content, 'utf8')\n return { path, previous, content, written, warnings }\n } catch (error) {\n if (error instanceof ModelFileError) throw new DashboardQueryError(error.message)\n throw error\n }\n}\n\nasync function saveLayout({ model, request, options }: Context) {\n if (!options.layoutsDir) throw new DashboardQueryError('O endpoint não configurou `layoutsDir`')\n const { layout, mode } = request.saveLayout ?? ({} as { layout?: ChartsLayout; mode?: string })\n const types = resolveChartTypes(options.chartTypes)\n const errors = layout ? validateLayout(layout, types) : ['Nada para salvar: não há layout']\n if (errors.length) throw new DashboardQueryError(`Layout inválido: ${errors.join('; ')}`)\n const valid = layout as ChartsLayout\n\n const path = inDir(options.layoutsDir, `${valid.id}.json`)\n const { readFile, writeFile } = await fs()\n let previous: string\n try {\n previous = await readFile(path, 'utf8')\n } catch {\n throw new DashboardQueryError(`Arquivo do layout não encontrado: ${path}`)\n }\n\n const content = await fileContent(layoutFileContent(valid), path, previous, options)\n const written = mode === 'write' && content !== previous\n if (written) await writeFile(path, content, 'utf8')\n // Contra o modelo em vigor na página (o rascunho, se houver)\n return { path, previous, content, written, warnings: layoutModelWarnings(valid, model, types) }\n}\n\nconst snapshotDir = (options: DevtoolsServerOptions, model: string) => {\n if (!options.snapshotsDir)\n throw new DashboardQueryError('O endpoint não configurou `snapshotsDir`')\n return inDir(options.snapshotsDir, model)\n}\n\nasync function saveSnapshot({ registered, query, request, options, runRows }: Context) {\n const { name, overwrite } = request.snapshot ?? { name: '' }\n if (!SLUG.test(name))\n throw new DashboardQueryError('Nome do snapshot: letras, números, \"-\" e \"_\" (até 80)')\n const dir = snapshotDir(options, registered.name)\n const path = `${dir}/${name}.json`\n const { mkdir, readFile, writeFile } = await fs()\n\n if (!overwrite) {\n const exists = await readFile(path, 'utf8').then(\n () => true,\n () => false\n )\n if (exists) throw new DashboardQueryError(`Já existe um snapshot \"${name}\"`)\n }\n\n // Sempre o modelo registrado: o snapshot é a referência que o rascunho tem que bater\n const snapshot: DashboardSnapshot = {\n name,\n createdAt: new Date().toISOString(),\n query: {\n model: registered.name,\n dimensions: query.dimensions ?? [],\n measures: query.measures ?? [],\n filters: query.filters ?? {}\n },\n rows: await runRows(registered)\n }\n await mkdir(dir, { recursive: true })\n await writeFile(path, `${JSON.stringify(snapshot, null, 2)}\\n`, 'utf8')\n return { path }\n}\n\nasync function listSnapshots({ registered, options }: Context): Promise<DashboardSnapshot[]> {\n const dir = snapshotDir(options, registered.name)\n const { readdir, readFile } = await fs()\n const files = await readdir(dir).catch(() => [] as string[])\n const snapshots = await Promise.all(\n files\n .filter((file) => file.endsWith('.json'))\n .sort()\n .map(\n async (file) => JSON.parse(await readFile(`${dir}/${file}`, 'utf8')) as DashboardSnapshot\n )\n )\n return snapshots\n}\n","import { modelMeta, validateModel } from '../model'\nimport type { DashboardBatchQuery, DashboardQuery, DashboardResult, DashboardRow } from '../types'\nimport { runBatch } from './batch'\nimport { compileQuery, DashboardQueryError } from './compile'\nimport type { ModelDef } from './defineModel'\nimport { type DevtoolsServerOptions, dbMessage, handleDevtoolsRequest } from './devtools'\nimport { normalizeRow } from './normalize'\n\n/** Executa o SQL no banco do app (ex.: `(sql, params) => prisma.$queryRawUnsafe(sql, ...params)`). */\nexport type QueryExecutor = (sql: string, params: (string | number)[]) => Promise<unknown[]>\n\nexport type RunQueryOptions = {\n /**\n * Por quanto tempo reaproveitar o resultado de uma mesma consulta (mesmo SQL + parâmetros).\n * Faz o papel do \"refresh\" do Power BI: alternar filtros não recalcula tudo de novo. 0 = sem cache.\n */\n cacheTtlMs?: number\n /**\n * Aceita os pedidos do devtools (`query.devtools`): debug, rascunho de modelo, EXPLAIN, validação\n * de SQL etc. O rascunho é SQL vindo do browser — ligue SÓ em desenvolvimento. Passe as pastas\n * (`modelsDir`, `snapshotsDir`) para habilitar salvar o modelo e os snapshots.\n */\n devtools?: boolean | DevtoolsServerOptions\n}\n\ntype Execution = { rows: DashboardRow[]; durationMs: number }\n\nconst MAX_CACHE_ENTRIES = 500\nconst cache = new Map<string, { expiresAt: number; execution: Promise<Execution> }>()\n\nfunction fromCache(key: string, ttl: number, load: () => Promise<Execution>) {\n const now = Date.now()\n const hit = cache.get(key)\n if (hit && hit.expiresAt > now) return { execution: hit.execution, cached: true }\n\n if (cache.size >= MAX_CACHE_ENTRIES) {\n for (const [k, entry] of cache) if (entry.expiresAt <= now) cache.delete(k)\n if (cache.size >= MAX_CACHE_ENTRIES) cache.delete(cache.keys().next().value as string)\n }\n // Guarda a promise: requests simultâneas da mesma consulta esperam a mesma execução\n const execution = load()\n cache.set(key, { expiresAt: now + ttl, execution })\n execution.catch(() => cache.delete(key))\n return { execution, cached: false }\n}\n\nexport async function runQuery(\n models: Record<string, ModelDef>,\n query: DashboardQuery,\n execute: QueryExecutor,\n options: RunQueryOptions = {}\n): Promise<DashboardResult> {\n // `debug` desligado é só ignorado (a página continua funcionando com um endpoint sem devtools);\n // o resto expõe ou altera o modelo e é recusado\n const { debug, ...privileged } = query?.devtools ?? {}\n const devtools = options.devtools ? query?.devtools : undefined\n if (!options.devtools && Object.values(privileged).some(Boolean))\n throw new DashboardQueryError('Devtools desabilitado neste ambiente')\n\n const registered = models[query?.model]\n if (!registered) throw new DashboardQueryError(`Modelo desconhecido: \"${query?.model}\"`)\n\n const draft = devtools?.model\n if (draft) {\n const errors = validateModel(draft)\n if (errors.length) throw new DashboardQueryError(`Rascunho inválido: ${errors.join('; ')}`)\n }\n const model = draft ?? registered\n\n // Rótulos/formatos (sem SQL): liberado em produção, os componentes usam como padrão\n if (query.meta) return { rows: [], meta: modelMeta(model) }\n\n const ttl = options.cacheTtlMs ?? 0\n const run = async (target: ModelDef, compiled: DashboardBatchQuery = query) => {\n const { sql, params } = compileQuery(target, { model: query.model, ...compiled })\n const load = async (): Promise<Execution> => {\n const startedAt = performance.now()\n const rows = (await execute(sql, params)) as Record<string, unknown>[]\n return { durationMs: Math.round(performance.now() - startedAt), rows: rows.map(normalizeRow) }\n }\n // O rascunho muda o SQL, então nunca colide com o cache do modelo registrado\n const { execution, cached } =\n ttl > 0\n ? fromCache(JSON.stringify([sql, params]), ttl, load)\n : { execution: load(), cached: false }\n return { sql, params, cached, ...(await execution) }\n }\n\n // Com o devtools, o erro do banco volta como 400 com a mensagem dele (o painel mostra o SQL)\n const toQueryError = (error: unknown) => {\n if (error instanceof DashboardQueryError) return error\n return devtools ? new DashboardQueryError(dbMessage(error)) : null\n }\n\n if (query.queries) {\n const { debug: _, model: __, ...others } = devtools ?? {}\n if (Object.values(others).some(Boolean))\n throw new DashboardQueryError('Num lote, o devtools só aceita `debug` e `model`')\n const results = await runBatch(\n model,\n query.queries,\n async (compiled) => {\n const { rows, sql, params, durationMs, cached } = await run(model, compiled)\n return { rows, debug: { sql, params, durationMs, cached, draft: !!draft } }\n },\n { debug: !!devtools?.debug, toQueryError }\n )\n return { rows: [], results }\n }\n\n if (devtools) {\n if (devtools.clearCache) cache.clear()\n try {\n const handled = await handleDevtoolsRequest({\n registered,\n model,\n query,\n request: devtools,\n execute: (sql, params) => execute(sql, params) as Promise<Record<string, unknown>[]>,\n normalize: normalizeRow,\n options: typeof options.devtools === 'object' ? options.devtools : {},\n runRows: async (target) => (await run(target)).rows\n })\n if (handled) return handled\n } catch (error) {\n // Erro de SQL no devtools é do rascunho/consulta, não do servidor: vira 400 com a mensagem do banco\n if (error instanceof DashboardQueryError) throw error\n throw new DashboardQueryError(dbMessage(error))\n }\n }\n\n const { rows, sql, params, durationMs, cached } = await run(model).catch((error) => {\n throw toQueryError(error) ?? error\n })\n if (!devtools?.debug) return { rows }\n return { rows, devtools: { debug: { sql, params, durationMs, cached, draft: !!draft } } }\n}\n"],"mappings":";;;;AAKA,IAAa,sBAAb,cAAyC,MAAM,CAAC;AAiBhD,MAAM,oBAAoB;AAE1B,MAAM,SAAS,eAAuB,KAAK,WAAW;;;;;AAMtD,SAAS,WACP,OACA,MACA,MACA,QAAkB,CAAC,GACX;CACR,IAAI,MAAM,SAAS,IAAI,GACrB,MAAM,IAAI,oBAAoB,oBAAoB,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,KAAK,MAAM,GAAG;CACnF,MAAM,UAAU,MAAM,SAAS;CAC/B,IAAI,CAAC,SAAS,MAAM,IAAI,oBAAoB,yBAAyB,KAAK,EAAE;CAC5E,IAAI,YAAY,OAAO,GAAG,OAAO,KAAK,MAAM,OAAO;CACnD,MAAM,OAAO,CAAC,GAAG,OAAO,IAAI;CAC5B,MAAM,OAAO,UAAkB,WAAW,OAAO,OAAO,MAAM,IAAI;CAClE,IAAI,QAAQ,SAAS,SACnB,OAAO,IAAI,IAAI,QAAQ,SAAS,EAAE,aAAa,IAAI,QAAQ,WAAW,EAAE;CAC1E,OAAO,IAAI,QAAQ,WAAW,QAAQ,kCAAkC,GAAG,UAAU,IAAI,IAAI,KAAK,EAAE,EAAE,EAAE;AAC1G;;AAGA,SAAS,aAAa,OAAiB,OAA2B;CAChE,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,QAAQ,OACjB,WAAW,OAAO,OAAO,MAAM,YAAY;EACzC,OAAO,IAAI,IAAI;EACf,OAAO,QAAQ;CACjB,CAAC;CACH,OAAO,CAAC,GAAG,MAAM;AACnB;;;;;;AAOA,SAAgB,eAAe,OAAiB,UAA8C;CAC5F,MAAM,aAAa,OAAO,KAAK,MAAM,MAAM;CAC3C,MAAM,QAAQ,WAAW,KAAK;CAC9B,MAAM,UAAuC,CAAC;CAC9C,KAAK,MAAM,QAAQ,aAAa,OAAO,QAAQ,GAAG;EAChD,MAAM,UAAU,MAAM,SAAS;EAC/B,IAAI,CAAC,YAAY,OAAO,GAAG;EAC3B,MAAM,SAAS,iBAAiB,QAAQ,KAAK,UAAU;EACvD,MAAM,OAAO,YAAY,QAAQ,KAAK;EACtC,IAAI,CAAC,MAAM,MAAM,IAAI,oBAAoB,WAAW,KAAK,uBAAuB;EAChF,QAAQ,0BAAU,IAAI,IAAI;EAC1B,KAAK,MAAM,SAAS,QAAQ,QAAQ,KAAK,CAAC,IAAI,KAAK;CACrD;CACA,OAAO,OAAO,YACZ,OAAO,QAAQ,OAAO,CAAC,CACpB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CACtC,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CACvD;AACF;AAIA,SAAS,eAAe,OAAiB,OAAiC;CACxE,MAAM,UAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,MAAM,WAAW,CAAC,CAAC,GAAG;EAChE,MAAM,YAAY,MAAM,WAAW;EACnC,IAAI,CAAC,WAAW,MAAM,IAAI,oBAAoB,qCAAqC,KAAK,EAAE;EAC1F,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAAC,OAAO,QAAQ;EAC9C,IAAI,OAAO,SAAS,mBAClB,MAAM,IAAI,oBAAoB,6BAA6B,KAAK,EAAE;EACpE,KAAK,MAAM,SAAS,QAElB,IAAI,EADU,UAAU,SAAS,WAAW,OAAO,SAAS,KAAK,IAAI,OAAO,UAAU,WAC1E,MAAM,IAAI,oBAAoB,6BAA6B,KAAK,KAAK,OAAO;EAE1F,QAAQ,KAAK;GACX,WAAW;GACX,QAAQ,GAAG,UAAU,IAAI,OAAO,OAAO,UAAU,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;GACjE,QAAQ;EACV,CAAC;CACH;CACA,OAAO;AACT;;;;;;;AAQA,SAAgB,aACd,OACA,OACA,UAA0B,CAAC,GACZ;CACf,MAAM,aAAa,MAAM,cAAc,CAAC;CACxC,MAAM,WAAW,MAAM,YAAY,CAAC;CACpC,IAAI,CAAC,WAAW,UAAU,CAAC,SAAS,QAClC,MAAM,IAAI,oBAAoB,yCAAyC;CAEzE,KAAK,MAAM,QAAQ,YACjB,IAAI,CAAC,MAAM,WAAW,OAAO,MAAM,IAAI,oBAAoB,2BAA2B,KAAK,EAAE;CAI/F,OAAO,YAAY,OAAO,YAAY,UADtB,eAAe,OAAO,KACgB,GAAG,OAAO;AAClE;AAEA,MAAM,SAAS,UAAkB,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,UAAU,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI;;;;;;;;;;;;;;;;AAiBjG,SAAS,YACP,OACA,YACA,UACA,SACA,EAAE,eACa;CACf,MAAM,EAAE,WAAW;CACnB,MAAM,aAAa,OAAO,KAAK,MAAM;CACrC,MAAM,QAAQ,WAAW,KAAK;CAC9B,MAAM,WAAW,QAAgB,iBAAiB,KAAK,UAAU;CACjE,MAAM,kBAAkB,SAAiB,QAAQ,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC,CAAC;CAG7E,MAAM,yBAAS,IAAI,IAAsB;CACzC,MAAM,UAAU,SAAiB,MAAM,SAAS;CAChD,KAAK,MAAM,QAAQ,aAAa,OAAO,QAAQ,GAAG;EAChD,MAAM,UAAU,MAAM,SAAS;EAC/B,IAAI,CAAC,YAAY,OAAO,GAAG;EAC3B,MAAM,OAAO,YAAY,QAAQ,QAAQ,GAAG,GAAG,KAAK;EACpD,IAAI,CAAC,MAAM,MAAM,IAAI,oBAAoB,WAAW,KAAK,uBAAuB;EAChF,OAAO,IAAI,MAAM,CAAC,GAAI,OAAO,IAAI,IAAI,KAAK,CAAC,GAAI,IAAI,CAAC;CACtD;CACA,IAAI,CAAC,OAAO,MAAM,OAAO,IAAI,cAAc,OAAO,YAAY,SAAS,KAAK,GAAG,CAAC,CAAC;CAEjF,MAAM,UAAU,SAAiB;EAC/B,MAAM,MAAM,OAAO,KAAK,CAAC;EACzB,OAAO,gBAAgB,KAAA,IACnB,IAAI,IAAI,KACR,mBAAmB,IAAI,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,CAAC,EAAE;CAC9E;CAEA,MAAM,aAAa,MAAc,WAAqB;EACpD,MAAM,EAAE,UAAU,UAAU,MAAM,KAAK;EACvC,KAAK,MAAM,QAAQ,YACjB,IAAI,CAAC,MAAM,IAAI,eAAe,IAAI,CAAC,GACjC,MAAM,IAAI,oBACR,OAAO,SACH,eAAe,KAAK,KAAK,eAAe,IAAI,EAAE,yBAAyB,KAAK,gBAAgB,OAAO,KAAK,IAAI,MAC5G,gBAAgB,WAAW,KAAK,IAAI,EAAE,4BAC5C;EACJ,MAAM,UAAU,QAAQ,QAAQ,WAAW,MAAM,IAAI,eAAe,OAAO,SAAS,CAAC,CAAC;EACtF,MAAM,yBAAS,IAAI,IAAI;GACrB,GAAG,WAAW,IAAI,cAAc;GAChC,GAAG,QAAQ,KAAK,WAAW,eAAe,OAAO,SAAS,CAAC;GAC3D,GAAG,OAAO,SAAS,SAAS,QAAQ,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC;EACvD,CAAC;EACD,MAAM,QAAQ,SAAS,MAAM,QAAQ,KAAK,CAAC,CAAC,KACzC,EAAE,OAAO,WACR,GAAG,KAAK,QAAQ,SAAS,YAAY,GAAG,OAAO,KAAK,EAAE,GAAG,MAAM,KAAK,EAAE,SAAS,MAAM,KAAK,EAAE,GAAG,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,MAAM,KAAK,KAAK,KAAK,EAAE,GAAG,MAAM,KAAK,KAAK,MAAM,GAC9K;EAcA,OAAO;GAAE;GAAM,KATH;IACV,UAAU,CAJV,GAAG,WAAW,KAAK,SAAS,GAAG,MAAM,WAAW,KAAK,CAAC,IAAI,MAAM,MAAM,IAAI,GAAG,GAC7E,GAAG,OAAO,KAAK,SAAS,GAAG,aAAa,OAAO,IAAI,CAAC,EAAE,MAAM,MAAM,IAAI,GAAG,CAG1D,CAAC,CAAC,KAAK,OAAO;IAC7B,QAAQ,OAAO,IAAI,EAAE,GAAG,MAAM,IAAI;IAClC,GAAG;IACH,QAAQ,SAAS,SAAS,QAAQ,KAAK,WAAW,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,MAAM;IACnF,WAAW,SAAS,YAAY,MAAM,WAAW,MAAM,MAAM;GAC/D,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IACS;GAAG,QAAQ,QAAQ,SAAS,WAAW,OAAO,MAAM;EAAE;CACzE;CAEA,MAAM,aAAa,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,UAAU,MAAM,MAAM,CAAC;CAC9E,MAAM,QAAQ,WAAW,KAAK,EAAE,MAAM,KAAK,cAAc;EACvD,MAAM,QAAQ;EACd,KAAK,8BAA8B,IAAI;EACvC;CACF,EAAE;CACF,MAAM,UAAU,WAAW,SAAS,cAAc,MAAM,WAAW,MAAM,MAAM;CAG/E,IAAI,WAAW,WAAW,GAAG;EAC3B,MAAM,CAAC,QAAQ;EACf,IAAI,CAAC,SAAS,UAAU,SAAS,OAAO,SAAS,YAAY,MAAM,SAAS,KAAK,CAAC,GAChF,OAAO;GAAE,KAAK,GAAG,KAAK,MAAM;GAAW,QAAQ,KAAK;GAAQ;EAAM;CACtE;CAKA,MAAM,UAAU,CAAC,GAAG,YAAY,GAAG,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;CAC9D,MAAM,QAAQ,WACX,KAAK,EAAE,QAAQ,UAAU;EACxB,MAAM,MAAM,IAAI,IAAI,OAAO,IAAI,IAAI,CAAC;EAIpC,OAAO,UAHQ,QAAQ,KAAK,WAC1B,WAAW,SAAS,MAAM,KAAK,IAAI,IAAI,MAAM,IAAI,MAAM,MAAM,IAAI,WAAW,MAAM,MAAM,GAEpE,CAAC,CAAC,KAAK,IAAI,EAAE,QAAQ,MAAM,SAAS,OAAO;CACnE,CAAC,CAAC,CACD,KAAK,gBAAgB;CACxB,MAAM,QAAQ,SAAiB,OAAO,MAAM,KAAK,EAAE,GAAG,MAAM,IAAI,EAAE;CAClE,MAAM,SAAS,CACb,GAAG,WAAW,KAAK,SAAS,GAAG,MAAM,KAAK,EAAE,GAAG,MAAM,IAAI,EAAE,MAAM,MAAM,IAAI,GAAG,GAC9E,GAAG,SAAS,KAAK,SAAS,GAAG,WAAW,OAAO,MAAM,IAAI,EAAE,MAAM,MAAM,IAAI,GAAG,CAChF;CASA,OAAO;EAAE,KARG;GACV,QAAQ,WAAW,KAAK,EAAE,OAAO,UAAU,GAAG,MAAM,SAAS,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK;GACnG,UAAU,OAAO,KAAK,OAAO;GAC7B,aAAa,MAAM,MAAM,MAAM,KAAK;GACpC,WAAW,SAAS,YAAY,MAAM,WAAW,MAAM,IAAI,YAAY;EACzE,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,IACG;EAAG,QAAQ,WAAW,SAAS,SAAS,KAAK,MAAM;EAAG;CAAM;AACzE;;;;;;;AAQA,SAAS,cACP,OACA,YACA,SACA,OACQ;CACR,MAAM,aAAa,OAAO,KAAK,MAAM,MAAM;CAC3C,MAAM,WAAW,SAAiB,iBAAiB,MAAM,WAAW,KAAK,CAAC,KAAK,UAAU,CAAC,CAAC;CAC3F,MAAM,MAAM,QAAQ,WAAW,EAAE;CACjC,MAAM,UAAU,WAAW,IAAI,OAAO;CACtC,MAAM,eAAe,QAAQ,KAAK,WAAW,QAAQ,OAAO,SAAS,CAAC;CACtE,IAAI,OAAgD;CAEpD,KAAK,MAAM,SAAS,CAAC,KAAK,GAAG,WAAW,QAAQ,SAAS,SAAS,GAAG,CAAC,GAAG;EACvE,MAAM,EAAE,UAAU,UAAU,OAAO,KAAK;EACxC,IAAI,CAAC,QAAQ,OAAO,WAAW,MAAM,IAAI,MAAM,CAAC,GAAG;EACnD,MAAM,QAAQ,aAAa,QAAQ,WAAW,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC;EACjE,IAAI,CAAC,QAAQ,QAAQ,KAAK,OAAO,OAAO;GAAE;GAAO;EAAM;CACzD;CACA,OAAO,MAAM,SAAS;AACxB;;;;;;;;;;;;;;;;;;ACnPA,MAAa,OAAO,KAAa,UAAoC;CAAE,GAAG;CAAM,MAAM;CAAO;AAAI;AAEjG,MAAM,aACoB,UACvB,KAAa,UAAwD;CAAE,GAAG;CAAM;CAAM;AAAI;;AAG7F,MAAa,QAAQ,UAAU,OAAO;;AAEtC,MAAa,gBAAgB,UAAU,eAAe;;AAEtD,MAAa,MAAM,UAAU,KAAK;AAClC,MAAa,MAAM,UAAU,KAAK;AAClC,MAAa,MAAM,UAAU,KAAK;;AAGlC,MAAa,SACX,WACA,aACA,UACkB;CAClB,GAAG;CACH,MAAM;CACN;CACA;AACF;;;;;;AAOA,MAAa,WAAW,YAAoB,UAAwC;CAClF,GAAG;CACH,MAAM;CACN;AACF;;AAGA,SAAgB,YAAsC,OAAa;CACjE,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,OAAO,QAAQ,MAAM,IAAI,MAAM,IAAI,MAAM,KAAK,IAAI,OAAO,KAAK,IAAI,GAAG;CACzE,OAAO;AACT;;;AChEA,MAAM,gBAAgB,UAA4B,CAAC,MACjD,OAAO,YACL,OAAO,KAAK,OAAO,CAAC,CACjB,QAAQ,SAAS,MAAM,QAAQ,QAAQ,KAAK,KAAK,QAAQ,KAAK,CAAC,MAAM,CAAC,CACtE,KAAK,CAAC,CACN,KAAK,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,CACxC;;;;;;AAOF,SAAS,WAAW,GAAY,GAAY,QAAiB;CAC3D,IAAI,CAAC,QAAQ,OAAO,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;CAC1D,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;AACrF;;;;;;AAOA,SAAgB,UAAU,OAAiB,SAAgC;CACzE,MAAM,aAA0B,CAAC;CACjC,MAAM,yBAAS,IAAI,IAAoB;CAEvC,QAAQ,SAAS,KAAK,UAAU;EAC9B,MAAM,QAAQ;GACZ,YAAY,KAAK,cAAc,CAAC;GAChC,UAAU,CAAC,GAAG,IAAI,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC;GAC1C,SAAS,aAAa,KAAK,OAAO;EACpC;EACA,MAAM,MAAM,KAAK,UAAU;GAAC,MAAM;GAAY,MAAM;GAAS,CAAC,MAAM,SAAS;EAAM,CAAC;EACpF,IAAI,UAA0B;EAC9B,IAAI;GACF,IAAI,MAAM,SAAS,QAAQ,UAAU,eAAe,OAAO,MAAM,QAAQ;EAC3E,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,sBAAsB,MAAM;GACnD,OAAO,IAAI,OAAO,MAAM,OAAO;GAC/B;EACF;EAEA,MAAM,SAAS,CAAC,MAAM,WAAW;EACjC,MAAM,QAAQ,WAAW,MACtB,cACC,UAAU,QAAQ,QACjB,WAAW,UAAU,UAClB,WAAW,UAAU,SAAS,SAAS,MAAM,IAC7C,KAAK,UAAU,UAAU,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,QAAQ,EAClF;EACA,IAAI,CAAC,OAAO;GACV,WAAW,KAAK;IAAE;IAAK;IAAS;IAAO,SAAS,CAAC,KAAK;GAAE,CAAC;GACzD;EACF;EACA,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,MAAM,WAAW,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,MAAM,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,CAAC;EAChF,IAAI,MAAM,WAAW,SAAS,MAAM,UAAU;GAAE,GAAG,MAAM;GAAS,GAAG;EAAQ;CAC/E,CAAC;CAMD,OAAO;EAAE,QAJoB,WAAW,KAAK,EAAE,OAAO,eAAe;GACnE,OAAO;IAAE,GAAG;IAAO,UAAU,CAAC,GAAG,MAAM,QAAQ,CAAC,CAAC,KAAK;GAAE;GACxD;EACF,EACc;EAAG;CAAO;AAC1B;;;;;;AASA,eAAsB,SACpB,OACA,SACA,KACA,SACiC;CACjC,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,MAAM,IAAI,oBAAoB,8BAA8B;CACzF,MAAM,EAAE,QAAQ,WAAW,UAAU,OAAO,OAAO;CACnD,MAAM,UAAkC,QAAQ,KAAK,GAAG,UAAU;EAChE,MAAM,QAAQ,OAAO,IAAI,KAAK;EAC9B,OAAO,QAAQ;GAAE,MAAM,CAAC;GAAG;EAAM,IAAI,EAAE,MAAM,CAAC,EAAE;CAClD,CAAC;CAED,MAAM,QAAQ,IACZ,OAAO,IAAI,OAAO,EAAE,OAAO,cAAc;EACvC,IAAI;EACJ,IAAI;GACF,YAAY,MAAM,IAAI,KAAK;EAC7B,SAAS,OAAO;GACd,MAAM,aAAa,QAAQ,aAAa,KAAK;GAC7C,IAAI,CAAC,YAAY,MAAM;GACvB,KAAK,MAAM,SAAS,SAAS,QAAQ,SAAS;IAAE,MAAM,CAAC;IAAG,OAAO,WAAW;GAAQ;GACpF;EACF;EACA,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,MAAM,QAAQ;GACpB,MAAM,UAAU,CAAC,GAAI,IAAI,cAAc,CAAC,GAAI,GAAI,IAAI,YAAY,CAAC,CAAE;GACnE,QAAQ,SAAS;IACf,MAAM,UAAU,KAAK,KAAK,QACxB,OAAO,YAAY,QAAQ,KAAK,WAAW,CAAC,QAAQ,IAAI,WAAW,IAAI,CAAC,CAAC,CAC3E;IACA,GAAI,QAAQ,SAAS,KAAK,EAAE,SAAS,QAAQ,OAAO;IACpD,GAAI,QAAQ,SAAS,EAAE,UAAU,EAAE,OAAO,UAAU,MAAM,EAAE;GAC9D;EACF;CACF,CAAC,CACH;CACA,OAAO;AACT;;;;;;;AC7HA,IAAa,iBAAb,cAAoC,MAAM,CAAC;;AAG3C,SAAS,iBAAiB,QAAgB,MAAc;CACtD,IAAI,QAAQ;CACZ,IAAI,WAAW;CAEf,MAAM,QAAkB,CAAC;CACzB,KAAK,IAAI,IAAI,MAAM,IAAI,OAAO,QAAQ,KAAK;EACzC,MAAM,OAAO,OAAO;EACpB,MAAM,OAAO,OAAO,IAAI;EAGxB,IAFmB,MAAM,MAAM,SAAS,OAAO,KAE/B;GACd,IAAI,SAAS,MAAM;QACd,IAAI,SAAS,KAAK,MAAM,IAAI;QAC5B,IAAI,SAAS,OAAO,SAAS,KAAK;IACrC,MAAM,KAAK,IAAI;IACf;GACF;GACA;EACF;EACA,IAAI,SAAS,OAAO,SAAS,MAAK;GAChC,KAAK,KAAK,IAAI,OAAO,UAAU,OAAO,OAAO,MAAM,KAAK,IAAI,OAAO,OAAO,MAAM;GAChF;EACF;EACA,IAAI,SAAS,OAAO,SAAS,KAAK;GAChC,WAAW;GACX,OAAO,IAAI,OAAO,UAAU,OAAO,OAAO,MAAM;GAChD;EACF;EACA,IAAI,SAAS,OAAO,SAAS,KAAK;GAChC,WAAW;GACX,IAAI,OAAO,QAAQ,MAAM,IAAI,CAAC,IAAI;GAClC,IAAI,MAAM,GAAG;GACb;EACF;EACA,IAAI,SAAS,KAAK,MAAM,KAAK,GAAG;OAC3B,IAAI,SAAS,KAAK,MAAM,KAAK,GAAG;OAChC,IAAI,SAAS,KAChB,MAAM,IAAI;OACP,IAAI,SAAS,KAAK;OAClB,IAAI,SAAS,OAAO,EAAE,UAAU,GAAG,OAAO;GAAE,OAAO;GAAG;EAAS;CACtE;CACA,MAAM,IAAI,eAAe,mDAAmD;AAC9E;AAEA,SAAgB,mBAAmB,QAAgB,OAAiB;CAClE,MAAM,WAAqB,CAAC;CAE5B,MAAM,QAAQ,IADG,OAAO,sBAAsB,MAAM,KAAK,wCACxC,CAAC,CAAC,KAAK,MAAM;CAC9B,IAAI,CAAC,OACH,MAAM,IAAI,eAAe,oCAAoC,MAAM,KAAK,sBAAsB;CAEhG,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,SAAS;CAC7C,MAAM,EAAE,OAAO,aAAa,iBAAiB,QAAQ,IAAI;CACzD,IAAI,UACF,SAAS,KAAK,2EAA2E;CAE3F,IAAI,UAAU,GAAG,OAAO,MAAM,GAAG,OAAO,CAAC,IAAI,gBAAgB,KAAK,IAAI,OAAO,MAAM,KAAK;CAGxF,MAAM,cAAc;CACpB,MAAM,UAAU,YAAY,KAAK,OAAO;CACxC,MAAM,0BAAU,IAAI,IAAI;EAAC;EAAe;EAAW;EAAS,GAAG;CAAY,CAAC;CAQ5E,MAAM,OAAO,YAHC,CAAC,IAJC,UAAU,MAAM,GAAA,CAC7B,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,QAAQ,CAAC,QAAQ,IAAI,IAAI,CACtB,GAAG,GAAG,aAAa,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,MACzD,EAAE,cAAc,GAAG,MAAM,EAAE,aAAa,OAAO,CAAC,CAErB,CAAC,CAAC,KAAK,IAAI,EAAE;CAC1C,UAAU,UAAU,QAAQ,QAAQ,aAAa,IAAI,IAAI,GAAG,KAAK,IAAI;CAErE,MAAM,UAAU,OAAO,MAAM,GAAG,MAAM,KAAK,IAAI,OAAO,MAAM,QAAQ,CAAC;CACrE,MAAM,iBAAiB,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,SAAS,SAAS,aAAa;CAC3E,IAAI,IAAI,OAAO,OAAO,eAAe,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC,KAAK,OAAO,GAChE,SAAS,KAAK,sEAAsE;CAEtF,OAAO;EAAE;EAAS;CAAS;AAC7B;;;;ACtFA,SAAgB,eAAe,OAAwC;CACrE,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;CAClD,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU,OAAO;CACnE,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CAClD,IAAI,iBAAiB,MAAM,OAAO,MAAM,YAAY;CACpD,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,OAAO,MAAM,MAAM,IAAI,OAAO,KAAK,IAAI;AAChD;AAEA,SAAgB,aAAa,KAA4C;CACvE,MAAM,aAA2B,CAAC;CAClC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAAG,WAAW,OAAO,eAAe,KAAK;CACtF,OAAO;AACT;;;ACuDA,MAAM,WAAW,OAAO;;AAGxB,SAAgB,UAAU,OAAgB;CACxC,MAAM,MAAM;CACZ,MAAM,UAAU,KAAK,MAAM,WAAW,KAAK,WAAW,OAAO,KAAK;CAClE,OAAO,QAAQ,MAAM,0BAA0B,CAAC,GAAG,MAAM;AAC3D;AAEA,MAAM,QAAQ,OAAU,SAA2B;CACjD,MAAM,YAAY,YAAY,IAAI;CAElC,OAAO;EAAE,QAAA,MADY,KAAK;EACT,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS;CAAE;AACzE;AAEA,MAAM,OAAO;;AAGb,MAAM,SAAS,KAAa,SAAiB,GAAG,IAAI,QAAQ,WAAW,EAAE,EAAE,GAAG;;;;;;AAO9E,eAAe,YACb,KACA,MACA,UACA,SACA;CACA,MAAM,EAAE,WAAW;CASnB,MAAM,WARY,SACd,MAAM,QAAQ,QAAQ,CAAC,CACpB,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC,CAC7B,OAAO,UAAU;EAChB,QAAQ,KAAK,+DAA+D,KAAK;EACjF,OAAO;CACT,CAAC,IACH,IAAA,CACsB,QAAQ,SAAS,IAAI;CAC/C,OAAO,SAAS,SAAS,MAAM,IAAI,QAAQ,QAAQ,OAAO,MAAM,IAAI;AACtE;AAEA,eAAsB,sBAAsB,SAAmD;CAC7F,MAAM,EAAE,YAAY;CACpB,IAAI,QAAQ,UAAU,OAAO,SAAS,OAAO;CAC7C,IAAI,QAAQ,QAAQ,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,QAAQ,MAAM,WAAW,QAAQ,OAAO,EAAE;CAAE;CAC/F,IAAI,QAAQ,SAAS;EACnB,MAAM,EAAE,SAAS,WAAW,MAAM,gBAAgB,OAAO;EACzD,OAAO;GAAE,MAAM,CAAC;GAAG,UAAU;IAAE;IAAS,cAAc;GAAO;EAAE;CACjE;CACA,IAAI,QAAQ,UAAU,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,YAAY,MAAM,YAAY,OAAO,EAAE;CAAE;CAC9F,IAAI,QAAQ,MAAM,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,MAAM,MAAM,UAAU,OAAO,EAAE;CAAE;CAClF,IAAI,QAAQ,YAAY,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,MAAM,MAAM,WAAW,OAAO,EAAE;CAAE;CACzF,IAAI,QAAQ,WAAW,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,WAAW,MAAM,cAAc,OAAO,EAAE;CAAE;CAChG,IAAI,QAAQ,UAAU,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,UAAU,MAAM,aAAa,OAAO,EAAE;CAAE;CAC7F,IAAI,QAAQ,SAAS;EACnB,MAAM,EAAE,KAAK,WAAW,aAAa,QAAQ,OAAO,QAAQ,KAAK;EAEjE,OAAO;GAAE,MAAM,CAAC;GAAG,UAAU,EAAE,OAAO;IAAE;IAAK;IAAQ,YAAY;IAAG,QAAQ;IAAO,OADrE,QAAQ,UAAU,QAAQ;GACiD,EAAE;EAAE;CAC/F;CACA,IAAI,QAAQ,SAAS;EACnB,MAAM,EAAE,KAAK,WAAW,aAAa,QAAQ,OAAO,QAAQ,KAAK;EAEjE,OAAO;GAAE,MAAM,CAAC;GAAG,UAAU,EAAE,UAAS,MADrB,QAAQ,QAAQ,WAAW,OAAO,MAAM,EAAA,CACd,IAAI,QAAQ,SAAS,EAAE;EAAE;CACxE;CACA,IAAI,QAAQ,SAAS,OAAO;EAAE,MAAM,CAAC;EAAG,UAAU,EAAE,SAAS,MAAM,QAAQ,OAAO,EAAE;CAAE;CACtF,OAAO;AACT;AAEA,SAAS,SAAS,EAAE,YAAY,WAAqC;CACnE,OAAO;EACL,MAAM,CAAC;EACP,MAAM,UAAU,UAAU;EAC1B,UAAU;GACR,OAAO;GACP,cAAc;IACZ,WAAW,CAAC,CAAC,QAAQ;IACrB,WAAW,CAAC,CAAC,QAAQ;IACrB,YAAY,CAAC,CAAC,QAAQ;IACtB,YAAY,OAAO,KAAK,QAAQ,cAAc,CAAC,CAAC;GAClD;EACF;CACF;AACF;AAEA,eAAe,QAAQ,EAAE,OAAO,OAAO,WAA4C;CACjF,MAAM,EAAE,KAAK,QAAQ,UAAU,aAAa,OAAO,KAAK;CAExD,MAAM,QAAuB,CAAC;CAC9B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,EAAE,QAAQ,eAAe,MAAM,YAAY,QAAQ,KAAK,KAAK,KAAK,MAAM,CAAC;EAC/E,MAAM,KAAK;GAAE,MAAM,KAAK;GAAM;GAAY,MAAM,OAAO,OAAO,EAAE,EAAE,KAAK,CAAC;EAAE,CAAC;CAC7E;CACA,MAAM,QAAQ,MAAM,YAAY,QAAQ,KAAK,MAAM,CAAC;CACpD,MAAM,KAAK;EAAE,MAAM;EAAY,YAAY,MAAM;EAAY,MAAM,MAAM,OAAO;CAAO,CAAC;CACxF,OAAO;AACT;AAEA,eAAe,WAAW,SAA2C;CACnE,MAAM,OAAO,MAAM,QACjB;;;;;;iDAOA,CAAC,CACH;CACA,MAAM,yBAAS,IAAI,IAAyB;CAC5C,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,OAAO,OAAO,IAAI,SAAS;EACjC,IAAI,QAAQ,OAAO,IAAI,IAAI;EAC3B,IAAI,CAAC,OAAO;GACV,QAAQ;IAAE;IAAM,MAAM,OAAO,IAAI,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,SAAS;IAAS,SAAS,CAAC;GAAE;GAC7F,OAAO,IAAI,MAAM,KAAK;EACxB;EACA,MAAM,QAAQ,KAAK;GAAE,MAAM,OAAO,IAAI,UAAU;GAAG,MAAM,OAAO,IAAI,QAAQ;EAAE,CAAC;CACjF;CACA,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC5B;;AAGA,MAAM,cAAc;AAEpB,MAAM,sBAAoC;CAAE,QAAQ,CAAC;CAAG,SAAS,CAAC;AAAE;;AAGpE,SAAS,eAAe,MAAc,KAA0C;CAC9E,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC,CAAC,CAC7B,QAAQ,GAAG,WAAW,OAAO,eAAe,KAAK,MAAM,QAAQ,CAAC,CAChE,KAAK,CAAC,YAAY,GAAG,KAAK,GAAG,QAAQ;AAC1C;;;;;AAMA,eAAe,YAAY,OAAiB,SAAmB;CAC7D,MAAM,UAAU,aAAa;CAC7B,MAAM,SAAiC,CAAC;CACxC,MAAM,UAAU,OAAO,KAAa,QAAgB;EAClD,IAAI;GACF,OAAO,MAAM,QAAQ,KAAK,CAAC,CAAC;EAC9B,SAAS,OAAO;GACd,OAAO,OAAO,UAAU,KAAK;GAC7B,OAAO;EACT;CACF;CACA,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,MAAM,GAAG;EACxD,MAAM,OAAO,MAAM,QAAQ,SAAS,QAAQ,kBAAkB,MAAM,IAAI,YAAY;EACpF,IAAI,CAAC,MAAM;EACX,QAAQ,OAAO,QAAQ,OAAO,KAAK,KAAK,MAAM,CAAC,CAAC;EAChD,QAAQ,QAAQ,KAAK,GAAG,eAAe,MAAM,KAAK,EAAE,CAAC;CACvD;CACA,OAAO;EAAE;EAAS;CAAO;AAC3B;AAEA,eAAe,gBAAgB,EAAE,OAAO,WAAoB;CAC1D,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,OAAO,QAAQ,MAAM,IAAI,oBAAoB,oBAAoB,OAAO,KAAK,IAAI,GAAG;CACxF,OAAO,YAAY,OAAO,OAAO;AACnC;AAEA,eAAe,YAAY,EAAE,OAAO,WAA8C;CAChF,MAAM,aAA8B;EAAE,QAAQ,CAAC;EAAG,UAAU,CAAC;EAAG,SAAS,aAAa;CAAE;CACxF,MAAM,cAAc,cAAc,KAAK;CACvC,IAAI,YAAY,QAAQ;EACtB,WAAW,OAAO,QAAQ,YAAY,KAAK,IAAI;EAC/C,OAAO;CACT;CAEA,MAAM,UAAU,OAAO,KAAa,QAAgB;EAClD,IAAI;GACF,OAAO,MAAM,QAAQ,KAAK,CAAC,CAAC;EAC9B,SAAS,OAAO;GACd,WAAW,OAAO,OAAO,UAAU,KAAK;GACxC,OAAO;EACT;CACF;CAEA,MAAM,SAAS,MAAM,YAAY,OAAO,OAAO;CAC/C,WAAW,UAAU,OAAO;CAC5B,OAAO,OAAO,WAAW,QAAQ,OAAO,MAAM;CAC9C,MAAM,aAAa,OAAO,KAAK,MAAM,MAAM;CAE3C,KAAK,MAAM,CAAC,OAAO,kBAAkB,MAAM,iBAAiB,CAAC,EAAA,CAAG,QAAQ,GAAG;EACzE,MAAM,MAAM,gBAAgB;EAC5B,MAAM,OAAO,eAAe,aAAa,IAAI;EAC7C,MAAM,KAAK,eAAe,aAAa,EAAE;EACzC,IAAI,CAAC,QAAQ,CAAC,IAAI;EAClB,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,MACxB,QACC,WAAW,QAAQ,OAAO,IAAI,MAAM,EAAE,UACtC,CAAC,WAAW,QAAQ,OAAO,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAC7D;EACA,IAAI,SAAS;GACX,WAAW,OAAO,OAAO,aAAa,QAAQ,MAAM,sBAAsB,QAAQ,OAAO;GACzF;EACF;EAEA,MAAM,QAAQ,MAAM,OAAO,GAAG,MAAM,EAAE;EACtC,IAAI,CAAC,SAAS,WAAW,OAAO,SAAS,GAAG,UAAU;EACtD,MAAM,WAAW,MAAM,QACrB,KACA,YAAY,GAAG,OAAO,gBAAgB,MAAM,cAAc,GAAG,OAAO,sDACtE;EACA,IAAI,UAAU,QACZ,WAAW,OAAO,OAChB,aAAa,aAAa,GAAG,wBAAwB,OAAO,eAAe,SAAS,EAAE,CAAC,CAAC,CAAC;CAC/F;CAEA,KAAK,MAAM,CAAC,MAAM,cAAc,OAAO,QAAQ,MAAM,UAAU,GAAG;EAChE,MAAM,QAAQ,iBAAiB,UAAU,KAAK,UAAU,CAAC,CAAC;EAC1D,IAAI,WAAW,OAAO,SAAS,UAAU;EACzC,MAAM,MAAM,UAAU,UAAU,IAAI,6BAA6B,MAAM,OAAO,MAAM,EAAE,IAAI,YAAY,YAAY,MAAM,MAAM,WAAW,UAAU,IAAI;EAEvJ,MAAM,SAAQ,MADK,QAAQ,aAAa,QAAQ,GAAG,EAAA,GAC9B,EAAE,EAAE;EACzB,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAC3C,MAAM,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU;EAC/D,IAAI,UAAU,SAAS,YAAY,CAAC,YAAY,OAAO,MAAM,OAAO,KAAK,CAAC,GACxE,WAAW,SAAS,aAAa,UAAU,sCAAsC,MAAM;EACzF,IAAI,UAAU,SAAS,YAAY,UACjC,WAAW,SAAS,aAAa,UAC/B;CACN;CAEA,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,MAAM,QAAQ,GAAG;EAC5D,IAAI,CAAC,YAAY,OAAO,GAAG;EAI3B,IAHe,iBAAiB,QAAQ,KAAK,UAAU,CAAC,CAAC,MACtD,UAAU,WAAW,OAAO,SAAS,QAE/B,GAAG;EACZ,MAAM,EAAE,KAAK,WAAW,aACtB,OACA;GAAE,OAAO,MAAM;GAAM,UAAU,CAAC,IAAI;EAAE,GACtC,EAAE,aAAa,YAAY,CAC7B;EACA,IAAI;GACF,MAAM,QAAQ,KAAK,MAAM;EAC3B,SAAS,OAAO;GACd,WAAW,OAAO,WAAW,UAAU,UAAU,KAAK;EACxD;CACF;CAEA,OAAO;AACT;AAEA,eAAe,UAAU,EAAE,YAAY,OAAO,SAAS,WAAoB;CACzE,IAAI,CAAC,QAAQ,WAAW,MAAM,IAAI,oBAAoB,uCAAuC;CAC7F,IAAI,CAAC,QAAQ,OAAO,MAAM,IAAI,oBAAoB,mCAAmC;CACrF,IAAI,MAAM,SAAS,WAAW,MAC5B,MAAM,IAAI,oBAAoB,6CAA6C;CAE7E,MAAM,OAAO,MAAM,QAAQ,WAAW,GAAG,WAAW,KAAK,IAAI;CAC7D,MAAM,EAAE,UAAU,cAAc,MAAM,GAAG;CACzC,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,SAAS,MAAM,MAAM;CACxC,QAAQ;EACN,MAAM,IAAI,oBAAoB,qCAAqC,MAAM;CAC3E;CAEA,IAAI;EACF,MAAM,WAAW,mBAAmB,UAAU,KAAK;EACnD,MAAM,EAAE,aAAa;EACrB,MAAM,UAAU,MAAM,YAAY,SAAS,SAAS,MAAM,UAAU,OAAO;EAC3E,MAAM,UAAU,QAAQ,SAAS,WAAW,YAAY;EACxD,IAAI,SAAS,MAAM,UAAU,MAAM,SAAS,MAAM;EAClD,OAAO;GAAE;GAAM;GAAU;GAAS;GAAS;EAAS;CACtD,SAAS,OAAO;EACd,IAAI,iBAAiB,gBAAgB,MAAM,IAAI,oBAAoB,MAAM,OAAO;EAChF,MAAM;CACR;AACF;AAEA,eAAe,WAAW,EAAE,OAAO,SAAS,WAAoB;CAC9D,IAAI,CAAC,QAAQ,YAAY,MAAM,IAAI,oBAAoB,wCAAwC;CAC/F,MAAM,EAAE,QAAQ,SAAS,QAAQ,cAAe,CAAC;CACjD,MAAM,QAAQ,kBAAkB,QAAQ,UAAU;CAClD,MAAM,SAAS,SAAS,eAAe,QAAQ,KAAK,IAAI,CAAC,iCAAiC;CAC1F,IAAI,OAAO,QAAQ,MAAM,IAAI,oBAAoB,oBAAoB,OAAO,KAAK,IAAI,GAAG;CACxF,MAAM,QAAQ;CAEd,MAAM,OAAO,MAAM,QAAQ,YAAY,GAAG,MAAM,GAAG,MAAM;CACzD,MAAM,EAAE,UAAU,cAAc,MAAM,GAAG;CACzC,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,SAAS,MAAM,MAAM;CACxC,QAAQ;EACN,MAAM,IAAI,oBAAoB,qCAAqC,MAAM;CAC3E;CAEA,MAAM,UAAU,MAAM,YAAY,kBAAkB,KAAK,GAAG,MAAM,UAAU,OAAO;CACnF,MAAM,UAAU,SAAS,WAAW,YAAY;CAChD,IAAI,SAAS,MAAM,UAAU,MAAM,SAAS,MAAM;CAElD,OAAO;EAAE;EAAM;EAAU;EAAS;EAAS,UAAU,oBAAoB,OAAO,OAAO,KAAK;CAAE;AAChG;AAEA,MAAM,eAAe,SAAgC,UAAkB;CACrE,IAAI,CAAC,QAAQ,cACX,MAAM,IAAI,oBAAoB,0CAA0C;CAC1E,OAAO,MAAM,QAAQ,cAAc,KAAK;AAC1C;AAEA,eAAe,aAAa,EAAE,YAAY,OAAO,SAAS,SAAS,WAAoB;CACrF,MAAM,EAAE,MAAM,cAAc,QAAQ,YAAY,EAAE,MAAM,GAAG;CAC3D,IAAI,CAAC,KAAK,KAAK,IAAI,GACjB,MAAM,IAAI,oBAAoB,2DAAuD;CACvF,MAAM,MAAM,YAAY,SAAS,WAAW,IAAI;CAChD,MAAM,OAAO,GAAG,IAAI,GAAG,KAAK;CAC5B,MAAM,EAAE,OAAO,UAAU,cAAc,MAAM,GAAG;CAEhD,IAAI,CAAC,WAKC;MAAA,MAJiB,SAAS,MAAM,MAAM,CAAC,CAAC,WACpC,YACA,KACR,GACY,MAAM,IAAI,oBAAoB,0BAA0B,KAAK,EAAE;CAAA;CAI7E,MAAM,WAA8B;EAClC;EACA,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EAClC,OAAO;GACL,OAAO,WAAW;GAClB,YAAY,MAAM,cAAc,CAAC;GACjC,UAAU,MAAM,YAAY,CAAC;GAC7B,SAAS,MAAM,WAAW,CAAC;EAC7B;EACA,MAAM,MAAM,QAAQ,UAAU;CAChC;CACA,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CACpC,MAAM,UAAU,MAAM,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,KAAK,MAAM;CACtE,OAAO,EAAE,KAAK;AAChB;AAEA,eAAe,cAAc,EAAE,YAAY,WAAkD;CAC3F,MAAM,MAAM,YAAY,SAAS,WAAW,IAAI;CAChD,MAAM,EAAE,SAAS,aAAa,MAAM,GAAG;CACvC,MAAM,QAAQ,MAAM,QAAQ,GAAG,CAAC,CAAC,YAAY,CAAC,CAAa;CAS3D,OAAO,MARiB,QAAQ,IAC9B,MACG,QAAQ,SAAS,KAAK,SAAS,OAAO,CAAC,CAAC,CACxC,KAAK,CAAC,CACN,IACC,OAAO,SAAS,KAAK,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,QAAQ,MAAM,CAAC,CACrE,CACJ;AAEF;;;AC9YA,MAAM,oBAAoB;AAC1B,MAAM,wBAAQ,IAAI,IAAkE;AAEpF,SAAS,UAAU,KAAa,KAAa,MAAgC;CAC3E,MAAM,MAAM,KAAK,IAAI;CACrB,MAAM,MAAM,MAAM,IAAI,GAAG;CACzB,IAAI,OAAO,IAAI,YAAY,KAAK,OAAO;EAAE,WAAW,IAAI;EAAW,QAAQ;CAAK;CAEhF,IAAI,MAAM,QAAQ,mBAAmB;EACnC,KAAK,MAAM,CAAC,GAAG,UAAU,OAAO,IAAI,MAAM,aAAa,KAAK,MAAM,OAAO,CAAC;EAC1E,IAAI,MAAM,QAAQ,mBAAmB,MAAM,OAAO,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAe;CACvF;CAEA,MAAM,YAAY,KAAK;CACvB,MAAM,IAAI,KAAK;EAAE,WAAW,MAAM;EAAK;CAAU,CAAC;CAClD,UAAU,YAAY,MAAM,OAAO,GAAG,CAAC;CACvC,OAAO;EAAE;EAAW,QAAQ;CAAM;AACpC;AAEA,eAAsB,SACpB,QACA,OACA,SACA,UAA2B,CAAC,GACF;CAG1B,MAAM,EAAE,OAAO,GAAG,eAAe,OAAO,YAAY,CAAC;CACrD,MAAM,WAAW,QAAQ,WAAW,OAAO,WAAW,KAAA;CACtD,IAAI,CAAC,QAAQ,YAAY,OAAO,OAAO,UAAU,CAAC,CAAC,KAAK,OAAO,GAC7D,MAAM,IAAI,oBAAoB,sCAAsC;CAEtE,MAAM,aAAa,OAAO,OAAO;CACjC,IAAI,CAAC,YAAY,MAAM,IAAI,oBAAoB,yBAAyB,OAAO,MAAM,EAAE;CAEvF,MAAM,QAAQ,UAAU;CACxB,IAAI,OAAO;EACT,MAAM,SAAS,cAAc,KAAK;EAClC,IAAI,OAAO,QAAQ,MAAM,IAAI,oBAAoB,sBAAsB,OAAO,KAAK,IAAI,GAAG;CAC5F;CACA,MAAM,QAAQ,SAAS;CAGvB,IAAI,MAAM,MAAM,OAAO;EAAE,MAAM,CAAC;EAAG,MAAM,UAAU,KAAK;CAAE;CAE1D,MAAM,MAAM,QAAQ,cAAc;CAClC,MAAM,MAAM,OAAO,QAAkB,WAAgC,UAAU;EAC7E,MAAM,EAAE,KAAK,WAAW,aAAa,QAAQ;GAAE,OAAO,MAAM;GAAO,GAAG;EAAS,CAAC;EAChF,MAAM,OAAO,YAAgC;GAC3C,MAAM,YAAY,YAAY,IAAI;GAClC,MAAM,OAAQ,MAAM,QAAQ,KAAK,MAAM;GACvC,OAAO;IAAE,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,SAAS;IAAG,MAAM,KAAK,IAAI,YAAY;GAAE;EAC/F;EAEA,MAAM,EAAE,WAAW,WACjB,MAAM,IACF,UAAU,KAAK,UAAU,CAAC,KAAK,MAAM,CAAC,GAAG,KAAK,IAAI,IAClD;GAAE,WAAW,KAAK;GAAG,QAAQ;EAAM;EACzC,OAAO;GAAE;GAAK;GAAQ;GAAQ,GAAI,MAAM;EAAW;CACrD;CAGA,MAAM,gBAAgB,UAAmB;EACvC,IAAI,iBAAiB,qBAAqB,OAAO;EACjD,OAAO,WAAW,IAAI,oBAAoB,UAAU,KAAK,CAAC,IAAI;CAChE;CAEA,IAAI,MAAM,SAAS;EACjB,MAAM,EAAE,OAAO,GAAG,OAAO,IAAI,GAAG,WAAW,YAAY,CAAC;EACxD,IAAI,OAAO,OAAO,MAAM,CAAC,CAAC,KAAK,OAAO,GACpC,MAAM,IAAI,oBAAoB,kDAAkD;EAUlF,OAAO;GAAE,MAAM,CAAC;GAAG,SAAA,MATG,SACpB,OACA,MAAM,SACN,OAAO,aAAa;IAClB,MAAM,EAAE,MAAM,KAAK,QAAQ,YAAY,WAAW,MAAM,IAAI,OAAO,QAAQ;IAC3E,OAAO;KAAE;KAAM,OAAO;MAAE;MAAK;MAAQ;MAAY;MAAQ,OAAO,CAAC,CAAC;KAAM;IAAE;GAC5E,GACA;IAAE,OAAO,CAAC,CAAC,UAAU;IAAO;GAAa,CAC3C;EAC2B;CAC7B;CAEA,IAAI,UAAU;EACZ,IAAI,SAAS,YAAY,MAAM,MAAM;EACrC,IAAI;GACF,MAAM,UAAU,MAAM,sBAAsB;IAC1C;IACA;IACA;IACA,SAAS;IACT,UAAU,KAAK,WAAW,QAAQ,KAAK,MAAM;IAC7C,WAAW;IACX,SAAS,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW,CAAC;IACpE,SAAS,OAAO,YAAY,MAAM,IAAI,MAAM,EAAA,CAAG;GACjD,CAAC;GACD,IAAI,SAAS,OAAO;EACtB,SAAS,OAAO;GAEd,IAAI,iBAAiB,qBAAqB,MAAM;GAChD,MAAM,IAAI,oBAAoB,UAAU,KAAK,CAAC;EAChD;CACF;CAEA,MAAM,EAAE,MAAM,KAAK,QAAQ,YAAY,WAAW,MAAM,IAAI,KAAK,CAAC,CAAC,OAAO,UAAU;EAClF,MAAM,aAAa,KAAK,KAAK;CAC/B,CAAC;CACD,IAAI,CAAC,UAAU,OAAO,OAAO,EAAE,KAAK;CACpC,OAAO;EAAE;EAAM,UAAU,EAAE,OAAO;GAAE;GAAK;GAAQ;GAAY;GAAQ,OAAO,CAAC,CAAC;EAAM,EAAE;CAAE;AAC1F"}
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as DashboardRow, D as DashboardQuery, E as DashboardFilters, F as ModelValidation, G as MeasureMeta, H as FieldMeta, I as ProfileStep, K as ModelDef, L as SaveModelResult, M as DevtoolsCapabilities, N as FilterValue, O as DashboardQueryDebug, P as ModelColumns, Q as ValueFormat, R as SchemaTable, T as DashboardDevtoolsRequest, U as FormulaMeasure, V as DimensionDef, W as MeasureDef, Y as RelationshipDef, Z as TableDef, _ as KpiChartDef, a as CardsStyle, c as ChartType, d as ChartsLayout, f as ComboChartDef, g as FilterDef, h as FieldSpec, i as CardStyle, j as DashboardSnapshot, k as DashboardResult, l as ChartTypeSpec, m as DonutChartDef, n as BuiltinChartDef, o as ChartDef, p as CustomChartDef, q as ModelMeta, r as CanvasDef, s as ChartNumberFormat, t as BarChartDef, u as ChartTypes, v as TableChartDef } from "./layout-B7eYgNyR.js";
2
- export { type BarChartDef, type BuiltinChartDef, type CanvasDef, type CardStyle, type CardsStyle, type ChartDef, type ChartNumberFormat, type ChartType, type ChartTypeSpec, type ChartTypes, type ChartsLayout, type ComboChartDef, type CustomChartDef, DashboardDevtoolsRequest, DashboardFilters, DashboardQuery, DashboardQueryDebug, DashboardResult, DashboardRow, DashboardSnapshot, DevtoolsCapabilities, type DimensionDef, type DonutChartDef, type FieldMeta, type FieldSpec, type FilterDef, FilterValue, type FormulaMeasure, type KpiChartDef, type MeasureDef, type MeasureMeta, ModelColumns, type ModelDef, type ModelMeta, ModelValidation, ProfileStep, type RelationshipDef, SaveModelResult, SchemaTable, type TableChartDef, type TableDef, type ValueFormat };
1
+ import { $ as TableDef, A as DashboardQueryDebug, B as SchemaTable, D as DashboardDevtoolsRequest, E as DashboardBatchResult, F as FilterValue, G as FormulaMeasure, I as ModelColumns, J as ModelDef, K as MeasureDef, L as ModelValidation, M as DashboardRow, N as DashboardSnapshot, O as DashboardFilters, P as DevtoolsCapabilities, R as ProfileStep, T as DashboardBatchQuery, U as DimensionDef, W as FieldMeta, Y as ModelMeta, Z as RelationshipDef, _ as KpiChartDef, a as CardsStyle, c as ChartType, d as ChartsLayout, et as ValueFormat, f as ComboChartDef, g as FilterDef, h as FieldSpec, i as CardStyle, j as DashboardResult, k as DashboardQuery, l as ChartTypeSpec, m as DonutChartDef, n as BuiltinChartDef, o as ChartDef, p as CustomChartDef, q as MeasureMeta, r as CanvasDef, s as ChartNumberFormat, t as BarChartDef, u as ChartTypes, v as TableChartDef, z as SaveModelResult } from "./layout-Bn1OiRov.js";
2
+ export { type BarChartDef, type BuiltinChartDef, type CanvasDef, type CardStyle, type CardsStyle, type ChartDef, type ChartNumberFormat, type ChartType, type ChartTypeSpec, type ChartTypes, type ChartsLayout, type ComboChartDef, type CustomChartDef, DashboardBatchQuery, DashboardBatchResult, DashboardDevtoolsRequest, DashboardFilters, DashboardQuery, DashboardQueryDebug, DashboardResult, DashboardRow, DashboardSnapshot, DevtoolsCapabilities, type DimensionDef, type DonutChartDef, type FieldMeta, type FieldSpec, type FilterDef, FilterValue, type FormulaMeasure, type KpiChartDef, type MeasureDef, type MeasureMeta, ModelColumns, type ModelDef, type ModelMeta, ModelValidation, ProfileStep, type RelationshipDef, SaveModelResult, SchemaTable, type TableChartDef, type TableDef, type ValueFormat };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluemarble/bi",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Camada semântica (defineModel) + componentes de dashboard e devtools para substituir o Power BI",
5
5
  "license": "MIT",
6
6
  "author": "bluemarble",
@@ -1 +0,0 @@
1
- {"version":3,"file":"ModelDictionary-BlDeDyAE.js","names":["loadDraft"],"sources":["../src/devtools/layoutStore.ts","../src/devtools/store.ts","../src/client/DashboardProvider.tsx","../src/client/format.ts","../src/client/useModelQuery.ts","../src/client/ModelDictionary.tsx"],"sourcesContent":["import { canonicalJson } from '../codegen'\nimport type { ChartsLayout } from '../layout'\n\n/**\n * Layouts montados na página (`<DashboardCharts>` / `<DashboardFilters>`) e o rascunho de cada um,\n * editado na aba Layout do devtools. Store externo como o de consultas: editar um gráfico\n * re-renderiza só os componentes daquele layout e o painel.\n */\n\n/** Parte do layout que um componente renderiza. */\nexport type LayoutPart = 'charts' | 'filters' | 'canvas'\n\nexport type LayoutEntry = {\n /** O layout que veio do arquivo (o que a página passou ao `<DashboardCharts>`). */\n registered: ChartsLayout\n /** Rascunho editado no devtools; `null` = página usando o registrado. */\n draft: ChartsLayout | null\n /**\n * Partes montadas na página e os formatters que cada uma recebeu (`formatDimension`,\n * `formatLabel`). Parte ausente = a página não renderiza (ex.: sem `<DashboardFilters>`).\n */\n parts: Partial<Record<LayoutPart, string[]>>\n}\n\n/**\n * Marca o elemento de cada item do layout com `<layoutId>/chart:<id>` ou\n * `<layoutId>/filter:<dimensão>` (destaque e inspetor do devtools).\n */\nexport const LAYOUT_ITEM_ATTRIBUTE = 'data-bm-dashboard-layout-item'\n\nexport const layoutItemKey = (layoutId: string, item: LayoutItem) =>\n `${layoutId}/${item.kind}:${item.key}`\n\nexport type LayoutItem = { kind: 'chart' | 'filter'; key: string }\n\nexport function parseLayoutItemKey(value: string): { layoutId: string; item: LayoutItem } | null {\n const match = /^([^/]+)\\/(chart|filter):(.+)$/.exec(value)\n return match\n ? {\n layoutId: match[1] as string,\n item: { kind: match[2] as LayoutItem['kind'], key: match[3] as string }\n }\n : null\n}\n\nconst draftKey = (id: string) => `bm-dashboard-devtools:layout:${id}`\n\nconst sameLayout = (a: ChartsLayout | null, b: ChartsLayout | null) =>\n canonicalJson(a) === canonicalJson(b)\n\nfunction loadDraft(id: string): ChartsLayout | null {\n try {\n const saved = sessionStorage.getItem(draftKey(id))\n return saved ? (JSON.parse(saved) as ChartsLayout) : null\n } catch {\n return null\n }\n}\n\nfunction storeDraft(id: string, draft: ChartsLayout | null) {\n try {\n if (draft) sessionStorage.setItem(draftKey(id), JSON.stringify(draft))\n else sessionStorage.removeItem(draftKey(id))\n } catch {}\n}\n\nexport function createLayoutStore() {\n const entries = new Map<string, LayoutEntry>()\n // Montagens por layout e parte (`<id>\\u0000<parte>`)\n const mounts = new Map<string, number>()\n let snapshot: LayoutEntry[] = []\n const listeners = new Set<() => void>()\n // Edições da aba Layout ainda não aplicadas: sobrevivem a trocar de aba (o painel desmonta)\n const pending = new Map<string, ChartsLayout>()\n\n const emit = () => {\n snapshot = [...entries.values()]\n for (const listener of listeners) listener()\n }\n\n const setDraft = (id: string, next: ChartsLayout | null) => {\n const entry = entries.get(id)\n if (!entry) return\n // Rascunho igual ao arquivo não é rascunho (ex.: desfez tudo à mão)\n const draft = next && !sameLayout(next, entry.registered) ? next : null\n storeDraft(id, draft)\n entries.set(id, { ...entry, draft })\n emit()\n }\n\n return {\n subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n get: (id: string) => entries.get(id) ?? null,\n /** Rascunho do layout, mesmo antes de registrar (evita renderizar o registrado e trocar). */\n draftOf(layout: ChartsLayout) {\n const entry = entries.get(layout.id)\n if (entry) return entry.draft\n const draft = loadDraft(layout.id)\n return draft && !sameLayout(draft, layout) ? draft : null\n },\n\n /** Chamado pelo `<DashboardCharts>`/`<DashboardFilters>` ao montar e quando o arquivo muda (HMR). */\n register(layout: ChartsLayout, part: LayoutPart, formatters: string[]) {\n const current = entries.get(layout.id)\n let draft = current ? current.draft : loadDraft(layout.id)\n // Depois de \"Salvar no arquivo\" o Next recarrega o JSON: o rascunho virou o registrado\n if (draft && sameLayout(draft, layout)) {\n draft = null\n storeDraft(layout.id, null)\n }\n const parts = { ...current?.parts, [part]: formatters }\n entries.set(layout.id, { registered: layout, draft, parts })\n const mountKey = `${layout.id}\\u0000${part}`\n mounts.set(mountKey, (mounts.get(mountKey) ?? 0) + 1)\n emit()\n return () => {\n const count = (mounts.get(mountKey) ?? 1) - 1\n mounts.set(mountKey, count)\n const entry = entries.get(layout.id)\n if (count > 0 || !entry) return\n const { [part]: _, ...rest } = entry.parts\n // O rascunho continua no sessionStorage e volta na próxima montagem\n if (Object.keys(rest).length) entries.set(layout.id, { ...entry, parts: rest })\n else entries.delete(layout.id)\n emit()\n }\n },\n setDraft,\n /** Edições ainda não aplicadas do layout (só em memória). */\n pendingOf: (id: string) => pending.get(id) ?? null,\n setPending(id: string, layout: ChartsLayout | null) {\n if (layout) pending.set(id, layout)\n else pending.delete(id)\n }\n }\n}\n\nexport type LayoutStore = ReturnType<typeof createLayoutStore>\n","import type { DashboardQuery, DashboardQueryDebug, DashboardRow } from '../types'\n\n/**\n * Registro das consultas feitas pelos componentes (`useModelQuery`), lido pelo painel do devtools.\n * É um store externo (`useSyncExternalStore`) para que registrar uma consulta não re-renderize a\n * página inteira — só o painel.\n */\n\nexport type RunStatus = 'loading' | 'success' | 'error' | 'cancelled'\n\nexport type QueryRun = {\n runId: number\n instanceId: string\n label: string\n query: DashboardQuery\n status: RunStatus\n startedAt: number\n /** Ida e volta medida no browser. */\n durationMs?: number\n rows?: DashboardRow[]\n error?: string\n debug?: DashboardQueryDebug\n}\n\nexport type TrackedInstance = {\n instanceId: string\n label: string\n runs: number\n last: QueryRun\n /** Refeita muitas vezes em pouco tempo: sinal de re-render em loop (ex.: array novo a cada render). */\n storm: boolean\n}\n\nexport type DevtoolsSnapshot = {\n instances: TrackedInstance[]\n /** Execuções mais recentes primeiro. */\n history: QueryRun[]\n}\n\nconst MAX_HISTORY = 100\n/** `STORM_RUNS` execuções da mesma instância dentro de `STORM_WINDOW_MS` = consulta em loop. */\nconst STORM_RUNS = 6\nconst STORM_WINDOW_MS = 5000\n\nexport function errorMessage(error: unknown): string {\n const err = error as {\n response?: { data?: { error?: string; message?: string } }\n message?: string\n }\n return err?.response?.data?.error ?? err?.response?.data?.message ?? err?.message ?? String(error)\n}\n\nexport function createDevtoolsStore() {\n const instances = new Map<string, TrackedInstance>()\n let history: QueryRun[] = []\n // Execuções em andamento (o histórico é limitado e pode já ter descartado uma consulta lenta)\n const pending = new Map<number, QueryRun>()\n const recentStarts = new Map<string, number[]>()\n // Re-execuções pedidas pelo próprio devtools (refresh, aplicar rascunho) não contam como loop\n let expectedUntil = 0\n let snapshot: DevtoolsSnapshot = { instances: [], history: [] }\n let nextRunId = 1\n const listeners = new Set<() => void>()\n\n const emit = () => {\n snapshot = { instances: [...instances.values()], history }\n for (const listener of listeners) listener()\n }\n\n const update = (runId: number, patch: Partial<QueryRun>) => {\n const current = pending.get(runId)\n if (!current) return\n pending.delete(runId)\n const run = { ...current, ...patch, durationMs: Date.now() - current.startedAt }\n history = history.map((item) => (item.runId === runId ? run : item))\n const instance = instances.get(run.instanceId)\n if (instance?.last.runId === runId) instances.set(run.instanceId, { ...instance, last: run })\n emit()\n }\n\n return {\n subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n\n start(instanceId: string, label: string, query: DashboardQuery) {\n const run: QueryRun = {\n runId: nextRunId++,\n instanceId,\n label,\n query,\n status: 'loading',\n startedAt: Date.now()\n }\n pending.set(run.runId, run)\n history = [run, ...history].slice(0, MAX_HISTORY)\n const previous = instances.get(instanceId)\n const expected = run.startedAt <= expectedUntil\n const starts = [\n ...(recentStarts.get(instanceId) ?? []),\n ...(expected ? [] : [run.startedAt])\n ].filter((time) => run.startedAt - time <= STORM_WINDOW_MS)\n recentStarts.set(instanceId, starts)\n const storm = previous?.storm || starts.length >= STORM_RUNS\n if (storm && !previous?.storm)\n console.warn(\n `[@bluemarble/bi] \"${label}\" executou ${starts.length}x em ${STORM_WINDOW_MS / 1000}s. ` +\n 'Confira se o componente não recria dimensions/measures/filtros a cada render.'\n )\n instances.set(instanceId, {\n instanceId,\n label,\n runs: (previous?.runs ?? 0) + 1,\n last: run,\n storm\n })\n emit()\n return run.runId\n },\n succeed: (runId: number, rows: DashboardRow[], debug?: DashboardQueryDebug) =>\n update(runId, { status: 'success', rows, debug }),\n fail: (runId: number, error: unknown) =>\n update(runId, { status: 'error', error: errorMessage(error) }),\n /** Consulta descartada antes de responder (filtro mudou, componente desmontou). */\n cancel: (runId: number) => update(runId, { status: 'cancelled' }),\n\n remove(instanceId: string) {\n recentStarts.delete(instanceId)\n if (instances.delete(instanceId)) emit()\n },\n /** Avisa que todas as consultas vão refazer agora, de propósito. */\n expectRefetch() {\n expectedUntil = Date.now() + 500\n },\n /** Zera os alertas de loop (depois de corrigir o componente, sem recarregar a página). */\n resetStorms() {\n recentStarts.clear()\n for (const [id, instance] of instances) instances.set(id, { ...instance, storm: false })\n emit()\n },\n clearHistory() {\n history = []\n emit()\n }\n }\n}\n\nexport type DevtoolsStore = ReturnType<typeof createDevtoolsStore>\n","import {\n type ComponentType,\n createContext,\n lazy,\n type ReactNode,\n Suspense,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState\n} from 'react'\nimport { createLayoutStore, type LayoutStore } from '../devtools/layoutStore'\nimport { createDevtoolsStore, type DevtoolsStore } from '../devtools/store'\nimport { type ChartTypeSpec, type ChartTypes, resolveChartTypes } from '../layout'\nimport type { ModelDef, ModelMeta } from '../model'\nimport type { DashboardFilters, DashboardQuery, DashboardResult, FilterValue } from '../types'\n\nexport type DashboardFetcher = (query: DashboardQuery) => Promise<DashboardResult>\n\n/**\n * Tipo de gráfico do app: os campos do JSON (`ChartTypeSpec`) e o componente que os renderiza. O\n * componente recebe os campos como props (com os `default` aplicados e formatters já resolvidos) e\n * `queryLabel` — ver `ChartComponentProps`.\n */\n// biome-ignore lint/suspicious/noExplicitAny: cada tipo tem as próprias props, vindas do JSON\nexport type ChartComponentSpec = ChartTypeSpec & { component: ComponentType<any> }\n\n/** Props de um componente registrado em `charts`: os campos do tipo + `queryLabel`. */\nexport type ChartComponentProps<Fields extends object = Record<string, unknown>> = Fields & {\n /** Nome da consulta no devtools (`<título> · <tipo>`): passe ao `useModelQuery({ label })`. */\n queryLabel: string\n}\n\n/** Só existe com o devtools ligado. */\nexport type DashboardDevtoolsContext = {\n store: DevtoolsStore\n /** Layouts da página (`<DashboardCharts>`/`<DashboardFilters>`) e os rascunhos da aba Layout. */\n layouts: LayoutStore\n /** Consulta o modelo registrado, ignorando o rascunho (comparações, snapshots). */\n rawFetcher: DashboardFetcher\n /** Rascunho do modelo aplicado: todas as consultas da página passam a usá-lo. */\n draft: ModelDef | null\n setDraft: (draft: ModelDef | null) => void\n /** Refaz todas as consultas da página. */\n refresh: () => void\n defaultFilters: DashboardFilters\n setFilters: (filters: DashboardFilters) => void\n}\n\ntype DashboardContextValue = {\n model: string\n filters: DashboardFilters\n setFilter: (dimension: string, values: FilterValue[]) => void\n fetcher: DashboardFetcher\n /** Dicionário do modelo (rótulos, descrições, formatos); `null` até carregar. */\n meta: ModelMeta | null\n /** Muda quando o devtools pede para refazer as consultas. */\n refreshKey: number\n /** Tipos de gráfico do layout: os da lib e os do app (`charts`). */\n chartTypes: ChartTypes\n /** Componentes dos tipos do app, por nome. */\n chartComponents: Record<string, ChartComponentSpec['component']>\n devtools: DashboardDevtoolsContext | null\n}\n\nconst DashboardContext = createContext<DashboardContextValue | null>(null)\n\n// Lazy: o painel não entra no bundle da página, só é baixado quando o devtools está ligado\nconst DashboardDevtools = lazy(() => import('../devtools/DashboardDevtools'))\n\nconst DEVTOOLS_DEFAULT = process.env.NODE_ENV === 'development'\n\nconst draftStorageKey = (model: string) => `bm-dashboard-devtools:draft:${model}`\n\nfunction loadDraft(model: string): ModelDef | null {\n try {\n const saved = sessionStorage.getItem(draftStorageKey(model))\n return saved ? (JSON.parse(saved) as ModelDef) : null\n } catch {\n return null\n }\n}\n\ntype DashboardProviderProps = {\n /** Nome do modelo no servidor (chave do registro de modelos do app). */\n model: string\n /** Como chamar o endpoint do app (ex.: `(q) => api.post('/dashboard/query', q).then((r) => r.data)`). */\n fetcher: DashboardFetcher\n defaultFilters?: DashboardFilters\n /**\n * Painel de desenvolvimento (debug das consultas, edição do modelo). Padrão: ligado só com\n * `NODE_ENV=development`. O endpoint também precisa de `runQuery(..., { devtools: true })`.\n */\n devtools?: boolean\n /**\n * Tipos de gráfico do app, usáveis no layout JSON (`\"type\": \"<nome>\"`) e no editor do devtools.\n * Declare fora do componente (referência estável). O servidor precisa dos mesmos campos em\n * `devtools.chartTypes` para salvar o layout.\n */\n charts?: Record<string, ChartComponentSpec>\n children: ReactNode\n}\n\n/**\n * Guarda os filtros do dashboard. Todo componente dentro dele (filtros, gráficos, tabelas) consulta o\n * mesmo modelo com os mesmos filtros — é o que faz um slicer filtrar a página inteira, como no BI.\n */\nexport function DashboardProvider({\n model,\n fetcher,\n defaultFilters = {},\n devtools = DEVTOOLS_DEFAULT,\n charts,\n children\n}: DashboardProviderProps) {\n const chartTypes = useMemo(() => resolveChartTypes(charts), [charts])\n const chartComponents = useMemo(\n () =>\n Object.fromEntries(\n Object.entries(charts ?? {}).map(([name, spec]) => [name, spec.component])\n ),\n [charts]\n )\n const [filters, setFilters] = useState<DashboardFilters>(defaultFilters)\n const defaultsRef = useRef(defaultFilters)\n\n const setFilter = useCallback(\n (dimension: string, values: FilterValue[]) =>\n setFilters((current) => ({ ...current, [dimension]: values })),\n []\n )\n\n // --- devtools ---\n const store = useMemo(() => (devtools ? createDevtoolsStore() : null), [devtools])\n const layouts = useMemo(() => (devtools ? createLayoutStore() : null), [devtools])\n // O rascunho aplicado sobrevive ao reload da aba (útil enquanto se edita o modelo)\n const [draft, setDraftState] = useState<ModelDef | null>(() =>\n devtools && typeof window !== 'undefined' ? loadDraft(model) : null\n )\n const [refreshKey, setRefreshKey] = useState(0)\n const [mounted, setMounted] = useState(false)\n useEffect(() => setMounted(true), [])\n\n const setDraft = useCallback(\n (next: ModelDef | null) => {\n store?.expectRefetch()\n setDraftState(next)\n try {\n if (next) sessionStorage.setItem(draftStorageKey(model), JSON.stringify(next))\n else sessionStorage.removeItem(draftStorageKey(model))\n } catch {}\n },\n [model, store]\n )\n\n const refresh = useCallback(() => {\n store?.expectRefetch()\n setRefreshKey((key) => key + 1)\n }, [store])\n\n // Com o devtools ligado, toda consulta pede debug e, se houver, roda contra o rascunho\n const effectiveFetcher = useMemo<DashboardFetcher>(() => {\n if (!devtools) return fetcher\n return (query) =>\n fetcher({\n ...query,\n devtools: { debug: true, ...(draft && { model: draft }), ...query.devtools }\n })\n }, [fetcher, devtools, draft])\n\n const rawFetcher = useMemo<DashboardFetcher>(\n () => (query) => fetcher({ ...query, devtools: { debug: true, ...query.devtools } }),\n [fetcher]\n )\n\n // Dicionário: segue o rascunho, para os rótulos editados aparecerem na hora\n const [meta, setMeta] = useState<ModelMeta | null>(null)\n useEffect(() => {\n let cancelado = false\n effectiveFetcher({ model, meta: true })\n .then((result) => !cancelado && setMeta(result.meta ?? null))\n .catch(() => !cancelado && setMeta(null))\n return () => {\n cancelado = true\n }\n }, [effectiveFetcher, model])\n\n const devtoolsValue = useMemo(\n () =>\n store &&\n layouts && {\n store,\n layouts,\n rawFetcher,\n draft,\n setDraft,\n refresh,\n defaultFilters: defaultsRef.current,\n setFilters\n },\n [store, layouts, rawFetcher, draft, setDraft, refresh]\n )\n\n const value = useMemo(\n () => ({\n model,\n filters,\n setFilter,\n fetcher: effectiveFetcher,\n meta,\n refreshKey,\n chartTypes,\n chartComponents,\n devtools: devtoolsValue\n }),\n [\n model,\n filters,\n setFilter,\n effectiveFetcher,\n meta,\n refreshKey,\n chartTypes,\n chartComponents,\n devtoolsValue\n ]\n )\n\n return (\n <DashboardContext.Provider value={value}>\n {children}\n {/* Só depois de montar: o painel lê localStorage e não deve participar da hidratação */}\n {devtoolsValue && mounted && (\n <Suspense fallback={null}>\n <DashboardDevtools />\n </Suspense>\n )}\n </DashboardContext.Provider>\n )\n}\n\nexport function useDashboard() {\n const context = useContext(DashboardContext)\n if (!context) throw new Error('useDashboard precisa estar dentro de <DashboardProvider>')\n return context\n}\n","import type { ChartNumberFormat } from '../layout'\nimport type { ModelMeta } from '../model'\n\nconst formatters = new Map<string, Intl.NumberFormat>()\n\n/** `compact`: notação curta do pt-BR (1,2 mil / 3,4 mi / 5,6 bi). */\nexport function formatNumber(\n value: number | string | null | undefined,\n decimals = 2,\n compact = false\n) {\n if (value === null || value === undefined || value === '') return ''\n const number = Number(value)\n if (Number.isNaN(number)) return String(value)\n const key = `${decimals}:${compact}`\n let formatter = formatters.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat('pt-BR', {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n ...(compact && { notation: 'compact' })\n })\n formatters.set(key, formatter)\n }\n return formatter.format(number)\n}\n\nexport const formatCurrency = (\n value: number | string | null | undefined,\n decimals = 2,\n compact = false\n) => {\n const text = formatNumber(value, decimals, compact)\n return text && `R$ ${text}`\n}\n\n/**\n * Formata pelo dicionário do modelo (`format`/`decimals` da medida) ou pelo formato do gráfico.\n * `fallbackDecimals` é o padrão do componente quando nenhum dos dois define `decimals`.\n */\nexport function formatMeasure(\n value: number | string | null | undefined,\n meta: ChartNumberFormat | undefined,\n fallbackDecimals = 2\n) {\n if (value === null || value === undefined || value === '') return ''\n const decimals = meta?.decimals ?? (meta?.format === 'integer' ? 0 : fallbackDecimals)\n const compact = meta?.compact ?? false\n let text: string\n switch (meta?.format) {\n case 'currency':\n text = formatCurrency(value, decimals, compact)\n break\n case 'percent': {\n const number = formatNumber(Number(value) * 100, decimals, compact)\n text = number && `${number}%`\n break\n }\n default:\n text = formatNumber(value, decimals, compact)\n }\n return text && `${meta?.prefix ?? ''}${text}${meta?.suffix ?? ''}`\n}\n\n/** Dicionário do modelo com o formato do gráfico por cima. */\nexport function measureFormat(\n meta: ModelMeta | null,\n measure: string,\n formats?: Record<string, ChartNumberFormat>\n): ChartNumberFormat | undefined {\n const fromModel = meta?.measures[measure]\n const fromChart = formats?.[measure]\n if (!fromChart) return fromModel\n return {\n ...(fromModel?.format !== undefined && { format: fromModel.format }),\n ...(fromModel?.decimals !== undefined && { decimals: fromModel.decimals }),\n ...fromChart\n }\n}\n\n/**\n * Formatter de uma medida: o `formats` do gráfico (se houver) por cima do dicionário do modelo; sem\n * `format`, número com `fallbackDecimals`.\n */\nexport function measureFormatter(\n meta: ModelMeta | null,\n measure: string,\n fallbackDecimals = 2,\n formats?: Record<string, ChartNumberFormat>\n): (value: number | string | null | undefined) => string {\n const format = measureFormat(meta, measure, formats)\n return (value) => formatMeasure(value, format, fallbackDecimals)\n}\n\n/** Rótulo de uma dimensão/medida: o `labels` do gráfico, o dicionário do modelo ou o nome. */\nexport function fieldLabel(\n meta: ModelMeta | null,\n name: string,\n labels?: Record<string, string>\n): string {\n return labels?.[name] || meta?.dimensions[name]?.label || meta?.measures[name]?.label || name\n}\n","import { useEffect, useId, useMemo, useState } from 'react'\nimport type { DashboardFilters, DashboardRow } from '../types'\nimport { useDashboard } from './DashboardProvider'\n\nexport const INSPECT_ATTRIBUTE = 'data-bm-dashboard-query'\n\ntype UseModelQueryParams = {\n dimensions?: string[]\n measures?: string[]\n /**\n * Filtros do dashboard que esta consulta NÃO deve respeitar. Um filtro ignora a si mesmo para\n * listar as opções (senão, ao escolher um navio, a lista mostraria só ele).\n */\n ignoreFilters?: string[]\n /** Nome da consulta no devtools (padrão: dimensões × medidas). */\n label?: string\n /** Não consulta (devolve `rows: []`); ex.: a linha de total de uma tabela que não mostra total. */\n skip?: boolean\n}\n\nexport function useModelQuery({\n dimensions,\n measures,\n ignoreFilters,\n label,\n skip = false\n}: UseModelQueryParams) {\n const { model, filters, fetcher, refreshKey, devtools } = useDashboard()\n const instanceId = useId()\n\n const applied: DashboardFilters = {}\n for (const [dimension, values] of Object.entries(filters)) {\n if (values.length && !ignoreFilters?.includes(dimension)) applied[dimension] = values\n }\n\n // Chave por valor: só refaz a consulta quando o que vai pro servidor muda (não a cada render,\n // nem quando muda um filtro que esta consulta ignora)\n const key = JSON.stringify([model, dimensions, measures, applied])\n const query = useMemo(() => ({ model, dimensions, measures, filters: applied }), [key])\n\n const [rows, setRows] = useState<DashboardRow[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<unknown>()\n\n const store = devtools?.store\n const debugLabel =\n label ?? [dimensions?.join(', '), measures?.join(', ')].filter(Boolean).join(' × ')\n\n useEffect(() => {\n if (skip) {\n setRows([])\n setIsLoading(false)\n return\n }\n let cancelado = false\n setIsLoading(true)\n const runId = store?.start(instanceId, debugLabel, query)\n fetcher(query)\n .then((result) => {\n if (cancelado) return\n setRows(result.rows)\n setError(undefined)\n if (runId) store?.succeed(runId, result.rows, result.devtools?.debug)\n })\n .catch((err) => {\n if (cancelado) return\n setError(err)\n if (runId) store?.fail(runId, err)\n })\n .finally(() => !cancelado && setIsLoading(false))\n return () => {\n cancelado = true\n if (runId) store?.cancel(runId)\n }\n }, [fetcher, query, refreshKey, skip])\n\n useEffect(() => (store ? () => store.remove(instanceId) : undefined), [store, instanceId, skip])\n\n // Espalhar no elemento raiz do componente: é como o inspetor do devtools acha o componente na tela\n const inspectProps = store && !skip ? { [INSPECT_ATTRIBUTE]: instanceId } : {}\n\n return { rows, isLoading, error, inspectProps }\n}\n","import {\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Typography\n} from '@mui/material'\nimport { AGGREGATION_LABEL, isAggregate, type ModelMeta, type ValueFormat } from '../model'\nimport { useDashboard } from './DashboardProvider'\n\nconst FORMAT_LABEL: Record<ValueFormat, string> = {\n number: 'Número',\n integer: 'Inteiro',\n currency: 'Moeda (R$)',\n percent: 'Percentual'\n}\n\n/** Como a medida é calculada, em termos de negócio (o SQL não sai do servidor). */\nfunction formula(meta: ModelMeta, name: string) {\n const measure = meta.measures[name]\n const label = (ref: string) => meta.measures[ref]?.label ?? ref\n if (measure.type === 'formula' && measure.refs?.length)\n return `Combina ${measure.refs.map(label).join(', ')}`\n if (isAggregate(measure)) return AGGREGATION_LABEL[measure.type]\n if (measure.type !== 'ratio' || !measure.numerator || !measure.denominator) return '—'\n return `${label(measure.numerator)} ÷ ${label(measure.denominator)}`\n}\n\n/**\n * Dicionário do modelo do `DashboardProvider`: o que cada filtro/eixo e cada número significa. É\n * montado a partir de `label`/`description` das dimensões e medidas — documentação que não\n * desatualiza, porque é a mesma definição que calcula os números.\n */\nexport function ModelDictionary() {\n const { meta } = useDashboard()\n if (!meta) return null\n\n const header = (labels: string[]) => (\n <TableHead>\n <TableRow>\n {labels.map((label) => (\n <TableCell key={label} sx={{ fontWeight: 700 }}>\n {label}\n </TableCell>\n ))}\n </TableRow>\n </TableHead>\n )\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Box>\n <Typography sx={{ fontWeight: 700, mb: 1 }}>Indicadores</Typography>\n <TableContainer>\n <Table size=\"small\">\n {header(['Indicador', 'Descrição', 'Cálculo', 'Formato'])}\n <TableBody>\n {Object.entries(meta.measures).map(([name, measure]) => (\n <TableRow key={name}>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{measure.label ?? name}</TableCell>\n <TableCell>{measure.description ?? '—'}</TableCell>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{formula(meta, name)}</TableCell>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>\n {measure.format ? FORMAT_LABEL[measure.format] : '—'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n <Box>\n <Typography sx={{ fontWeight: 700, mb: 1 }}>Filtros e agrupamentos</Typography>\n <TableContainer>\n <Table size=\"small\">\n {header(['Campo', 'Descrição'])}\n <TableBody>\n {Object.entries(meta.dimensions).map(([name, dimension]) => (\n <TableRow key={name}>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{dimension.label ?? name}</TableCell>\n <TableCell>{dimension.description ?? '—'}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n </Box>\n )\n}\n"],"mappings":"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"}