@databrainhq/mcp-server 0.3.0-rc.10 → 0.3.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/apps/server.d.ts +82 -44
- package/dist/apps/server.js +5 -4
- package/dist/apps/stdio.js +6 -5
- package/dist/apps/stdio.js.map +1 -1
- package/dist/bundle-TDQXWIPM.js +24 -0
- package/dist/bundle-TDQXWIPM.js.map +1 -0
- package/dist/{chunk-CUPL7CAN.js → chunk-DM7S4R57.js} +2 -2
- package/dist/{chunk-ERTAKB2H.js → chunk-ES2LUP4U.js} +729 -608
- package/dist/chunk-ES2LUP4U.js.map +1 -0
- package/dist/{chunk-N7X762VU.js → chunk-IBYBK5ZE.js} +4 -4
- package/dist/{chunk-5IGP6A6T.js → chunk-NBFUKVWP.js} +131 -24
- package/dist/chunk-NBFUKVWP.js.map +1 -0
- package/dist/{chunk-OCBTNAAO.js → chunk-Q5EYDKSA.js} +550 -1272
- package/dist/chunk-Q5EYDKSA.js.map +1 -0
- package/dist/chunk-RHK62ZJ6.js +884 -0
- package/dist/chunk-RHK62ZJ6.js.map +1 -0
- package/dist/cli.js +6 -4
- package/dist/cli.js.map +1 -1
- package/dist/custom-chart/custom-runtime.js +63 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/{main-KZRHWE3K.js → main-JEYYFGJM.js} +23 -8
- package/dist/main-JEYYFGJM.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-5IGP6A6T.js.map +0 -1
- package/dist/chunk-ERTAKB2H.js.map +0 -1
- package/dist/chunk-OCBTNAAO.js.map +0 -1
- package/dist/main-KZRHWE3K.js.map +0 -1
- /package/dist/{chunk-CUPL7CAN.js.map → chunk-DM7S4R57.js.map} +0 -0
- /package/dist/{chunk-N7X762VU.js.map → chunk-IBYBK5ZE.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/errors.ts","../src/utils/logger.ts","../package.json","../src/version.ts","../src/custom-chart/studio.ts","../src/utils/cache.ts","../src/client/error-body.ts","../src/utils/tool-context.ts","../src/client/oauth-client-credentials.ts","../src/client/databrain-client.ts","../src/api/custom-charts-api.ts","../src/custom-chart/api.ts","../src/custom-chart/scaffold.ts"],"sourcesContent":["export class DatabrainApiError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n public readonly details?: unknown,\n /** The server's own next step for this error class, when it sent one. */\n public readonly serverHint?: string,\n public readonly retryable?: boolean,\n ) {\n super(message);\n this.name = 'DatabrainApiError';\n }\n\n toToolResponse(): { content: Array<{ type: 'text'; text: string }>; isError: true } {\n const parts = [`Error: ${this.message}`];\n const detailDump = this.formatDetails();\n if (detailDump) parts.push(detailDump);\n const hint = this.serverHint ? `Hint: ${this.serverHint}` : this.hint();\n if (hint) parts.push(hint);\n return {\n content: [{ type: 'text', text: parts.join('\\n\\n') }],\n isError: true,\n };\n }\n\n /**\n * Surface API-returned details for 4xx errors so the LLM can recover or\n * report the real reason (validation messages, field names, etc.). Skips\n * fields already collapsed into `message`/`code`, and skips 5xx where the\n * payload usually isn't actionable.\n */\n private formatDetails(): string {\n if (this.status < 400 || this.status >= 500) return '';\n if (!this.details || typeof this.details !== 'object') return '';\n\n const skipKeys = new Set(['message', 'detail', 'error', 'code', 'data', 'hint', 'retryable']);\n const extra: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(this.details as Record<string, unknown>)) {\n if (skipKeys.has(k)) continue;\n if (v === null || v === undefined) continue;\n extra[k] = v;\n }\n if (Object.keys(extra).length === 0) return '';\n try {\n return `API details: ${JSON.stringify(extra)}`;\n } catch {\n return '';\n }\n }\n\n private hint(): string {\n // dashboard_snapshot: the API's own reasons, so the generic status hints below do not mislead.\n if (this.code === 'OAUTH_SIGN_IN_REQUIRED') {\n return 'Hint: Snapshots draw as a signed-in Databrain user. Connect this AI app with a Databrain sign-in (the hosted connector, or /mcp in Claude Code); a service token cannot take them.';\n }\n if (this.code === 'RENDER_IN_PROGRESS') {\n return 'Hint: Your other snapshot is still rendering. Wait a few seconds, then call the tool again.';\n }\n if (this.code === 'SNAPSHOT_TIMEOUT' || this.code === 'SNAPSHOT_TOO_LARGE') {\n return 'Hint: Ask for one chart with metricId, or a smaller width.';\n }\n if (this.code === 'UNAUTHORIZED_ORIGIN') {\n return 'Hint: The embed origin is not on the allowed domains list. Add the domain to the allowed domains in the Databrain settings; the token itself is fine.';\n }\n if (this.status === 401 || this.status === 403) {\n if (this.code === 'OAUTH_API_ERROR') {\n return 'Hint: Check that the backend accepts the configured OAuth issuer and resource, and that the service principal has permission for this operation. Static fallback applies only to token acquisition failures.';\n }\n if (\n this.code === 'INVALID_SERVICE_TOKEN' ||\n this.code === 'MISSING_SERVICE_TOKEN' ||\n this.message.toLowerCase().includes('service token')\n ) {\n return 'Hint: The service token (DATABRAIN_SERVICE_TOKEN) is invalid, expired, or missing. It must be set in the MCP server config (not at runtime). Check Settings > Service Tokens in the Databrain UI.';\n }\n return 'Hint: The per-data-app API token is invalid or expired. Use `create_api_token` to generate a new one. Do not ask the user — manage tokens via MCP tools.';\n }\n if (this.status === 400) {\n return 'Hint: The request was invalid. Check parameter values match expected types and formats. Use the corresponding list tool to verify IDs and names.';\n }\n if (this.status === 404) {\n return 'Hint: The requested resource was not found. Verify the ID or name is correct using the corresponding list tool.';\n }\n if (this.status === 409) {\n return 'Hint: A resource with this name already exists. Use a different name or list existing resources first.';\n }\n if (this.status === 422) {\n return 'Hint: The request parameters are invalid. Check the required fields and their formats.';\n }\n if (this.status === 429) {\n return 'Hint: API rate limit reached. Wait a moment and try again.';\n }\n if (this.status >= 500) {\n return 'Hint: The Databrain API is experiencing issues. Try again in a few moments.';\n }\n return '';\n }\n}\n\nexport function toolError(message: string): { content: Array<{ type: 'text'; text: string }>; isError: true } {\n return {\n content: [{ type: 'text', text: message }],\n isError: true,\n };\n}\n\nexport function stringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nexport function toolSuccess(text: string): { content: Array<{ type: 'text'; text: string }> } {\n return {\n content: [{ type: 'text', text }],\n };\n}\n","const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\ntype LogLevel = keyof typeof LOG_LEVELS;\n\nconst currentLevel: LogLevel =\n (process.env.DATABRAIN_LOG_LEVEL as LogLevel) || 'warn';\n\n/**\n * Credentials the process holds. Every log line is scrubbed of them before it\n * reaches stderr, because an API error body can quote the Authorization\n * header back and stderr is a terminal, a CI log or the MCP host's log.\n */\nconst secrets = new Set<string>();\n\nexport function registerSecret(value: string | undefined | null): void {\n if (typeof value === 'string' && value.length >= 8) secrets.add(value);\n}\n\nexport function redactSecrets(text: string): string {\n let out = text;\n for (const secret of secrets) out = out.split(secret).join('[redacted]');\n return out;\n}\n\nfunction log(level: LogLevel, message: string, data?: unknown): void {\n if (LOG_LEVELS[level] < LOG_LEVELS[currentLevel]) return;\n const line = `[databrain-mcp] [${level.toUpperCase()}] ${message}`;\n const text = data !== undefined ? `${line} ${JSON.stringify(data)}` : line;\n process.stderr.write(`${redactSecrets(text)}\\n`);\n}\n\nexport const logger = {\n debug: (msg: string, data?: unknown) => log('debug', msg, data),\n info: (msg: string, data?: unknown) => log('info', msg, data),\n warn: (msg: string, data?: unknown) => log('warn', msg, data),\n error: (msg: string, data?: unknown) => log('error', msg, data),\n};\n","{\n \"name\": \"@databrainhq/mcp-server\",\n \"mcpName\": \"com.usedatabrain/mcp-server\",\n \"version\": \"0.3.0-rc.11\",\n \"description\": \"MCP server for Databrain embedded analytics - connect databases, create datamarts, configure embeds, and generate frontend code\",\n \"type\": \"module\",\n \"bin\": {\n \"mcp-server\": \"dist/index.js\",\n \"databrain-mcp-server\": \"dist/index.js\",\n \"databrain-mcp-chart-apps\": \"dist/apps/stdio.js\",\n \"databrain\": \"dist/cli.js\"\n },\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"files\": [\n \"dist\",\n \"skills\",\n \".claude-plugin\",\n \".mcp.json\"\n ],\n \"scripts\": {\n \"build\": \"tsup && node scripts/copy-custom-chart-assets.mjs\",\n \"dev\": \"tsx src/index.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:live\": \"vitest run tests/integration/prompt-flows.test.ts\",\n \"lint\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"keywords\": [\n \"mcp\",\n \"databrain\",\n \"embedded-analytics\",\n \"ai\",\n \"model-context-protocol\"\n ],\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.32.1\",\n \"@posthog/mcp\": \"^0.10.7\",\n \"esbuild\": \"0.27.3\",\n \"fflate\": \"0.8.3\",\n \"posthog-node\": \"^5.21.2\",\n \"yaml\": \"^2.9.1\",\n \"zod\": \"^3.24.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.3.5\",\n \"tsup\": \"^8.4.0\",\n \"tsx\": \"^4.19.0\",\n \"typescript\": \"^5.7.0\",\n \"vitest\": \"^3.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/databrainhq/mcp-server.git\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n","import packageJson from '../package.json';\n\n// tsup embeds this package.json value in dist while tsx resolves the same file\n// during development. The handshake and request headers therefore share the\n// package version without maintaining a second release string.\nexport const MCP_SERVER_VERSION = packageJson.version;\n","import { z } from 'zod';\nimport type { DatabrainClient } from '../client/databrain-client.js';\nimport { DatabrainApiError } from '../utils/errors.js';\n\nconst studioCell = z.union([z.string().max(4096), z.number().finite(), z.boolean(), z.null()]);\nexport const studioDraftSchema = z.object({\n code: z.string().max(64_000), css: z.string().max(32_000), manifest: z.record(z.unknown()),\n sampleData: z.object({ columns: z.array(z.object({ id: z.string(), label: z.string(),\n type: z.enum(['string', 'number', 'boolean']), slotId: z.string().optional() }).strict()).max(64),\n rows: z.array(z.array(studioCell)).max(200) }).strict(),\n name: z.string().max(256), description: z.string().max(500), iconSvg: z.string().max(16_384),\n}).strict();\nexport type StudioDraft = z.infer<typeof studioDraftSchema>;\n\n/** Accept only the studio's domain result, never general SSE data or credentials. */\nexport function parseStudioStream(text: string): { draft?: StudioDraft; sessionId?: string; applied: boolean } {\n if (text.length > 4 * 1024 * 1024) throw new Error('Studio stream too large');\n let draft: StudioDraft | undefined;\n let sessionId: string | undefined;\n let failure = false;\n for (const frame of text.split(/\\r?\\n\\r?\\n/)) {\n const line = frame.split(/\\r?\\n/).filter(l => l.startsWith('data:')).map(l => l.slice(5).trimStart()).join('\\n');\n if (!line || line === '[DONE]') continue;\n const event = JSON.parse(line);\n if (event.type === 'session' && typeof event.sessionId === 'string') sessionId = event.sessionId;\n if (event.type === 'error') failure = true;\n if (event.type === 'tool_execution_end' && event.name === 'update_custom_chart' &&\n event.status === 'completed' && event.result?.kind === 'applied') {\n draft = studioDraftSchema.parse(event.result.draft);\n }\n }\n if (failure && !draft) throw new Error('Bryn did not complete an edit');\n return { draft, sessionId, applied: !!draft };\n}\n\n/** Only absence permits the old authoring path. An auth denial or outage is not absence. */\nexport async function editWithBryn(client: DatabrainClient, input: {\n key: string; workspaceId: string; message: string; draft: StudioDraft; sessionId?: string;\n}): Promise<{ available: boolean; draft: StudioDraft; sessionId?: string; applied: boolean }> {\n // Bryn requires a signed-in session. A stdio service token is still valid for\n // the existing authoring API; do not send it to a session-only route.\n if (!client.isHosted()) return { available: false, draft: input.draft, applied: false };\n try {\n const result = await client.request<ReturnType<typeof parseStudioStream>>({\n method: 'POST', path: `/api/v2/custom-charts/${encodeURIComponent(input.key)}/bryn/turn`, auth: 'service',\n body: { workspaceId: input.workspaceId, message: input.message, draft: input.draft,\n sessionId: input.sessionId, mode: 'build', viewingOldVersion: false },\n responseFormat: 'studio-sse', timeoutMs: 180_000, noRetryOnTimeout: true,\n });\n return { available: true, draft: result.draft ?? input.draft, applied: result.applied, sessionId: result.sessionId };\n } catch (error) {\n if (error instanceof DatabrainApiError && error.status === 404) {\n return { available: false, draft: input.draft, applied: false };\n }\n throw error;\n }\n}\n","interface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\nexport class TtlCache {\n private store = new Map<string, CacheEntry<unknown>>();\n\n get<T>(key: string): T | undefined {\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return entry.value as T;\n }\n\n set<T>(key: string, value: T, ttlMs: number): void {\n this.store.set(key, { value, expiresAt: Date.now() + ttlMs });\n }\n\n invalidateByPrefix(prefix: string): void {\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.store.delete(key);\n }\n }\n }\n\n clear(): void {\n this.store.clear();\n }\n}\n","/**\n * Reads a Databrain error body into one shape. The backend sends the same\n * envelope `{ code, message, hint, retryable, details }` in three places:\n * flat (`{ code, message }`), under `error` (public REST, datasource API) and\n * under `data.error` (the externalApis routes, which also send `error` as a\n * plain string). Reading only the flat form dropped the code and hint.\n */\nexport interface ParsedErrorBody {\n code?: string;\n message?: string;\n hint?: string;\n retryable?: boolean;\n details?: unknown;\n}\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n\nconst str = (v: unknown): string | undefined =>\n typeof v === 'string' && v.trim() ? v : undefined;\n\nconst fromEnvelope = (v: unknown): ParsedErrorBody | undefined => {\n if (!isRecord(v)) return undefined;\n const out: ParsedErrorBody = {\n code: str(v.code),\n message: str(v.message) ?? str(v.detail),\n hint: str(v.hint),\n retryable: typeof v.retryable === 'boolean' ? v.retryable : undefined,\n details: v.details,\n };\n return out.code || out.message || out.hint ? out : undefined;\n};\n\nexport function parseErrorBody(body: unknown): ParsedErrorBody {\n if (!isRecord(body)) return {};\n const nested =\n fromEnvelope(isRecord(body.data) ? body.data.error : undefined) ??\n fromEnvelope(body.error) ??\n fromEnvelope(body);\n if (nested) {\n return { ...nested, message: nested.message ?? str(body.error) };\n }\n return { message: str(body.error) };\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Per-tool-call intent metadata. Set when an MCP tool handler runs (see\n * registerAllTools, which wraps every handler), and read by DatabrainClient to\n * tag outbound API requests with X-Databrain-MCP-* headers. The backend\n * (mcpTracking) records these so usage can be attributed to a specific tool and\n * workflow intent — letting us see, e.g., embedding vs internal-analytics flows.\n */\nexport interface ToolCallContext {\n /** The MCP tool name being executed, e.g. 'create_workspace_metric'. */\n tool: string;\n /** Coarse domain bucket, e.g. 'embedding' | 'internal_analytics'. */\n category: string;\n /** Use-case bucket, e.g. 'sql_to_workspace_metric'. */\n workflowIntent: string;\n /** How the request was initiated; 'tool_call' for MCP tool invocations. */\n inputMode: string;\n}\n\nexport const toolContext = new AsyncLocalStorage<ToolCallContext>();\n","import { z } from 'zod';\n\n// Validate without transforming: issuer identity uses exact string comparison.\nexport const oauthHttpsUrl = z.string().refine(value => {\n try {\n const url = new URL(value);\n return url.protocol === 'https:' && !url.username && !url.password && !url.hash;\n } catch { return false; }\n}, 'OAuth URLs must use HTTPS without userinfo or fragments');\n\nexport const oauthConfigSchema = z.object({\n issuer: oauthHttpsUrl.refine(value => {\n try { return !new URL(value).search; } catch { return false; }\n }, 'OAuth issuer must not contain a query'),\n clientId: z.string().min(1),\n clientSecret: z.string().min(1),\n resource: oauthHttpsUrl,\n scope: z.string().min(1).optional(),\n authMethod: z.enum(['client_secret_basic', 'client_secret_post']).default('client_secret_basic'),\n});\n\nexport type OAuthConfig = z.infer<typeof oauthConfigSchema>;\n\nconst metadataSchema = z.object({\n issuer: z.string(),\n token_endpoint: oauthHttpsUrl,\n token_endpoint_auth_methods_supported: z.array(z.string()).optional(),\n grant_types_supported: z.array(z.string()).optional(),\n});\nconst tokenSchema = z.object({\n access_token: z.string().regex(/^[A-Za-z0-9\\-._~+/]+=*$/),\n token_type: z.string().refine(value => value.toLowerCase() === 'bearer'),\n expires_in: z.number().finite().positive().optional(),\n});\n\nconst TOKEN_TIMEOUT_MS = 10_000;\nconst FAILURE_COOLDOWN_MS = 30_000;\nconst MAX_REFRESH_SKEW_MS = 60_000;\n\nfunction authError(): Error {\n // Never retain an IdP body, a fetch error, or a schema error as a cause:\n // those can contain credentials and are serialized by callers/telemetry.\n return new Error('OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes.');\n}\n\n/** One immutable credential/issuer/resource binding per MCP process client. */\nexport class OAuthClientCredentials {\n private readonly config: OAuthConfig;\n private tokenEndpoint?: string;\n private cached?: { value: string; renewAt: number };\n private pending?: Promise<string>;\n private retryAt = 0;\n\n constructor(config: OAuthConfig) {\n // Also validate programmatic callers; do not expose invalid values in errors.\n const parsed = oauthConfigSchema.safeParse(config);\n if (!parsed.success) throw authError();\n this.config = Object.freeze(parsed.data);\n }\n\n getStatus(): 'configured' | 'ready' | 'unavailable' {\n if (this.cached && Date.now() < this.cached.renewAt) return 'ready';\n if (this.retryAt > 0) return 'unavailable';\n return 'configured';\n }\n\n async getToken(): Promise<string> {\n if (this.cached && Date.now() < this.cached.renewAt) return this.cached.value;\n if (this.pending) return this.pending;\n if (Date.now() < this.retryAt) throw authError();\n\n this.cached = undefined;\n this.pending = this.acquire().catch(() => {\n this.retryAt = Date.now() + FAILURE_COOLDOWN_MS;\n throw authError();\n }).finally(() => { this.pending = undefined; });\n return this.pending;\n }\n\n private async discoverTokenEndpoint(signal: AbortSignal): Promise<string> {\n if (this.tokenEndpoint) return this.tokenEndpoint;\n const issuer = new URL(this.config.issuer);\n const path = issuer.pathname.replace(/\\/$/, '');\n const candidates = [...new Set([\n `${issuer.origin}/.well-known/oauth-authorization-server${path}`,\n `${issuer.origin}/.well-known/openid-configuration${path}`,\n `${issuer.origin}${path}/.well-known/openid-configuration`,\n ])];\n\n for (const url of candidates) {\n // No credentials, cookies, API headers, or redirects during discovery.\n const response = await fetch(url, {\n headers: { Accept: 'application/json' }, redirect: 'error', signal,\n });\n if (response.status === 404 || response.status === 405) continue;\n if (!response.ok) throw authError();\n const metadata = metadataSchema.parse(await response.json());\n if (metadata.issuer !== this.config.issuer) throw authError();\n const methods = metadata.token_endpoint_auth_methods_supported ?? ['client_secret_basic'];\n if (!methods.includes(this.config.authMethod)) throw authError();\n if (metadata.grant_types_supported && !metadata.grant_types_supported.includes('client_credentials')) {\n throw authError();\n }\n this.tokenEndpoint = metadata.token_endpoint;\n return this.tokenEndpoint;\n }\n throw authError();\n }\n\n private async acquire(): Promise<string> {\n // One time budget covers all discovery attempts and token acquisition.\n const signal = AbortSignal.timeout(TOKEN_TIMEOUT_MS);\n const endpoint = await this.discoverTokenEndpoint(signal);\n const body = new URLSearchParams({\n grant_type: 'client_credentials', resource: this.config.resource,\n });\n if (this.config.scope) body.set('scope', this.config.scope);\n const headers: Record<string, string> = {\n Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (this.config.authMethod === 'client_secret_post') {\n body.set('client_id', this.config.clientId);\n body.set('client_secret', this.config.clientSecret);\n } else {\n // RFC 6749 2.3.1: form-encode EACH value before constructing Basic auth.\n const encode = (value: string) => new URLSearchParams({ value }).toString().slice(6);\n headers.Authorization = `Basic ${Buffer.from(`${encode(this.config.clientId)}:${encode(this.config.clientSecret)}`).toString('base64')}`;\n }\n\n const startedAt = Date.now();\n const response = await fetch(endpoint, {\n method: 'POST', headers, body: body.toString(), redirect: 'error', signal,\n });\n if (!response.ok) throw authError();\n const token = tokenSchema.parse(await response.json());\n if (token.expires_in !== undefined) {\n const lifetimeMs = token.expires_in * 1000;\n const expiresAt = startedAt + lifetimeMs;\n if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt) throw authError();\n this.cached = {\n value: token.access_token,\n renewAt: expiresAt - Math.min(MAX_REFRESH_SKEW_MS, lifetimeMs * 0.1),\n };\n }\n // With no expires_in, use the token for this call only. No JWT decoding,\n // assumed lifetime, refresh_token grant, disk storage, or background timer.\n this.retryAt = 0;\n return token.access_token;\n }\n}\n","import { parseStudioStream } from '../custom-chart/studio.js';\nimport { TtlCache } from '../utils/cache.js';\nimport type { Config } from '../utils/config.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport { logger, registerSecret } from '../utils/logger.js';\nimport type { RequestOptions } from './types.js';\nimport { parseErrorBody } from './error-body.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { OAuthClientCredentials, oauthHttpsUrl } from './oauth-client-credentials.js';\n\nconst MCP_USER_AGENT = `databrain-mcp-server/${MCP_SERVER_VERSION}`;\n\nconst MAX_RETRIES = 3;\nconst REQUEST_TIMEOUT_MS = 30_000;\n\nconst NON_TRANSIENT_PATTERNS = [\n 'out of context',\n 'Definition not found',\n 'not a data question',\n 'Query generation failed',\n 'not found',\n 'not authenticated',\n 'Invalid Service Token',\n 'service token',\n];\n\nexport class DatabrainClient {\n private cache = new TtlCache();\n private runtimeApiToken: string | undefined;\n private readonly oauth?: OAuthClientCredentials;\n private oauthFallback = false;\n\n constructor(private config: Config) {\n registerSecret(config.serviceToken);\n registerSecret(config.apiToken);\n if (config.oauth) {\n if (!oauthHttpsUrl.safeParse(config.apiUrl).success) {\n throw new Error('OAuth requires an HTTPS Databrain API URL without userinfo or fragments.');\n }\n this.oauth = new OAuthClientCredentials(config.oauth);\n // Prevent later mutation of a caller-owned config from moving credentials\n // or the resource bearer to another API/issuer within this client session.\n this.config = Object.freeze({ ...config });\n }\n }\n\n hasApiToken(): boolean {\n return !!(this.runtimeApiToken || this.config.apiToken);\n }\n\n /** The hosted composition supplies a signed-in identity, unlike local API credentials. */\n isHosted(): boolean { return false; }\n\n setRuntimeApiToken(token: string): void {\n registerSecret(token);\n this.runtimeApiToken = token;\n logger.info('Runtime API token set for this session');\n }\n\n getServiceToken(): string | undefined {\n return this.config.serviceToken;\n }\n\n getApiUrl(): string {\n return this.config.apiUrl;\n }\n\n getDemoDomain(): string {\n return this.config.demoDomain;\n }\n\n /**\n * Describes which credential is currently used as the bearer for outgoing\n * API calls. Used by `whoami` so the LLM can give the user a truthful answer.\n */\n getActiveAuth():\n | { source: 'service-token'; oauthFallback?: boolean }\n | { source: 'oauth-client-credentials'; status: 'configured' | 'ready' | 'unavailable' }\n | { source: 'api-token' }\n | { source: 'none' } {\n if (this.oauth) {\n if (this.oauthFallback) return { source: 'service-token', oauthFallback: true };\n return { source: 'oauth-client-credentials', status: this.oauth.getStatus() };\n }\n if (this.config.serviceToken) return { source: 'service-token' };\n if (this.runtimeApiToken || this.config.apiToken) return { source: 'api-token' };\n return { source: 'none' };\n }\n\n private async getToken(auth: 'service' | 'api' | 'none'): Promise<string | undefined> {\n if (auth === 'none') return undefined;\n if (auth === 'service') {\n if (this.oauth) {\n try {\n const token = await this.oauth.getToken();\n this.oauthFallback = false;\n return token;\n } catch {\n if (!this.config.serviceToken) {\n throw new DatabrainApiError(0, 'OAUTH_TOKEN_ERROR',\n 'OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes.');\n }\n if (!this.oauthFallback) logger.warn('OAuth unavailable; using the configured static service token.');\n this.oauthFallback = true;\n }\n }\n return this.config.serviceToken;\n }\n return this.runtimeApiToken || this.config.apiToken;\n }\n\n async request<T>(options: RequestOptions): Promise<T> {\n return this.requestInternal<T>(options);\n }\n\n private async requestInternal<T>(options: RequestOptions): Promise<T> {\n const usesOAuth = !!this.oauth && options.auth === 'service' && !options.token;\n // An OAuth renewal can switch to a differently scoped static principal.\n // Do not serve/store service response caches across that auth boundary.\n const cacheAllowed = !usesOAuth;\n // Invalidate caches first\n if (options.invalidateCache) {\n for (const prefix of options.invalidateCache) {\n this.cache.invalidateByPrefix(prefix);\n }\n }\n\n // Check cache\n if (cacheAllowed && options.cacheKey && options.cacheTtlMs) {\n const cached = this.cache.get<T>(options.cacheKey);\n if (cached !== undefined) {\n logger.debug(`Cache hit: ${options.cacheKey}`);\n return cached;\n }\n }\n\n let token = options.token ?? await this.getToken(options.auth);\n if (options.auth !== 'none' && !options.token && !token) {\n if (options.auth === 'service') {\n throw new DatabrainApiError(\n 401,\n 'MISSING_SERVICE_TOKEN',\n 'DATABRAIN_SERVICE_TOKEN is required for this operation (org-level token for discovery and data app management). It must be set in the MCP server configuration — do not ask the user for it at runtime.',\n );\n }\n throw new DatabrainApiError(\n 401,\n 'MISSING_API_TOKEN',\n 'A per-data-app API token is required for this embedding operation but none is configured. ' +\n 'Call `create_api_token` to generate one, or `set_api_token` if you already have the value. Do not ask the user for this token — create it via the MCP tools.',\n );\n }\n\n // Build URL\n let url = `${this.config.apiUrl}${options.path}`;\n if (options.query) {\n const params = new URLSearchParams(options.query);\n url += `?${params.toString()}`;\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n if (usesOAuth && attempt > 1) token = await this.getToken(options.auth);\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': MCP_USER_AGENT,\n 'X-Databrain-Client': MCP_USER_AGENT,\n ...options.headers,\n };\n // Tag the call with the active tool's intent so backend telemetry can\n // attribute usage (embedding vs internal analytics, per-tool). Present\n // only when invoked from within a tool handler (see registry wrapper).\n const ctx = toolContext.getStore();\n if (ctx) {\n headers['X-Databrain-MCP-Tool'] = ctx.tool;\n headers['X-Databrain-MCP-Tool-Category'] = ctx.category;\n headers['X-Databrain-MCP-Workflow-Intent'] = ctx.workflowIntent;\n headers['X-Databrain-MCP-Input-Mode'] = ctx.inputMode;\n }\n if (token) {\n // Guest-token endpoints (auth: 'none' with a minted token) read the\n // token from `x-plugin-token` and require a whitelisted Origin.\n // DATABRAIN_DOMAINS includes `usedatabrain.com`, so `mcp.usedatabrain.com`\n // passes the whitelist without needing a per-company config.\n if (options.auth === 'none') {\n headers['x-plugin-token'] = token;\n headers['Origin'] = 'https://mcp.usedatabrain.com';\n } else {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: AbortSignal.timeout(options.timeoutMs || REQUEST_TIMEOUT_MS),\n ...(usesOAuth ? { redirect: 'error' as const } : {}),\n });\n\n if (!response.ok) {\n // An untrusted API can echo credentials in its error body. Keep\n // OAuth errors body-free in both tool output and telemetry. Never\n // retry/downgrade a backend denial with the static service token.\n if (usesOAuth) {\n throw new DatabrainApiError(response.status, 'OAUTH_API_ERROR',\n `Databrain API request failed with status ${response.status} using configured service credentials.`);\n }\n const errorBody = await response.text();\n logger.warn(`[API Error] ${options.method} ${options.path} status=${response.status} body=${errorBody.slice(0, 500)}`);\n let parsed: unknown = {};\n try {\n parsed = JSON.parse(errorBody);\n } catch {\n // not JSON\n }\n\n const envelope = parseErrorBody(parsed);\n const errorMsg = envelope.message || `API request failed with status ${response.status}`;\n const error = new DatabrainApiError(\n response.status,\n envelope.code || `HTTP_${response.status}`,\n errorMsg,\n parsed,\n envelope.hint,\n envelope.retryable,\n );\n\n // Retry on 429 or 5xx (but not when the error is clearly non-transient)\n // The server's own retryable flag wins over the status heuristic.\n const isTransient =\n envelope.retryable !== undefined\n ? envelope.retryable && (response.status === 429 || response.status >= 500)\n : response.status === 429 ||\n (response.status >= 500 &&\n !NON_TRANSIENT_PATTERNS.some((p) => errorMsg.includes(p)));\n if (isTransient && attempt < MAX_RETRIES) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);\n logger.warn(`Retrying in ${delay}ms (attempt ${attempt}/${MAX_RETRIES})`, {\n status: response.status,\n path: options.path,\n });\n await new Promise((r) => setTimeout(r, delay));\n lastError = error;\n continue;\n }\n\n throw error;\n }\n\n const text = await response.text();\n let data = (options.responseFormat === 'studio-sse' ? parseStudioStream(text) : text ? JSON.parse(text) : {}) as T;\n\n // Databrain API wraps most responses in { data: ... } — unwrap automatically\n if (\n !options.skipDataUnwrap &&\n data &&\n typeof data === 'object' &&\n !Array.isArray(data) &&\n 'data' in data\n ) {\n const inner = (data as Record<string, unknown>).data;\n if (inner !== null && inner !== undefined) {\n data = inner as unknown as T;\n }\n }\n\n // Store in cache\n if (cacheAllowed && options.cacheKey && options.cacheTtlMs) {\n this.cache.set(options.cacheKey, data, options.cacheTtlMs);\n }\n\n return data;\n } catch (err) {\n if (err instanceof DatabrainApiError) throw err;\n\n if (attempt < MAX_RETRIES && (err as Error)?.name === 'TimeoutError' && !options.noRetryOnTimeout) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);\n logger.warn(`Request timed out, retrying in ${delay}ms`, { path: options.path });\n await new Promise((r) => setTimeout(r, delay));\n lastError = err as Error;\n continue;\n }\n\n throw new DatabrainApiError(\n 0,\n 'NETWORK_ERROR',\n usesOAuth ? 'Failed to connect to Databrain API using configured service credentials.'\n : `Failed to connect to Databrain API: ${(err as Error).message}`,\n );\n }\n }\n\n throw lastError || new DatabrainApiError(0, 'UNKNOWN', 'Request failed after retries');\n }\n}\n","import type { DatabrainClient } from '../client/databrain-client.js';\nimport type { CustomChartBundle, Problem } from '../custom-chart/types.js';\n\n/**\n * Service-token custom chart API (brief section 5), mounted by the backend\n * under /api/mcp/custom-charts. Company scope comes from the token; nothing\n * here sends a company or tenant selector.\n */\nexport const CUSTOM_CHARTS_PATH = '/api/mcp/custom-charts';\nconst LIST_CACHE_KEY = 'list_custom_charts';\n\nexport interface CustomChartVersionSummary {\n id: string;\n versionNumber: number;\n contentHash: string;\n sizeBytes?: number;\n createdAt?: string;\n createdBy?: string | null;\n [key: string]: unknown;\n}\n\nexport interface CustomChartSummary {\n id: string;\n key: string;\n name: string;\n description?: string | null;\n status: 'private' | 'published' | string;\n chartType?: string;\n publishedVersionId?: string | null;\n previewVersionId?: string | null;\n publishedVersionNumber?: number | null;\n previewVersionNumber?: number | null;\n versionsCount?: number;\n iconSvg?: string;\n createdBy?: string | null;\n createdAt?: string;\n updatedAt?: string;\n [key: string]: unknown;\n}\n\nexport interface CustomChartDetail extends CustomChartSummary {\n versions?: CustomChartVersionSummary[];\n}\n\nexport interface ValidateResponse {\n ok: boolean;\n problems: Problem[];\n contentHash?: string;\n sizeBytes?: number;\n}\n\nexport interface UploadResponse {\n chart: CustomChartSummary;\n version: CustomChartVersionSummary;\n created: 'chart' | 'version' | 'none';\n}\n\nexport interface UploadBody extends CustomChartBundle {\n name?: string;\n description?: string;\n}\n\nconst encodeKey = (key: string): string => encodeURIComponent(key);\n\nexport class CustomChartsApi {\n constructor(private readonly client: DatabrainClient) {}\n\n async list(): Promise<CustomChartSummary[]> {\n const response = await this.client.request<{ charts?: CustomChartSummary[] } | CustomChartSummary[]>({\n method: 'GET',\n path: CUSTOM_CHARTS_PATH,\n auth: 'service',\n cacheKey: LIST_CACHE_KEY,\n cacheTtlMs: 30_000,\n });\n if (Array.isArray(response)) return response;\n return Array.isArray(response?.charts) ? response.charts : [];\n }\n\n async get(key: string): Promise<CustomChartDetail> {\n const response = await this.client.request<{ chart?: CustomChartDetail } | CustomChartDetail>({\n method: 'GET',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}`,\n auth: 'service',\n });\n const wrapped = response as { chart?: CustomChartDetail };\n return wrapped.chart ? wrapped.chart : (response as CustomChartDetail);\n }\n\n /** Never persists. The server repeats every client side check. */\n async validate(body: UploadBody): Promise<ValidateResponse> {\n const response = await this.client.request<ValidateResponse>({\n method: 'POST',\n path: `${CUSTOM_CHARTS_PATH}/validate`,\n auth: 'service',\n body,\n timeoutMs: 60_000,\n });\n return {\n ok: response.ok === true,\n problems: Array.isArray(response.problems) ? response.problems : [],\n contentHash: response.contentHash,\n sizeBytes: response.sizeBytes,\n };\n }\n\n /** Creates the chart (first upload) or a preview version; idempotent on contentHash. */\n async upload(body: UploadBody): Promise<UploadResponse> {\n return this.client.request<UploadResponse>({\n method: 'POST',\n path: CUSTOM_CHARTS_PATH,\n auth: 'service',\n body,\n timeoutMs: 60_000,\n invalidateCache: [LIST_CACHE_KEY],\n });\n }\n\n async bundle(key: string, versionId: string): Promise<CustomChartBundle> {\n return this.client.request<CustomChartBundle>({ method: 'GET',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}/versions/${encodeURIComponent(versionId)}/bundle`,\n auth: 'service' });\n }\n\n async publish(key: string, versionId?: string): Promise<{ chart: CustomChartSummary }> {\n return this.client.request<{ chart: CustomChartSummary }>({\n method: 'POST',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}/publish`,\n auth: 'service',\n body: versionId ? { versionId } : {},\n invalidateCache: [LIST_CACHE_KEY],\n });\n }\n\n async discardPreview(key: string): Promise<{ chart: CustomChartSummary }> {\n return this.client.request<{ chart: CustomChartSummary }>({\n method: 'POST',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}/discard-preview`,\n auth: 'service',\n body: {},\n invalidateCache: [LIST_CACHE_KEY],\n });\n }\n}\n","import type { DatabrainClient } from '../client/databrain-client.js';\nimport { CustomChartsApi } from '../api/custom-charts-api.js';\nimport type { CustomChartSummary } from '../api/custom-charts-api.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport type { Problem } from './types.js';\n\nexport {\n CustomChartsApi,\n CUSTOM_CHARTS_PATH,\n type CustomChartDetail,\n type CustomChartSummary,\n type CustomChartVersionSummary,\n type UploadBody,\n type UploadResponse,\n type ValidateResponse,\n} from '../api/custom-charts-api.js';\n\nexport function createCustomChartsApi(client: DatabrainClient): CustomChartsApi {\n return new CustomChartsApi(client);\n}\n\n/** The backend error body is { error: { code, message, problems? } }. Pull the problems out. */\nexport function apiProblems(err: unknown): Problem[] {\n if (!(err instanceof DatabrainApiError)) return [];\n const details = err.details as { error?: { problems?: unknown }; problems?: unknown } | undefined;\n const list = details?.error?.problems ?? details?.problems;\n if (!Array.isArray(list)) return [];\n return list.filter(\n (item): item is Problem =>\n !!item && typeof item === 'object' && typeof (item as Problem).code === 'string' && typeof (item as Problem).message === 'string',\n );\n}\n\nexport interface UploadPlan {\n action: 'create chart' | 'create version' | 'no change';\n key: string;\n existing?: CustomChartSummary;\n nextVersionNumber: number;\n summary: string;\n}\n\n/**\n * What an apply will do, computed from the chart list (brief section 8):\n * \"will create chart\" when the key is new, \"will create version n\" when the\n * chart exists. The content-hash replay (\"none\") is only known to the server.\n */\nexport function planUpload(key: string, existing: CustomChartSummary | undefined, versionsKnown?: number): UploadPlan {\n if (!existing) {\n return { action: 'create chart', key, nextVersionNumber: 1, summary: `will create chart custom:${key} (version 1, status private)` };\n }\n const count = typeof versionsKnown === 'number' ? versionsKnown : typeof existing.versionsCount === 'number' ? existing.versionsCount : undefined;\n const next = count !== undefined ? count + 1 : NaN;\n const label = Number.isFinite(next) ? `version ${next}` : 'the next version';\n return {\n action: 'create version',\n key,\n existing,\n nextVersionNumber: Number.isFinite(next) ? next : 0,\n summary: `will create ${label} of custom:${key} in preview (status stays ${existing.status}; the published version keeps serving until you publish)`,\n };\n}\n","import { readTemplateFiles } from './assets.js';\nimport { isId, validateManifest } from './manifest.js';\nimport type { CustomChartOption, CustomChartSlot, Problem } from './types.js';\n\n/**\n * A chart project is the template folder with manifest.json and package.json\n * rewritten for the requested key, name, slots and options. Every other file\n * is copied verbatim so that the guide, the types and the sample chart stay\n * byte identical to what the tests exercise.\n */\nexport interface ScaffoldInput {\n key: string;\n name: string;\n description?: string;\n slots?: CustomChartSlot[];\n options?: CustomChartOption[];\n}\n\nexport interface ScaffoldResult {\n ok: boolean;\n problems: Problem[];\n files: Record<string, string>;\n nextStep: string;\n}\n\nconst DEFAULT_SLOTS: CustomChartSlot[] = [\n { id: 'category', label: 'Category', role: 'dimension', types: ['string'], required: true, description: 'One bar per value.' },\n { id: 'value', label: 'Value', role: 'measure', types: ['number'], required: true },\n { id: 'target', label: 'Target', role: 'measure', types: ['number'], required: false },\n];\n\nexport function scaffoldProject(input: ScaffoldInput): ScaffoldResult {\n const template = readTemplateFiles();\n const templateManifest = JSON.parse(template['manifest.json']) as {\n slots: CustomChartSlot[];\n options: CustomChartOption[];\n };\n const problems: Problem[] = [];\n if (!isId(input.key)) {\n problems.push({\n code: 'manifest.id.invalid',\n path: '/id',\n message: 'key must match ^[a-z][a-z0-9_-]{0,63}$ (lowercase letters, digits, _ and -).',\n });\n }\n const name = typeof input.name === 'string' ? input.name.trim() : '';\n if (name.length === 0 || name.length > 256) {\n problems.push({ code: 'manifest.name.invalid', path: '/name', message: 'name must be 1 to 256 characters.' });\n }\n\n const manifest: Record<string, unknown> = {\n schemaVersion: 2,\n id: input.key,\n name,\n version: '1.0.0',\n ...(input.description ? { description: input.description } : {}),\n slots: input.slots && input.slots.length > 0 ? input.slots : templateManifest.slots ?? DEFAULT_SLOTS,\n options: input.options ?? templateManifest.options,\n };\n problems.push(...validateManifest(manifest));\n\n const packageJson = JSON.parse(template['package.json']) as Record<string, unknown>;\n packageJson.name = input.key;\n\n const files: Record<string, string> = {};\n for (const [file, content] of Object.entries(template)) {\n if (file === 'manifest.json') files[file] = JSON.stringify(manifest, null, 2) + '\\n';\n else if (file === 'package.json') files[file] = JSON.stringify(packageJson, null, 2) + '\\n';\n else files[file] = content;\n }\n\n return {\n ok: problems.length === 0,\n problems,\n files,\n nextStep:\n 'Write these files into a folder, edit chart.ts, then run `databrain custom-chart validate` and ' +\n '`databrain custom-chart build`. Upload with `databrain custom-chart upload` or the custom_chart_upload tool.',\n };\n}\n"],"mappings":"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","names":["z"]}
|
package/dist/cli.js
CHANGED
|
@@ -4,15 +4,17 @@ import {
|
|
|
4
4
|
DatabrainClient,
|
|
5
5
|
MCP_SERVER_VERSION,
|
|
6
6
|
apiProblems,
|
|
7
|
-
buildProject,
|
|
8
7
|
createCustomChartsApi,
|
|
9
8
|
planUpload,
|
|
10
|
-
readProject,
|
|
11
9
|
redactSecrets,
|
|
12
10
|
registerSecret,
|
|
13
|
-
scaffoldProject
|
|
11
|
+
scaffoldProject
|
|
12
|
+
} from "./chunk-RHK62ZJ6.js";
|
|
13
|
+
import {
|
|
14
|
+
buildProject,
|
|
15
|
+
readProject,
|
|
14
16
|
validateProject
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-Q5EYDKSA.js";
|
|
16
18
|
|
|
17
19
|
// src/cli/index.ts
|
|
18
20
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/index.ts","../src/cli/dev-server.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';\nimport { basename, join, resolve } from 'node:path';\nimport { createInterface } from 'node:readline';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport { buildProject, validateProject } from '../custom-chart/bundle.js';\nimport { apiProblems, createCustomChartsApi, planUpload } from '../custom-chart/api.js';\nimport { scaffoldProject } from '../custom-chart/scaffold.js';\nimport type { Problem } from '../custom-chart/types.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport { redactSecrets, registerSecret } from '../utils/logger.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { startDevServer } from './dev-server.js';\n\n/**\n * The `databrain` bin (DAT-925). One command family today: custom-chart.\n * Output is for people: short lines, problems as `path code message`,\n * exit code 0 on success and 1 on any problem.\n */\n\nconst USAGE = `databrain ${MCP_SERVER_VERSION}\n\nUsage:\n databrain custom-chart init <dir> [--key <key>] [--name <name>] [--force]\n databrain custom-chart validate [dir]\n databrain custom-chart build [dir]\n databrain custom-chart upload [dir] [--publish] [--yes]\n databrain custom-chart dev [dir] [--port <port>]\n\nCommands:\n init Create a chart project from the template (manifest.json, chart.ts, chart.css, icon.svg, guide).\n validate Check manifest.json, icon.svg and chart.css against the rules. No build.\n build Compile chart.ts with esbuild and write dist/index.js, manifest.json, icon.svg, index.css, bundle.zip.\n upload Build, validate on the server, show the plan, then upload as a private chart or a preview version.\n Needs DATABRAIN_SERVICE_TOKEN (and DATABRAIN_API_URL for self hosted). --publish publishes it.\n dev Serve dist/index.js with sample-data.json in a local sandbox page for a quick look.\n\nOptions:\n --yes Skip the confirmation prompt on upload.\n --publish Publish the uploaded version right away.\n --force Let init write into a folder that is not empty.\n --help Show this help.\n --version Show the version.\n`;\n\ninterface ParsedArgs {\n positional: string[];\n flags: Record<string, string | boolean>;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const positional: string[] = [];\n const flags: Record<string, string | boolean> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith('--')) {\n const [name, inlineValue] = arg.slice(2).split('=', 2);\n if (inlineValue !== undefined) {\n flags[name] = inlineValue;\n } else if (['key', 'name', 'port'].includes(name) && i + 1 < argv.length && !argv[i + 1].startsWith('--')) {\n flags[name] = argv[++i];\n } else {\n flags[name] = true;\n }\n } else {\n positional.push(arg);\n }\n }\n return { positional, flags };\n}\n\n// The service token must never reach a terminal or a CI log, wherever it is\n// quoted from (a server error body that echoes the Authorization header, a\n// wrapped fetch error, a problem message). It is registered with the logger\n// in commandUpload; every line printed here is scrubbed the same way.\nconst out = (line: string): void => {\n process.stdout.write(redactSecrets(line) + '\\n');\n};\nconst err = (line: string): void => {\n process.stderr.write(redactSecrets(line) + '\\n');\n};\n\nexport function formatProblemLines(problems: Problem[]): string[] {\n return problems.map((p) => `${p.path || '/'} ${p.code} ${p.message}`);\n}\n\nfunction printProblems(problems: Problem[]): void {\n for (const line of formatProblemLines(problems)) err(line);\n}\n\nfunction keyFromDir(dir: string): string {\n const raw = basename(resolve(dir)).toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');\n const key = raw.length === 0 ? 'my-chart' : /^[a-z]/.test(raw) ? raw : `chart-${raw}`;\n return key.slice(0, 64);\n}\n\nfunction nameFromKey(key: string): string {\n const words = key.split(/[-_]+/).filter(Boolean);\n return words.map((word, index) => (index === 0 ? word[0].toUpperCase() + word.slice(1) : word)).join(' ');\n}\n\nfunction commandInit(args: ParsedArgs): number {\n const dir = args.positional[0];\n if (!dir) {\n err('init needs a folder: databrain custom-chart init <dir>');\n return 1;\n }\n const target = resolve(dir);\n if (existsSync(target) && readdirSync(target).length > 0 && args.flags.force !== true) {\n err(`${target} is not empty. Pick another folder or pass --force.`);\n return 1;\n }\n const key = typeof args.flags.key === 'string' ? args.flags.key : keyFromDir(target);\n const name = typeof args.flags.name === 'string' ? args.flags.name : nameFromKey(key);\n const result = scaffoldProject({ key, name });\n if (!result.ok) {\n printProblems(result.problems);\n return 1;\n }\n mkdirSync(target, { recursive: true });\n for (const [file, content] of Object.entries(result.files)) {\n writeFileSync(join(target, file), content);\n }\n out(`Created chart project ${key} in ${target}`);\n for (const file of Object.keys(result.files)) out(` ${file}`);\n out('');\n out('Next:');\n out(` cd ${dir}`);\n out(' edit chart.ts (read AGENTS.md first)');\n out(' databrain custom-chart validate');\n out(' databrain custom-chart build');\n out(' databrain custom-chart upload');\n return 0;\n}\n\nfunction commandValidate(args: ParsedArgs): number {\n const dir = resolve(args.positional[0] ?? '.');\n const result = validateProject(dir);\n if (!result.ok) {\n err(`${result.problems.length} problem${result.problems.length === 1 ? '' : 's'} in ${dir}:`);\n printProblems(result.problems);\n return 1;\n }\n const manifest = result.project.manifest as { id: string; slots: unknown[]; options: unknown[] };\n out(`Valid: custom:${manifest.id} (${manifest.slots.length} slots, ${manifest.options.length} options) in ${dir}`);\n return 0;\n}\n\nasync function commandBuild(args: ParsedArgs): Promise<number> {\n const dir = resolve(args.positional[0] ?? '.');\n const result = await buildProject(dir);\n if (!result.ok || !result.outputs) {\n err(`Build failed with ${result.problems.length} problem${result.problems.length === 1 ? '' : 's'}:`);\n printProblems(result.problems);\n return 1;\n }\n out(`Built custom:${result.outputs.manifest.id}`);\n for (const path of result.written) out(` ${path}`);\n out(` contentHash ${result.outputs.contentHash}`);\n out(` size ${result.outputs.sizeBytes} bytes (bundle ${Buffer.byteLength(result.outputs.bundleJs, 'utf8')} of 2097152)`);\n return 0;\n}\n\nasync function confirm(question: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n err('stdin is not a terminal. Pass --yes to upload without a prompt.');\n return false;\n }\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolveAnswer) => rl.question(question, resolveAnswer));\n rl.close();\n return answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes';\n}\n\nasync function commandUpload(args: ParsedArgs): Promise<number> {\n const dir = resolve(args.positional[0] ?? '.');\n const serviceToken = process.env.DATABRAIN_SERVICE_TOKEN;\n if (!serviceToken) {\n err('DATABRAIN_SERVICE_TOKEN is not set. Create one in Settings > Service Tokens and export it, then try again.');\n return 1;\n }\n registerSecret(serviceToken);\n const apiUrl = process.env.DATABRAIN_API_URL || 'https://api.usedatabrain.com';\n if (!process.env.DATABRAIN_API_URL) out(`DATABRAIN_API_URL is not set, using ${apiUrl}`);\n\n const built = await buildProject(dir);\n if (!built.ok || !built.outputs) {\n err('Build failed:');\n printProblems(built.problems);\n return 1;\n }\n const { manifest, bundleJs, bundleCss, iconSvg } = built.outputs;\n const key = manifest.id;\n out(`Built custom:${key} (${built.outputs.contentHash.slice(0, 12)}, ${built.outputs.sizeBytes} bytes)`);\n\n const client = new DatabrainClient({ serviceToken, apiUrl, demoDomain: 'https://demo.usedatabrain.com' });\n const api = createCustomChartsApi(client);\n try {\n const validation = await api.validate({ manifest, bundleJs, bundleCss, iconSvg });\n if (!validation.ok) {\n err('The server refused the bundle:');\n printProblems(validation.problems);\n return 1;\n }\n out('Server validation: ok');\n const existing = (await api.list()).find((chart) => chart.key === key);\n const plan = planUpload(key, existing);\n out(`Plan: ${plan.summary}`);\n if (args.flags.publish === true) out('Then: publish that version (every dashboard using this key will render it).');\n\n if (args.flags.yes !== true) {\n const go = await confirm('Continue? [y/N] ');\n if (!go) {\n out('Cancelled. Nothing was uploaded.');\n return 1;\n }\n }\n\n const uploaded = await api.upload({ manifest, bundleJs, bundleCss, iconSvg });\n const versionNumber = uploaded.version?.versionNumber ?? '?';\n if (uploaded.created === 'none') out(`This exact bundle is already v${versionNumber} of custom:${key}.`);\n else if (uploaded.created === 'chart') out(`Created custom:${key} (v${versionNumber}, private).`);\n else out(`Uploaded v${versionNumber} of custom:${key}. It is in preview until you publish it.`);\n\n if (args.flags.publish === true) {\n const published = await api.publish(key, uploaded.version?.id);\n out(`Published v${versionNumber} of custom:${key} (status ${published.chart?.status ?? 'published'}).`);\n } else if (uploaded.created !== 'none') {\n out(`Publish it with: databrain custom-chart upload --publish, the publish_custom_chart tool, or Settings > Developers > Custom charts.`);\n }\n return 0;\n } catch (error) {\n if (error instanceof DatabrainApiError) {\n err(`Error ${error.status || ''} ${error.code}: ${error.message}`.replace(/\\s+/g, ' ').trim());\n printProblems(apiProblems(error));\n return 1;\n }\n err(`Unexpected error: ${(error as Error).message}`);\n return 1;\n }\n}\n\nasync function commandDev(args: ParsedArgs): Promise<number> {\n const dir = resolve(args.positional[0] ?? '.');\n const port = typeof args.flags.port === 'string' ? Number(args.flags.port) : 4174;\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n err('--port must be a number between 1 and 65535.');\n return 1;\n }\n const built = await buildProject(dir);\n if (!built.ok) {\n err('Build failed:');\n printProblems(built.problems);\n return 1;\n }\n const server = await startDevServer({ dir, port });\n out(`Custom chart dev page: http://127.0.0.1:${server.port}/ (Ctrl+C to stop)`);\n out('The page rebuilds on every reload. Edit chart.ts, save, reload.');\n await new Promise<void>((resolveDone) => {\n const stop = () => {\n server.close();\n resolveDone();\n };\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n });\n return 0;\n}\n\nexport async function runCli(argv: string[]): Promise<number> {\n const args = parseArgs(argv);\n if (args.flags.version === true) {\n out(MCP_SERVER_VERSION);\n return 0;\n }\n const [family, command, ...rest] = args.positional;\n if (args.flags.help === true || !family) {\n out(USAGE);\n return family ? 0 : 1;\n }\n if (family !== 'custom-chart') {\n err(`Unknown command \"${family}\".`);\n out(USAGE);\n return 1;\n }\n const sub: ParsedArgs = { positional: rest, flags: args.flags };\n switch (command) {\n case 'init':\n return commandInit(sub);\n case 'validate':\n return commandValidate(sub);\n case 'build':\n return commandBuild(sub);\n case 'upload':\n return commandUpload(sub);\n case 'dev':\n return commandDev(sub);\n default:\n err(command ? `Unknown custom-chart command \"${command}\".` : 'custom-chart needs a command.');\n out(USAGE);\n return 1;\n }\n}\n\nconst invokedDirectly =\n typeof process.argv[1] === 'string' && /(^|[\\\\/])(cli\\.(js|mjs|ts)|databrain)$/.test(process.argv[1]);\nif (invokedDirectly) {\n runCli(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (error: Error) => {\n err(`Unexpected error: ${error.message}`);\n process.exitCode = 1;\n },\n );\n}\n","import { createServer, type Server } from 'node:http';\nimport { buildProject, readProject } from '../custom-chart/bundle.js';\nimport type { CustomChartManifest, CustomChartOption } from '../custom-chart/types.js';\n\n/**\n * `databrain custom-chart dev`: a local page that hosts dist/index.js the way\n * the product does, in a sandboxed frame that only sees the sandbox protocol.\n * It is a tiny copy of the plugin relay, enough to see the chart draw, click\n * and resize with sample-data.json. Like the product host it injects\n * chart.css into the frame; the bundle itself carries no css. It is not the\n * product sandbox: no CSP, no relay frame, no limits. Never point it at real\n * data.\n */\nexport interface DevServerOptions {\n dir: string;\n port: number;\n}\n\nconst LIGHT_THEME = {\n fontFamily: 'Inter, system-ui, sans-serif',\n fontSize: 12,\n textColor: '#1f2937',\n textSecondaryColor: '#6b7280',\n background: '#ffffff',\n surfaceColor: '#f9fafb',\n borderColor: '#e5e7eb',\n borderRadius: 6,\n primaryColor: '#3366cc',\n palette: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#990099', '#0099c6', '#dd4477', '#66aa00'],\n};\n\nconst DARK_THEME = {\n ...LIGHT_THEME,\n textColor: '#f3f4f6',\n textSecondaryColor: '#9ca3af',\n background: '#111827',\n surfaceColor: '#1f2937',\n borderColor: '#374151',\n primaryColor: '#60a5fa',\n palette: ['#60a5fa', '#f87171', '#fbbf24', '#34d399', '#c084fc', '#22d3ee', '#f472b6', '#a3e635'],\n};\n\nfunction defaultOptions(manifest: CustomChartManifest): Record<string, string | number | boolean> {\n const options: Record<string, string | number | boolean> = {};\n for (const option of manifest.options as CustomChartOption[]) options[option.id] = option.default;\n return options;\n}\n\n/** Deterministic sample data from the manifest slots when sample-data.json is absent. */\nfunction generateSampleData(manifest: CustomChartManifest): { columns: unknown[]; rows: unknown[][] } {\n const labels = ['North', 'South', 'East', 'West', 'Central', 'Coast', 'Island', 'Valley'];\n const columns = manifest.slots.map((slot, index) => ({\n id: `${slot.id}_${index}`,\n label: slot.label,\n type: slot.types[0],\n filterable: slot.role === 'dimension',\n slotId: slot.id,\n }));\n const rows = labels.map((label, row) =>\n manifest.slots.map((slot, column) => {\n const type = slot.types[0];\n if (type === 'string') return `${label}${column > 0 ? ` ${column}` : ''}`;\n if (type === 'boolean') return (row + column) % 2 === 0;\n return Math.round(1000 - row * 97 + column * 53 + ((row * 31 + column * 17) % 90));\n }),\n );\n return { columns, rows };\n}\n\nconst PAGE = `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Custom chart dev</title>\n<style>\n :root { color-scheme: light dark; }\n body { margin: 0; font: 14px system-ui, sans-serif; display: grid; grid-template-rows: auto 1fr; height: 100vh; }\n header { display: flex; gap: 12px; align-items: center; padding: 10px 16px; border-bottom: 1px solid #8884; }\n header code { font-size: 12px; opacity: 0.8; }\n main { display: grid; grid-template-columns: 1fr 320px; min-height: 0; }\n #stage { padding: 16px; display: flex; align-items: flex-start; justify-content: center; overflow: auto; }\n #frame { border: 1px solid #8886; border-radius: 8px; background: transparent; }\n aside { border-left: 1px solid #8884; padding: 12px 16px; overflow: auto; }\n aside h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 8px; opacity: 0.7; }\n #log { font: 12px ui-monospace, monospace; white-space: pre-wrap; margin: 0; }\n select, button { font: inherit; }\n</style>\n</head>\n<body>\n<header>\n <strong id=\"title\">Custom chart</strong>\n <code id=\"key\"></code>\n <label>Theme <select id=\"theme\"><option value=\"light\">light</option><option value=\"dark\">dark</option></select></label>\n <label>Size <select id=\"size\"><option value=\"600x320\">600 x 320</option><option value=\"900x420\">900 x 420</option><option value=\"320x240\">320 x 240</option></select></label>\n <button id=\"reload\">Rebuild and reload</button>\n</header>\n<main>\n <div id=\"stage\"><iframe id=\"frame\" sandbox=\"allow-scripts\" width=\"600\" height=\"320\" title=\"Custom chart\"></iframe></div>\n <aside><h2>Events</h2><pre id=\"log\">Waiting for ready...</pre></aside>\n</main>\n<script>\n(async () => {\n const log = document.getElementById('log');\n const frame = document.getElementById('frame');\n const themeSelect = document.getElementById('theme');\n const sizeSelect = document.getElementById('size');\n const say = (line) => { log.textContent = (line + '\\\\n' + log.textContent).slice(0, 8000); };\n const [manifest, bundle, sample, themes, css] = await Promise.all([\n fetch('/manifest.json').then((r) => r.json()),\n fetch('/bundle.js').then((r) => r.ok ? r.text() : r.text().then((t) => { throw new Error(t); })).catch((e) => { say('BUILD ERROR\\\\n' + e.message); return null; }),\n fetch('/sample-data.json').then((r) => r.json()),\n fetch('/themes.json').then((r) => r.json()),\n fetch('/index.css').then((r) => r.text()),\n ]);\n document.getElementById('title').textContent = manifest.name;\n document.getElementById('key').textContent = 'custom:' + manifest.id;\n if (!bundle) return;\n let revision = 0;\n const data = () => ({\n columns: sample.columns, rows: sample.rows,\n options: Object.assign({}, sample.defaults, sample.options || {}),\n theme: themes[themeSelect.value], language: 'en'\n });\n const send = () => frame.contentWindow.postMessage({ protocol: 'dbn-chart-sandbox', version: 1, channel: 'dev', type: 'data', revision: ++revision, data: data() }, '*');\n addEventListener('message', (event) => {\n if (event.source !== frame.contentWindow) return;\n const m = event.data || {};\n if (m.type === 'ready') { say('ready'); send(); return; }\n if (m.type === 'resize' && Number.isFinite(m.height)) frame.height = Math.max(80, Math.min(1200, m.height));\n say(m.type + ' ' + JSON.stringify(m, (k, v) => (k === 'protocol' || k === 'version' || k === 'channel' || k === 'type') ? undefined : v));\n });\n const boot = '(' + (function () {\n var channel = 'dev'; var revision = -1; var latest = null; var listener = null;\n var send = function (payload) { parent.postMessage(Object.assign({ protocol: 'dbn-chart-sandbox', version: 1, channel: channel }, payload), '*'); };\n window.dbnSandbox = Object.freeze({\n onData: function (cb) { listener = cb; send({ type: 'ready' }); if (latest) cb(latest); },\n click: function (rowIndex, columnId) { send({ type: 'click', revision: revision, rowIndex: rowIndex, columnId: columnId }); },\n filter: function (columnId, values) { send({ type: 'filter', revision: revision, columnId: columnId, values: values }); },\n resize: function (height) { send({ type: 'resize', height: height }); }\n });\n addEventListener('error', function (e) { send({ type: 'error', code: 'runtime', message: String(e.message || e) }); });\n addEventListener('message', function (event) {\n var m = event.data; if (!m || m.type !== 'data' || m.revision <= revision) return;\n revision = m.revision; latest = m.data; if (listener) listener(latest);\n });\n }).toString() + ')();';\n const child = '<!doctype html><html><head><meta charset=\"utf-8\"><style>html,body{margin:0;width:100%;height:100%;overflow:hidden}body{font:14px system-ui}#chart{width:100%;height:100%}</style><style>' + css.replace(/<\\\\/style/gi, '') + '</style></head><body><div id=\"chart\"></div><script>' + boot + '<\\\\/script><script>' + bundle.replace(/<\\\\/script/gi, '<\\\\\\\\/script') + '<\\\\/script></body></html>';\n const mount = () => { frame.srcdoc = child; };\n themeSelect.addEventListener('change', () => { if (revision > 0) send(); });\n sizeSelect.addEventListener('change', () => { const [w, h] = sizeSelect.value.split('x'); frame.width = w; frame.height = h; if (revision > 0) send(); });\n document.getElementById('reload').addEventListener('click', () => location.reload());\n mount();\n})();\n</script>\n</body>\n</html>\n`;\n\nexport async function startDevServer(options: DevServerOptions): Promise<{ port: number; close: () => void; server: Server }> {\n const server = createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', 'http://127.0.0.1');\n const json = (status: number, body: unknown) => {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(JSON.stringify(body));\n };\n try {\n if (url.pathname === '/') {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(PAGE);\n return;\n }\n if (url.pathname === '/bundle.js') {\n const result = await buildProject(options.dir, { write: false });\n if (!result.ok || !result.outputs) {\n res.writeHead(422, { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(result.problems.map((p) => `${p.path || '/'} ${p.code} ${p.message}`).join('\\n'));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(result.outputs.bundleJs);\n return;\n }\n const project = readProject(options.dir);\n const manifest = project.manifest as CustomChartManifest | undefined;\n if (url.pathname === '/manifest.json') {\n json(manifest ? 200 : 422, manifest ?? { problems: project.problems });\n return;\n }\n if (url.pathname === '/sample-data.json') {\n if (!manifest) {\n json(422, { problems: project.problems });\n return;\n }\n const sample = (project.sampleData as { columns?: unknown[]; rows?: unknown[][]; options?: unknown } | undefined) ?? generateSampleData(manifest);\n json(200, { ...sample, defaults: defaultOptions(manifest) });\n return;\n }\n if (url.pathname === '/index.css') {\n // The host injects bundleCss into the frame; this page plays the host.\n res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(project.css ?? '');\n return;\n }\n if (url.pathname === '/themes.json') {\n json(200, { light: LIGHT_THEME, dark: DARK_THEME });\n return;\n }\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('not found');\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });\n res.end((err as Error).message);\n }\n });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(options.port, '127.0.0.1', () => resolve());\n });\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : options.port;\n return { port, server, close: () => server.close() };\n}\n"],"mappings":"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","names":["err","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/index.ts","../src/cli/dev-server.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';\nimport { basename, join, resolve } from 'node:path';\nimport { createInterface } from 'node:readline';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport { buildProject, validateProject } from '../custom-chart/bundle.js';\nimport { apiProblems, createCustomChartsApi, planUpload } from '../custom-chart/api.js';\nimport { scaffoldProject } from '../custom-chart/scaffold.js';\nimport type { Problem } from '../custom-chart/types.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport { redactSecrets, registerSecret } from '../utils/logger.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { startDevServer } from './dev-server.js';\n\n/**\n * The `databrain` bin (DAT-925). One command family today: custom-chart.\n * Output is for people: short lines, problems as `path code message`,\n * exit code 0 on success and 1 on any problem.\n */\n\nconst USAGE = `databrain ${MCP_SERVER_VERSION}\n\nUsage:\n databrain custom-chart init <dir> [--key <key>] [--name <name>] [--force]\n databrain custom-chart validate [dir]\n databrain custom-chart build [dir]\n databrain custom-chart upload [dir] [--publish] [--yes]\n databrain custom-chart dev [dir] [--port <port>]\n\nCommands:\n init Create a chart project from the template (manifest.json, chart.ts, chart.css, icon.svg, guide).\n validate Check manifest.json, icon.svg and chart.css against the rules. No build.\n build Compile chart.ts with esbuild and write dist/index.js, manifest.json, icon.svg, index.css, bundle.zip.\n upload Build, validate on the server, show the plan, then upload as a private chart or a preview version.\n Needs DATABRAIN_SERVICE_TOKEN (and DATABRAIN_API_URL for self hosted). --publish publishes it.\n dev Serve dist/index.js with sample-data.json in a local sandbox page for a quick look.\n\nOptions:\n --yes Skip the confirmation prompt on upload.\n --publish Publish the uploaded version right away.\n --force Let init write into a folder that is not empty.\n --help Show this help.\n --version Show the version.\n`;\n\ninterface ParsedArgs {\n positional: string[];\n flags: Record<string, string | boolean>;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const positional: string[] = [];\n const flags: Record<string, string | boolean> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith('--')) {\n const [name, inlineValue] = arg.slice(2).split('=', 2);\n if (inlineValue !== undefined) {\n flags[name] = inlineValue;\n } else if (['key', 'name', 'port'].includes(name) && i + 1 < argv.length && !argv[i + 1].startsWith('--')) {\n flags[name] = argv[++i];\n } else {\n flags[name] = true;\n }\n } else {\n positional.push(arg);\n }\n }\n return { positional, flags };\n}\n\n// The service token must never reach a terminal or a CI log, wherever it is\n// quoted from (a server error body that echoes the Authorization header, a\n// wrapped fetch error, a problem message). It is registered with the logger\n// in commandUpload; every line printed here is scrubbed the same way.\nconst out = (line: string): void => {\n process.stdout.write(redactSecrets(line) + '\\n');\n};\nconst err = (line: string): void => {\n process.stderr.write(redactSecrets(line) + '\\n');\n};\n\nexport function formatProblemLines(problems: Problem[]): string[] {\n return problems.map((p) => `${p.path || '/'} ${p.code} ${p.message}`);\n}\n\nfunction printProblems(problems: Problem[]): void {\n for (const line of formatProblemLines(problems)) err(line);\n}\n\nfunction keyFromDir(dir: string): string {\n const raw = basename(resolve(dir)).toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');\n const key = raw.length === 0 ? 'my-chart' : /^[a-z]/.test(raw) ? raw : `chart-${raw}`;\n return key.slice(0, 64);\n}\n\nfunction nameFromKey(key: string): string {\n const words = key.split(/[-_]+/).filter(Boolean);\n return words.map((word, index) => (index === 0 ? word[0].toUpperCase() + word.slice(1) : word)).join(' ');\n}\n\nfunction commandInit(args: ParsedArgs): number {\n const dir = args.positional[0];\n if (!dir) {\n err('init needs a folder: databrain custom-chart init <dir>');\n return 1;\n }\n const target = resolve(dir);\n if (existsSync(target) && readdirSync(target).length > 0 && args.flags.force !== true) {\n err(`${target} is not empty. Pick another folder or pass --force.`);\n return 1;\n }\n const key = typeof args.flags.key === 'string' ? args.flags.key : keyFromDir(target);\n const name = typeof args.flags.name === 'string' ? args.flags.name : nameFromKey(key);\n const result = scaffoldProject({ key, name });\n if (!result.ok) {\n printProblems(result.problems);\n return 1;\n }\n mkdirSync(target, { recursive: true });\n for (const [file, content] of Object.entries(result.files)) {\n writeFileSync(join(target, file), content);\n }\n out(`Created chart project ${key} in ${target}`);\n for (const file of Object.keys(result.files)) out(` ${file}`);\n out('');\n out('Next:');\n out(` cd ${dir}`);\n out(' edit chart.ts (read AGENTS.md first)');\n out(' databrain custom-chart validate');\n out(' databrain custom-chart build');\n out(' databrain custom-chart upload');\n return 0;\n}\n\nfunction commandValidate(args: ParsedArgs): number {\n const dir = resolve(args.positional[0] ?? '.');\n const result = validateProject(dir);\n if (!result.ok) {\n err(`${result.problems.length} problem${result.problems.length === 1 ? '' : 's'} in ${dir}:`);\n printProblems(result.problems);\n return 1;\n }\n const manifest = result.project.manifest as { id: string; slots: unknown[]; options: unknown[] };\n out(`Valid: custom:${manifest.id} (${manifest.slots.length} slots, ${manifest.options.length} options) in ${dir}`);\n return 0;\n}\n\nasync function commandBuild(args: ParsedArgs): Promise<number> {\n const dir = resolve(args.positional[0] ?? '.');\n const result = await buildProject(dir);\n if (!result.ok || !result.outputs) {\n err(`Build failed with ${result.problems.length} problem${result.problems.length === 1 ? '' : 's'}:`);\n printProblems(result.problems);\n return 1;\n }\n out(`Built custom:${result.outputs.manifest.id}`);\n for (const path of result.written) out(` ${path}`);\n out(` contentHash ${result.outputs.contentHash}`);\n out(` size ${result.outputs.sizeBytes} bytes (bundle ${Buffer.byteLength(result.outputs.bundleJs, 'utf8')} of 2097152)`);\n return 0;\n}\n\nasync function confirm(question: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n err('stdin is not a terminal. Pass --yes to upload without a prompt.');\n return false;\n }\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolveAnswer) => rl.question(question, resolveAnswer));\n rl.close();\n return answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes';\n}\n\nasync function commandUpload(args: ParsedArgs): Promise<number> {\n const dir = resolve(args.positional[0] ?? '.');\n const serviceToken = process.env.DATABRAIN_SERVICE_TOKEN;\n if (!serviceToken) {\n err('DATABRAIN_SERVICE_TOKEN is not set. Create one in Settings > Service Tokens and export it, then try again.');\n return 1;\n }\n registerSecret(serviceToken);\n const apiUrl = process.env.DATABRAIN_API_URL || 'https://api.usedatabrain.com';\n if (!process.env.DATABRAIN_API_URL) out(`DATABRAIN_API_URL is not set, using ${apiUrl}`);\n\n const built = await buildProject(dir);\n if (!built.ok || !built.outputs) {\n err('Build failed:');\n printProblems(built.problems);\n return 1;\n }\n const { manifest, bundleJs, bundleCss, iconSvg } = built.outputs;\n const key = manifest.id;\n out(`Built custom:${key} (${built.outputs.contentHash.slice(0, 12)}, ${built.outputs.sizeBytes} bytes)`);\n\n const client = new DatabrainClient({ serviceToken, apiUrl, demoDomain: 'https://demo.usedatabrain.com' });\n const api = createCustomChartsApi(client);\n try {\n const validation = await api.validate({ manifest, bundleJs, bundleCss, iconSvg });\n if (!validation.ok) {\n err('The server refused the bundle:');\n printProblems(validation.problems);\n return 1;\n }\n out('Server validation: ok');\n const existing = (await api.list()).find((chart) => chart.key === key);\n const plan = planUpload(key, existing);\n out(`Plan: ${plan.summary}`);\n if (args.flags.publish === true) out('Then: publish that version (every dashboard using this key will render it).');\n\n if (args.flags.yes !== true) {\n const go = await confirm('Continue? [y/N] ');\n if (!go) {\n out('Cancelled. Nothing was uploaded.');\n return 1;\n }\n }\n\n const uploaded = await api.upload({ manifest, bundleJs, bundleCss, iconSvg });\n const versionNumber = uploaded.version?.versionNumber ?? '?';\n if (uploaded.created === 'none') out(`This exact bundle is already v${versionNumber} of custom:${key}.`);\n else if (uploaded.created === 'chart') out(`Created custom:${key} (v${versionNumber}, private).`);\n else out(`Uploaded v${versionNumber} of custom:${key}. It is in preview until you publish it.`);\n\n if (args.flags.publish === true) {\n const published = await api.publish(key, uploaded.version?.id);\n out(`Published v${versionNumber} of custom:${key} (status ${published.chart?.status ?? 'published'}).`);\n } else if (uploaded.created !== 'none') {\n out(`Publish it with: databrain custom-chart upload --publish, the publish_custom_chart tool, or Settings > Developers > Custom charts.`);\n }\n return 0;\n } catch (error) {\n if (error instanceof DatabrainApiError) {\n err(`Error ${error.status || ''} ${error.code}: ${error.message}`.replace(/\\s+/g, ' ').trim());\n printProblems(apiProblems(error));\n return 1;\n }\n err(`Unexpected error: ${(error as Error).message}`);\n return 1;\n }\n}\n\nasync function commandDev(args: ParsedArgs): Promise<number> {\n const dir = resolve(args.positional[0] ?? '.');\n const port = typeof args.flags.port === 'string' ? Number(args.flags.port) : 4174;\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n err('--port must be a number between 1 and 65535.');\n return 1;\n }\n const built = await buildProject(dir);\n if (!built.ok) {\n err('Build failed:');\n printProblems(built.problems);\n return 1;\n }\n const server = await startDevServer({ dir, port });\n out(`Custom chart dev page: http://127.0.0.1:${server.port}/ (Ctrl+C to stop)`);\n out('The page rebuilds on every reload. Edit chart.ts, save, reload.');\n await new Promise<void>((resolveDone) => {\n const stop = () => {\n server.close();\n resolveDone();\n };\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n });\n return 0;\n}\n\nexport async function runCli(argv: string[]): Promise<number> {\n const args = parseArgs(argv);\n if (args.flags.version === true) {\n out(MCP_SERVER_VERSION);\n return 0;\n }\n const [family, command, ...rest] = args.positional;\n if (args.flags.help === true || !family) {\n out(USAGE);\n return family ? 0 : 1;\n }\n if (family !== 'custom-chart') {\n err(`Unknown command \"${family}\".`);\n out(USAGE);\n return 1;\n }\n const sub: ParsedArgs = { positional: rest, flags: args.flags };\n switch (command) {\n case 'init':\n return commandInit(sub);\n case 'validate':\n return commandValidate(sub);\n case 'build':\n return commandBuild(sub);\n case 'upload':\n return commandUpload(sub);\n case 'dev':\n return commandDev(sub);\n default:\n err(command ? `Unknown custom-chart command \"${command}\".` : 'custom-chart needs a command.');\n out(USAGE);\n return 1;\n }\n}\n\nconst invokedDirectly =\n typeof process.argv[1] === 'string' && /(^|[\\\\/])(cli\\.(js|mjs|ts)|databrain)$/.test(process.argv[1]);\nif (invokedDirectly) {\n runCli(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (error: Error) => {\n err(`Unexpected error: ${error.message}`);\n process.exitCode = 1;\n },\n );\n}\n","import { createServer, type Server } from 'node:http';\nimport { buildProject, readProject } from '../custom-chart/bundle.js';\nimport type { CustomChartManifest, CustomChartOption } from '../custom-chart/types.js';\n\n/**\n * `databrain custom-chart dev`: a local page that hosts dist/index.js the way\n * the product does, in a sandboxed frame that only sees the sandbox protocol.\n * It is a tiny copy of the plugin relay, enough to see the chart draw, click\n * and resize with sample-data.json. Like the product host it injects\n * chart.css into the frame; the bundle itself carries no css. It is not the\n * product sandbox: no CSP, no relay frame, no limits. Never point it at real\n * data.\n */\nexport interface DevServerOptions {\n dir: string;\n port: number;\n}\n\nconst LIGHT_THEME = {\n fontFamily: 'Inter, system-ui, sans-serif',\n fontSize: 12,\n textColor: '#1f2937',\n textSecondaryColor: '#6b7280',\n background: '#ffffff',\n surfaceColor: '#f9fafb',\n borderColor: '#e5e7eb',\n borderRadius: 6,\n primaryColor: '#3366cc',\n palette: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#990099', '#0099c6', '#dd4477', '#66aa00'],\n};\n\nconst DARK_THEME = {\n ...LIGHT_THEME,\n textColor: '#f3f4f6',\n textSecondaryColor: '#9ca3af',\n background: '#111827',\n surfaceColor: '#1f2937',\n borderColor: '#374151',\n primaryColor: '#60a5fa',\n palette: ['#60a5fa', '#f87171', '#fbbf24', '#34d399', '#c084fc', '#22d3ee', '#f472b6', '#a3e635'],\n};\n\nfunction defaultOptions(manifest: CustomChartManifest): Record<string, string | number | boolean> {\n const options: Record<string, string | number | boolean> = {};\n for (const option of manifest.options as CustomChartOption[]) options[option.id] = option.default;\n return options;\n}\n\n/** Deterministic sample data from the manifest slots when sample-data.json is absent. */\nfunction generateSampleData(manifest: CustomChartManifest): { columns: unknown[]; rows: unknown[][] } {\n const labels = ['North', 'South', 'East', 'West', 'Central', 'Coast', 'Island', 'Valley'];\n const columns = manifest.slots.map((slot, index) => ({\n id: `${slot.id}_${index}`,\n label: slot.label,\n type: slot.types[0],\n filterable: slot.role === 'dimension',\n slotId: slot.id,\n }));\n const rows = labels.map((label, row) =>\n manifest.slots.map((slot, column) => {\n const type = slot.types[0];\n if (type === 'string') return `${label}${column > 0 ? ` ${column}` : ''}`;\n if (type === 'boolean') return (row + column) % 2 === 0;\n return Math.round(1000 - row * 97 + column * 53 + ((row * 31 + column * 17) % 90));\n }),\n );\n return { columns, rows };\n}\n\nconst PAGE = `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Custom chart dev</title>\n<style>\n :root { color-scheme: light dark; }\n body { margin: 0; font: 14px system-ui, sans-serif; display: grid; grid-template-rows: auto 1fr; height: 100vh; }\n header { display: flex; gap: 12px; align-items: center; padding: 10px 16px; border-bottom: 1px solid #8884; }\n header code { font-size: 12px; opacity: 0.8; }\n main { display: grid; grid-template-columns: 1fr 320px; min-height: 0; }\n #stage { padding: 16px; display: flex; align-items: flex-start; justify-content: center; overflow: auto; }\n #frame { border: 1px solid #8886; border-radius: 8px; background: transparent; }\n aside { border-left: 1px solid #8884; padding: 12px 16px; overflow: auto; }\n aside h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 8px; opacity: 0.7; }\n #log { font: 12px ui-monospace, monospace; white-space: pre-wrap; margin: 0; }\n select, button { font: inherit; }\n</style>\n</head>\n<body>\n<header>\n <strong id=\"title\">Custom chart</strong>\n <code id=\"key\"></code>\n <label>Theme <select id=\"theme\"><option value=\"light\">light</option><option value=\"dark\">dark</option></select></label>\n <label>Size <select id=\"size\"><option value=\"600x320\">600 x 320</option><option value=\"900x420\">900 x 420</option><option value=\"320x240\">320 x 240</option></select></label>\n <button id=\"reload\">Rebuild and reload</button>\n</header>\n<main>\n <div id=\"stage\"><iframe id=\"frame\" sandbox=\"allow-scripts\" width=\"600\" height=\"320\" title=\"Custom chart\"></iframe></div>\n <aside><h2>Events</h2><pre id=\"log\">Waiting for ready...</pre></aside>\n</main>\n<script>\n(async () => {\n const log = document.getElementById('log');\n const frame = document.getElementById('frame');\n const themeSelect = document.getElementById('theme');\n const sizeSelect = document.getElementById('size');\n const say = (line) => { log.textContent = (line + '\\\\n' + log.textContent).slice(0, 8000); };\n const [manifest, bundle, sample, themes, css] = await Promise.all([\n fetch('/manifest.json').then((r) => r.json()),\n fetch('/bundle.js').then((r) => r.ok ? r.text() : r.text().then((t) => { throw new Error(t); })).catch((e) => { say('BUILD ERROR\\\\n' + e.message); return null; }),\n fetch('/sample-data.json').then((r) => r.json()),\n fetch('/themes.json').then((r) => r.json()),\n fetch('/index.css').then((r) => r.text()),\n ]);\n document.getElementById('title').textContent = manifest.name;\n document.getElementById('key').textContent = 'custom:' + manifest.id;\n if (!bundle) return;\n let revision = 0;\n const data = () => ({\n columns: sample.columns, rows: sample.rows,\n options: Object.assign({}, sample.defaults, sample.options || {}),\n theme: themes[themeSelect.value], language: 'en'\n });\n const send = () => frame.contentWindow.postMessage({ protocol: 'dbn-chart-sandbox', version: 1, channel: 'dev', type: 'data', revision: ++revision, data: data() }, '*');\n addEventListener('message', (event) => {\n if (event.source !== frame.contentWindow) return;\n const m = event.data || {};\n if (m.type === 'ready') { say('ready'); send(); return; }\n if (m.type === 'resize' && Number.isFinite(m.height)) frame.height = Math.max(80, Math.min(1200, m.height));\n say(m.type + ' ' + JSON.stringify(m, (k, v) => (k === 'protocol' || k === 'version' || k === 'channel' || k === 'type') ? undefined : v));\n });\n const boot = '(' + (function () {\n var channel = 'dev'; var revision = -1; var latest = null; var listener = null;\n var send = function (payload) { parent.postMessage(Object.assign({ protocol: 'dbn-chart-sandbox', version: 1, channel: channel }, payload), '*'); };\n window.dbnSandbox = Object.freeze({\n onData: function (cb) { listener = cb; send({ type: 'ready' }); if (latest) cb(latest); },\n click: function (rowIndex, columnId) { send({ type: 'click', revision: revision, rowIndex: rowIndex, columnId: columnId }); },\n filter: function (columnId, values) { send({ type: 'filter', revision: revision, columnId: columnId, values: values }); },\n resize: function (height) { send({ type: 'resize', height: height }); }\n });\n addEventListener('error', function (e) { send({ type: 'error', code: 'runtime', message: String(e.message || e) }); });\n addEventListener('message', function (event) {\n var m = event.data; if (!m || m.type !== 'data' || m.revision <= revision) return;\n revision = m.revision; latest = m.data; if (listener) listener(latest);\n });\n }).toString() + ')();';\n const child = '<!doctype html><html><head><meta charset=\"utf-8\"><style>html,body{margin:0;width:100%;height:100%;overflow:hidden}body{font:14px system-ui}#chart{width:100%;height:100%}</style><style>' + css.replace(/<\\\\/style/gi, '') + '</style></head><body><div id=\"chart\"></div><script>' + boot + '<\\\\/script><script>' + bundle.replace(/<\\\\/script/gi, '<\\\\\\\\/script') + '<\\\\/script></body></html>';\n const mount = () => { frame.srcdoc = child; };\n themeSelect.addEventListener('change', () => { if (revision > 0) send(); });\n sizeSelect.addEventListener('change', () => { const [w, h] = sizeSelect.value.split('x'); frame.width = w; frame.height = h; if (revision > 0) send(); });\n document.getElementById('reload').addEventListener('click', () => location.reload());\n mount();\n})();\n</script>\n</body>\n</html>\n`;\n\nexport async function startDevServer(options: DevServerOptions): Promise<{ port: number; close: () => void; server: Server }> {\n const server = createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', 'http://127.0.0.1');\n const json = (status: number, body: unknown) => {\n res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(JSON.stringify(body));\n };\n try {\n if (url.pathname === '/') {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(PAGE);\n return;\n }\n if (url.pathname === '/bundle.js') {\n const result = await buildProject(options.dir, { write: false });\n if (!result.ok || !result.outputs) {\n res.writeHead(422, { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(result.problems.map((p) => `${p.path || '/'} ${p.code} ${p.message}`).join('\\n'));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'text/javascript; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(result.outputs.bundleJs);\n return;\n }\n const project = readProject(options.dir);\n const manifest = project.manifest as CustomChartManifest | undefined;\n if (url.pathname === '/manifest.json') {\n json(manifest ? 200 : 422, manifest ?? { problems: project.problems });\n return;\n }\n if (url.pathname === '/sample-data.json') {\n if (!manifest) {\n json(422, { problems: project.problems });\n return;\n }\n const sample = (project.sampleData as { columns?: unknown[]; rows?: unknown[][]; options?: unknown } | undefined) ?? generateSampleData(manifest);\n json(200, { ...sample, defaults: defaultOptions(manifest) });\n return;\n }\n if (url.pathname === '/index.css') {\n // The host injects bundleCss into the frame; this page plays the host.\n res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-store' });\n res.end(project.css ?? '');\n return;\n }\n if (url.pathname === '/themes.json') {\n json(200, { light: LIGHT_THEME, dark: DARK_THEME });\n return;\n }\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('not found');\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });\n res.end((err as Error).message);\n }\n });\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(options.port, '127.0.0.1', () => resolve());\n });\n const address = server.address();\n const port = typeof address === 'object' && address ? address.port : options.port;\n return { port, server, close: () => server.close() };\n}\n"],"mappings":"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","names":["err","resolve"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";(()=>{var ie=Object.defineProperty;var ae=(e,t,n)=>t in e?ie(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var V=(e,t,n)=>ae(e,typeof t!="symbol"?t+"":t,n);var f=e=>e!==null&&typeof e=="object"&&Object.getPrototypeOf(e)===Object.prototype,O=(e,t)=>Object.keys(e).length===t.length&&t.every(n=>Object.hasOwn(e,n)),S=e=>typeof e=="string"&&/^[a-z][a-z0-9_-]{0,63}$/.test(e)&&!["constructor","prototype","__proto__"].includes(e),T=(e,t=256)=>typeof e=="string"&&e.length>0&&e.length<=t,_=e=>e===null||typeof e=="boolean"||typeof e=="number"&&Number.isFinite(e)||typeof e=="string"&&e.length<=4096,A=e=>new TextEncoder().encode(JSON.stringify(e)).byteLength;var de=32*1024,X=16,K=32,ce=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$/,q=/^#[0-9a-fA-F]{6}$/,le=/^[a-z]{2}(-[A-Z]{2})?$/,fe=["string","number","boolean"],M=["schemaVersion","id","name","slots","options"],me=[...M,"version","description","translations"],L=["id","label","role","types","required"],ue=[...L,"description"],ge=["id","label","type","default"],Y={boolean:[],number:["min","max"],string:["maxLength"],select:["choices"],color:[]},pe={boolean:["group"],number:["group","step"],string:["group"],select:["group"],color:["group"]},j=e=>typeof e=="number"&&Number.isFinite(e),I=e=>typeof e=="string"&&e.length<=256,D=class{constructor(){V(this,"list",[])}add(t,n,o){this.list.push({code:t,path:n,message:o})}},C=(e,t,n,o,s,i,d=r=>`${o}.${r}`)=>{let r=new Set([...s,...i]),c=!0;for(let a of Object.keys(t))r.has(a)||(e.add(`${o}.unknownKey`,`${n}/${a}`,`Remove "${a}". It is not a known key here.`),c=!1);for(let a of s)Object.hasOwn(t,a)||(e.add(d(a),`${n}/${a}`,`Add the required key "${a}".`),c=!1);return c},be=(e,t,n,o)=>{if(!f(t)){e.add("manifest.slot.invalid",n,"A slot must be an object.");return}if(!C(e,t,n,"manifest.slot",L,o?ue.slice(L.length):[]))return;S(t.id)||e.add("manifest.slot.id",`${n}/id`,'Use a slot id that starts with a letter and has only lowercase letters, digits, "_" or "-" (max 64).'),T(t.label)||e.add("manifest.slot.label",`${n}/label`,"Give the slot a label of 1 to 256 characters.");let s=t.role;s!=="dimension"&&s!=="measure"&&e.add("manifest.slot.role",`${n}/role`,'Set role to "dimension" or "measure".'),typeof t.required!="boolean"&&e.add("manifest.slot.required",`${n}/required`,"Set required to true or false.");let i=t.types;Array.isArray(i)&&i.length>0&&i.length<=3&&new Set(i).size===i.length&&i.every(r=>fe.includes(r))?s==="measure"&&(i.length!==1||i[0]!=="number")&&e.add("manifest.slot.types",`${n}/types`,'A measure slot accepts only ["number"].'):e.add("manifest.slot.types",`${n}/types`,'List 1 to 3 unique types from "string", "number" and "boolean".'),Object.hasOwn(t,"description")&&!T(t.description,256)&&e.add("manifest.slot.description",`${n}/description`,"A slot description is 1 to 256 characters.")},ye=(e,t,n,o)=>{if(!f(t)){e.add("manifest.option.invalid",n,"An option must be an object.");return}let s=t.id;if(typeof s=="string"&&(s==="theme"||s.startsWith("theme."))){e.add("manifest.option.reserved",`${n}/id`,'The option id "theme" is reserved for the host theme. Choose another id.');return}let i=typeof t.type=="string"?t.type:"",d=Object.hasOwn(Y,i)?Y[i]:void 0;if(!d||i==="color"&&!o){e.add("manifest.option.type",`${n}/type`,o?'Set type to "boolean", "number", "string", "select" or "color".':'Set type to "boolean", "number", "string" or "select". "color" needs schemaVersion 2.');return}if(C(e,t,n,"manifest.option",[...ge,...d],o?pe[i]:[]))switch(S(s)||e.add("manifest.option.id",`${n}/id`,'Use an option id that starts with a letter and has only lowercase letters, digits, "_" or "-" (max 64).'),T(t.label)||e.add("manifest.option.label",`${n}/label`,"Give the option a label of 1 to 256 characters."),Object.hasOwn(t,"group")&&!T(t.group,64)&&e.add("manifest.option.group",`${n}/group`,"A group label is 1 to 64 characters."),i){case"boolean":typeof t.default!="boolean"&&e.add("manifest.option.default",`${n}/default`,"A boolean option needs a true or false default.");break;case"number":{if(!(j(t.min)&&j(t.max)&&t.min<=t.max)){e.add("manifest.option.range",`${n}/min`,"Set finite min and max with min <= max.");break}let c=t.min,a=t.max;(!j(t.default)||t.default<c||t.default>a)&&e.add("manifest.option.default",`${n}/default`,"The default must be a finite number between min and max."),Object.hasOwn(t,"step")&&(!j(t.step)||t.step<=0||t.step>a-c)&&e.add("manifest.option.step",`${n}/step`,"A step must be a positive number no larger than max - min.");break}case"string":{let r=t.maxLength;if(!j(r)||!Number.isInteger(r)||r<1||r>1024){e.add("manifest.option.maxLength",`${n}/maxLength`,"Set maxLength to an integer from 1 to 1024.");break}(typeof t.default!="string"||t.default.length>r)&&e.add("manifest.option.default",`${n}/default`,"The default must be a string no longer than maxLength.");break}case"select":{let r=t.choices;if(!(Array.isArray(r)&&r.length>0&&r.length<=32&&r.every(a=>f(a)&&Object.keys(a).length===2&&S(a.value)&&T(a.label))&&new Set(r.map(a=>a.value)).size===r.length)){e.add("manifest.option.choices",`${n}/choices`,"List 1 to 32 choices, each { value, label } with unique ids as values.");break}r.some(a=>a.value===t.default)||e.add("manifest.option.default",`${n}/default`,"The default must be the value of one of the choices.");break}case"color":(typeof t.default!="string"||!q.test(t.default))&&e.add("manifest.option.color",`${n}/default`,'A color default is a six-digit hex string such as "#3366cc".');break}},he=(e,t,n,o)=>{let s="/translations";if(!f(t)){e.add("manifest.translations.invalid",s,"translations must be an object keyed by language code.");return}for(let[i,d]of Object.entries(t)){let r=`${s}/${i}`;if(!le.test(i)){e.add("manifest.translations.language",r,'Use a language code such as "fr" or "pt-BR".');continue}if(!f(d)){e.add("manifest.translations.invalid",r,"A translation must be an object.");continue}if(C(e,d,r,"manifest.translations",[],["name","description","slots","options"])){for(let c of["name","description"])Object.hasOwn(d,c)&&!I(d[c])&&e.add("manifest.translations.text",`${r}/${c}`,"A translated string is at most 256 characters.");if(Object.hasOwn(d,"slots")){let c=d.slots;if(!f(c))e.add("manifest.translations.invalid",`${r}/slots`,"slots must be an object keyed by slot id.");else for(let[a,l]of Object.entries(c)){let g=`${r}/slots/${a}`;if(!n.some(m=>m.id===a)){e.add("manifest.translations.slot",g,`There is no slot "${a}" to translate.`);continue}if(!f(l)||!C(e,l,g,"manifest.translations",[],["label","description"])){f(l)||e.add("manifest.translations.invalid",g,"A slot translation must be an object.");continue}for(let m of["label","description"])Object.hasOwn(l,m)&&!I(l[m])&&e.add("manifest.translations.text",`${g}/${m}`,"A translated string is at most 256 characters.")}}if(Object.hasOwn(d,"options")){let c=d.options;if(!f(c))e.add("manifest.translations.invalid",`${r}/options`,"options must be an object keyed by option id.");else for(let[a,l]of Object.entries(c)){let g=`${r}/options/${a}`,m=o.find(p=>p.id===a);if(!m){e.add("manifest.translations.option",g,`There is no option "${a}" to translate.`);continue}if(!f(l)||!C(e,l,g,"manifest.translations",[],["label","group","choices"])){f(l)||e.add("manifest.translations.invalid",g,"An option translation must be an object.");continue}for(let p of["label","group"])Object.hasOwn(l,p)&&!I(l[p])&&e.add("manifest.translations.text",`${g}/${p}`,"A translated string is at most 256 characters.");if(Object.hasOwn(l,"choices")){let p=l.choices;if(!f(p)){e.add("manifest.translations.invalid",`${g}/choices`,"choices must be an object keyed by choice value.");continue}for(let[x,u]of Object.entries(p)){let y=`${g}/choices/${x}`;m.type!=="select"||!m.choices.some(b=>b.value===x)?e.add("manifest.translations.choice",y,`There is no choice "${x}" on option "${a}".`):I(u)||e.add("manifest.translations.text",y,"A translated string is at most 256 characters.")}}}}}}},xe=e=>{let t=new D;if(!f(e))return t.add("manifest.invalid","","The manifest must be a JSON object."),t.list;let n=e.schemaVersion;if(n!==1&&n!==2)return t.add("manifest.schemaVersion.unsupported","/schemaVersion","Set schemaVersion to 1 or 2."),t.list;let o=n===2;if(!C(t,e,"","manifest",M,o?me.slice(M.length):[],d=>`manifest.${d}.required`))return t.list;S(e.id)||t.add("manifest.id.invalid","/id",'Use an id that starts with a letter and has only lowercase letters, digits, "_" or "-" (max 64). The chart type becomes "custom:<id>".'),T(e.name)||t.add("manifest.name.invalid","/name","Give the chart a name of 1 to 256 characters."),Object.hasOwn(e,"version")&&(typeof e.version!="string"||!ce.test(e.version))&&t.add("manifest.version.invalid","/version",'version must be a semver string such as "1.2.0".'),Object.hasOwn(e,"description")&&!T(e.description,500)&&t.add("manifest.description.invalid","/description","description is 1 to 500 characters.");let s=e.slots;if(!Array.isArray(s))t.add("manifest.slots.invalid","/slots","slots must be an array.");else if(s.length===0)t.add("manifest.slots.empty","/slots","Declare at least one slot.");else if(s.length>X)t.add("manifest.slots.tooMany","/slots",`Declare at most ${X} slots.`);else{for(let[r,c]of s.entries())be(t,c,`/slots/${r}`,o);let d=new Set;s.forEach((r,c)=>{!f(r)||typeof r.id!="string"||(d.has(r.id)&&t.add("manifest.slot.duplicate",`/slots/${c}/id`,`Slot id "${r.id}" is used more than once.`),d.add(r.id))})}let i=e.options;if(!Array.isArray(i))t.add("manifest.options.invalid","/options","options must be an array (it may be empty).");else if(i.length>K)t.add("manifest.options.tooMany","/options",`Declare at most ${K} options.`);else{for(let[r,c]of i.entries())ye(t,c,`/options/${r}`,o);let d=new Set;i.forEach((r,c)=>{!f(r)||typeof r.id!="string"||(d.has(r.id)&&t.add("manifest.option.duplicate",`/options/${c}/id`,`Option id "${r.id}" is used more than once.`),d.add(r.id))})}return t.list.length===0&&Object.hasOwn(e,"translations")&&he(t,e.translations,s,i),t.list.length===0&&A(e)>de&&t.add("manifest.size","","The manifest must be 32 KiB or smaller as JSON."),t.list},N=e=>xe(e).length===0?e:null;var R=(e,t)=>{if(!N(e)||!f(t)||Object.keys(t).some(o=>!e.options.some(s=>s.id===o)))return null;let n={};for(let o of e.options){let s=Object.hasOwn(t,o.id)?t[o.id]:o.default,i=!1;switch(o.type){case"boolean":i=typeof s=="boolean";break;case"number":i=typeof s=="number"&&Number.isFinite(s)&&s>=o.min&&s<=o.max;break;case"string":i=typeof s=="string"&&s.length<=o.maxLength;break;case"select":i=o.choices.some(d=>d.value===s);break;case"color":i=typeof s=="string"&&q.test(s);break}if(!i)return null;n[o.id]=s}return n};var J=2*1024*1024,G=256*1024,B="allow-scripts",P="camera 'none'; microphone 'none'; geolocation 'none'; payment 'none'; usb 'none'; clipboard-read 'none'; clipboard-write 'none'; display-capture 'none'; fullscreen 'none'",Se=e=>{if(!N(e.manifest))throw new Error("Invalid chart manifest. Check schema version 1.");if(typeof e.javascript!="string"||e.javascript.trim().length===0||e.javascript.length>J||new TextEncoder().encode(e.javascript).byteLength>J)throw new Error("Chart bundle must contain JavaScript and be no larger than 2 MiB.");if(e.css!==void 0&&(typeof e.css!="string"||e.css.length>G||new TextEncoder().encode(e.css).byteLength>G))throw new Error("Chart stylesheet must be text no larger than 256 KiB.")},we=e=>`<style>${e.replace(/<\/(style)/gi,"<\\/$1")}</style>`,Oe=e=>["default-src 'none'","script-src 'unsafe-inline' data:","script-src-attr 'none'","style-src 'unsafe-inline'","connect-src 'none'","img-src data:","font-src 'none'","media-src 'none'","object-src 'none'","worker-src 'none'","manifest-src 'none'","base-uri 'none'","form-action 'none'",`frame-src ${e?"blob:":"'none'"}`].join("; "),Te=e=>{let t=new TextEncoder().encode(e),n="";for(let o of t)n+=String.fromCharCode(o);return btoa(n)},ke=e=>e.replace(/<\/(script)/gi,"<\\/$1").replace(/<!--/g,"<\\!--"),H=(e,t)=>`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="${Oe(t)}"><meta name="referrer" content="no-referrer"><meta http-equiv="x-dns-prefetch-control" content="off"><title>Custom chart</title><style>html,body{margin:0;width:100%;height:100%;overflow:hidden}body{font:14px system-ui;color:CanvasText;background:Canvas}iframe{border:0;width:100%;height:100%}</style></head><body>${e}</body></html>`,Ae=e=>`(() => {
|
|
2
|
+
'use strict';
|
|
3
|
+
const channel = ${JSON.stringify(e)};
|
|
4
|
+
const send = (payload) => parent.postMessage({protocol:'dbn-chart-sandbox',version:1,channel,...payload}, '*');
|
|
5
|
+
let revision = -1;
|
|
6
|
+
let latest = null;
|
|
7
|
+
let listener = null;
|
|
8
|
+
Object.defineProperty(window, 'dbnSandbox', {value: Object.freeze({
|
|
9
|
+
onData(callback) { if (typeof callback !== 'function') return; listener = callback; send({type:'ready'}); if (latest) callback(latest); },
|
|
10
|
+
click(rowIndex, columnId) { send({type:'click',revision,rowIndex,columnId}); },
|
|
11
|
+
filter(columnId, values) { send({type:'filter',revision,columnId,values}); },
|
|
12
|
+
resize(height) { send({type:'resize',height}); }
|
|
13
|
+
}), writable:false, configurable:false});
|
|
14
|
+
addEventListener('error', () => send({type:'error',code:'runtime'}));
|
|
15
|
+
addEventListener('message', (event) => {
|
|
16
|
+
const m = event.data;
|
|
17
|
+
if (event.source !== parent || event.origin !== 'null' || !m || m.protocol !== 'dbn-chart-sandbox' ||
|
|
18
|
+
m.version !== 1 || m.channel !== channel || m.type !== 'data' || !Number.isSafeInteger(m.revision) ||
|
|
19
|
+
m.revision <= revision || !m.data || !Array.isArray(m.data.columns) || !Array.isArray(m.data.rows) ||
|
|
20
|
+
!m.data.options || typeof m.data.options !== 'object') return;
|
|
21
|
+
revision = m.revision; latest = m.data;
|
|
22
|
+
if (listener) listener(latest);
|
|
23
|
+
});
|
|
24
|
+
})();`,W=(e,t,n)=>{if(Se(e),!/^[a-zA-Z0-9-]{16,80}$/.test(t))throw new Error("Invalid sandbox channel.");let o=n==="null"?null:new URL(n);if(o&&(!["http:","https:"].includes(o.protocol)||o.origin!==n))throw new Error("Sandbox host must have an HTTP or opaque origin.");let s=H(`${e.css?we(e.css):""}<div id="chart" role="img" aria-label="Custom chart"></div><script>${Ae(t)}<\/script><script>${ke(e.javascript)}<\/script>`,!1),i=`(() => {
|
|
25
|
+
'use strict';
|
|
26
|
+
const channel = ${JSON.stringify(t)};
|
|
27
|
+
const hostOrigin = ${JSON.stringify(n)};
|
|
28
|
+
const hostTarget = hostOrigin === 'null' ? '*' : hostOrigin;
|
|
29
|
+
const childHtml = new TextDecoder().decode(Uint8Array.from(atob('${Te(s)}'), c => c.charCodeAt(0)));
|
|
30
|
+
const frame = document.createElement('iframe');
|
|
31
|
+
frame.setAttribute('sandbox', '${B}');
|
|
32
|
+
frame.setAttribute('allow', ${JSON.stringify(P)});
|
|
33
|
+
frame.referrerPolicy = 'no-referrer';
|
|
34
|
+
frame.title = 'Custom chart content';
|
|
35
|
+
const url = URL.createObjectURL(new Blob([childHtml], {type:'text/html'}));
|
|
36
|
+
let loads = 0;
|
|
37
|
+
let active = true;
|
|
38
|
+
let ready = false;
|
|
39
|
+
let count = 0;
|
|
40
|
+
let start = performance.now();
|
|
41
|
+
frame.addEventListener('load', () => {
|
|
42
|
+
URL.revokeObjectURL(url);
|
|
43
|
+
if (++loads > 1) {
|
|
44
|
+
active = false; frame.remove();
|
|
45
|
+
parent.postMessage({protocol:'dbn-chart-sandbox',version:1,channel,type:'error',code:'navigation'}, hostTarget);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
addEventListener('message', (event) => {
|
|
49
|
+
const m = event.data;
|
|
50
|
+
if (!active || !m || typeof m !== 'object' || m.protocol !== 'dbn-chart-sandbox' || m.version !== 1 || m.channel !== channel) return;
|
|
51
|
+
if (event.source === parent && event.origin === hostOrigin && m.type === 'data' && ready) {
|
|
52
|
+
frame.contentWindow.postMessage(m, '*'); return;
|
|
53
|
+
}
|
|
54
|
+
if (event.source !== frame.contentWindow || event.origin !== 'null' || !['ready','click','filter','resize','error'].includes(m.type)) return;
|
|
55
|
+
const now = performance.now();
|
|
56
|
+
if (now - start >= 1000) { start = now; count = 0; }
|
|
57
|
+
if (++count > 60) return;
|
|
58
|
+
if (m.type === 'ready') { if (ready) return; ready = true; }
|
|
59
|
+
parent.postMessage(m, hostTarget);
|
|
60
|
+
});
|
|
61
|
+
frame.src = url;
|
|
62
|
+
document.body.append(frame);
|
|
63
|
+
})();`;return H(`<script>${i}<\/script>`,!0)};var ee="dbn-chart-sandbox",$e=1,z=1024*1024,F=5e3,Ee=32,Z=["fontFamily","textColor","textSecondaryColor","background","surfaceColor","borderColor","primaryColor"],Q=["fontSize","borderRadius"],Ce=/^[a-z]{2}(-[A-Z]{2})?$/,je=e=>!f(e)||!O(e,[...Z,...Q,"palette"])?!1:Z.every(t=>typeof e[t]=="string"&&e[t].length<=256)&&Q.every(t=>typeof e[t]=="number"&&Number.isFinite(e[t]))&&Array.isArray(e.palette)&&e.palette.length<=Ee&&e.palette.every(t=>typeof t=="string"&&t.length<=256),_e=["theme","language"],Ie=e=>{let t=Object.keys(e),n=_e.filter(o=>Object.hasOwn(e,o)).length;return t.length===3+n&&["columns","rows","options"].every(o=>Object.hasOwn(e,o))},v=e=>{if(!f(e)||!Ie(e)||Object.hasOwn(e,"theme")&&!je(e.theme)||Object.hasOwn(e,"language")&&!(typeof e.language=="string"&&Ce.test(e.language))||!Array.isArray(e.columns)||e.columns.length<1||e.columns.length>64||!Array.isArray(e.rows)||e.rows.length>F||!f(e.options)||Object.keys(e.options).length>32)return!1;let t=e.columns;return!t.every(n=>f(n)&&(O(n,["id","label","type","filterable"])||O(n,["id","label","type","filterable","slotId"])&&S(n.slotId))&&S(n.id)&&T(n.label)&&["string","number","boolean"].includes(n.type)&&typeof n.filterable=="boolean")||new Set(t.map(({id:n})=>n)).size!==t.length||!e.rows.every(n=>Array.isArray(n)&&n.length===t.length&&n.every((o,s)=>_(o)&&(o===null||typeof o===t[s].type)))||!Object.entries(e.options).every(([n,o])=>S(n)&&o!==null&&_(o))?!1:A(e)<=z},te=(e,t)=>{if(!t.source||e.source!==t.source||e.origin!=="null")return null;let n=e.data;if(!f(n)||n.protocol!==ee||n.version!==1||n.channel!==t.channel)return null;let o=["protocol","version","channel","type"];if(n.type==="ready")return O(n,o)?n:null;if(n.type==="error")return O(n,[...o,"code"])&&(n.code==="navigation"||n.code==="runtime")?n:null;if(n.type==="resize")return O(n,[...o,"height"])&&typeof n.height=="number"&&Number.isInteger(n.height)&&n.height>=80&&n.height<=1200?n:null;if(n.revision!==t.revision)return null;let s=t.data.columns.findIndex(({id:i})=>i===n.columnId);return s<0?null:n.type==="click"?O(n,[...o,"revision","rowIndex","columnId"])&&typeof n.rowIndex=="number"&&Number.isInteger(n.rowIndex)&&n.rowIndex>=0&&n.rowIndex<t.data.rows.length?n:null:n.type==="filter"&&O(n,[...o,"revision","columnId","values"])&&t.data.columns[s].filterable&&Array.isArray(n.values)&&n.values.length<=100&&n.values.every(i=>_(i)&&t.data.rows.some(d=>d[s]===i))&&A(n)<=32*1024?n:null},ne=(e,t,n)=>({protocol:ee,version:$e,channel:e,type:"data",revision:t,data:n});var oe=4096,Re=(e,t)=>{let n=e.toLowerCase().replace(/[^a-z0-9_-]+/g,"_").replace(/^[^a-z]+|_+$/g,"").slice(0,48);return n?`${n}_${t}`:`col_${t}`},se=e=>{if(typeof e=="number")return Number.isFinite(e)?e:null;if(typeof e=="boolean")return e?1:0;if(typeof e=="string"&&e.trim()!==""){let t=Number(e);return Number.isFinite(t)?t:null}return null},ve=e=>{if(e==null)return null;if(e instanceof Date)return Number.isNaN(e.getTime())?null:e.toISOString();let t=typeof e=="string"?e:String(e);return t.length>oe?t.slice(0,oe):t},Me=e=>typeof e=="boolean"?e:e==="true"?!0:e==="false"?!1:e===1||e===0?e===1:null,Le=(e,t)=>{if(e.types.length===1)return e.types[0];let n=t.filter(o=>o!=null);return e.types.includes("boolean")&&n.length>0&&n.every(o=>typeof o=="boolean")?"boolean":e.types.includes("number")&&n.length>0&&n.every(o=>se(o)!==null)?"number":e.types.includes("string")?"string":e.types[0]},De=(e,t)=>{switch(e){case"number":return se(t);case"boolean":return Me(t);default:return ve(t)}},Ne=(e,t)=>{let n=R(e,t);if(n)return n;let o={};for(let[s,i]of Object.entries(t))R(e,{[s]:i})&&(o[s]=i);return R(e,o)??{}},re=({manifest:e,config:t,rows:n,columns:o,theme:s,language:i})=>{let d=new Map;if(o)for(let u of o)d.set(u.key,u.label);if(d.size===0&&n[0])for(let u of Object.keys(n[0]))d.set(u,u);let r=new Set,c={},a=[],l=[];for(let u of e.slots){let y=(t.bindings[u.id]??[]).filter(b=>d.has(b));y.length===0&&u.required&&l.push(u);for(let b of y){let h=Re(b,a.length);for(;r.has(h)||!S(h);)h=`col_${a.length}_${r.size}`;r.add(h),c[h]=b;let $=n.slice(0,200).map(E=>E[b]);a.push({id:h,label:(d.get(b)||b).slice(0,256)||h,type:Le(u,$),filterable:u.role==="dimension",slotId:u.id,source:b})}}let g=n.length,m=Math.min(g,F),p=u=>({columns:a.map(({id:y,label:b,type:h,filterable:$,slotId:E})=>({id:y,label:b,type:h,filterable:$,slotId:E})),rows:n.slice(0,u).map(y=>a.map(b=>De(b.type,y[b.source]))),options:Ne(e,t.options),...s?{theme:s}:{},...i?{language:i}:{}}),x=p(m);for(;m>0&&A(x)>z;)m=Math.floor(m/2),x=p(m);return a.length===0?{data:x,truncated:!1,totalRows:g,sources:c,missingSlots:l}:(v(x)||(x={...p(0)},m=0),{data:x,truncated:m<g,totalRows:g,sources:c,missingSlots:l})};function Be(e,t,n,o,s){let i=!0,d=!1,r=0,c=0,a=crypto.randomUUID(),l=document.createElement("iframe");l.title=t.manifest.name,l.setAttribute("sandbox",B),l.setAttribute("allow",P),l.referrerPolicy="no-referrer",l.style.cssText="width:100%;height:320px;border:0;display:block";let g=()=>{let w=getComputedStyle(document.body),k=getComputedStyle(document.documentElement).colorScheme==="dark";return{fontFamily:w.fontFamily,fontSize:parseFloat(w.fontSize)||14,textColor:w.color,textSecondaryColor:w.color,background:w.backgroundColor,surfaceColor:w.backgroundColor,borderColor:k?"#454652":"#dedee6",borderRadius:8,primaryColor:"#7c6cf0",palette:["#7c6cf0","#35b9a7","#e6a548","#df7197","#539bdd","#a985db"]}},m=()=>re({manifest:t.manifest,config:n,rows:o,theme:g()}),p=m();if(p.missingSlots.length||!v(p.data))throw Error("Bind each required custom chart slot to a result column. The rows are below.");let x=W({manifest:t.manifest,javascript:t.bundleJs,css:t.bundleCss},a,globalThis.origin),u=URL.createObjectURL(new Blob([x],{type:"text/html"})),y=()=>{!i||!d||(p=m(),l.contentWindow?.postMessage(ne(a,++r,p.data),"*"))},b=()=>{i=!1,clearTimeout(E),U.disconnect(),removeEventListener("message",$),URL.revokeObjectURL(u),l.remove()},h=w=>{b(),s(w)},$=w=>{if(!i)return;let k=te(w,{source:l.contentWindow,channel:a,revision:r,data:p.data});if(k)if(k.type==="ready"){if(d)return;d=!0,clearTimeout(E),s(""),y()}else k.type==="error"?h("The custom chart could not run safely. The rows are below."):k.type==="resize"&&(l.style.height=`${k.height}px`)},E=setTimeout(()=>h("This host could not open the custom chart sandbox. The rows are below."),8e3),U=new ResizeObserver(y);return l.addEventListener("load",()=>{URL.revokeObjectURL(u),++c>1&&h("The custom chart navigated away. The rows are below.")}),addEventListener("message",$),l.src=u,e.append(l),U.observe(e),{dispose:b,update:y}}Object.assign(globalThis,{DbnCustomChartRuntime:{mount:Be}});})();
|
package/dist/index.js
CHANGED
|
@@ -3,17 +3,18 @@ import {
|
|
|
3
3
|
DEFAULT_POSTHOG_API_KEY,
|
|
4
4
|
DEFAULT_POSTHOG_HOST,
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DM7S4R57.js";
|
|
7
7
|
import {
|
|
8
8
|
registerAllPrompts,
|
|
9
9
|
registerAllResources,
|
|
10
10
|
registerAllTools
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ES2LUP4U.js";
|
|
12
12
|
import {
|
|
13
13
|
DatabrainClient,
|
|
14
14
|
MCP_SERVER_VERSION,
|
|
15
15
|
logger
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-RHK62ZJ6.js";
|
|
17
|
+
import "./chunk-Q5EYDKSA.js";
|
|
17
18
|
|
|
18
19
|
// src/index.ts
|
|
19
20
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -61,7 +62,7 @@ Production safety: OAuth may not be available yet. In service-token mode, writes
|
|
|
61
62
|
|
|
62
63
|
Semantic models: search_semantic_model, get_semantic_entity, propose_semantic_change, apply_semantic_change, and eval_semantic_model use the configured service credential and datamartId from list_datamarts. For a v2 model, use these tools instead of update_semantic_layer. Search and inspect first. Propose a change, review its errors and plan, then evaluate its returned source. Apply that exact source with its expected_content_hash only after the user supplies confirm="APPLY_TO_PRODUCTION" and dry_run=false. Never supply confirmation yourself. Apply changes the live model for every consumer. A conflict requires a new proposal and review. dry_run defaults to true and never writes.
|
|
63
64
|
|
|
64
|
-
Custom chart types (chart type custom:<key>): custom_chart_scaffold returns a
|
|
65
|
+
Custom chart types (chart type custom:<key>): custom_chart_scaffold returns a draft and project files. In hosted agents, custom_chart_edit sends the description and draft to Chart studio Bryn when installed; if unavailable, edit draft.code yourself. Pass chartTs, manifest, bundleCss and iconSvg for server-side compilation without files or imports. custom_chart_preview shows validated draft code on bounded real sample rows without saving. custom_chart_validate checks a manifest and bundle without persisting, custom_chart_upload creates the chart or a preview version only with mode="apply" and confirm="APPLY_TO_PRODUCTION" (dry_run shows the plan first), and publish_custom_chart makes a version live for every builder only with the same confirmation because every dashboard using that key re-renders. For local projects only, build with \`databrain custom-chart build\`. A custom metric uses chartType="custom:<key>" and customChart={key,bindings,options}; its MCP App tile loads only the company published bundle in the app/embed sandbox. Hosts without MCP Apps show rows and a clear fallback.
|
|
65
66
|
|
|
66
67
|
Infrastructure creation is intentionally narrow in MCP: datasource and workspace creation still happen outside this workflow; sync_datasource and dry-run/apply create_datamart are available. The semantic layer is populated and maintained via MCP tools (get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status). When the semantic layer is empty, use these tools \u2014 do NOT send the user to the UI for semantic metadata.
|
|
67
68
|
|
|
@@ -117,7 +118,7 @@ function banner(msg) {
|
|
|
117
118
|
async function main() {
|
|
118
119
|
try {
|
|
119
120
|
if (process.argv.includes("--http")) {
|
|
120
|
-
const { startHosted } = await import("./main-
|
|
121
|
+
const { startHosted } = await import("./main-JEYYFGJM.js");
|
|
121
122
|
await startHosted();
|
|
122
123
|
return;
|
|
123
124
|
}
|