@databrainhq/mcp-server 0.3.0-rc.12 → 0.3.0-rc.14

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.
Files changed (46) hide show
  1. package/.mcp.json +6 -1
  2. package/dist/apps/server.d.ts +102 -389
  3. package/dist/apps/server.js +5 -5
  4. package/dist/apps/stdio.d.ts +1 -2
  5. package/dist/apps/stdio.js +6 -6
  6. package/dist/{bundle-TDQXWIPM.js → bundle-QJNCUMNY.js} +2 -2
  7. package/dist/{chunk-Q5EYDKSA.js → chunk-2F7V6RZR.js} +511 -107
  8. package/dist/chunk-2F7V6RZR.js.map +1 -0
  9. package/dist/{chunk-347U6GXR.js → chunk-AWICHHDU.js} +153 -61
  10. package/dist/chunk-AWICHHDU.js.map +1 -0
  11. package/dist/{chunk-Z3ZHN4UW.js → chunk-CEGHG5U3.js} +59 -18
  12. package/dist/chunk-CEGHG5U3.js.map +1 -0
  13. package/dist/{chunk-ZTHGF27R.js → chunk-NIC5MAGC.js} +317 -147
  14. package/dist/chunk-NIC5MAGC.js.map +1 -0
  15. package/dist/{chunk-V3UW3B6U.js → chunk-S65KA3Z7.js} +26 -11
  16. package/dist/chunk-S65KA3Z7.js.map +1 -0
  17. package/dist/{chunk-OLOC2MTA.js → chunk-UYFFFAYV.js} +11722 -10560
  18. package/dist/chunk-UYFFFAYV.js.map +1 -0
  19. package/dist/{chunk-LACJHQYH.js → chunk-VQ5G5AZV.js} +10 -7
  20. package/dist/chunk-VQ5G5AZV.js.map +1 -0
  21. package/dist/cli.d.ts +16 -15
  22. package/dist/cli.js +134 -36
  23. package/dist/cli.js.map +1 -1
  24. package/dist/custom-chart/custom-runtime.js +6 -6
  25. package/dist/custom-chart/template/chart.ts +116 -28
  26. package/dist/custom-chart/template/icon.svg +1 -1
  27. package/dist/custom-chart/template/manifest.json +66 -12
  28. package/dist/custom-chart/template/sample-data.json +21 -3
  29. package/dist/index.d.ts +1 -2
  30. package/dist/index.js +20 -10
  31. package/dist/index.js.map +1 -1
  32. package/dist/{main-IUV24DMP.js → main-QCBVJSSR.js} +363 -155
  33. package/dist/main-QCBVJSSR.js.map +1 -0
  34. package/dist/{test-only-K7QTSVO4.js → test-only-6474NMTR.js} +12 -8
  35. package/dist/test-only-6474NMTR.js.map +1 -0
  36. package/package.json +20 -9
  37. package/dist/chunk-347U6GXR.js.map +0 -1
  38. package/dist/chunk-LACJHQYH.js.map +0 -1
  39. package/dist/chunk-OLOC2MTA.js.map +0 -1
  40. package/dist/chunk-Q5EYDKSA.js.map +0 -1
  41. package/dist/chunk-V3UW3B6U.js.map +0 -1
  42. package/dist/chunk-Z3ZHN4UW.js.map +0 -1
  43. package/dist/chunk-ZTHGF27R.js.map +0 -1
  44. package/dist/main-IUV24DMP.js.map +0 -1
  45. package/dist/test-only-K7QTSVO4.js.map +0 -1
  46. /package/dist/{bundle-TDQXWIPM.js.map → bundle-QJNCUMNY.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/custom-chart/bundle.ts","../src/custom-chart/assets.ts","../src/custom-chart/sanitize-svg.ts","../src/custom-chart/types.ts","../src/custom-chart/icon.ts","../src/custom-chart/manifest.ts","../src/custom-chart/zip.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { build as esbuild, type Plugin } from 'esbuild';\nimport { readRuntimeShim } from './assets.js';\nimport { sanitizeIcon, validateIcon } from './icon.js';\nimport { sortKeys, validateManifest } from './manifest.js';\nimport {\n MAX_BUNDLE_CSS_BYTES,\n MAX_BUNDLE_JS_BYTES,\n type CustomChartBundle,\n type CustomChartManifest,\n type Problem,\n} from './types.js';\nimport { writeBundleZip } from './zip.js';\n\nconst utf8Bytes = (text: string): number => Buffer.byteLength(text, 'utf8');\n\n/**\n * Content hash exactly as the backend computes it (brief section 5 and\n * FINDINGS be-core 2): sha256 hex over\n * JSON.stringify({ manifest, bundleJs, bundleCss, iconSvg }) in that key\n * order, the manifest keys sorted recursively (arrays keep their order) and\n * bundleCss always present, as \"\" when there is none.\n */\nexport function contentHash(bundle: CustomChartBundle): string {\n const payload = JSON.stringify({\n manifest: sortKeys(bundle.manifest),\n bundleJs: bundle.bundleJs,\n bundleCss: bundle.bundleCss ?? '',\n iconSvg: bundle.iconSvg,\n });\n return createHash('sha256').update(payload, 'utf8').digest('hex');\n}\n\n/** Bytes the upload carries: the four artifacts as UTF-8. */\nexport function bundleSizeBytes(bundle: CustomChartBundle): number {\n return (\n utf8Bytes(JSON.stringify(bundle.manifest)) +\n utf8Bytes(bundle.bundleJs) +\n (bundle.bundleCss ? utf8Bytes(bundle.bundleCss) : 0) +\n utf8Bytes(bundle.iconSvg)\n );\n}\n\nexport interface ValidateBundleInput {\n manifest: unknown;\n bundleJs?: string;\n bundleCss?: string;\n iconSvg?: string;\n}\n\n/**\n * The client side of the upload validation (the server repeats it). Manifest\n * rules first, then the bundle sizes, then the icon.\n */\nexport function validateBundle(input: ValidateBundleInput): Problem[] {\n const problems = validateManifest(input.manifest);\n if (input.bundleJs !== undefined) {\n if (typeof input.bundleJs !== 'string') {\n problems.push({ code: 'bundle.js.type', path: '/bundleJs', message: 'bundleJs must be a string.' });\n } else if (input.bundleJs.trim().length === 0) {\n problems.push({ code: 'bundle.js.empty', path: '/bundleJs', message: 'bundleJs must contain JavaScript.' });\n } else if (utf8Bytes(input.bundleJs) > MAX_BUNDLE_JS_BYTES) {\n problems.push({\n code: 'bundle.js.size',\n path: '/bundleJs',\n message: `bundleJs must be at most ${MAX_BUNDLE_JS_BYTES} bytes (2 MiB) as UTF-8. Drop or shrink a dependency.`,\n });\n }\n }\n if (input.bundleCss !== undefined) {\n if (typeof input.bundleCss !== 'string') {\n problems.push({ code: 'bundle.css.type', path: '/bundleCss', message: 'bundleCss must be a string when present.' });\n } else if (utf8Bytes(input.bundleCss) > MAX_BUNDLE_CSS_BYTES) {\n problems.push({\n code: 'bundle.css.size',\n path: '/bundleCss',\n message: `bundleCss must be at most ${MAX_BUNDLE_CSS_BYTES} bytes (256 KiB).`,\n });\n }\n }\n if (input.iconSvg !== undefined) problems.push(...validateIcon(input.iconSvg, '/iconSvg'));\n return problems;\n}\n\nexport interface ChartProject {\n dir: string;\n manifest: unknown;\n manifestText?: string;\n chartEntry?: string;\n css?: string;\n icon?: string;\n sampleData?: unknown;\n problems: Problem[];\n}\n\n/** Read a chart project folder. Missing or unparsable files become problems. */\nexport function readProject(dirInput: string): ChartProject {\n const dir = resolve(dirInput);\n const problems: Problem[] = [];\n const project: ChartProject = { dir, manifest: undefined, problems };\n\n const manifestPath = join(dir, 'manifest.json');\n if (!existsSync(manifestPath)) {\n problems.push({ code: 'project.manifest.missing', path: '/manifest.json', message: `No manifest.json in ${dir}.` });\n } else {\n project.manifestText = readFileSync(manifestPath, 'utf8');\n try {\n project.manifest = JSON.parse(project.manifestText);\n } catch (err) {\n problems.push({\n code: 'project.manifest.parse',\n path: '/manifest.json',\n message: `manifest.json is not valid JSON: ${(err as Error).message}`,\n });\n }\n }\n\n const entry = ['chart.ts', 'chart.js'].map((name) => join(dir, name)).find((path) => existsSync(path));\n if (!entry) {\n problems.push({ code: 'project.chart.missing', path: '/chart.ts', message: `No chart.ts in ${dir}. It must export render(ctx).` });\n } else {\n project.chartEntry = entry;\n }\n\n const cssPath = join(dir, 'chart.css');\n if (existsSync(cssPath)) project.css = readFileSync(cssPath, 'utf8');\n\n const iconPath = join(dir, 'icon.svg');\n if (!existsSync(iconPath)) {\n problems.push({ code: 'bundle.icon.required', path: '/icon.svg', message: `No icon.svg in ${dir}.` });\n } else {\n project.icon = readFileSync(iconPath, 'utf8');\n }\n\n const samplePath = join(dir, 'sample-data.json');\n if (existsSync(samplePath)) {\n try {\n project.sampleData = JSON.parse(readFileSync(samplePath, 'utf8'));\n } catch (err) {\n problems.push({\n code: 'project.sampleData.parse',\n path: '/sample-data.json',\n message: `sample-data.json is not valid JSON: ${(err as Error).message}`,\n });\n }\n }\n return project;\n}\n\n/** validate = read the folder, check the manifest and the icon. No esbuild. */\nexport function validateProject(dir: string): { ok: boolean; problems: Problem[]; project: ChartProject } {\n const project = readProject(dir);\n const problems = [...project.problems];\n if (project.manifest !== undefined) problems.push(...validateManifest(project.manifest));\n // The build strips the XML declaration and comments before upload, so check that form.\n if (project.icon !== undefined) problems.push(...validateIcon(sanitizeIcon(project.icon)));\n if (project.css !== undefined && utf8Bytes(project.css) > MAX_BUNDLE_CSS_BYTES) {\n problems.push({ code: 'bundle.css.size', path: '/chart.css', message: `chart.css must be at most ${MAX_BUNDLE_CSS_BYTES} bytes.` });\n }\n return { ok: problems.length === 0, problems, project };\n}\n\nexport interface BuildOutputs extends CustomChartBundle {\n manifest: CustomChartManifest;\n contentHash: string;\n sizeBytes: number;\n}\n\nexport interface BuildResult {\n ok: boolean;\n problems: Problem[];\n outputs?: BuildOutputs;\n /** Absolute paths written under dist/, in write order. Empty when write is false. */\n written: string[];\n}\n\nexport const DIST_FILES = ['index.js', 'manifest.json', 'icon.svg', 'index.css', 'bundle.zip'] as const;\n\n/**\n * Every import must resolve to a file inside the chart project. A relative\n * path that climbs out (`../../.config/x.json`) would be inlined into the\n * bundle and uploaded with it; the build refuses it with a problem instead.\n */\nfunction insideProjectOnly(dir: string): Plugin {\n const root = dir.endsWith(sep) ? dir : `${dir}${sep}`;\n return {\n name: 'databrain-inside-project-only',\n setup(build) {\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.kind === 'entry-point' || args.pluginData?.databrainChecked) return undefined;\n const resolved = await build.resolve(args.path, {\n kind: args.kind,\n resolveDir: args.resolveDir,\n importer: args.importer,\n pluginData: { databrainChecked: true },\n });\n if (resolved.errors.length > 0) return { errors: resolved.errors };\n if (resolved.external || resolved.path.startsWith(root)) return resolved;\n return {\n errors: [\n {\n text: `\"${args.path}\" is outside the chart project. Only files inside ${dir} can be imported.`,\n },\n ],\n };\n });\n },\n };\n}\n\n/**\n * Compile chart.ts with esbuild to one IIFE, prepend the runtime shim and\n * the slot id list the shim reads, enforce the 2 MiB gate and write dist/.\n * See brief section 4 and contract/runtime-shim.js.\n */\nexport async function buildProject(dir: string, options: { write?: boolean } = {}): Promise<BuildResult> {\n const validated = validateProject(dir);\n const { project } = validated;\n const problems = [...validated.problems];\n if (!project.chartEntry || project.manifest === undefined || problems.length > 0) {\n return { ok: false, problems, written: [] };\n }\n const manifest = project.manifest as CustomChartManifest;\n\n // Bundled for the browser with no externals: every import is resolved\n // inside the project or it is a build error (a node builtin like \"fs\" does\n // not resolve on the browser platform), so dist/index.js never carries a\n // dangling require. The entry must be an ES module that exports render():\n // the shim reads globalThis.__dbnCustomChart.render, and a CommonJS\n // module.exports or a missing export would only fail later, as a blank tile.\n const common = {\n plugins: [insideProjectOnly(project.dir)],\n entryPoints: [project.chartEntry],\n absWorkingDir: project.dir,\n bundle: true as const,\n target: 'es2020',\n platform: 'browser' as const,\n write: false as const,\n legalComments: 'none' as const,\n logLevel: 'silent' as const,\n charset: 'utf8' as const,\n };\n let moduleJs: string;\n try {\n const shape = await esbuild({ ...common, format: 'esm', metafile: true });\n const exported = Object.values(shape.metafile.outputs)[0]?.exports ?? [];\n if (!exported.includes('render')) {\n problems.push({\n code: 'bundle.build.noRender',\n path: '/chart.ts',\n message:\n 'chart.ts must be an ES module that exports a function named render: `export function render(ctx) { ... }`. module.exports and default exports are not read.',\n });\n return { ok: false, problems, written: [] };\n }\n const result = await esbuild({\n ...common,\n format: 'iife',\n globalName: '__dbnCustomChart',\n minify: true,\n });\n moduleJs = result.outputFiles.map((file) => file.text).join('\\n');\n } catch (err) {\n const errors = (err as { errors?: { text: string; location?: { file?: string; line?: number } | null }[] }).errors;\n if (Array.isArray(errors) && errors.length > 0) {\n for (const error of errors) {\n const where = error.location ? `${error.location.file ?? 'chart.ts'}:${error.location.line ?? 0}` : 'chart.ts';\n problems.push({ code: 'bundle.build.failed', path: `/${where}`, message: error.text });\n }\n } else {\n problems.push({ code: 'bundle.build.failed', path: '/chart.ts', message: (err as Error).message });\n }\n return { ok: false, problems, written: [] };\n }\n\n // Shim first (byte identical to contract/runtime-shim.js), then the slot ids\n // so every manifest slot is present on ctx.slots, then the chart module.\n // chart.css is NOT inlined: the host injects bundleCss into the frame\n // (brief section 6.5); dist/index.css carries it for provenance and upload.\n const shim = readRuntimeShim();\n const slotIds = manifest.slots.map((slot) => slot.id);\n const globals = `globalThis.__dbnCustomChartSlots=${JSON.stringify(slotIds)};\\n`;\n const bundleJs = `${shim.trimEnd()}\\n${globals}${moduleJs.trimEnd()}\\nglobalThis.__dbnCustomChart=__dbnCustomChart;\\n`;\n const iconSvg = sanitizeIcon(project.icon as string);\n const bundleCss = project.css;\n\n problems.push(...validateBundle({ manifest, bundleJs, bundleCss, iconSvg }));\n if (problems.length > 0) return { ok: false, problems, written: [] };\n\n const bundle: CustomChartBundle = { manifest, bundleJs, bundleCss, iconSvg };\n const outputs: BuildOutputs = {\n ...bundle,\n contentHash: contentHash(bundle),\n sizeBytes: bundleSizeBytes(bundle),\n };\n\n const written: string[] = [];\n if (options.write !== false) {\n const dist = join(project.dir, 'dist');\n mkdirSync(dist, { recursive: true });\n const manifestJson = JSON.stringify(manifest, null, 2) + '\\n';\n const write = (name: string, content: string | Uint8Array) => {\n const path = join(dist, name);\n writeFileSync(path, content);\n written.push(path);\n };\n write('index.js', bundleJs);\n write('manifest.json', manifestJson);\n write('icon.svg', iconSvg);\n if (bundleCss !== undefined) write('index.css', bundleCss);\n write(\n 'bundle.zip',\n writeBundleZip({\n 'index.js': bundleJs,\n 'manifest.json': manifestJson,\n 'icon.svg': iconSvg,\n ...(bundleCss !== undefined ? { 'index.css': bundleCss } : {}),\n }),\n );\n }\n\n return { ok: true, problems: [], outputs, written };\n}\n\n/** Read a previous build from dist/ (for upload without rebuilding). */\nexport function readDist(dir: string): { bundle?: CustomChartBundle; problems: Problem[] } {\n const dist = join(resolve(dir), 'dist');\n const problems: Problem[] = [];\n const need = ['index.js', 'manifest.json', 'icon.svg'];\n for (const name of need) {\n if (!existsSync(join(dist, name))) {\n problems.push({ code: 'project.dist.missing', path: `/dist/${name}`, message: `dist/${name} is missing. Run databrain custom-chart build first.` });\n }\n }\n if (problems.length > 0) return { problems };\n let manifest: unknown;\n try {\n manifest = JSON.parse(readFileSync(join(dist, 'manifest.json'), 'utf8'));\n } catch (err) {\n return { problems: [{ code: 'project.dist.parse', path: '/dist/manifest.json', message: (err as Error).message }] };\n }\n const manifestProblems = validateManifest(manifest);\n if (manifestProblems.length > 0) return { problems: manifestProblems };\n const cssPath = join(dist, 'index.css');\n return {\n problems,\n bundle: {\n manifest: manifest as CustomChartManifest,\n bundleJs: readFileSync(join(dist, 'index.js'), 'utf8'),\n bundleCss: existsSync(cssPath) ? readFileSync(cssPath, 'utf8') : undefined,\n iconSvg: readFileSync(join(dist, 'icon.svg'), 'utf8'),\n },\n };\n}\n\n/** Compile caller-supplied source in memory. No filesystem, package resolution or execution. */\nexport async function buildSource(input: {\n manifest: unknown; chartTs: string; bundleCss?: string; iconSvg: string;\n}): Promise<BuildResult> {\n const problems = validateManifest(input.manifest);\n if (problems.length) return { ok: false, problems, written: [] };\n const manifest = input.manifest as CustomChartManifest;\n try {\n const common = {\n stdin: { contents: input.chartTs, loader: 'ts' as const, sourcefile: 'chart.ts' },\n bundle: true, platform: 'browser' as const, target: 'es2020', write: false,\n legalComments: 'none' as const, logLevel: 'silent' as const,\n plugins: [{ name: 'no-host-imports', setup(build: Parameters<Plugin['setup']>[0]) {\n build.onResolve({ filter: /.*/ }, args => ({ errors: [{ text:\n `Import \"${args.path}\" is unavailable in hosted chart source. Use browser APIs or pass a prebuilt bundle.` }] }));\n } }],\n };\n let shape = await esbuild({ ...common, format: 'esm', metafile: true });\n if (!Object.values(shape.metafile.outputs)[0]?.exports.includes('render')) {\n common.stdin.contents += '\\nexport { render };';\n shape = await esbuild({ ...common, format: 'esm', metafile: true });\n }\n if (!Object.values(shape.metafile.outputs)[0]?.exports.includes('render')) {\n return { ok: false, problems: [{ code: 'bundle.build.noRender', path: '/chart.ts', message: 'Export function render(ctx).' }], written: [] };\n }\n const built = await esbuild({ ...common, format: 'iife', globalName: '__dbnCustomChart', minify: true });\n const bundle: CustomChartBundle = {\n manifest, bundleCss: input.bundleCss, iconSvg: sanitizeIcon(input.iconSvg),\n bundleJs: `${readRuntimeShim().trimEnd()}\\nglobalThis.__dbnCustomChartSlots=${JSON.stringify(manifest.slots.map(s => s.id))};\\n${built.outputFiles!.map(f => f.text).join('\\n')}\\nglobalThis.__dbnCustomChart=__dbnCustomChart;\\n`,\n };\n const errors = validateBundle(bundle);\n return errors.length ? { ok: false, problems: errors, written: [] } : {\n ok: true, problems: [], written: [], outputs: { ...bundle, contentHash: contentHash(bundle), sizeBytes: bundleSizeBytes(bundle) },\n };\n } catch (error) {\n return { ok: false, problems: [{ code: 'bundle.build.failed', path: '/chart.ts', message: (error as Error).message }], written: [] };\n }\n}\n","import { buildSync } from 'esbuild';\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * The template project and the runtime shim are shipped as plain files, not\n * as strings baked into the bundle, so that they stay byte identical to the\n * copies other repos test against. In source they live in\n * src/custom-chart/; tsup copies them to dist/custom-chart/ after a build\n * (see tsup.config.ts onSuccess). This finds them from either place.\n */\nlet cachedDir: string | undefined;\n\nexport function customChartAssetDir(): string {\n if (cachedDir) return cachedDir;\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n here,\n join(here, 'custom-chart'),\n join(here, '..', 'custom-chart'),\n join(here, '..', '..', 'custom-chart'),\n join(here, '..', 'src', 'custom-chart'),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, 'runtime-shim.js')) && existsSync(join(candidate, 'template', 'manifest.json'))) {\n cachedDir = resolve(candidate);\n return cachedDir;\n }\n }\n throw new Error(\n `Custom chart assets not found near ${here}. Expected runtime-shim.js and template/ (run npm run build).`,\n );\n}\n\nexport function readRuntimeShim(): string {\n return readFileSync(join(customChartAssetDir(), 'runtime-shim.js'), 'utf8');\n}\n\nexport function templateDir(): string {\n return join(customChartAssetDir(), 'template');\n}\n\n/** Every file in the template folder, keyed by its file name. */\nexport function readTemplateFiles(): Record<string, string> {\n const dir = templateDir();\n const files: Record<string, string> = {};\n for (const name of readdirSync(dir).sort()) {\n const path = join(dir, name);\n if (statSync(path).isFile()) files[name] = readFileSync(path, 'utf8');\n }\n return files;\n}\n\nlet cachedBrowserRuntime: string | undefined;\n/** Packaged asset in dist; source mode compiles only this fixed trusted entrypoint. */\nexport function readCustomChartBrowserRuntime(): string {\n if (cachedBrowserRuntime) return cachedBrowserRuntime;\n const dir = customChartAssetDir();\n const packed = join(dir, 'custom-runtime.js');\n cachedBrowserRuntime = existsSync(packed) ? readFileSync(packed, 'utf8') : buildSync({\n entryPoints: [join(dir, '..', 'apps', 'custom-runtime', 'browser.ts')],\n bundle: true, write: false, platform: 'browser', target: 'es2020', minify: true,\n }).outputFiles[0].text;\n return cachedBrowserRuntime;\n}\n","/**\n * Allow-list SVG sanitiser for icon.svg. A verbatim copy of the backend's\n * `serverless/express/src/utils/helpers/sanitizeSvg.ts` (the one that gates\n * the upload), so the CLI and the validate tool refuse exactly what the\n * server refuses instead of guessing with regexes. Keep the two files in\n * step: a rule added on one side and not the other means \"validate says ok,\n * upload says no\".\n *\n * WHAT IT DOES\n * ------------\n * - Drops the DOCTYPE (entity expansion) and every processing instruction\n * except the XML declaration; drops CDATA and comments.\n * - Keeps only elements on ALLOWED_ELEMENTS. A disallowed element and\n * everything inside it is removed (`<script>`, `<foreignObject>`,\n * `<iframe>`, `<animate>` ... ).\n * - Keeps only attributes on ALLOWED_ATTRIBUTES (plus `data-*` and the\n * `xmlns*` declarations). Every `on*` attribute goes. Any attribute whose\n * value carries `javascript:`, `data:text/html`, or `vbscript:` after\n * entity decoding goes.\n * - `href` / `xlink:href` survive only as a same-document `#id` reference,\n * or, on `<image>` alone, as a `data:image/(png|jpeg|gif|webp);base64`\n * URI. An external `<image href>` is removed with the element.\n * - `<style>` and `style=\"\"` survive only when they contain no `url(`,\n * `@import`, `expression(` or `-moz-binding`. A style that reaches out\n * of the file is dropped entirely rather than partially rewritten.\n *\n * Returns the cleaned markup and a count of what was removed, so the caller\n * can refuse the icon rather than silently rewriting it.\n */\nconst ALLOWED_ELEMENTS: ReadonlySet<string> = new Set([\n 'svg',\n 'g',\n 'defs',\n 'symbol',\n 'use',\n 'title',\n 'desc',\n 'metadata',\n 'path',\n 'rect',\n 'circle',\n 'ellipse',\n 'line',\n 'polyline',\n 'polygon',\n 'text',\n 'tspan',\n 'textpath',\n 'image',\n 'lineargradient',\n 'radialgradient',\n 'stop',\n 'clippath',\n 'mask',\n 'pattern',\n 'marker',\n 'filter',\n 'fegaussianblur',\n 'feoffset',\n 'feblend',\n 'fecolormatrix',\n 'fecomposite',\n 'feflood',\n 'femerge',\n 'femergenode',\n 'fedropshadow',\n 'style',\n 'switch',\n]);\n\nconst ALLOWED_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'id',\n 'class',\n 'style',\n 'href',\n 'xlink:href',\n 'xml:space',\n 'lang',\n 'xml:lang',\n 'version',\n 'baseprofile',\n 'viewbox',\n 'preserveaspectratio',\n 'width',\n 'height',\n 'x',\n 'y',\n 'x1',\n 'y1',\n 'x2',\n 'y2',\n 'cx',\n 'cy',\n 'r',\n 'rx',\n 'ry',\n 'fx',\n 'fy',\n 'fr',\n 'd',\n 'points',\n 'transform',\n 'transform-origin',\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'stroke',\n 'stroke-width',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'stroke-opacity',\n 'opacity',\n 'color',\n 'display',\n 'visibility',\n 'overflow',\n 'clip-path',\n 'clip-rule',\n 'clippathunits',\n 'mask',\n 'maskunits',\n 'maskcontentunits',\n 'filter',\n 'filterunits',\n 'primitiveunits',\n 'in',\n 'in2',\n 'result',\n 'mode',\n 'operator',\n 'values',\n 'type',\n 'stddeviation',\n 'dx',\n 'dy',\n 'flood-color',\n 'flood-opacity',\n 'offset',\n 'stop-color',\n 'stop-opacity',\n 'gradientunits',\n 'gradienttransform',\n 'spreadmethod',\n 'patternunits',\n 'patterncontentunits',\n 'patterntransform',\n 'markerunits',\n 'markerwidth',\n 'markerheight',\n 'refx',\n 'refy',\n 'orient',\n 'font-family',\n 'font-size',\n 'font-weight',\n 'font-style',\n 'letter-spacing',\n 'text-anchor',\n 'dominant-baseline',\n 'alignment-baseline',\n 'text-decoration',\n 'word-spacing',\n 'paint-order',\n 'vector-effect',\n 'shape-rendering',\n 'text-rendering',\n 'image-rendering',\n 'color-interpolation',\n 'color-interpolation-filters',\n 'enable-background',\n 'startoffset',\n 'method',\n 'spacing',\n 'requiredfeatures',\n 'systemlanguage',\n 'role',\n 'aria-label',\n 'aria-labelledby',\n 'aria-hidden',\n 'focusable',\n 'tabindex',\n]);\n\n/** Pattern over a tag's raw text: name, then attributes, then optional `/`. */\nconst TAG_RE = /^<\\s*(\\/?)\\s*([A-Za-z_][\\w:.-]*)([\\s\\S]*?)(\\/?)\\s*>$/;\n\nconst ATTRIBUTE_RE =\n /([^\\s=/\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/g;\n\nconst decodeEntities = (value: string): string =>\n value\n .replace(/&#x([0-9a-fA-F]+);?/g, (_m, hex: string) =>\n String.fromCodePoint(parseInt(hex, 16)),\n )\n .replace(/&#(\\d+);?/g, (_m, dec: string) =>\n String.fromCodePoint(parseInt(dec, 10)),\n )\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, \"'\")\n .replace(/&amp;/g, '&');\n\n/** Entity-decode, drop control and whitespace characters, lower-case. Then a scheme cannot hide as `java\\tscript:`. */\nconst normaliseForSchemeCheck = (value: string): string =>\n decodeEntities(value)\n .replace(/[\\u0000-\\u0020\\u007f-\\u00a0\\u200b-\\u200f\\u2028\\u2029\\ufeff]/g, '')\n .toLowerCase();\n\nconst DANGEROUS_SCHEME_RE =\n /^(javascript|vbscript|livescript|mocha|data:text\\/html|data:application)/;\n\nconst isDangerousValue = (value: string): boolean => {\n const normalised = normaliseForSchemeCheck(value);\n return (\n DANGEROUS_SCHEME_RE.test(normalised) ||\n normalised.includes('javascript:') ||\n normalised.includes('vbscript:')\n );\n};\n\nconst SAFE_DATA_IMAGE_RE =\n /^data:image\\/(png|jpeg|jpg|gif|webp);base64,[a-z0-9+/=]+$/;\n\nconst isAcceptableHref = (value: string, element: string): boolean => {\n const normalised = normaliseForSchemeCheck(value);\n if (normalised.startsWith('#')) return true;\n return element === 'image' && SAFE_DATA_IMAGE_RE.test(normalised);\n};\n\n/**\n * A style that leaves the document is dropped whole. `url(#id)` is a same\n * document paint server reference (`fill: url(#gradient)`) and is the one\n * `url(` a logo legitimately uses, so it is blanked before the test.\n */\nconst LOCAL_URL_RE = /url\\s*\\(\\s*(['\"]?)#[^)'\"]*\\1\\s*\\)/gi;\nconst STYLE_ESCAPE_RE =\n /url\\s*\\(|@import|expression\\s*\\(|-moz-binding|behavior\\s*:/i;\n\nconst isSafeStyle = (value: string): boolean =>\n !STYLE_ESCAPE_RE.test(decodeEntities(value).replace(LOCAL_URL_RE, ''));\n\nexport type SanitizeSvgResult = {\n svg: string;\n /** Elements, attributes and declarations that were taken out. */\n removed: number;\n /** True when the input did not even look like an SVG document. */\n isSvg: boolean;\n};\n\ntype Token =\n | { kind: 'text'; raw: string }\n | { kind: 'tag'; raw: string }\n | { kind: 'special'; raw: string };\n\n/**\n * Split markup into text, tags, and the specials (comments, CDATA, DOCTYPE,\n * processing instructions) that need their own terminators.\n */\nconst tokenize = (input: string): Token[] => {\n const tokens: Token[] = [];\n let index = 0;\n const length = input.length;\n\n const pushSpecial = (end: number, terminator: string): void => {\n const close = input.indexOf(terminator, end);\n const stop = close === -1 ? length : close + terminator.length;\n tokens.push({ kind: 'special', raw: input.slice(index, stop) });\n index = stop;\n };\n\n while (index < length) {\n const open = input.indexOf('<', index);\n if (open === -1) {\n tokens.push({ kind: 'text', raw: input.slice(index) });\n break;\n }\n if (open > index) {\n tokens.push({ kind: 'text', raw: input.slice(index, open) });\n index = open;\n }\n if (input.startsWith('<!--', index)) {\n pushSpecial(index + 4, '-->');\n continue;\n }\n if (input.startsWith('<![CDATA[', index)) {\n pushSpecial(index + 9, ']]>');\n continue;\n }\n if (input.startsWith('<?', index)) {\n pushSpecial(index + 2, '?>');\n continue;\n }\n if (input.startsWith('<!', index)) {\n // DOCTYPE may carry an internal subset in [ ... ]; find the real end.\n const bracket = input.indexOf('[', index);\n const plainEnd = input.indexOf('>', index);\n if (bracket !== -1 && (plainEnd === -1 || bracket < plainEnd)) {\n const subsetEnd = input.indexOf(']', bracket);\n pushSpecial(subsetEnd === -1 ? index + 2 : subsetEnd, '>');\n } else {\n pushSpecial(index + 2, '>');\n }\n continue;\n }\n // Ordinary tag: ends at the first `>` outside a quoted attribute value.\n let cursor = index + 1;\n let quote: string | null = null;\n while (cursor < length) {\n const ch = input[cursor];\n if (quote) {\n if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === '>') {\n break;\n }\n cursor += 1;\n }\n const stop = cursor >= length ? length : cursor + 1;\n tokens.push({ kind: 'tag', raw: input.slice(index, stop) });\n index = stop;\n }\n return tokens;\n};\n\nconst isXmlDeclaration = (raw: string): boolean => /^<\\?xml\\b/i.test(raw);\n\n/**\n * Filter a tag's attributes. Returns the rebuilt attribute string (or `null`\n * when the element itself must go because its only href was hostile) and the\n * number of attributes dropped.\n */\nconst cleanAttributes = (\n element: string,\n rawAttributes: string,\n): { attributes: string; removed: number; dropElement: boolean } => {\n let removed = 0;\n let dropElement = false;\n const kept: string[] = [];\n ATTRIBUTE_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = ATTRIBUTE_RE.exec(rawAttributes)) !== null) {\n const [whole, name, dq, sq, bare] = match;\n if (!name || name === '/') continue;\n const lower = name.toLowerCase();\n const value = dq ?? sq ?? bare ?? '';\n\n if (lower.startsWith('on')) {\n removed += 1;\n continue;\n }\n const isNamespaceDeclaration =\n lower === 'xmlns' || lower.startsWith('xmlns:');\n const isData = lower.startsWith('data-');\n if (!isNamespaceDeclaration && !isData && !ALLOWED_ATTRIBUTES.has(lower)) {\n removed += 1;\n continue;\n }\n if (isDangerousValue(value)) {\n removed += 1;\n continue;\n }\n if (lower === 'href' || lower === 'xlink:href') {\n if (!isAcceptableHref(value, element)) {\n removed += 1;\n // An <image> or <use> whose only reason to exist is an external\n // reference is not a logo any more once that reference is gone.\n if (element === 'image' || element === 'use') dropElement = true;\n continue;\n }\n }\n if (lower === 'style' && !isSafeStyle(value)) {\n removed += 1;\n continue;\n }\n kept.push(whole.trim());\n }\n return {\n attributes: kept.length ? ` ${kept.join(' ')}` : '',\n removed,\n dropElement,\n };\n};\n\n/**\n * Sentinel pushed before a `<style>` open tag so the whole element can be\n * unwound if its content turns out to reach outside the file. NUL bytes cannot\n * occur in well formed XML text, so no real chunk can collide with it, and it\n * is filtered out before the chunks are joined.\n */\nconst STYLE_OPEN_MARKER = '\\u0000style-open\\u0000';\n\nexport const sanitizeSvg = (input: string): SanitizeSvgResult => {\n const tokens = tokenize(input);\n const out: string[] = [];\n let removed = 0;\n let isSvg = false;\n\n // Depth inside a dropped element: while > 0 every token is discarded. The\n // stack tracks which open tags were dropped so the matching close is too.\n let dropDepth = 0;\n const dropStack: string[] = [];\n // Inside <style>: content must be checked as CSS, not copied blind.\n let styleBuffer: string[] | null = null;\n\n for (const token of tokens) {\n if (token.kind === 'special') {\n if (isXmlDeclaration(token.raw) && dropDepth === 0) {\n out.push(token.raw);\n } else {\n removed += 1;\n }\n continue;\n }\n\n if (token.kind === 'text') {\n if (dropDepth > 0) continue;\n if (styleBuffer) {\n styleBuffer.push(token.raw);\n } else {\n out.push(token.raw);\n }\n continue;\n }\n\n const parsed = TAG_RE.exec(token.raw);\n if (!parsed) {\n // Unparseable tag text. Inside a drop it goes anyway; outside, it is\n // not markup we can vouch for, so it goes too.\n removed += 1;\n continue;\n }\n const [, closing, rawName, rawAttributes, selfClosingSlash] = parsed;\n const name = rawName.toLowerCase();\n const isClosing = closing === '/';\n const isSelfClosing = selfClosingSlash === '/';\n\n if (name === 'svg') isSvg = true;\n\n if (dropDepth > 0) {\n if (isClosing) {\n if (dropStack[dropStack.length - 1] === name) {\n dropStack.pop();\n dropDepth -= 1;\n }\n } else if (!isSelfClosing) {\n dropStack.push(name);\n dropDepth += 1;\n }\n continue;\n }\n\n if (isClosing) {\n if (name === 'style' && styleBuffer) {\n const css = styleBuffer.join('');\n styleBuffer = null;\n if (isSafeStyle(css)) {\n out.push(css, token.raw);\n } else {\n // Drop the <style> we already emitted the open tag for.\n const openIndex = out.lastIndexOf(STYLE_OPEN_MARKER);\n if (openIndex !== -1) out.splice(openIndex);\n removed += 1;\n }\n continue;\n }\n if (ALLOWED_ELEMENTS.has(name)) out.push(token.raw);\n continue;\n }\n\n if (!ALLOWED_ELEMENTS.has(name)) {\n removed += 1;\n if (!isSelfClosing) {\n dropStack.push(name);\n dropDepth += 1;\n }\n continue;\n }\n\n const cleaned = cleanAttributes(name, rawAttributes);\n removed += cleaned.removed;\n if (cleaned.dropElement) {\n removed += 1;\n if (!isSelfClosing) {\n dropStack.push(name);\n dropDepth += 1;\n }\n continue;\n }\n\n const rebuilt = `<${rawName}${cleaned.attributes}${\n isSelfClosing ? '/' : ''\n }>`;\n if (name === 'style' && !isSelfClosing) {\n out.push(STYLE_OPEN_MARKER, rebuilt);\n styleBuffer = [];\n continue;\n }\n out.push(rebuilt);\n }\n\n // An unterminated <style> at EOF: keep nothing of it.\n if (styleBuffer) {\n const openIndex = out.lastIndexOf(STYLE_OPEN_MARKER);\n if (openIndex !== -1) out.splice(openIndex);\n removed += 1;\n }\n\n return {\n svg: out.filter((chunk) => chunk !== STYLE_OPEN_MARKER).join(''),\n removed,\n isSvg,\n };\n};\n","/**\n * Shared shapes for the custom chart package pipeline (DAT-925).\n * Problem is the one error shape used by the manifest validator, the bundle\n * builder, the icon check and the backend API (section 5 of the brief).\n */\nexport interface Problem {\n code: string;\n path: string;\n message: string;\n}\n\nexport type CustomChartFieldType = 'string' | 'number' | 'boolean';\n\nexport interface CustomChartSlot {\n id: string;\n label: string;\n role: 'dimension' | 'measure';\n types: CustomChartFieldType[];\n required: boolean;\n description?: string;\n}\n\nexport type CustomChartOption = { id: string; label: string; group?: string } & (\n | { type: 'boolean'; default: boolean }\n | { type: 'number'; default: number; min: number; max: number; step?: number }\n | { type: 'string'; default: string; maxLength: number }\n | { type: 'select'; default: string; choices: { value: string; label: string }[] }\n | { type: 'color'; default: string }\n);\n\nexport interface CustomChartTranslation {\n name?: string;\n description?: string;\n slots?: Record<string, { label?: string; description?: string }>;\n options?: Record<string, { label?: string; group?: string; choices?: Record<string, string> }>;\n}\n\nexport interface CustomChartManifest {\n schemaVersion: 1 | 2;\n id: string;\n name: string;\n version?: string;\n description?: string;\n slots: CustomChartSlot[];\n options: CustomChartOption[];\n translations?: Record<string, CustomChartTranslation>;\n}\n\n/** The four artifacts a build produces and an upload sends. */\nexport interface CustomChartBundle {\n manifest: CustomChartManifest;\n bundleJs: string;\n bundleCss?: string;\n iconSvg: string;\n}\n\nexport const MAX_MANIFEST_BYTES = 32 * 1024;\nexport const MAX_BUNDLE_JS_BYTES = 2 * 1024 * 1024;\nexport const MAX_BUNDLE_CSS_BYTES = 256 * 1024;\nexport const MAX_ICON_BYTES = 16 * 1024;\nexport const MAX_SLOTS = 16;\nexport const MAX_OPTIONS = 32;\n","import { sanitizeSvg } from './sanitize-svg.js';\nimport { MAX_ICON_BYTES, type Problem } from './types.js';\n\n/**\n * Strict SVG check for icon.svg (brief sections 4 and 5). Nothing is rewritten:\n * an icon either passes every rule or is refused with a problem that says what\n * to remove. The check is the backend's own allow-list sanitiser\n * (`sanitize-svg.ts`, a copy of the server file): the icon is refused when the\n * sanitiser would remove anything, when it carries an `<image>` element, or\n * when it is not one `<svg>` element. The backend runs the same sanitiser on\n * upload and refuses with the same codes: bundle.icon.required,\n * bundle.icon.size, bundle.icon.notSvg, bundle.icon.unsafe. The message names\n * the rule that tripped when one of the common ones did.\n */\nconst UNSAFE = 'bundle.icon.unsafe';\nconst RULES: { pattern: RegExp; message: string }[] = [\n { pattern: /<\\s*script\\b/i, message: 'Remove every <script> element from icon.svg.' },\n { pattern: /<\\s*foreignObject\\b/i, message: 'Remove every <foreignObject> element from icon.svg.' },\n { pattern: /<\\s*image\\b/i, message: 'Remove every <image> element from icon.svg. Draw with paths and shapes.' },\n { pattern: /[\\s/]on[a-z]+\\s*=/i, message: 'Remove every on*= event attribute from icon.svg.' },\n { pattern: /<!--/, message: 'Remove every <!-- comment --> from icon.svg.' },\n { pattern: /<!(?:ENTITY|DOCTYPE)\\b/i, message: 'Remove the DOCTYPE and entity declarations from icon.svg.' },\n { pattern: /<!\\[CDATA\\[/, message: 'Remove every CDATA section from icon.svg.' },\n { pattern: /(?:xlink:)?href\\s*=\\s*[\"']\\s*(?!#)/i, message: 'Every href in icon.svg must start with # (no external or data references).' },\n { pattern: /javascript\\s*:/i, message: 'Remove every javascript: reference from icon.svg.' },\n { pattern: /@import|expression\\s*\\(|-moz-binding/i, message: 'Remove @import, expression() and -moz-binding from icon.svg styles.' },\n { pattern: /url\\s*\\(\\s*['\"]?\\s*(?!#)/i, message: 'Every url() in icon.svg must point at a same-document #id.' },\n];\nconst GENERIC_UNSAFE =\n 'icon.svg must use only plain SVG drawing elements and attributes: no script, event handlers, foreignObject, image, animation, external hrefs, url() styles, comments, DOCTYPE or unknown attributes.';\n\nconst XML_DECLARATION = /^\\s*<\\?xml[^>]*\\?>\\s*/i;\n\n/** One `<svg ...>...</svg>` (or `<svg .../>`) and nothing else, after the XML declaration. */\nexport function isSingleSvgRoot(svg: string): boolean {\n const body = svg.replace(/^/, '').replace(XML_DECLARATION, '').trim();\n return /^<svg[\\s>][\\s\\S]*<\\/svg>$/i.test(body) || /^<svg\\b[^>]*\\/>$/i.test(body);\n}\n\nexport function validateIcon(svg: unknown, path = '/icon.svg'): Problem[] {\n if (typeof svg !== 'string' || svg.trim().length === 0) {\n return [{ code: 'bundle.icon.required', path, message: 'icon.svg is required and must not be empty.' }];\n }\n if (Buffer.byteLength(svg, 'utf8') > MAX_ICON_BYTES) {\n return [{ code: 'bundle.icon.size', path, message: `icon.svg must be at most ${MAX_ICON_BYTES} bytes.` }];\n }\n const cleaned = sanitizeSvg(svg);\n if (!cleaned.isSvg || !isSingleSvgRoot(cleaned.svg)) {\n return [{ code: 'bundle.icon.notSvg', path, message: 'icon.svg must be one <svg> element with nothing after it.' }];\n }\n if (cleaned.removed > 0 || /<\\s*image\\b/i.test(svg)) {\n const rule = RULES.find((candidate) => candidate.pattern.test(svg));\n return [{ code: UNSAFE, path, message: rule ? rule.message : GENERIC_UNSAFE }];\n }\n return [];\n}\n\n/**\n * The form the CLI writes to dist/icon.svg and uploads: BOM, XML declaration\n * and comments removed, trimmed. The server refuses a comment rather than\n * stripping it, so the build strips them here; everything else is left as the\n * author wrote it and must already pass validateIcon.\n */\nexport function sanitizeIcon(svg: string): string {\n return svg\n .replace(/^/, '')\n .replace(XML_DECLARATION, '')\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .trim();\n}\n","import { z } from 'zod';\nimport {\n MAX_MANIFEST_BYTES,\n MAX_OPTIONS,\n MAX_SLOTS,\n type CustomChartManifest,\n type Problem,\n} from './types.js';\n\n/**\n * Manifest v2 validator (brief section 3). It is a strict superset of the\n * plugin's sandbox manifest v1: every v1 manifest validates with\n * schemaVersion 1, and schemaVersion 2 unlocks version, description, the\n * color option type, the number step, the option group, slot descriptions and\n * translations. Unknown keys are refused at every level.\n *\n * The checks run in the plugin parser's order, step for step, and the backend\n * (`serverless/express/src/lib/customCharts/manifest.ts`) runs the same steps,\n * so the three validators produce the same problem list for the same\n * manifest. The cross-repo fixtures in tests/fixtures/custom-chart-manifests\n * pin the first code for every invalid case.\n *\n * 1. object, schemaVersion (stop on failure)\n * 2. top-level unknown and missing keys (stop on failure)\n * 3. id, name, version, description\n * 4. slots: shape and count, then each slot (unknown and missing keys\n * first, then the fields), then duplicate ids\n * 5. options: shape and count, then each option (reserved id, type,\n * unknown and missing keys, then the fields), then duplicate ids\n * 6. translations, only when everything above is clean\n * 7. size, only when everything above is clean\n */\n\nconst ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;\nconst FORBIDDEN_IDS = ['constructor', 'prototype', '__proto__'];\nconst LANGUAGE_PATTERN = /^[a-z]{2}(-[A-Z]{2})?$/;\nconst SEMVER_PATTERN =\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$/;\nconst COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;\nconst FIELD_TYPES = ['string', 'number', 'boolean'];\nconst MAX_TEXT = 256;\nconst MAX_DESCRIPTION = 500;\nconst MAX_GROUP = 64;\nconst MAX_CHOICES = 32;\nconst MAX_STRING_OPTION_LENGTH = 1024;\n\nconst V1_TOP_KEYS = ['schemaVersion', 'id', 'name', 'slots', 'options'];\nconst V2_TOP_EXTRA_KEYS = ['version', 'description', 'translations'];\nconst V1_SLOT_KEYS = ['id', 'label', 'role', 'types', 'required'];\nconst V2_SLOT_EXTRA_KEYS = ['description'];\nconst OPTION_BASE_KEYS = ['id', 'label', 'type', 'default'];\nconst OPTION_TYPE_KEYS: Record<string, string[]> = {\n boolean: [],\n number: ['min', 'max'],\n string: ['maxLength'],\n select: ['choices'],\n color: [],\n};\nconst V2_OPTION_EXTRA_KEYS: Record<string, string[]> = {\n boolean: ['group'],\n number: ['group', 'step'],\n string: ['group'],\n select: ['group'],\n color: ['group'],\n};\nconst TRANSLATION_KEYS = ['name', 'description', 'slots', 'options'];\nconst SLOT_TRANSLATION_KEYS = ['label', 'description'];\nconst OPTION_TRANSLATION_KEYS = ['label', 'group', 'choices'];\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) === Object.prototype;\n\nexport const isId = (value: unknown): value is string =>\n typeof value === 'string' && ID_PATTERN.test(value) && !FORBIDDEN_IDS.includes(value);\n\nconst isText = (value: unknown, max = MAX_TEXT): value is string =>\n typeof value === 'string' && value.length > 0 && value.length <= max;\n\n/** A translated string may be empty: the plugin renders it as given. */\nconst isTranslationText = (value: unknown): value is string =>\n typeof value === 'string' && value.length <= MAX_TEXT;\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\nexport const jsonBytes = (value: unknown): number =>\n Buffer.byteLength(JSON.stringify(value), 'utf8');\n\nclass Collector {\n readonly problems: Problem[] = [];\n\n add(code: string, path: string, message: string): void {\n this.problems.push({ code, path, message });\n }\n\n /**\n * Unknown keys report `<scope>.unknownKey`, one per key; a missing required\n * key reports `missingCode(key)`. Returns false when anything was reported,\n * so the caller can stop looking at a shape it cannot trust.\n */\n checkKeys(\n value: Record<string, unknown>,\n path: string,\n scope: string,\n required: readonly string[],\n optional: readonly string[],\n missingCode: (key: string) => string = (key) => `${scope}.${key}`,\n ): boolean {\n const allowed = new Set([...required, ...optional]);\n let ok = true;\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) {\n this.add(`${scope}.unknownKey`, `${path}/${key}`, `Remove \"${key}\". It is not a known key here.`);\n ok = false;\n }\n }\n for (const key of required) {\n if (!Object.hasOwn(value, key)) {\n this.add(missingCode(key), `${path}/${key}`, `Add the required key \"${key}\".`);\n ok = false;\n }\n }\n return ok;\n }\n}\n\nfunction validateSlot(c: Collector, value: unknown, path: string, v2: boolean): void {\n if (!isRecord(value)) {\n c.add('manifest.slot.invalid', path, 'A slot must be an object.');\n return;\n }\n if (!c.checkKeys(value, path, 'manifest.slot', V1_SLOT_KEYS, v2 ? V2_SLOT_EXTRA_KEYS : [])) return;\n if (!isId(value.id)) {\n c.add('manifest.slot.id', `${path}/id`, 'A slot id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters.');\n }\n if (!isText(value.label)) {\n c.add('manifest.slot.label', `${path}/label`, `A slot label is 1 to ${MAX_TEXT} characters.`);\n }\n const role = value.role;\n if (role !== 'dimension' && role !== 'measure') {\n c.add('manifest.slot.role', `${path}/role`, 'A slot role is \"dimension\" or \"measure\".');\n }\n if (typeof value.required !== 'boolean') {\n c.add('manifest.slot.required', `${path}/required`, 'required is true or false.');\n }\n const types = value.types;\n const typesOk =\n Array.isArray(types) &&\n types.length > 0 &&\n types.length <= 3 &&\n new Set(types).size === types.length &&\n types.every((type) => FIELD_TYPES.includes(type as string));\n if (!typesOk) {\n c.add('manifest.slot.types', `${path}/types`, 'types is a list of 1 to 3 distinct values from \"string\", \"number\" and \"boolean\".');\n } else if (role === 'measure' && (types.length !== 1 || (types as string[])[0] !== 'number')) {\n c.add('manifest.slot.types', `${path}/types`, 'A measure slot accepts only [\"number\"].');\n }\n if (Object.hasOwn(value, 'description') && !isText(value.description)) {\n c.add('manifest.slot.description', `${path}/description`, `A slot description is 1 to ${MAX_TEXT} characters.`);\n }\n}\n\nfunction validateOption(c: Collector, value: unknown, path: string, v2: boolean): void {\n if (!isRecord(value)) {\n c.add('manifest.option.invalid', path, 'An option must be an object.');\n return;\n }\n // Reserved before anything else: the author needs to hear \"reserved\" first.\n const id = value.id;\n if (typeof id === 'string' && (id === 'theme' || id.startsWith('theme.'))) {\n c.add('manifest.option.reserved', `${path}/id`, 'The option id \"theme\" (and any id starting with \"theme.\") is reserved for the host theme.');\n return;\n }\n const type = typeof value.type === 'string' ? value.type : '';\n const typeKeys = Object.hasOwn(OPTION_TYPE_KEYS, type) ? OPTION_TYPE_KEYS[type] : undefined;\n if (!typeKeys || (type === 'color' && !v2)) {\n c.add(\n 'manifest.option.type',\n `${path}/type`,\n v2\n ? 'An option type is one of \"boolean\", \"number\", \"string\", \"select\", \"color\".'\n : 'An option type is one of \"boolean\", \"number\", \"string\", \"select\" (color needs schemaVersion 2).',\n );\n return;\n }\n if (!c.checkKeys(value, path, 'manifest.option', [...OPTION_BASE_KEYS, ...typeKeys], v2 ? V2_OPTION_EXTRA_KEYS[type] : [])) return;\n if (!isId(id)) {\n c.add('manifest.option.id', `${path}/id`, 'An option id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters.');\n }\n if (!isText(value.label)) {\n c.add('manifest.option.label', `${path}/label`, `An option label is 1 to ${MAX_TEXT} characters.`);\n }\n if (Object.hasOwn(value, 'group') && !isText(value.group, MAX_GROUP)) {\n c.add('manifest.option.group', `${path}/group`, `An option group is 1 to ${MAX_GROUP} characters.`);\n }\n switch (type) {\n case 'boolean':\n if (typeof value.default !== 'boolean') {\n c.add('manifest.option.default', `${path}/default`, 'A boolean default is true or false.');\n }\n break;\n case 'number': {\n const rangeOk = isFiniteNumber(value.min) && isFiniteNumber(value.max) && value.min <= value.max;\n if (!rangeOk) {\n c.add('manifest.option.range', `${path}/min`, 'Set finite min and max with min <= max.');\n break;\n }\n const min = value.min as number;\n const max = value.max as number;\n if (!isFiniteNumber(value.default) || value.default < min || value.default > max) {\n c.add('manifest.option.default', `${path}/default`, 'A number default is a finite number between min and max.');\n }\n if (Object.hasOwn(value, 'step') && (!isFiniteNumber(value.step) || value.step <= 0 || value.step > max - min)) {\n c.add('manifest.option.step', `${path}/step`, 'step is a positive number no larger than max - min.');\n }\n break;\n }\n case 'string': {\n const maxLength = value.maxLength;\n if (!isFiniteNumber(maxLength) || !Number.isInteger(maxLength) || maxLength < 1 || maxLength > MAX_STRING_OPTION_LENGTH) {\n c.add('manifest.option.maxLength', `${path}/maxLength`, `A string option needs an integer \"maxLength\" from 1 to ${MAX_STRING_OPTION_LENGTH}.`);\n break;\n }\n if (typeof value.default !== 'string' || value.default.length > maxLength) {\n c.add('manifest.option.default', `${path}/default`, 'A string default is a string no longer than maxLength.');\n }\n break;\n }\n case 'select': {\n const choices = value.choices;\n const choicesOk =\n Array.isArray(choices) &&\n choices.length > 0 &&\n choices.length <= MAX_CHOICES &&\n choices.every(\n (choice) => isRecord(choice) && Object.keys(choice).length === 2 && isId(choice.value) && isText(choice.label),\n ) &&\n new Set(choices.map((choice) => choice.value)).size === choices.length;\n if (!choicesOk) {\n c.add('manifest.option.choices', `${path}/choices`, `choices is a list of 1 to ${MAX_CHOICES} distinct { value, label } pairs; a value follows the id rule.`);\n break;\n }\n if (!choices.some((choice) => choice.value === value.default)) {\n c.add('manifest.option.default', `${path}/default`, 'A select default is the value of one of its choices.');\n }\n break;\n }\n case 'color':\n if (typeof value.default !== 'string' || !COLOR_PATTERN.test(value.default)) {\n c.add('manifest.option.color', `${path}/default`, 'A color default is a 6 digit hex colour like \"#3366cc\".');\n }\n break;\n default:\n break;\n }\n}\n\n/** Runs only on a manifest whose slots and options are already valid. */\nfunction validateTranslations(\n c: Collector,\n value: unknown,\n slots: { id: string }[],\n options: { id: string; type: string; choices?: { value: string }[] }[],\n): void {\n const path = '/translations';\n if (!isRecord(value)) {\n c.add('manifest.translations.invalid', path, 'translations is an object keyed by language code.');\n return;\n }\n for (const [language, entry] of Object.entries(value)) {\n const langPath = `${path}/${language}`;\n if (!LANGUAGE_PATTERN.test(language)) {\n c.add('manifest.translations.language', langPath, `\"${language}\" is not a language code like \"fr\" or \"pt-BR\".`);\n continue;\n }\n if (!isRecord(entry)) {\n c.add('manifest.translations.invalid', langPath, 'A translation is an object with optional name, description, slots and options.');\n continue;\n }\n if (!c.checkKeys(entry, langPath, 'manifest.translations', [], TRANSLATION_KEYS)) continue;\n for (const key of ['name', 'description'] as const) {\n if (Object.hasOwn(entry, key) && !isTranslationText(entry[key])) {\n c.add('manifest.translations.text', `${langPath}/${key}`, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n if (Object.hasOwn(entry, 'slots')) {\n const entrySlots = entry.slots;\n if (!isRecord(entrySlots)) {\n c.add('manifest.translations.invalid', `${langPath}/slots`, 'slots is an object keyed by slot id.');\n } else {\n for (const [slotId, slotEntry] of Object.entries(entrySlots)) {\n const slotPath = `${langPath}/slots/${slotId}`;\n if (!slots.some((slot) => slot.id === slotId)) {\n c.add('manifest.translations.slot', slotPath, `\"${slotId}\" is not a slot id in this manifest.`);\n continue;\n }\n if (!isRecord(slotEntry)) {\n c.add('manifest.translations.invalid', slotPath, 'A slot translation is an object with optional label and description.');\n continue;\n }\n if (!c.checkKeys(slotEntry, slotPath, 'manifest.translations', [], SLOT_TRANSLATION_KEYS)) continue;\n for (const key of ['label', 'description'] as const) {\n if (Object.hasOwn(slotEntry, key) && !isTranslationText(slotEntry[key])) {\n c.add('manifest.translations.text', `${slotPath}/${key}`, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n }\n }\n }\n if (Object.hasOwn(entry, 'options')) {\n const entryOptions = entry.options;\n if (!isRecord(entryOptions)) {\n c.add('manifest.translations.invalid', `${langPath}/options`, 'options is an object keyed by option id.');\n } else {\n for (const [optionId, optionEntry] of Object.entries(entryOptions)) {\n const optionPath = `${langPath}/options/${optionId}`;\n const option = options.find((item) => item.id === optionId);\n if (!option) {\n c.add('manifest.translations.option', optionPath, `\"${optionId}\" is not an option id in this manifest.`);\n continue;\n }\n if (!isRecord(optionEntry)) {\n c.add('manifest.translations.invalid', optionPath, 'An option translation is an object with optional label, group and choices.');\n continue;\n }\n if (!c.checkKeys(optionEntry, optionPath, 'manifest.translations', [], OPTION_TRANSLATION_KEYS)) continue;\n for (const key of ['label', 'group'] as const) {\n if (Object.hasOwn(optionEntry, key) && !isTranslationText(optionEntry[key])) {\n c.add('manifest.translations.text', `${optionPath}/${key}`, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n if (!Object.hasOwn(optionEntry, 'choices')) continue;\n const choices = optionEntry.choices;\n if (!isRecord(choices)) {\n c.add('manifest.translations.invalid', `${optionPath}/choices`, 'choices is an object keyed by choice value.');\n continue;\n }\n for (const [choiceValue, label] of Object.entries(choices)) {\n const choicePath = `${optionPath}/choices/${choiceValue}`;\n if (option.type !== 'select' || !(option.choices ?? []).some((choice) => choice.value === choiceValue)) {\n c.add('manifest.translations.choice', choicePath, `\"${choiceValue}\" is not a choice of option \"${optionId}\".`);\n } else if (!isTranslationText(label)) {\n c.add('manifest.translations.text', choicePath, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n }\n }\n }\n }\n}\n\n/**\n * Validate a manifest value. Returns every problem found, first problem first.\n * An empty array means the manifest is valid.\n */\nexport function validateManifest(value: unknown): Problem[] {\n const c = new Collector();\n if (!isRecord(value)) {\n c.add('manifest.invalid', '', 'manifest.json must be a JSON object.');\n return c.problems;\n }\n\n // 1. schemaVersion\n const schemaVersion = value.schemaVersion;\n if (schemaVersion !== 1 && schemaVersion !== 2) {\n c.add('manifest.schemaVersion.unsupported', '/schemaVersion', 'schemaVersion must be 1 or 2.');\n return c.problems;\n }\n const v2 = schemaVersion === 2;\n\n // 2. unknown and missing top-level keys\n if (!c.checkKeys(value, '', 'manifest', V1_TOP_KEYS, v2 ? V2_TOP_EXTRA_KEYS : [], (key) => `manifest.${key}.required`)) {\n return c.problems;\n }\n\n // 3. id, name and the v2 labels (on v1 the keys were already refused above)\n if (!isId(value.id)) {\n c.add('manifest.id.invalid', '/id', 'id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters. It becomes the chart type \"custom:<id>\".');\n }\n if (!isText(value.name)) {\n c.add('manifest.name.invalid', '/name', `name is 1 to ${MAX_TEXT} characters.`);\n }\n if (Object.hasOwn(value, 'version') && (typeof value.version !== 'string' || !SEMVER_PATTERN.test(value.version))) {\n c.add('manifest.version.invalid', '/version', 'version is a semver string like \"1.2.0\".');\n }\n if (Object.hasOwn(value, 'description') && !isText(value.description, MAX_DESCRIPTION)) {\n c.add('manifest.description.invalid', '/description', `description is 1 to ${MAX_DESCRIPTION} characters.`);\n }\n\n // 4. slots\n const slots = value.slots;\n if (!Array.isArray(slots)) {\n c.add('manifest.slots.invalid', '/slots', 'slots must be an array.');\n } else if (slots.length === 0) {\n c.add('manifest.slots.empty', '/slots', 'Declare at least one slot.');\n } else if (slots.length > MAX_SLOTS) {\n c.add('manifest.slots.tooMany', '/slots', `Declare at most ${MAX_SLOTS} slots.`);\n } else {\n slots.forEach((slot, index) => validateSlot(c, slot, `/slots/${index}`, v2));\n const seen = new Set<string>();\n slots.forEach((slot, index) => {\n if (!isRecord(slot) || typeof slot.id !== 'string') return;\n if (seen.has(slot.id)) {\n c.add('manifest.slot.duplicate', `/slots/${index}/id`, `Slot id \"${slot.id}\" is used more than once.`);\n }\n seen.add(slot.id);\n });\n }\n\n // 5. options\n const options = value.options;\n if (!Array.isArray(options)) {\n c.add('manifest.options.invalid', '/options', 'options must be an array (it may be empty).');\n } else if (options.length > MAX_OPTIONS) {\n c.add('manifest.options.tooMany', '/options', `Declare at most ${MAX_OPTIONS} options.`);\n } else {\n options.forEach((option, index) => validateOption(c, option, `/options/${index}`, v2));\n const seen = new Set<string>();\n options.forEach((option, index) => {\n if (!isRecord(option) || typeof option.id !== 'string') return;\n if (seen.has(option.id)) {\n c.add('manifest.option.duplicate', `/options/${index}/id`, `Option id \"${option.id}\" is used more than once.`);\n }\n seen.add(option.id);\n });\n }\n\n // 6. translations, only on an otherwise clean manifest (v2 only; on v1 the key is unknown)\n if (c.problems.length === 0 && Object.hasOwn(value, 'translations')) {\n validateTranslations(\n c,\n value.translations,\n slots as { id: string }[],\n options as { id: string; type: string; choices?: { value: string }[] }[],\n );\n }\n\n // 7. size, only on an otherwise clean manifest\n if (c.problems.length === 0 && jsonBytes(value) > MAX_MANIFEST_BYTES) {\n c.add('manifest.size', '', `manifest.json must be at most ${MAX_MANIFEST_BYTES} bytes as JSON.`);\n }\n\n return c.problems;\n}\n\n/** Parse a manifest; returns null when validateManifest reports anything. */\nexport function parseManifest(value: unknown): CustomChartManifest | null {\n return validateManifest(value).length === 0 ? (value as CustomChartManifest) : null;\n}\n\n/** Zod view of manifest v2 for tool input schemas. Shape only: validateManifest owns the rules. */\nconst slotSchema = z.object({\n id: z.string(),\n label: z.string(),\n role: z.enum(['dimension', 'measure']),\n types: z.array(z.enum(['string', 'number', 'boolean'])),\n required: z.boolean(),\n description: z.string().optional(),\n});\n\nconst optionSchema = z.object({\n id: z.string(),\n label: z.string(),\n type: z.enum(['boolean', 'number', 'string', 'select', 'color']),\n default: z.union([z.string(), z.number(), z.boolean()]),\n min: z.number().optional(),\n max: z.number().optional(),\n step: z.number().optional(),\n maxLength: z.number().optional(),\n choices: z.array(z.object({ value: z.string(), label: z.string() })).optional(),\n group: z.string().optional(),\n});\n\nexport const manifestSchema = z.object({\n schemaVersion: z.union([z.literal(1), z.literal(2)]),\n id: z.string(),\n name: z.string(),\n version: z.string().optional(),\n description: z.string().optional(),\n slots: z.array(slotSchema),\n options: z.array(optionSchema),\n translations: z.record(z.unknown()).optional(),\n});\n\nexport type ManifestSlotInput = z.infer<typeof slotSchema>;\nexport type ManifestOptionInput = z.infer<typeof optionSchema>;\nexport { slotSchema as manifestSlotSchema, optionSchema as manifestOptionSchema };\n\n/** Deep key sort used by the content hash (brief section 5). Arrays keep their order. */\nexport function sortKeys<T>(value: T): T {\n if (Array.isArray(value)) return value.map((item) => sortKeys(item)) as T;\n if (isRecord(value)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value).sort()) out[key] = sortKeys(value[key]);\n return out as T;\n }\n return value;\n}\n","import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate';\n\n/**\n * dist/bundle.zip is for humans (drag and drop upload in the app). The API\n * takes JSON. The zip holds exactly the four build outputs at its root.\n */\nexport const BUNDLE_ZIP_FILES = ['index.js', 'manifest.json', 'icon.svg', 'index.css'] as const;\nexport type BundleZipFile = (typeof BUNDLE_ZIP_FILES)[number];\n\nexport type BundleZipInput = Partial<Record<BundleZipFile, string>> &\n Pick<Record<BundleZipFile, string>, 'index.js' | 'manifest.json' | 'icon.svg'>;\n\nexport function writeBundleZip(files: BundleZipInput): Uint8Array {\n const entries: Record<string, Uint8Array> = {};\n for (const name of BUNDLE_ZIP_FILES) {\n const content = files[name];\n if (typeof content === 'string') entries[name] = strToU8(content);\n }\n // Fixed mtime keeps the zip bytes stable for the same inputs.\n return zipSync(entries, { level: 6, mtime: new Date('2000-01-01T00:00:00Z') });\n}\n\nexport function readBundleZip(bytes: Uint8Array): Partial<Record<BundleZipFile, string>> {\n const unzipped = unzipSync(bytes, {\n filter: (file) => (BUNDLE_ZIP_FILES as readonly string[]).includes(file.name),\n });\n const files: Partial<Record<BundleZipFile, string>> = {};\n for (const name of BUNDLE_ZIP_FILES) {\n if (unzipped[name]) files[name] = strFromU8(unzipped[name]);\n }\n return files;\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","resolve","resolve","join","existsSync","readFileSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/config.ts"],"sourcesContent":["import { z, ZodError } from 'zod';\nimport { oauthConfigSchema, oauthHttpsUrl } from '../client/oauth-client-credentials.js';\n\n// PostHog project token for Databrain's own MCP usage analytics. This is a\n// public write-only key (like a web analytics site ID), safe to ship in the\n// package — it lets the CLI send telemetry out of the box, matching how\n// DATABRAIN_API_URL already ships with a working default.\nexport const DEFAULT_POSTHOG_API_KEY = 'phc_YgR4BO3sbtQAUvTfRUEN5xBzzPrJfrEG9STJEy3mNO2';\nexport const DEFAULT_POSTHOG_HOST = 'https://us.i.posthog.com';\n\nconst configSchema = z\n .object({\n serviceToken: z.string().min(1).optional(),\n apiToken: z.string().min(1).optional(),\n oauth: oauthConfigSchema.optional(),\n apiUrl: z.string().url().default('https://api.usedatabrain.com'),\n demoDomain: z.string().url().default('https://demo.usedatabrain.com'),\n posthogApiKey: z.string().min(1).optional(),\n posthogHost: z.string().url().optional(),\n })\n .refine((c) => c.serviceToken || c.apiToken || c.oauth, {\n message:\n 'Set DATABRAIN_SERVICE_TOKEN, DATABRAIN_API_TOKEN, or complete DATABRAIN_OAUTH_* client credentials',\n })\n .refine((c) => !c.oauth || oauthHttpsUrl.safeParse(c.apiUrl).success, {\n message: 'DATABRAIN_API_URL must use HTTPS without userinfo or fragments when OAuth is configured',\n });\n\nexport type Config = z.infer<typeof configSchema>;\n\nfunction formatConfigError(err: ZodError): string {\n const issues = err.issues.map((i) => i.path[0] === 'oauth'\n ? `Invalid or missing OAuth setting: ${i.path.join('.')}` : i.message);\n const lines = ['[databrain] Configuration error:'];\n for (const issue of issues) {\n lines.push(` - ${issue}`);\n }\n lines.push('');\n lines.push(' Set DATABRAIN_SERVICE_TOKEN (Settings > Service Tokens) — org-level token for setup and management.');\n lines.push(' Then add to your MCP config: { \"env\": { \"DATABRAIN_SERVICE_TOKEN\": \"...\" } }');\n lines.push(' Or configure DATABRAIN_OAUTH_ISSUER, DATABRAIN_OAUTH_CLIENT_ID and DATABRAIN_OAUTH_CLIENT_SECRET for service credentials. See specs/oauth-client-credentials.md.');\n lines.push('');\n lines.push(' Self-hosted? Also set DATABRAIN_API_URL to your instance\\'s API origin (e.g. https://api.your-databrain.example.com). Tokens minted on cloud do not work against a self-hosted instance and vice versa.');\n return lines.join('\\n');\n}\n\nexport function loadConfig(): Config {\n try {\n const oauthEnv = {\n issuer: process.env.DATABRAIN_OAUTH_ISSUER || undefined,\n clientId: process.env.DATABRAIN_OAUTH_CLIENT_ID || undefined,\n clientSecret: process.env.DATABRAIN_OAUTH_CLIENT_SECRET || undefined,\n resource: process.env.DATABRAIN_OAUTH_RESOURCE || undefined,\n scope: process.env.DATABRAIN_OAUTH_SCOPE || undefined,\n authMethod: process.env.DATABRAIN_OAUTH_AUTH_METHOD || undefined,\n };\n const oauthConfigured = Object.values(oauthEnv).some(value => value !== undefined);\n return configSchema.parse({\n serviceToken: process.env.DATABRAIN_SERVICE_TOKEN || undefined,\n apiToken: process.env.DATABRAIN_API_TOKEN || undefined,\n oauth: oauthConfigured ? {\n ...oauthEnv,\n resource: oauthEnv.resource || process.env.DATABRAIN_API_URL || 'https://api.usedatabrain.com',\n } : undefined,\n apiUrl:\n process.env.DATABRAIN_API_URL || 'https://api.usedatabrain.com',\n demoDomain:\n process.env.DATABRAIN_DEMO_DOMAIN || 'https://demo.usedatabrain.com',\n posthogApiKey: process.env.POSTHOG_PROJECT_API_KEY || DEFAULT_POSTHOG_API_KEY,\n posthogHost: process.env.POSTHOG_HOST || DEFAULT_POSTHOG_HOST,\n });\n } catch (err) {\n if (err instanceof ZodError) {\n process.stderr.write(formatConfigError(err) + '\\n');\n // Zod enum failures can contain the supplied value; index.ts serializes\n // startup errors. Do not expose any OAuth config values to that logger.\n throw new Error('Invalid Databrain configuration. Check the named environment settings.');\n }\n throw err;\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAS,GAAG,gBAAgB;AAOrB,IAAM,0BAA0B;AAChC,IAAM,uBAAuB;AAEpC,IAAM,eAAe,EAClB,OAAO;AAAA,EACN,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACzC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACrC,OAAO,kBAAkB,SAAS;AAAA,EAClC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,8BAA8B;AAAA,EAC/D,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,+BAA+B;AAAA,EACpE,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACzC,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO;AAAA,EACtD,SACE;AACJ,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,cAAc,UAAU,EAAE,MAAM,EAAE,SAAS;AAAA,EACpE,SAAS;AACX,CAAC;AAIH,SAAS,kBAAkB,KAAuB;AAChD,QAAM,SAAS,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,UAC/C,qCAAqC,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO;AACvE,QAAM,QAAQ,CAAC,kCAAkC;AACjD,aAAW,SAAS,QAAQ;AAC1B,UAAM,KAAK,OAAO,KAAK,EAAE;AAAA,EAC3B;AACA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,4GAAuG;AAClH,QAAM,KAAK,gFAAgF;AAC3F,QAAM,KAAK,oKAAoK;AAC/K,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,0MAA2M;AACtN,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,aAAqB;AACnC,MAAI;AACF,UAAM,WAAW;AAAA,MACf,QAAQ,QAAQ,IAAI,0BAA0B;AAAA,MAC9C,UAAU,QAAQ,IAAI,6BAA6B;AAAA,MACnD,cAAc,QAAQ,IAAI,iCAAiC;AAAA,MAC3D,UAAU,QAAQ,IAAI,4BAA4B;AAAA,MAClD,OAAO,QAAQ,IAAI,yBAAyB;AAAA,MAC5C,YAAY,QAAQ,IAAI,+BAA+B;AAAA,IACzD;AACA,UAAM,kBAAkB,OAAO,OAAO,QAAQ,EAAE,KAAK,WAAS,UAAU,MAAS;AACjF,WAAO,aAAa,MAAM;AAAA,MACxB,cAAc,QAAQ,IAAI,2BAA2B;AAAA,MACrD,UAAU,QAAQ,IAAI,uBAAuB;AAAA,MAC7C,OAAO,kBAAkB;AAAA,QACvB,GAAG;AAAA,QACH,UAAU,SAAS,YAAY,QAAQ,IAAI,qBAAqB;AAAA,MAClE,IAAI;AAAA,MACJ,QACE,QAAQ,IAAI,qBAAqB;AAAA,MACnC,YACE,QAAQ,IAAI,yBAAyB;AAAA,MACvC,eAAe,QAAQ,IAAI,2BAA2B;AAAA,MACtD,aAAa,QAAQ,IAAI,gBAAgB;AAAA,IAC3C,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,eAAe,UAAU;AAC3B,cAAQ,OAAO,MAAM,kBAAkB,GAAG,IAAI,IAAI;AAGlD,YAAM,IAAI,MAAM,wEAAwE;AAAA,IAC1F;AACA,UAAM;AAAA,EACR;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hosted/types.ts","../src/hosted/auth.ts"],"sourcesContent":["import type { RequestOptions } from '../client/types.js';\n\nexport const HOSTED_SCOPES = [\n 'mcp:dashboards:read', 'mcp:dashboards:write',\n 'mcp:datamarts:read', 'mcp:datamarts:write', 'mcp:embeds:write', 'mcp:query',\n 'mcp:custom-charts:read', 'mcp:custom-charts:write',\n] as const;\nexport type HostedScope = typeof HOSTED_SCOPES[number];\n\n/** A workspace this sign-in may act in, as the issuer resolved it for this request. */\nexport interface GrantedWorkspace {\n id: string;\n name?: string;\n}\n\n/** What the user approved at consent: a list of workspaces, or every workspace they can access. */\nexport type GrantWorkspaces = { mode: 'all' } | { mode: 'list'; ids: string[] };\n\n/** Only the trusted auth adapter may produce this context. Never populate it from MCP input. */\nexport interface HostedPrincipal {\n actorKind: 'user';\n credentialType: 'oauth' | 'pat';\n userId: string;\n companyId: string;\n /** Set only when exactly one workspace is granted: the default for every call. */\n workspaceId?: string;\n workspaceName?: string;\n /** The grant as approved. Absent from issuers that grant one workspace only. */\n workspaces?: GrantWorkspaces;\n /**\n * Live: the granted workspaces the user can still access, read on every request.\n * Absent only on a principal built before multi-workspace grants (then `workspaceId`).\n */\n grantedWorkspaces?: GrantedWorkspace[];\n /** OAuth application ID, NOT the tenant identifier called clientId by legacy tools. */\n clientId: string;\n resource: string;\n scopes: HostedScope[];\n /** Required for stored PATs. OAuth may delegate expiry to fresh provider validation. */\n expiresAt?: number;\n /** Explicit tenant allow-list resolved by the backend; absent means no tenant access. */\n tenantIds?: string[];\n}\n\nexport interface PatRecord {\n hash: string;\n revokedAt: number | null;\n principal: HostedPrincipal;\n}\n\nexport interface PatStore {\n findByHash(hash: string): Promise<PatRecord | undefined>;\n}\n\n/**\n * Opaque per-request handle from `HostedAdapter.delegate`. It carries nothing a tool,\n * log or serializer can read; only the adapter that made it can act with it.\n */\nexport interface HostedDelegation {\n readonly kind: 'hosted-delegation';\n}\n\n/** Production integration must recheck membership, policy and revocation on every call. */\nexport interface HostedAdapter {\n verifyOAuth(token: string, resource: string): Promise<HostedPrincipal>;\n resolvePatGrant(principal: HostedPrincipal): Promise<HostedPrincipal>;\n /**\n * Optional. Bind this request's verified OAuth bearer to an opaque handle, so\n * executeRequest can act as this user at the configured issuer, which re-verifies\n * the token and enforces workspace, object and role checks on every call.\n */\n delegate?(token: string, principal: HostedPrincipal): HostedDelegation;\n /**\n * Execute as this user/workspace with backend resource and tenant checks and auditing.\n * Receives NO incoming bearer or server service credential: only the opaque handle.\n * Do not impersonate a static service token.\n * Return the raw API result: the shared client performs its usual data unwrapping.\n * `workspace` is the granted workspace this tool call acts in, when one is known; the\n * adapter names it to the issuer and nowhere else.\n */\n executeRequest(options: RequestOptions, principal: HostedPrincipal,\n delegation?: HostedDelegation, workspace?: GrantedWorkspace): Promise<unknown>;\n}\n\nexport interface HostedConfig {\n publicUrl: string;\n issuer: string;\n port: number;\n bind: string;\n testOnly: boolean;\n}\n\n/** The backend's own status, code and message for a 4xx that is not an auth denial. */\nexport interface HostedErrorDetail { status: number; code?: string; message?: string }\n\nexport class HostedAuthError extends Error {\n constructor(public readonly status: 400 | 401 | 403 | 404 | 503, public readonly code: string,\n public readonly detail?: HostedErrorDetail) {\n super(code);\n }\n}\n","import { createHash, timingSafeEqual } from 'node:crypto';\nimport { open } from 'node:fs/promises';\nimport { constants } from 'node:fs';\nimport { z } from 'zod';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedPrincipal, PatRecord, PatStore } from './types.js';\n\nconst identity = z.string().min(1).max(512).regex(/^[^\\x00-\\x1f\\x7f]+$/);\nconst workspaceSchema = z.object({ id: identity, name: identity.optional() }).strip();\nexport const principalSchema = z.object({\n actorKind: z.literal('user'), credentialType: z.enum(['oauth', 'pat']),\n userId: identity, companyId: identity, workspaceId: identity.optional(), workspaceName: identity.optional(),\n workspaces: z.union([\n z.object({ mode: z.literal('all') }).strip(),\n z.object({ mode: z.literal('list'), ids: z.array(identity).min(1).max(100) }).strip(),\n ]).optional(),\n grantedWorkspaces: z.array(workspaceSchema).max(1000).optional(),\n // The issuer's own pick; recomputed below from grantedWorkspaces, never trusted on its own.\n defaultWorkspace: workspaceSchema.optional(),\n clientId: identity, resource: z.string().url(), scopes: z.array(z.enum(HOSTED_SCOPES)),\n expiresAt: z.number().finite().positive().optional(), tenantIds: z.array(identity).optional(),\n}).strict();\n\n/**\n * One shape for every issuer: the live granted list, and `workspaceId`/`workspaceName`\n * only when exactly one workspace is granted (the default for each call). An issuer\n * that grants one workspace sends only `workspaceId`; read that as a one-item list.\n * No workspace left, duplicate ids, or a `workspaceId` outside the list is refused.\n */\nfunction normalizeWorkspaces(value: z.infer<typeof principalSchema>): HostedPrincipal {\n const { defaultWorkspace: _default, workspaceId, workspaceName, grantedWorkspaces, ...rest } = value;\n const granted = grantedWorkspaces\n ?? (workspaceId ? [workspaceName === undefined ? { id: workspaceId } : { id: workspaceId, name: workspaceName }] : []);\n if (!granted.length || new Set(granted.map(item => item.id)).size !== granted.length ||\n (workspaceId !== undefined && !granted.some(item => item.id === workspaceId))) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n const only = granted.length === 1 ? granted[0] : undefined;\n return {\n ...rest, grantedWorkspaces: granted,\n ...(only ? { workspaceId: only.id } : {}), ...(only?.name !== undefined ? { workspaceName: only.name } : {}),\n };\n}\nconst recordSchema = z.object({\n hash: z.string().regex(/^[a-f0-9]{64}$/), revokedAt: z.number().finite().nullable(),\n principal: principalSchema,\n}).strict();\n\nexport function hashCredential(token: string): string {\n return createHash('sha256').update(token).digest('hex');\n}\n\n/** Reread on every request: an atomic file replacement revokes immediately, without restart. */\nexport class FilePatStore implements PatStore {\n constructor(private readonly path: string) {}\n async findByHash(hash: string): Promise<PatRecord | undefined> {\n const handle = await open(this.path, constants.O_RDONLY | constants.O_NOFOLLOW);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || (stat.mode & 0o077) !== 0 || stat.size > 1_048_576 ||\n (process.getuid && stat.uid !== process.getuid())) throw new Error('Invalid credential store');\n const records = z.array(recordSchema).max(1000).parse(JSON.parse(await handle.readFile('utf8')));\n const matches = records.filter(record => timingSafeEqual(Buffer.from(record.hash, 'hex'), Buffer.from(hash, 'hex')));\n // Duplicate records are ambiguous, including an active/revoked pair.\n if (matches.length > 1) throw new Error('Ambiguous credential store');\n return matches[0];\n } finally { await handle.close(); }\n }\n}\n\nfunction validatePrincipal(value: unknown, resource: string, kind: 'oauth' | 'pat'): HostedPrincipal {\n const parsed = principalSchema.safeParse(value);\n if (!parsed.success || parsed.data.resource !== resource || parsed.data.credentialType !== kind ||\n (parsed.data.expiresAt !== undefined && parsed.data.expiresAt <= Date.now() / 1000) ||\n (kind === 'pat' && parsed.success && parsed.data.expiresAt === undefined)) throw new HostedAuthError(401, 'invalid_token');\n return normalizeWorkspaces(parsed.data);\n}\n\nexport function createBearerVerifier(adapter: HostedAdapter, resource: string, pats?: PatStore) {\n return async (token: string): Promise<HostedPrincipal> => {\n try {\n if (!/^[A-Za-z0-9._~+\\/-]+=*$/.test(token) || token.length > 8192) throw new HostedAuthError(401, 'invalid_token');\n if (!token.startsWith('dbp_')) {\n return validatePrincipal(await adapter.verifyOAuth(token, resource), resource, 'oauth');\n }\n if (!/^dbp_[A-Za-z0-9_-]{32,}$/.test(token)) throw new HostedAuthError(401, 'invalid_token');\n const record = await pats?.findByHash(hashCredential(token));\n if (!record || record.revokedAt !== null || record.hash !== hashCredential(token)) throw new HostedAuthError(401, 'invalid_token');\n const stored = validatePrincipal(record.principal, resource, 'pat');\n const resolved = validatePrincipal(await adapter.resolvePatGrant(structuredClone(stored)), resource, 'pat');\n // Membership resolution may narrow a stored grant, never move or expand it.\n for (const key of ['userId', 'companyId', 'clientId', 'resource'] as const) {\n if (resolved[key] !== stored[key]) throw new HostedAuthError(401, 'invalid_token');\n }\n if (resolved.grantedWorkspaces!.some(item => !stored.grantedWorkspaces!.some(old => old.id === item.id))) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n if (resolved.expiresAt! > stored.expiresAt! || resolved.scopes.some(scope => !stored.scopes.includes(scope)) ||\n resolved.tenantIds?.some(id => !stored.tenantIds?.includes(id))) throw new HostedAuthError(401, 'invalid_token');\n return resolved;\n } catch (error) {\n if (error instanceof HostedAuthError) throw error;\n // No raw verifier, filesystem or remote errors can disclose credential values.\n throw new HostedAuthError(401, 'invalid_token');\n }\n };\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/apps/custom-chart-assets.ts","../src/apps/registry.ts","../src/apps/backend.ts","../src/apps/tile.ts"],"sourcesContent":["import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { DatabrainClient } from '../client/databrain-client.js';\nimport { CustomChartsApi } from '../api/custom-charts-api.js';\nimport { contentHash, validateBundle } from '../custom-chart/bundle.js';\nimport { READ_ONLY } from '../tools/annotations.js';\nimport { appMeta } from './feature.js';\nimport { bounded, MAX_CUSTOM_BUNDLE_CHARS, CUSTOM_BUNDLE_CHUNK_CHARS } from './contract.js';\nimport { readCustomChartBrowserRuntime } from '../custom-chart/assets.js';\n\n// Content-addressed immutable artifacts only. Authorization and the published pointer/hash\n// are fetched live before every cache lookup; no principal or API response is cached.\nconst assets = new Map<string, { text: string; expires: number }>();\n\n/** Chunk large code assets; every chunk rechecks the live published pointer and caller. */\nexport function registerCustomChartAssets(server: McpServer, client: DatabrainClient): void {\n const runtime = server.tool('load_custom_chart_runtime',\n 'Load the trusted app/embed sandbox adapter for a custom chart tile. App-only; no company data or chart code.',\n {}, READ_ONLY, async () => bounded({ content: [{ type: 'text' as const, text: 'Custom chart sandbox runtime.' }],\n _meta: { 'databrain/custom-chart-runtime': readCustomChartBrowserRuntime() } }));\n runtime.update({ _meta: appMeta(['app']) });\n const api = new CustomChartsApi(client);\n const tool = server.tool('load_custom_chart_bundle',\n 'Load one bounded chunk of the company published custom chart bundle for the tile. No draft version, credentials, or query rows.',\n { key: z.string().regex(/^[a-z][a-z0-9_-]{0,63}$/), offset: z.number().int().min(0).max(MAX_CUSTOM_BUNDLE_CHARS).default(0),\n contentHash: z.string().regex(/^[a-f0-9]{64}$/).optional() }, READ_ONLY,\n async ({ key, offset, contentHash: expected }) => {\n try {\n const detail = await api.get(key);\n if (detail.status !== 'published' || !detail.publishedVersionId) throw new Error('not published');\n const publishedHash = detail.versions?.find(version => version.id === detail.publishedVersionId)?.contentHash;\n const cached = publishedHash && /^[a-f0-9]{64}$/.test(publishedHash) ? assets.get(publishedHash) : undefined;\n let hash = publishedHash, asset = cached && cached.expires > Date.now() ? cached.text : undefined;\n if (asset === undefined) {\n const raw = await api.bundle(key, detail.publishedVersionId);\n const bundle = { ...raw, bundleCss: raw.bundleCss ?? undefined };\n if (validateBundle(bundle).length || bundle.manifest.id !== key) throw new Error('invalid bundle');\n hash = contentHash(bundle);\n if (publishedHash && publishedHash !== hash) throw new Error('published hash mismatch');\n // Only runtime artifacts cross the bridge; never any API envelope or extra fields.\n asset = JSON.stringify({ manifest: bundle.manifest, bundleJs: bundle.bundleJs, bundleCss: bundle.bundleCss ?? '', iconSvg: bundle.iconSvg });\n if (assets.size >= 4) assets.delete(assets.keys().next().value!);\n assets.set(hash, { text: asset, expires: Date.now() + 60_000 });\n }\n if (expected && hash !== expected) throw new Error('published version changed');\n if (offset > asset.length) throw new Error('offset');\n const chunk = asset.slice(offset, offset + CUSTOM_BUNDLE_CHUNK_CHARS);\n return bounded({ content: [{ type: 'text' as const, text: 'Published custom chart asset chunk.' }],\n structuredContent: { key, contentHash: hash, offset, chunk,\n nextOffset: offset + chunk.length < asset.length ? offset + chunk.length : null } });\n } catch {\n return { isError: true, content: [{ type: 'text' as const,\n text: `Could not load published custom:${key}. It may be unpublished, changed, or unavailable to this sign-in.` }] };\n }\n });\n tool.update({ _meta: appMeta(['app']) });\n}\n","import { registerCustomChartAssets } from './custom-chart-assets.js';\nimport { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { DatabrainClient } from '../client/databrain-client.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport { READ_ONLY } from '../tools/annotations.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { createChartBackend, type ChartBackend, type ChartAction } from './backend.js';\nimport { APP_MIME, CHART_URI, KPI_URI, dateRangeSchema, pageSchema, referenceSchema, viewSchema } from './contract.js';\nimport { appMeta, chartResult } from './feature.js';\nimport { tileHtml } from './tile.js';\nimport { draftSchema, draftPage, RENDER_TYPES, saveDraftAsDashboard, workspacesForDatamart } from './preview.js';\nimport { rowsSchema } from './contract.js';\nimport { WRITE_CREATE } from '../tools/annotations.js';\nimport { APPLY_TO_PRODUCTION_CONFIRMATION } from '../tools/widgets/production-guards.js';\n\nconst actions: Record<ChartAction, string> = {\n render_chart: 'Render one authorized chart page as SVG with point metadata. Hosted reads use the signed-in workspace; embed references keep guest scope.',\n refresh_widget: 'Refresh a chart in the current workspace or guest scope. Revalidates access before returning data.',\n drill_down: 'Explore a server-issued drill identifier within the current chart and guest scope.',\n change_date_range: 'Render a chart for an ordered date range within the current workspace or guest scope.',\n};\n\n/** Call after registerAllTools so existing registration policy/instrumentation remains active. */\nexport function registerChartApps(server: McpServer, client: DatabrainClient, backend: ChartBackend = createChartBackend(client)): void {\n for (const [name, description] of Object.entries(actions)) {\n const action = name as ChartAction;\n const tool = server.tool(name, description, {\n reference: referenceSchema, view: viewSchema.default({}),\n context: z.string().max(200).optional().describe('Chart interaction context'),\n }, READ_ONLY, async ({ reference, view }) => {\n try {\n if ((action === 'drill_down' && !view.drillId) || (action === 'change_date_range' && !view.dateRange)) throw new Error('Missing view selector');\n const page = pageSchema.parse(await toolContext.run({ tool: name, category: 'embedding', workflowIntent: 'chart_app', inputMode: 'app' },\n () => backend.execute(action, reference, view)));\n // SVG is rendered as an isolated image, never inserted into the HTML DOM.\n // Reject active/external/animated constructs as a further contract check.\n if (page.svg && (!/^\\s*<svg\\b/i.test(page.svg) ||\n /<\\s*(?:script|foreignObject|iframe|image|animate\\w*|set)\\b|<!DOCTYPE|<!ENTITY|\\bon\\w+\\s*=|@(?:import|keyframes)|\\banimation\\s*:|(?:href\\s*=\\s*[\"'](?!#)|url\\s*\\(\\s*[\"']?(?!#))/i.test(page.svg))) {\n throw new Error('Unsafe SVG');\n }\n return chartResult(`${page.kind === 'kpi' ? 'KPI' : 'Chart'} ready. ${page.rows.length} rows in this page.${page.fallback ? ' ' + page.fallback : ''}${page.description ? ' ' + page.description : ''}`, {\n ...page, reference, view,\n });\n } catch (error) {\n if (error instanceof DatabrainApiError) return error.toToolResponse();\n return { isError: true, content: [{ type: 'text' as const,\n text: 'Chart unavailable. Check dashboard access and the chart renderer integration, then retry.' }] };\n }\n });\n tool.update({ _meta: appMeta(['app']) });\n }\n registerMetricTile(server, client, backend);\n registerDraftTools(server, client);\n registerCustomChartAssets(server, client);\n for (const [uri, kind] of [[CHART_URI, 'chart'], [KPI_URI, 'kpi']] as const) {\n server.resource(`Databrain ${kind} tile`, uri, { mimeType: APP_MIME, description: `Interactive ${kind} using host theme and scoped server tools` }, async () => ({\n contents: [{ uri, mimeType: APP_MIME, text: tileHtml(kind), _meta: { ui: { prefersBorder: true, csp: { resourceDomains: [\"data:\", \"blob:\"], frameDomains: [\"blob:\"], connectDomains: [] } } } }],\n }));\n }\n}\n\n/** The tile's controls over an unsaved answer. `preview_chart` is the \"show as\" row and\n * is app-only: the model already has ask_question. `save_chart_as_dashboard` is the commit,\n * visible to both, and gated the way every production write here is gated. */\nfunction registerDraftTools(server: McpServer, client: DatabrainClient): void {\n const preview = server.tool('preview_chart',\n 'Redraw an unsaved ask_question answer as another chart type, from the rows already on screen. No query runs.',\n { draft: draftSchema, rows: rowsSchema, chartType: z.enum(RENDER_TYPES),\n context: z.string().max(200).optional().describe('Chart interaction context') },\n READ_ONLY, async ({ draft, rows, chartType }) => {\n try {\n const page = await draftPage(client, draft, rows, chartType);\n return chartResult(`${page.kind === 'table' ? 'Table' : 'Chart'} ready as ${chartType}.`, page);\n } catch {\n return { isError: true, content: [{ type: 'text' as const, text: 'Chart preview unavailable. Ask the question again to retry.' }] };\n }\n });\n preview.update({ _meta: appMeta(['app']) });\n\n const save = server.tool('save_chart_as_dashboard',\n 'Save an ask_question answer: creates a workspace metric from its SQL and a new dashboard holding it. ' +\n `Production write; requires confirm: \"${APPLY_TO_PRODUCTION_CONFIRMATION}\". ` +\n 'A workspaceName is needed when the datamart backs more than one workspace.',\n { draft: draftSchema, chartType: z.enum(RENDER_TYPES),\n dashboardTitle: z.string().trim().min(1).max(200),\n workspaceName: z.string().trim().min(1).max(256).optional(),\n attemptId: z.string().trim().min(1).max(64).optional()\n .describe('Identifies one save attempt. Reuse it to retry safely; omit or change it to save again on purpose.'),\n confirm: z.string().optional(),\n context: z.string().max(200).optional().describe('Chart interaction context') },\n WRITE_CREATE, async ({ draft, chartType, dashboardTitle, workspaceName, attemptId, confirm }) => {\n if (confirm !== APPLY_TO_PRODUCTION_CONFIRMATION) {\n return { isError: true, content: [{ type: 'text' as const,\n text: `Saving writes to production. Retry with confirm: \"${APPLY_TO_PRODUCTION_CONFIRMATION}\".` }] };\n }\n // The draft's workspace list is a hint for the tile. The caller wrote it, so the\n // set a save may target is recomputed here from the datamart, on the server's own credential.\n const choices = await workspacesForDatamart(client, draft.datamartName, 'authorize');\n const refuse = (text: string) => ({ isError: true, content: [{ type: 'text' as const, text }],\n structuredContent: { kind: 'save', ok: false, choices } });\n // A named workspace must be one the draft's datamart can serve, spelled the draft's way.\n const target = workspaceName\n ? choices.find(w => w.trim().toLowerCase() === workspaceName.toLowerCase())\n : choices.length === 1 ? choices[0] : undefined;\n if (workspaceName && !target) {\n return refuse(`Workspace \"${workspaceName}\" cannot run this datamart's SQL. Choose one of: ${choices.join(', ')}.`);\n }\n if (!target) {\n return refuse(choices.length\n ? `Choose a workspace: ${choices.join(', ')}.`\n : `No workspace is built on datamart \"${draft.datamartName}\". Create one in Databrain, then save again.`);\n }\n try {\n const saved = await saveDraftAsDashboard(client, draft, { chartType, dashboardTitle, workspaceName: target, attemptId });\n return chartResult(`Saved \"${saved.title}\" to workspace ${saved.workspaceName} (dashboard ${saved.dashboardId}, metric ${saved.metricId}).`,\n { kind: 'save', ok: true, ...saved, draft: { ...draft, chartType } });\n } catch (error) {\n const code = error instanceof DatabrainApiError ? error.code : 'SAVE_FAILED';\n return { isError: true, content: [{ type: 'text' as const,\n text: `Could not save (${code}). Check the workspace name and your access, then try again.` }],\n structuredContent: { kind: 'save', ok: false, code } };\n }\n });\n save.update({ _meta: appMeta(['model', 'app']) });\n}\n\n/** Model entry point for stored metrics; controls reuse the exact same reference and grant. */\nfunction registerMetricTile(server: McpServer, client: DatabrainClient, backend: ChartBackend): void {\n const tool = server.tool('render_metric_tile',\n 'Render an existing workspace metric without an embed, data app or datamart. Use metricRef from get_dashboard. ' +\n 'Client-scoped metrics require their saved clientId. KPI shows the saved scalar, or the sum of one numeric chart series.',\n { workspaceName: z.string().min(1).optional(), workspaceId: z.string().min(1).optional(),\n metricId: z.string().trim().min(1).max(256), dashboardId: z.string().trim().min(1).max(256).optional(),\n display: z.enum(['chart', 'kpi']).default('chart'), clientId: z.string().trim().min(1).max(256).optional(),\n dateRange: dateRangeSchema.optional(), context: z.string().max(200).optional() },\n READ_ONLY, async ({ workspaceName, workspaceId, metricId, dashboardId, display, clientId, dateRange }) => {\n try {\n if (!client.isHosted()) throw new Error('Use the hosted sign-in for workspace metrics. API-key connections retain list_widgets with an embed and clientId.');\n const resolved = await client.request<{ dashboardId: string; clientId: string | null }>({\n method: 'POST', path: '/api/v2/workspace/dashboard/widgets', auth: 'service',\n body: { workspaceName, workspaceId, metricId, ...(dashboardId ? { dashboardId } : {}) },\n });\n if (resolved.clientId && clientId !== resolved.clientId) {\n throw new Error(`CLIENT_REQUIRED: Pass clientId ${JSON.stringify(resolved.clientId)}, saved on this dashboard/workspace. No unscoped fallback is allowed.`);\n }\n const reference = { dashboardId: resolved.dashboardId, widgetId: metricId, display,\n ...(clientId ? { clientId } : {}) };\n const view = dateRange ? { dateRange } : {};\n const page = pageSchema.parse(await backend.execute('render_chart', reference, view));\n if (page.svg && (!/^\\s*<svg\\b/i.test(page.svg) ||\n /<\\s*(?:script|foreignObject|iframe|image|animate\\w*|set)\\b|<!DOCTYPE|<!ENTITY|\\bon\\w+\\s*=|@(?:import|keyframes)|\\banimation\\s*:|(?:href\\s*=\\s*[\"'](?!#)|url\\s*\\(\\s*[\"']?(?!#))/i.test(page.svg))) throw new Error('Unsafe SVG');\n return chartResult(`${page.kind === 'kpi' ? 'KPI' : 'Chart'} ready. ${page.rows.length} rows in this page.${page.description ? ' ' + page.description : ''}`, { ...page, reference, view });\n } catch (error) {\n if (error instanceof DatabrainApiError) return error.toToolResponse();\n return { isError: true, content: [{ type: 'text' as const, text: (error as Error).message.startsWith('CLIENT_REQUIRED:') || (error as Error).message.startsWith('Use the hosted sign-in')\n ? (error as Error).message : 'Chart unavailable. Check metric access and the saved tenant settings, then retry.' }] };\n }\n });\n tool.update({ _meta: appMeta(['model', 'app']) });\n}\n","import { DatabrainClient } from '../client/databrain-client.js';\nimport type { RequestOptions } from '../client/types.js';\nimport { createServiceBundle } from '../services/factory.js';\nimport { pageSchema, referenceSchema, viewSchema, type ChartPage, type ChartReference, type ChartView } from './contract.js';\n\nexport const RENDER_PATH = '/api/v2/dashboard/mcp-chart';\nexport type ChartAction = 'render_chart' | 'refresh_widget' | 'drill_down' | 'change_date_range';\nexport interface ChartBackend {\n execute(action: ChartAction, reference: ChartReference, view: ChartView): Promise<ChartPage>;\n}\n\nclass UncachedChartClient extends DatabrainClient {\n constructor(private readonly delegate: DatabrainClient) {\n super({ apiUrl: delegate.getApiUrl(), demoDomain: delegate.getDemoDomain() });\n }\n override request<T>(options: RequestOptions): Promise<T> {\n return this.delegate.request<T>({ ...options, cacheKey: undefined, cacheTtlMs: undefined });\n }\n}\n\n/** Proposed backend route; no local renderer, ambient service fallback, or UI credentials. */\nexport function createChartBackend(client: DatabrainClient): ChartBackend {\n // The general embed lookup caches for five minutes. Authorization must not.\n const services = createServiceBundle(new UncachedChartClient(client));\n return {\n async execute(action, rawReference, rawView) {\n const reference = referenceSchema.parse(rawReference);\n const view = viewSchema.parse(rawView);\n if ('dashboardId' in reference) {\n if (!client.isHosted()) throw new Error('Hosted sign-in required');\n return pageSchema.parse(await client.request<unknown>({\n method: 'POST', path: '/api/v2/workspace/dashboard/chart', auth: 'service',\n body: { action, dashboardId: reference.dashboardId, widgetId: reference.widgetId,\n ...(reference.clientId ? { clientId: reference.clientId } : {}),\n ...(reference.display ? { display: reference.display } : {}), ...view,\n pageSize: 50, width: 600, height: 320, animation: false },\n skipDataUnwrap: true,\n }));\n }\n // Every page/refresh/drill re-reads the embed live, so a revoked tenant stops working at once.\n const app = await services.widgetService.resolveDataAppName(reference.dataAppName, reference.embedId);\n if (!app.ok) throw new Error('Chart scope unavailable');\n const scope = await services.widgetService.ensureReadableDashboardScope({\n embedId: reference.embedId, clientId: reference.clientId, dataAppName: app.value });\n if (!scope.ok) throw new Error('Chart scope denied');\n const { token } = await services.guestTokenService.generateToken({\n clientId: reference.clientId, dataAppName: app.value, allowedEmbeds: [reference.embedId],\n permissions: { isDisableMetricCreation: true, isEnableManageMetrics: false,\n isEnableMetricUpdation: false, isEnableArchiveMetrics: false },\n });\n if (!token) throw new Error('Guest scope unavailable');\n const page = await client.request<unknown>({\n method: 'POST', path: RENDER_PATH, auth: 'none', token,\n body: { action, embedId: reference.embedId, widgetId: reference.widgetId, ...view,\n pageSize: 50, width: 600, height: 320, animation: false },\n skipDataUnwrap: true,\n });\n return pageSchema.parse(page);\n },\n };\n}\n","import { transformSync } from 'esbuild';\nimport { MAX_CUSTOM_BUNDLE_CHARS, CUSTOM_BUNDLE_CHUNK_CHARS } from './contract.js';\n\n/** No iframe imports. All requests use the host bridge. */\nexport const TILE_SCRIPT = String.raw`(()=>{\nconst $=id=>document.getElementById(id),root=document.documentElement,pending=new Map();\nlet seq=0,origin='*',ready=false,early=[],closed=false,ref=null,view={},next=null,context={},busy=false,revision=0,rawSvg=null,dialect=null,shown=false,draft=null,rows=[],saved=null,attempt='',workspace=null,custom=null,customMode=false;\n/* One lifecycle, two sets of names. Hosts ship either or both, so notifications go out\n under both and arrive under either. Same order in both rows: result, input, context. */\nconst DASH=['ui/notifications/tool-result','ui/notifications/tool-input','ui/notifications/host-context-changed'],\nSNAKE=['notifications/tools/call/result','notifications/tools/call/input','notifications/host_context/changed'],\nINIT=['ui/notifications/initialized','notifications/initialized'],\nSIZE=['ui/notifications/size-changed','notifications/size_changed'],\nDISPLAY=['ui/request-display-mode','requestDisplayMode'];\nfunction send(message){if(!closed)parent.postMessage({jsonrpc:'2.0',...message},origin)}\nfunction notify(names,params){for(const method of names)send({method,params})}\nfunction rpc(method,params){return new Promise((resolve,reject)=>{const id=++seq;\nconst timer=setTimeout(()=>{pending.delete(id);reject(Error('timeout'))},65000);\npending.set(id,{resolve,reject,timer});send({id,method,params})})}\n/* Try each alias while the host says it never heard of it; any other error stands. */\nasync function ask(names,params){let last;\nfor(const method of names){try{return await rpc(method,params)}catch(error){last=error;if(error.code!==-32601)throw error}}\nthrow last}\n/* Fixed codes only; never log customer rows. */\nfunction trouble(code){send({method:'notifications/error',params:{code}})}\n/* One formatter for rail, table, KPI and answer line. Compact past 10K, grouped past 100,\n two decimals below; null is an absence, so it prints as a dash and never as 0. */\nfunction fmt(v){if(typeof v!=='number')return v==null||v===''?'\\u2014':String(v);const a=Math.abs(v);\nreturn new Intl.NumberFormat(navigator.language||'en',a>=1e4?{notation:'compact',maximumFractionDigits:1}:a>=100?{maximumFractionDigits:0}:{maximumFractionDigits:2}).format(v)}\nfunction exact(el,v){el.title=typeof v==='number'?String(v):''}\n/* The answer line: what this shows, and the headline value. Gated on carrying a number or\n a count, so it never renders as a second title or an empty reserved slot. */\nfunction answer(p){if(p.description)return p.description;const bits=[];\nif(p.kind==='dashboard'){const n=p.widgets?.length||0;return n?n+(n===1?' chart':' charts')+' on this dashboard':''}\nconst [dim,measure]=p.columns||[];const pts=(p.points||[]).filter(q=>typeof q.value==='number');\nif(pts.length<2)return '';if(dim&&measure&&!p.draft)bits.push(measure+' by '+dim);\nconst top=pts.reduce((a,b)=>b.value>a.value?b:a);bits.push(top.label.split(' \\u2014 ')[0]+' highest at '+fmt(top.value));return bits.join(' \\u00b7 ')}\nfunction status(text,bad,ok){const s=$('status');s.textContent=text;s.toggleAttribute('data-error',!!bad);s.toggleAttribute('data-ok',!!ok);if(bad)$('plot').dataset.state='off'}\nfunction paint(){const img=$('chart-image');if(!img||!rawSvg)return;\nconst c=getComputedStyle(document.body).color,mix=pc=>'color-mix(in srgb,'+c+' '+pc+'%,transparent)';\nimg.src='data:image/svg+xml,'+encodeURIComponent(rawSvg.replace(/<\\/svg>\\s*$/i,'<style>text{fill:'+mix(72)+'!important}'\n+'[stroke=\"#dbdee4\"],[stroke=\"#f1f1f2\" i]{stroke:'+mix(13)+'!important}[stroke=\"#54555a\"],[stroke=\"#d4d4d8\" i]{stroke:'+mix(30)+'!important}'\n+'</style></svg>'))}\nfunction controls(){document.querySelectorAll('button,input,select').forEach(e=>{e.disabled=busy});\n$('refresh').hidden=!ref;$('next').hidden=!next;$('range').hidden=!ref||customMode;$('plot').toggleAttribute('data-busy',busy);\n$('save').hidden=!draft||!!saved||!$('form').hidden;\n$('expand').hidden=!shown||root.dataset.kind==='dashboard'||!context.availableDisplayModes?.includes('fullscreen');\nroot.toggleAttribute('data-deep',context.displayMode==='fullscreen'||$('expand').hidden)}\nfunction host(change){context={...context,...change};if(change.theme==='dark'||change.theme==='light')root.style.colorScheme=change.theme;\nconst vars=change.styles?.variables||{};for(const [key,value] of Object.entries(vars)){\nif(/^--(?:color-|font-|border-|shadow-)/.test(key)&&typeof value==='string'&&!/url\\s*\\(|@import/i.test(value))root.style.setProperty(key,value)}\nconst face=change.styles?.css?.fonts;if(typeof face==='string'&&face.length<4000&&!/@import|javascript:/i.test(face))$('hostfont').textContent=face;\nconst insets=change.safeAreaInsets;if(insets)for(const edge of ['top','right','bottom','left']){\nconst n=insets[edge];if(Number.isFinite(n))root.style.setProperty('--safe-'+edge,Math.max(0,Math.min(n,200))+'px')}\nroot.dataset.mode=context.displayMode==='fullscreen'?'full':'inline';\n$('expand').textContent=context.displayMode==='fullscreen'?'Back to conversation':'Expand';paint();custom?.update();controls()}\nfunction scope(p,n){const bits=[];\nif(p.kind==='chart'||p.kind==='table')bits.push(p.rowsTotal>n?'first '+n+' of '+p.rowsTotal+' rows':n?(n===1?'1 row':n+' rows'):'No rows in this view');\nif(view.dateRange)bits.push(view.dateRange.from+' to '+view.dateRange.to);\nif(next)bits.push('more pages available');\nif(p.customChart&&n)bits.push('calculations use this page');\n$('scope').textContent=bits.join(' · ')}\nfunction resize(){if(ready)notify(SIZE,{width:document.body.scrollWidth,height:document.body.scrollHeight})}\nfunction table(rows,columns){$('rows').replaceChildren();$('data').hidden=!rows?.length;if(!rows?.length)return;\n$('count').textContent=rows.length===1?'1 row behind this':rows.length+' rows behind this';\nconst t=document.createElement('table');\nconst num=name=>rows.some(r=>typeof r[name]==='number');\nconst header=document.createElement('tr');for(const name of columns){const th=document.createElement('th');th.scope='col';th.textContent=name;if(num(name))th.className='n';header.append(th)}\nconst head=document.createElement('thead');head.append(header);t.append(head);const body=document.createElement('tbody');\nfor(const row of rows.slice(0,50)){const tr=document.createElement('tr');for(const key of columns){const td=document.createElement('td');td.textContent=fmt(row[key]);exact(td,row[key]);if(num(key))td.className='n';tr.append(td)}body.append(tr)}t.append(body);$('rows').append(t)}\nasync function display(mode){try{const answer=await ask(DISPLAY,{mode});\nhost({displayMode:answer.mode||context.displayMode});if(root.dataset.mode==='full'&&!$('data').hidden)$('data').open=true;resize()}\ncatch{status('Expand is unavailable in this host.',1)}}\nfunction wait(){busy=true;if(!$('plot').firstChild)$('plot').dataset.state='wait';status('Loading chart…');controls()}\nasync function call(name,extra={}){if(!ready||!ref||busy)return;run(name,{reference:ref,view:{...view,...extra}})}\n/* The draft tools take the draft and the rows on screen instead of a reference. */\nasync function run(name,args){if(!ready||busy)return;wait();const rev=++revision;\nconst selection=workspace?(name==='save_chart_as_dashboard'?{workspaceName:args.workspaceName||workspace.name}:{workspace:workspace.id}):{};\ntry{const result=await rpc('tools/call',{name,arguments:{...args,...selection,context:'tile'}});\nif(rev===revision)render(result)}catch{if(rev===revision){trouble('tool-call-failed');if(name==='save_chart_as_dashboard'){$('plot').dataset.state='ready';status('Could not save. Try again.',1)}else if(name==='preview_chart'){status('Could not draw that. Pick another type.',1);$('plot').dataset.state='ready'}else stale()}}finally{busy=false;controls()}}\nfunction stale(){if(shown&&$('plot').firstChild){status('Could not refresh. Showing the last chart that loaded. Select Refresh to try again.',1);$('plot').dataset.state='stale'}else status('Could not load the chart. Select Refresh to try again.',1)}\nfunction render(result){busy=false;controls();\nif(result?._meta?.['databrain/workspace'])workspace=result._meta['databrain/workspace'];\nconst sp=result?.structuredContent;if(sp&&sp.kind==='save'){$('plot').dataset.state='ready';if(sp.ok){saved=sp;$('form').hidden=true;status('Saved \"'+sp.title+'\" to workspace '+sp.workspaceName+'.',0,1)}\nelse if(sp.choices?.length){choose(sp.choices);status('Choose a workspace, then save again.',1)}\nelse status((result.content?.[0]?.text)||'Could not save. Try again.',1);controls();resize();return}\nif(result?.isError&&draft&&$('form').hidden===false){$('plot').dataset.state='ready';status((result.content?.[0]?.text)||'Could not save. Try again.',1);controls();return}\nif(result?.isError){trouble('tool-returned-error');\nif(shown&&$('plot').firstChild){stale();controls();return}shown=false;controls();\nstatus(ref?'Chart unavailable. Check your access to this dashboard, then Refresh.'\n:'Chart unavailable. Ask again to retry.',1);return}\nconst p=result?.structuredContent;if(!p||typeof p!=='object'){shown=false;controls();status('No chart data came back for this result.',1);return}\nrevision++;custom?.dispose();custom=null;customMode=!!p.customChart;shown=true;rawSvg=null;$('plot').replaceChildren();$('plot').dataset.state='ready';$('choices').replaceChildren();delete $('rail').dataset.value;\nref=p.reference||null;view=p.view||{};next=p.nextCursor||null;draft=p.draft||null;rows=p.rows||[];saved=null;$('form').hidden=true;\n$('title').textContent=p.title||'Analytics';$('description').textContent=answer(p);root.dataset.kind=p.kind;$('choices').toggleAttribute('data-grid',p.kind==='dashboard');\nif(p.kind==='dashboard'){$('plot').dataset.state='off';\nfor(const item of p.widgets||[]){const b=document.createElement('button');b.textContent=item.name||'Untitled widget';b.onclick=()=>{ref=item.reference;view={};call('render_chart')};$('choices').append(b)}\nstatus(p.widgets?.length?'':'No charts on this dashboard yet. Add one in Databrain.');\n}else if(p.kind==='kpi'){\nconst number=document.createElement('p');number.className='kpi';number.textContent=fmt(p.value);exact(number,p.value);$('plot').append(number);status('');\n}else if(p.customChart){\nif(!rows.length){$('plot').dataset.state='off';status('No rows in this view.');}\nelse{status('Loading custom chart…');const rev=revision;\nconst box=document.createElement('div');box.style.width='100%';$('plot').append(box);\nloadCustom(result,p,box,rev);}\n}else if(p.svg){\nconst box=document.createElement('div');box.className='chart';box.style.aspectRatio=p.width+'/'+p.height;\nconst img=document.createElement('img');img.id='chart-image';rawSvg=p.svg;img.alt=p.title||'Chart';img.onerror=()=>{status('The chart could not be drawn. The rows are below.',1);$('data').open=$('data').toggleAttribute('data-only',true)};box.append(img);\nfor(const point of p.points||[]){const b=document.createElement('button');b.className='point';b.setAttribute('aria-label',point.label+': '+fmt(point.value));b.setAttribute('aria-pressed','false');\nb.style.left=100*point.x/p.width+'%';b.style.top=100*point.y/p.height+'%';b.style.width=100*point.width/p.width+'%';b.style.height=100*point.height/p.height+'%';\nconst hover=()=>{$('vlabel').textContent=point.label;$('vnum').textContent=fmt(point.value);exact($('vnum'),point.value);$('rail').dataset.value='1'};b.onmouseenter=hover;b.onfocus=hover;\nb.onclick=()=>{box.querySelectorAll('.point').forEach(e=>e.setAttribute('aria-pressed','false'));b.setAttribute('aria-pressed','true');hover();\n$('choices').replaceChildren();if(point.drillId){const drill=document.createElement('button');drill.className='primary';drill.textContent='Explore selected data';drill.onclick=()=>call('drill_down',{drillId:point.drillId,cursor:undefined});$('choices').append(drill)}};box.append(b)}$('plot').append(box);\nstatus('');\n}else if(p.kind==='table'){\n// ask_question returns a table by design; calling that a missing chart called success a failure.\n$('plot').dataset.state='off';status('');\n}else{$('plot').dataset.state='off';\nstatus('Nothing to show. Try a wider date range, then Refresh.',1)}\nif(draft&&p.previewTypes?.length){for(const t of p.previewTypes){const b=document.createElement('button');b.textContent=t[0].toUpperCase()+t.slice(1);b.className='chip';b.setAttribute('aria-pressed',String(t===draft.chartType));\nb.onclick=()=>run('preview_chart',{draft,rows,chartType:t});$('choices').append(b)}$('choices').dataset.chips='1'}else delete $('choices').dataset.chips;\nif(p.previewError)status('Cannot draw this as '+(p.draft?.chartType||'a chart')+' ('+p.previewError+'). Showing the rows.',1);\npaint();table(p.rows||[],p.columns||Object.keys(p.rows?.[0]||{}));scope(p,p.rows?.length||0);\nconst only=!p.svg&&!p.customChart&&p.value===undefined&&p.kind!=='dashboard';$('data').open=only;$('data').toggleAttribute('data-only',only);controls();resize()}\nasync function loadCustom(result,p,box,rev){try{\nif(!globalThis.DbnCustomChartRuntime){\nconst runtime=await rpc('tools/call',{name:'load_custom_chart_runtime',arguments:{context:'tile'}});\nif(rev!==revision)return;\nconst code=runtime._meta?.['databrain/custom-chart-runtime'];\nif(runtime.isError||typeof code!=='string'||code.length>64000)throw Error('runtime unavailable');\nconst script=document.createElement('script');script.textContent=code;document.head.append(script);script.remove();\nif(!globalThis.DbnCustomChartRuntime)throw Error('runtime unavailable');\n}\nlet bundle=result._meta?.['databrain/custom-chart-bundle'];\nif(!bundle){let text='',offset=0,hash;\nfor(let chunks=0;chunks<${Math.ceil(MAX_CUSTOM_BUNDLE_CHARS / CUSTOM_BUNDLE_CHUNK_CHARS)};chunks++){\nconst r=await rpc('tools/call',{name:'load_custom_chart_bundle',arguments:{context:'tile',key:p.customChart.key,offset,...(hash?{contentHash:hash}:{})}});\nif(rev!==revision)return;\nif(r.isError)throw Error('unavailable');const part=r.structuredContent;\nif(!part||part.key!==p.customChart.key||part.offset!==offset||typeof part.chunk!=='string'||part.chunk.length>${CUSTOM_BUNDLE_CHUNK_CHARS}||!part.chunk.length)throw Error('invalid chunk');\nif(hash&&hash!==part.contentHash)throw Error('changed');hash=part.contentHash;text+=part.chunk;\nif(text.length>${MAX_CUSTOM_BUNDLE_CHARS})throw Error('too large');if(part.nextOffset===null){bundle=JSON.parse(text);break}\nif(part.nextOffset!==offset+part.chunk.length)throw Error('invalid offset');offset=part.nextOffset;\n}\n}\nif(rev!==revision)return;if(!bundle||bundle.manifest.id!==p.customChart.key)throw Error('invalid bundle');\ncustom=DbnCustomChartRuntime.mount(box,bundle,p.customChart,p.rows||[],message=>{if(rev!==revision)return;status(message,!!message);if(message){$('data').open=true;$('data').setAttribute('data-only','')}resize()});\n}catch{if(rev===revision){status(result._meta?.['databrain/custom-chart-bundle']?'Draft preview could not open. Check the bindings and sandbox support. The rows are below.':'Published custom chart unavailable, or this host cannot open its sandbox. The rows are below.',1);$('data').open=true;$('data').setAttribute('data-only','');resize()}}}\n$('refresh').onclick=()=>call('refresh_widget',{cursor:undefined});$('next').onclick=()=>call('render_chart',{cursor:next});\nfunction choose(names){const w=$('ws');w.replaceChildren();for(const n of names){const o=document.createElement('option');o.value=n;o.textContent=n;w.append(o)}$('wsrow').hidden=names.length<2;$('wsone').textContent=names.length===1?'in workspace '+names[0]:'';$('form').hidden=false;$('dtitle').focus();controls();resize()}\n$('save').onclick=()=>{attempt=crypto.randomUUID();$('dtitle').value=draft?.title||'';choose(draft?.workspaces||[])};\n$('cancel').onclick=()=>{$('form').hidden=true;status('');controls();resize()};\n$('commit').onclick=()=>{const t=$('dtitle').value.trim();if(!t){status('Name the dashboard.',1);return}\nrun('save_chart_as_dashboard',{draft,chartType:draft.chartType,dashboardTitle:t,workspaceName:$('wsrow').hidden?undefined:$('ws').value,attemptId:attempt,confirm:'APPLY_TO_PRODUCTION'})};\n$('expand').onclick=()=>display(context.displayMode==='fullscreen'?'inline':'fullscreen');\n$('dates').onclick=()=>{const from=$('from').value,to=$('to').value;if(!from||!to||from>to){status('Start date must not be after the end date.',1);return}call('change_date_range',{dateRange:{from,to},cursor:undefined,drillId:undefined})};\nfunction receive(event){if(event.source!==parent||closed)return;const m=event.data;\nif(!m||Array.isArray(m)||m.jsonrpc!=='2.0'||(origin!=='*'&&event.origin!==origin))return;\nif(!m.method&&pending.has(m.id)&&('result'in m||'error'in m)){if(origin==='*'&&event.origin!=='null')origin=event.origin;\nconst p=pending.get(m.id);pending.delete(m.id);clearTimeout(p.timer);\nif(m.error){const error=Error('host error');error.code=m.error.code;p.reject(error)}else p.resolve(m.result);return}\nif(m.method==='ping'&&m.id!==undefined){send({id:m.id,result:{}});return}\nif(m.method==='ui/resource-teardown'){send({id:m.id,result:{}});closed=true;revision++;custom?.dispose();custom=null;for(const p of pending.values()){clearTimeout(p.timer);p.reject(Error('closed'))}pending.clear();observer.disconnect();removeEventListener('message',receive);return}\nif(m.id!==undefined)return;\n/* A host can post the result before it answers initialize. Keep it for the replay. */\nif(!ready){if(early.length<8)early.push(m);return}\nhandle(m)}\nfunction handle(m){\nlet slot=DASH.indexOf(m.method),kind='dashed';\nif(slot<0){slot=SNAKE.indexOf(m.method);kind='snake'}\nif(slot>=0){\n/* A dual host posts each notification twice. Lock to the first name and drop its twin:\n same payload, and a second render is a visible flicker. */\nif(!dialect)dialect=kind;if(dialect!==kind)return;\n// The snake result nests one level deeper.\nif(slot===0)render(kind==='snake'?m.params&&m.params.result:m.params);\nelse if(slot===1)wait();\nelse{host(m.params||{});resize()}\nreturn}\nif(m.method==='ui/notifications/tool-cancelled'){revision++;custom?.dispose();custom=null;busy=false;$('plot').dataset.state='off';status('Request cancelled.');controls()}}\naddEventListener('message',receive);const observer=new ResizeObserver(resize);observer.observe(document.body);controls();\n/* Many tiles mount at once while a reply streams. A host that attaches its listener\n after this frame's first initialize never sees it, and the tile then waits with no\n size and no body until a reload. Ask again with backoff; the first answer wins. */\nlet started=false,tries=0;\nfunction fail(){if(started)return;started=true;trouble('initialize-failed');status('This chart needs an MCP Apps host.',1)}\nfunction start(){if(started||closed)return;tries++;\nrpc('ui/initialize',{protocolVersion:'2026-01-26',appInfo:{name:'Databrain tile',version:'1.0.0'},appCapabilities:{availableDisplayModes:['inline','fullscreen']}})\n.then(r=>{if(started)return;if(r.protocolVersion!=='2026-01-26'){fail();return}started=true;\nfor(const p of pending.values())clearTimeout(p.timer);pending.clear();\nhost(r.hostContext||{});ready=true;notify(INIT,{});for(const m of early.splice(0))handle(m);resize()})\n.catch(e=>{if(e.code!==undefined||tries>=10)fail()});\nif(tries<10)setTimeout(start,Math.min(8000,400*2**tries))}\nstart();\n})();`;\n\n/** Compact the trusted bridge once, preserving the existing built-in result budget. */\nconst SERVED_SCRIPT = transformSync(TILE_SCRIPT, { minify: true, target: 'es2020' }).code;\nexport function tileHtml(kind: 'chart' | 'kpi' = 'chart'): string {\n return strip(`<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; font-src data:; connect-src 'none'; frame-src blob:; base-uri 'none'; form-action 'none'\">\n<title>Databrain ${kind} tile</title><style id=\"hostfont\"></style><style>\n/* Host tokens are the Claude MCP Apps style-variable names. Every one carries a fallback:\n hosts may ship any subset, so nothing here may depend on a token existing. */\n:root{color-scheme:light dark;\n--dbn-bg:var(--color-background-primary,Canvas);--dbn-text:var(--color-text-primary,CanvasText);\n--dbn-mute:var(--color-text-secondary,GrayText);--dbn-faint:var(--color-text-tertiary,GrayText);\n--dbn-edge:var(--color-border-secondary,color-mix(in srgb,currentColor 20%,transparent));\n--dbn-line:var(--color-border-tertiary,color-mix(in srgb,currentColor 9%,transparent));\n--dbn-accent:var(--color-text-info,LinkText);--dbn-ring:var(--color-ring-primary,var(--dbn-accent));\n--dbn-bad:var(--color-text-danger,var(--dbn-text));\n--dbn-quiet:color-mix(in srgb,currentColor 6%,transparent);\n--bw:var(--border-width-regular,1px);--r-s:var(--border-radius-sm,6px);--r-m:var(--border-radius-md,8px);\n--med:var(--font-weight-medium,500);--semi:var(--font-weight-semibold,600);--xs:var(--font-text-xs-size,12px);\n/* One curve. Quick / standard / display-mode. Corporate: no overshoot. */\n--e:cubic-bezier(.2,0,0,1);--q:120ms;--s:300ms;--l:400ms}\n*{box-sizing:border-box}\nbody{margin:0;padding:var(--safe-top,0px) var(--safe-right,0px) var(--safe-bottom,0px) var(--safe-left,0px);background:var(--dbn-bg);color:var(--dbn-text);\nfont:var(--font-text-sm-size,14px)/var(--font-text-sm-line-height,1.4) var(--font-sans,system-ui)}\nmain{padding:20px;max-width:1200px;margin:auto;transition:padding var(--l) var(--e)}\nheader{display:flex;align-items:start;justify-content:space-between;gap:12px}header>div{min-width:0}\n.acts{display:flex;gap:6px;flex:none;align-items:center}\nh1{font-size:var(--font-heading-md-size,18px);line-height:var(--font-heading-md-line-height,1.4);margin:0 0 6px;font-weight:var(--semi);letter-spacing:-.01em;overflow-wrap:anywhere;transition:font-size var(--l) var(--e)}\np{margin:6px 0;color:var(--dbn-mute);overflow-wrap:anywhere}\n#description{font-size:var(--xs);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-top:2px}#description:empty{display:none}\nbutton,input,select{font:inherit;color:inherit;background:var(--dbn-bg);border:var(--bw) solid var(--dbn-edge);border-radius:var(--r-s);padding:7px 10px}\nbutton{cursor:pointer;transition:background-color var(--q) var(--e),border-color var(--q) var(--e)}\ninput{max-width:160px;min-width:0}button:hover:not(:disabled){background:var(--dbn-quiet)}button:disabled{color:var(--dbn-faint);cursor:default}\nbutton:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid var(--dbn-ring);outline-offset:2px}\n/* The picker is a scannable grid of quiet chips: 26 charts fit in nine rows, not a wall. */\n#choices[data-grid]{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:6px;margin-top:12px}\n#choices[data-grid] button{text-align:left;border-color:var(--dbn-line);padding:8px 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}\n#choices[data-grid] button:hover:not(:disabled){background:var(--dbn-quiet);border-color:var(--dbn-edge)}\n/* One primary per state: the drill pill, and Next page when there is a next page. */\n#choices button.primary,#next{flex:none;text-align:center;border:var(--bw) solid var(--dbn-accent);\nborder-radius:var(--r-s);padding:7px 10px;font-weight:var(--med);color:var(--dbn-accent)}\n#choices button.primary{align-self:flex-start;margin-top:10px}\n/* Refresh is a re-fetch, not a choice. Quiet it so it stops reading as a fifth chart. */\n#refresh{border-color:transparent;color:var(--dbn-mute);padding:7px 8px}\n#refresh:hover:not(:disabled){color:var(--dbn-text);background:none;text-decoration:underline}\n#status:empty{margin:0}\n#status[data-ok],#status[data-error]{font-weight:var(--med);border-left:2px solid;border-radius:0 var(--r-m) var(--r-m) 0;padding:10px 12px;margin:10px 0;background:color-mix(in srgb,currentColor 5%,transparent)}\n#status[data-ok]{color:var(--color-text-success,var(--dbn-text));border-color:var(--color-border-success,color-mix(in srgb,currentColor 40%,transparent))}\n/* \"Show as\": one row of quiet chips, the current type pressed. Never a second title. */\n#choices[data-chips]{flex-direction:row;flex-wrap:wrap;gap:6px;margin-top:10px}\n.chip{border-color:var(--dbn-line);border-radius:999px;padding:5px 11px;font-size:var(--xs);color:var(--dbn-mute)}\n.chip:hover:not(:disabled){color:var(--dbn-text);border-color:var(--dbn-edge)}\n.chip[aria-pressed=true]{color:var(--dbn-text);border-color:var(--dbn-text);background:var(--dbn-quiet)}\n/* Save is the one commit on a draft, so it is the one filled button in the tile. */\n#save,#commit{border-color:var(--dbn-accent);background:var(--dbn-accent);color:var(--dbn-bg);font-weight:var(--med)}\n#save:hover:not(:disabled),#commit:hover:not(:disabled){background:color-mix(in srgb,var(--dbn-accent) 85%,var(--dbn-text))}\n#form{margin-top:12px;padding:12px;border:var(--bw) solid var(--dbn-line);border-radius:var(--r-m);display:flex;flex-direction:column;gap:8px}\n[hidden]{display:none!important}#form label{flex-direction:column;align-items:stretch;gap:4px;font-size:var(--xs)}\n#form input,#form select{max-width:none;width:100%;background:color-mix(in srgb,currentColor 4%,var(--dbn-bg))}\n#wsone{color:var(--dbn-mute);font-size:var(--xs)}#wsone:empty{display:none}\n#status[data-error]{color:var(--dbn-bad);border-color:var(--color-border-danger,color-mix(in srgb,currentColor 40%,transparent));background:var(--color-background-danger,color-mix(in srgb,currentColor 5%,transparent))}\n/* Reserved plot. The skeleton is a static bar silhouette matching the 15:8 backend render:\n Claude's guidance asks for a skeleton that matches the final layout, and bans nothing here,\n but shimmer is banned by our own motion budget - so this paints once and never repaints. */\n#plot{position:relative;transition:opacity var(--q) var(--e)}#plot[data-busy],#plot[data-state=stale]{opacity:.55}\n#plot[data-state=wait]{aspect-ratio:15/8}\n#plot[data-state=wait]::before,#plot[data-state=ready]::before{content:'';position:absolute;pointer-events:none;inset:16% 6% 13%;\nborder-bottom:var(--bw) solid var(--dbn-line);transition:opacity var(--s) var(--e);\n--g:linear-gradient(var(--dbn-quiet),var(--dbn-quiet));background:var(--g) 0 100%/11% 44% no-repeat,var(--g) 22% 100%/11% 71% no-repeat,var(--g) 44% 100%/11% 57% no-repeat,var(--g) 66% 100%/11% 86% no-repeat,var(--g) 88% 100%/11% 33% no-repeat}\n#plot[data-state=ready]::before{opacity:0}\n.chart{position:relative;width:100%;margin:12px 0}.chart img{width:100%;height:100%;display:block}\n/* A point's geometry comes from the renderer and can be 8px; the hit area must not.\n ::after pads it to at least 24px; ::before is the marker that says \"you can pick this\". */\n.point{position:absolute;padding:0;border:0;background:transparent;border-radius:50%}\n.point::after{content:'';position:absolute;inset:-8px}\n.point::before{content:'';position:absolute;left:50%;top:50%;width:8px;height:8px;margin:-4px;border-radius:50%;\nbackground:var(--dbn-accent);box-shadow:0 0 0 2px var(--dbn-bg);opacity:0;transform:scale(.6);transition:opacity var(--q) var(--e),transform var(--q) var(--e)}\n.point:hover::before,.point:focus-visible::before{opacity:.7;transform:none}\n.point[aria-pressed=true]::before{opacity:1;transform:none;box-shadow:0 0 0 2px var(--dbn-bg),0 0 0 4px color-mix(in srgb,var(--dbn-accent) 35%,transparent)}\n.kpi{overflow-wrap:anywhere;font-size:clamp(34px,10vw,44px);line-height:1.1;font-weight:var(--semi);letter-spacing:-.025em;font-variant-numeric:tabular-nums;color:var(--dbn-text);margin:14px 0 6px;transition:font-size var(--l) var(--e)}\n/* The readout rail. At rest it states the scope of what is on screen; on selection it\n becomes the value. One slot, two jobs, so selecting a bar never reflows the tile. */\n#rail{display:flex;align-items:center;min-height:2.5em;margin:2px 0 0}\n#scope,#value{margin:0;display:none;overflow-wrap:anywhere}\n#rail:not([data-value]) #scope{display:block;color:var(--dbn-mute);font-size:var(--xs)}\n#rail:not([data-value]):has(#scope:empty){display:none}\n#rail[data-value] #value{display:flex;flex-direction:column;gap:1px;line-height:1.15}\n#vlabel{color:var(--dbn-mute);font-size:var(--xs)}\n#vnum{font-size:var(--font-heading-md-size,18px);font-weight:var(--semi);letter-spacing:-.015em;font-variant-numeric:tabular-nums}\n[data-mode=full] #vnum{font-size:var(--font-heading-xl-size,24px)}\nfooter,.dates{display:flex;gap:8px;flex-wrap:wrap;align-items:center}\n#choices{display:flex;flex-direction:column;align-items:stretch;margin:10px 0 0}footer{margin-top:12px}footer:not(:has(button:not([hidden]))){display:none}.dates{margin:8px 0 2px}\nlabel{display:flex;align-items:center;gap:6px;color:var(--dbn-mute)}\n#range{margin-top:10px}#data{margin-top:2px}\nhtml:not([data-deep]) #range,html:not([data-deep]) #data:not([data-only]){display:none}\nsummary{cursor:pointer;color:var(--dbn-mute);font-size:var(--xs);padding:5px 0;width:fit-content;transition:color var(--q) var(--e)}summary:hover{color:var(--dbn-text)}\n#rows{overflow:auto;max-height:260px;border-radius:var(--r-s)}\ntable{border-collapse:collapse;width:100%;font-size:var(--xs)}\nth,td{text-align:left;padding:8px;border-bottom:var(--bw) solid var(--dbn-line);overflow-wrap:anywhere}\ntd.n,th.n{text-align:right;font-variant-numeric:tabular-nums}\nth{font-weight:var(--semi);position:sticky;top:0;background:var(--dbn-bg);color:var(--dbn-mute);\nfont-size:var(--xs);letter-spacing:.02em;text-transform:uppercase}\n/* Inline to fullscreen is a layout morph, not a new screen: main widens over 400ms on the\n one curve and every dependent box follows it. Fullscreen is the detailed view, so the\n table loses its inline height cap. */\n/* The SVG is rendered at 600x320 and scaled by width, so an unbounded fullscreen column\n makes the chart's own axis type larger than the tile's heading. Hold a measure. */\n[data-mode=full] main{padding:28px 32px;max-width:900px}\n[data-mode=full] h1{font-size:var(--font-heading-xl-size,24px);line-height:var(--font-heading-xl-line-height,1.25)}\n[data-mode=full] .kpi{overflow-wrap:anywhere;font-size:clamp(44px,7vw,72px)}\n[data-mode=full] #rows{max-height:none}\n/* Touch: the host gives vertical pan to the conversation, so a nested scroller is a trap. */\n@media(pointer:coarse){button,input{min-height:44px}summary{padding:12px 0}#rows{max-height:none;overflow:visible}}\nhtml[data-kind=table] #rows{max-height:360px;overflow:auto}html[data-kind=table] table{min-width:480px}\n@media(max-width:480px){main{padding:14px}}\n/* Entrance. Content first, chrome 60ms later: 300+60 = 360ms, inside the 400ms budget.\n No stagger past two groups - a chat tile that cascades reads as lag, not luxury. */\n@keyframes rise{from{opacity:0;transform:translateY(6px)}}\n@keyframes land{from{opacity:0;transform:translateY(4px) scale(.995)}}\nheader,#status,#plot{animation:rise var(--s) var(--e) both}\n#rail,#choices,footer,#range,#data{animation:rise var(--s) var(--e) 60ms both}\n.chart,.kpi{animation:land var(--s) var(--e) both}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}\n</style></head><body><main id=\"tile\"><header><div><h1 id=\"title\">Analytics</h1><p id=\"description\"></p></div><div class=\"acts\"><button id=\"refresh\" hidden>Refresh</button><button id=\"expand\" hidden>Expand</button></div></header>\n<p id=\"status\" role=\"status\">Loading chart…</p><div id=\"plot\" data-state=\"wait\"></div>\n<div id=\"rail\"><p id=\"scope\"></p><p id=\"value\"><span id=\"vlabel\"></span><b id=\"vnum\"></b></p></div><div id=\"choices\"></div>\n<footer><button id=\"save\" hidden>Save as dashboard</button><button id=\"next\" hidden>Next page</button></footer>\n<form id=\"form\" hidden onsubmit=\"return false\"><label>Dashboard name<input id=\"dtitle\" type=\"text\" maxlength=\"200\" autocomplete=\"off\"></label>\n<label id=\"wsrow\" hidden>Workspace<select id=\"ws\"></select></label><p id=\"wsone\"></p>\n<div class=\"dates\"><button id=\"commit\" type=\"button\">Save to Databrain</button><button id=\"cancel\" type=\"button\">Cancel</button></div></form>\n<details id=\"range\" hidden><summary>Change the date range</summary><div class=\"dates\"><label>From <input id=\"from\" type=\"date\"></label><label>To <input id=\"to\" type=\"date\"></label><button id=\"dates\" type=\"button\">Apply dates</button></div></details>\n<details id=\"data\" hidden><summary id=\"count\">Show the data behind this</summary><div id=\"rows\" tabindex=\"0\" role=\"region\" aria-label=\"Chart data\"></div></details></main><script>${SERVED_SCRIPT}</script></body></html>`);\n}\nfunction strip(html: string): string {\n return html.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').replace(/\\n{2,}/g, '\\n')\n .replace(/<style>([\\s\\S]*?)<\\/style><\\/head>/, (_m, css: string) => `<style>${css.replace(/;}/g, '}').replace(/\\n/g, '')}</style></head>`);\n}\n"],"mappings":"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","names":["z","z"]}