@databrainhq/mcp-server 0.3.0-rc.13 → 0.3.0-rc.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +6 -1
- package/dist/apps/server.d.ts +102 -389
- package/dist/apps/server.js +5 -5
- package/dist/apps/stdio.d.ts +1 -2
- package/dist/apps/stdio.js +6 -6
- package/dist/{bundle-TDQXWIPM.js → bundle-QJNCUMNY.js} +2 -2
- package/dist/{chunk-Q5EYDKSA.js → chunk-2F7V6RZR.js} +511 -107
- package/dist/chunk-2F7V6RZR.js.map +1 -0
- package/dist/{chunk-AEQTEE5C.js → chunk-AWICHHDU.js} +153 -61
- package/dist/chunk-AWICHHDU.js.map +1 -0
- package/dist/{chunk-X44C5GW2.js → chunk-CEGHG5U3.js} +59 -18
- package/dist/chunk-CEGHG5U3.js.map +1 -0
- package/dist/{chunk-233VGFHY.js → chunk-NIC5MAGC.js} +317 -147
- package/dist/chunk-NIC5MAGC.js.map +1 -0
- package/dist/{chunk-DTFYHLYN.js → chunk-S65KA3Z7.js} +26 -11
- package/dist/chunk-S65KA3Z7.js.map +1 -0
- package/dist/{chunk-PSJVYD4W.js → chunk-UYFFFAYV.js} +11722 -10560
- package/dist/chunk-UYFFFAYV.js.map +1 -0
- package/dist/{chunk-HCYZN7I3.js → chunk-VQ5G5AZV.js} +10 -7
- package/dist/chunk-VQ5G5AZV.js.map +1 -0
- package/dist/cli.d.ts +16 -15
- package/dist/cli.js +134 -36
- package/dist/cli.js.map +1 -1
- package/dist/custom-chart/custom-runtime.js +6 -6
- package/dist/custom-chart/template/chart.ts +116 -28
- package/dist/custom-chart/template/icon.svg +1 -1
- package/dist/custom-chart/template/manifest.json +66 -12
- package/dist/custom-chart/template/sample-data.json +21 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +20 -10
- package/dist/index.js.map +1 -1
- package/dist/{main-VEKNKWL5.js → main-QCBVJSSR.js} +348 -155
- package/dist/main-QCBVJSSR.js.map +1 -0
- package/dist/{test-only-LJGOY2QA.js → test-only-6474NMTR.js} +12 -8
- package/dist/test-only-6474NMTR.js.map +1 -0
- package/package.json +20 -9
- package/dist/chunk-233VGFHY.js.map +0 -1
- package/dist/chunk-AEQTEE5C.js.map +0 -1
- package/dist/chunk-DTFYHLYN.js.map +0 -1
- package/dist/chunk-HCYZN7I3.js.map +0 -1
- package/dist/chunk-PSJVYD4W.js.map +0 -1
- package/dist/chunk-Q5EYDKSA.js.map +0 -1
- package/dist/chunk-X44C5GW2.js.map +0 -1
- package/dist/main-VEKNKWL5.js.map +0 -1
- package/dist/test-only-LJGOY2QA.js.map +0 -1
- /package/dist/{bundle-TDQXWIPM.js.map → bundle-QJNCUMNY.js.map} +0 -0
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
registerChartApps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NIC5MAGC.js";
|
|
5
5
|
import {
|
|
6
6
|
enableChartApps,
|
|
7
7
|
registerAllPrompts,
|
|
8
8
|
registerAllResources,
|
|
9
9
|
registerAllTools
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UYFFFAYV.js";
|
|
11
11
|
import {
|
|
12
12
|
DatabrainClient,
|
|
13
13
|
MCP_SERVER_VERSION
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AWICHHDU.js";
|
|
15
15
|
|
|
16
16
|
// src/apps/server.ts
|
|
17
17
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18
18
|
function createChartAppServer(config, backend) {
|
|
19
19
|
const client = new DatabrainClient(config);
|
|
20
|
-
const server = new McpServer(
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const server = new McpServer(
|
|
21
|
+
{ name: "databrain-chart-apps", version: MCP_SERVER_VERSION },
|
|
22
|
+
{
|
|
23
|
+
instructions: "Use read-only chart previews to inspect analytics. Chart interactions inherit guest scope. Never request or expose credentials. Mutation tools retain their existing dry-run and confirmation requirements."
|
|
24
|
+
}
|
|
25
|
+
);
|
|
23
26
|
enableChartApps(server);
|
|
24
27
|
registerAllTools(server, client);
|
|
25
28
|
registerAllResources(server);
|
|
@@ -31,4 +34,4 @@ function createChartAppServer(config, backend) {
|
|
|
31
34
|
export {
|
|
32
35
|
createChartAppServer
|
|
33
36
|
};
|
|
34
|
-
//# sourceMappingURL=chunk-
|
|
37
|
+
//# sourceMappingURL=chunk-VQ5G5AZV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/apps/server.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport { registerAllPrompts } from '../prompts/registry.js';\nimport { registerAllResources } from '../resources/registry.js';\nimport { registerAllTools } from '../tools/registry.js';\nimport type { Config } from '../utils/config.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport type { ChartBackend } from './backend.js';\nimport { enableChartApps } from './feature.js';\nimport { registerChartApps } from './registry.js';\n\n/** Reviewable composition seam; hosted owners must retain their principal-bound client/policy. */\nexport function createChartAppServer(\n config: Config,\n backend?: ChartBackend\n): McpServer {\n const client = new DatabrainClient(config);\n const server = new McpServer(\n { name: 'databrain-chart-apps', version: MCP_SERVER_VERSION },\n {\n instructions:\n 'Use read-only chart previews to inspect analytics. Chart interactions inherit guest scope. ' +\n 'Never request or expose credentials. Mutation tools retain their existing dry-run and confirmation requirements.',\n }\n );\n enableChartApps(server);\n registerAllTools(server, client);\n registerAllResources(server);\n registerAllPrompts(server);\n registerChartApps(server, client, backend);\n return server;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAYnB,SAAS,qBACd,QACA,SACW;AACX,QAAM,SAAS,IAAI,gBAAgB,MAAM;AACzC,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,wBAAwB,SAAS,mBAAmB;AAAA,IAC5D;AAAA,MACE,cACE;AAAA,IAEJ;AAAA,EACF;AACA,kBAAgB,MAAM;AACtB,mBAAiB,QAAQ,MAAM;AAC/B,uBAAqB,MAAM;AAC3B,qBAAmB,MAAM;AACzB,oBAAkB,QAAQ,QAAQ,OAAO;AACzC,SAAO;AACT;","names":[]}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
export declare function formatProblemLines(problems: Problem[]): string[];
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared shapes for the custom chart package pipeline (DAT-925).
|
|
5
|
+
* Problem is the one error shape used by the manifest validator, the bundle
|
|
6
|
+
* builder, the icon check and the backend API (section 5 of the brief).
|
|
7
|
+
*/
|
|
8
|
+
declare interface Problem {
|
|
9
|
+
code: string;
|
|
10
|
+
path: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare function runCli(argv: string[]): Promise<number>;
|
|
15
|
+
|
|
16
|
+
export { }
|
package/dist/cli.js
CHANGED
|
@@ -9,12 +9,12 @@ import {
|
|
|
9
9
|
redactSecrets,
|
|
10
10
|
registerSecret,
|
|
11
11
|
scaffoldProject
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-AWICHHDU.js";
|
|
13
13
|
import {
|
|
14
14
|
buildProject,
|
|
15
15
|
readProject,
|
|
16
16
|
validateProject
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2F7V6RZR.js";
|
|
18
18
|
|
|
19
19
|
// src/cli/index.ts
|
|
20
20
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
@@ -33,7 +33,16 @@ var LIGHT_THEME = {
|
|
|
33
33
|
borderColor: "#e5e7eb",
|
|
34
34
|
borderRadius: 6,
|
|
35
35
|
primaryColor: "#3366cc",
|
|
36
|
-
palette: [
|
|
36
|
+
palette: [
|
|
37
|
+
"#3366cc",
|
|
38
|
+
"#dc3912",
|
|
39
|
+
"#ff9900",
|
|
40
|
+
"#109618",
|
|
41
|
+
"#990099",
|
|
42
|
+
"#0099c6",
|
|
43
|
+
"#dd4477",
|
|
44
|
+
"#66aa00"
|
|
45
|
+
]
|
|
37
46
|
};
|
|
38
47
|
var DARK_THEME = {
|
|
39
48
|
...LIGHT_THEME,
|
|
@@ -43,15 +52,34 @@ var DARK_THEME = {
|
|
|
43
52
|
surfaceColor: "#1f2937",
|
|
44
53
|
borderColor: "#374151",
|
|
45
54
|
primaryColor: "#60a5fa",
|
|
46
|
-
palette: [
|
|
55
|
+
palette: [
|
|
56
|
+
"#60a5fa",
|
|
57
|
+
"#f87171",
|
|
58
|
+
"#fbbf24",
|
|
59
|
+
"#34d399",
|
|
60
|
+
"#c084fc",
|
|
61
|
+
"#22d3ee",
|
|
62
|
+
"#f472b6",
|
|
63
|
+
"#a3e635"
|
|
64
|
+
]
|
|
47
65
|
};
|
|
48
66
|
function defaultOptions(manifest) {
|
|
49
67
|
const options = {};
|
|
50
|
-
for (const option of manifest.options)
|
|
68
|
+
for (const option of manifest.options)
|
|
69
|
+
options[option.id] = option.default;
|
|
51
70
|
return options;
|
|
52
71
|
}
|
|
53
72
|
function generateSampleData(manifest) {
|
|
54
|
-
const labels = [
|
|
73
|
+
const labels = [
|
|
74
|
+
"North",
|
|
75
|
+
"South",
|
|
76
|
+
"East",
|
|
77
|
+
"West",
|
|
78
|
+
"Central",
|
|
79
|
+
"Coast",
|
|
80
|
+
"Island",
|
|
81
|
+
"Valley"
|
|
82
|
+
];
|
|
55
83
|
const columns = manifest.slots.map((slot, index) => ({
|
|
56
84
|
id: `${slot.id}_${index}`,
|
|
57
85
|
label: slot.label,
|
|
@@ -64,7 +92,9 @@ function generateSampleData(manifest) {
|
|
|
64
92
|
const type = slot.types[0];
|
|
65
93
|
if (type === "string") return `${label}${column > 0 ? ` ${column}` : ""}`;
|
|
66
94
|
if (type === "boolean") return (row + column) % 2 === 0;
|
|
67
|
-
return Math.round(
|
|
95
|
+
return Math.round(
|
|
96
|
+
1e3 - row * 97 + column * 53 + (row * 31 + column * 17) % 90
|
|
97
|
+
);
|
|
68
98
|
})
|
|
69
99
|
);
|
|
70
100
|
return { columns, rows };
|
|
@@ -161,23 +191,37 @@ async function startDevServer(options) {
|
|
|
161
191
|
const server = createServer(async (req, res) => {
|
|
162
192
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
163
193
|
const json = (status, body) => {
|
|
164
|
-
res.writeHead(status, {
|
|
194
|
+
res.writeHead(status, {
|
|
195
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
196
|
+
"Cache-Control": "no-store"
|
|
197
|
+
});
|
|
165
198
|
res.end(JSON.stringify(body));
|
|
166
199
|
};
|
|
167
200
|
try {
|
|
168
201
|
if (url.pathname === "/") {
|
|
169
|
-
res.writeHead(200, {
|
|
202
|
+
res.writeHead(200, {
|
|
203
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
204
|
+
"Cache-Control": "no-store"
|
|
205
|
+
});
|
|
170
206
|
res.end(PAGE);
|
|
171
207
|
return;
|
|
172
208
|
}
|
|
173
209
|
if (url.pathname === "/bundle.js") {
|
|
174
210
|
const result = await buildProject(options.dir, { write: false });
|
|
175
211
|
if (!result.ok || !result.outputs) {
|
|
176
|
-
res.writeHead(422, {
|
|
177
|
-
|
|
212
|
+
res.writeHead(422, {
|
|
213
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
214
|
+
"Cache-Control": "no-store"
|
|
215
|
+
});
|
|
216
|
+
res.end(
|
|
217
|
+
result.problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`).join("\n")
|
|
218
|
+
);
|
|
178
219
|
return;
|
|
179
220
|
}
|
|
180
|
-
res.writeHead(200, {
|
|
221
|
+
res.writeHead(200, {
|
|
222
|
+
"Content-Type": "text/javascript; charset=utf-8",
|
|
223
|
+
"Cache-Control": "no-store"
|
|
224
|
+
});
|
|
181
225
|
res.end(result.outputs.bundleJs);
|
|
182
226
|
return;
|
|
183
227
|
}
|
|
@@ -197,7 +241,10 @@ async function startDevServer(options) {
|
|
|
197
241
|
return;
|
|
198
242
|
}
|
|
199
243
|
if (url.pathname === "/index.css") {
|
|
200
|
-
res.writeHead(200, {
|
|
244
|
+
res.writeHead(200, {
|
|
245
|
+
"Content-Type": "text/css; charset=utf-8",
|
|
246
|
+
"Cache-Control": "no-store"
|
|
247
|
+
});
|
|
201
248
|
res.end(project.css ?? "");
|
|
202
249
|
return;
|
|
203
250
|
}
|
|
@@ -267,10 +314,12 @@ function parseArgs(argv) {
|
|
|
267
314
|
return { positional, flags };
|
|
268
315
|
}
|
|
269
316
|
var out = (line) => {
|
|
270
|
-
process.stdout.write(redactSecrets(line)
|
|
317
|
+
process.stdout.write(`${redactSecrets(line)}
|
|
318
|
+
`);
|
|
271
319
|
};
|
|
272
320
|
var err = (line) => {
|
|
273
|
-
process.stderr.write(redactSecrets(line)
|
|
321
|
+
process.stderr.write(`${redactSecrets(line)}
|
|
322
|
+
`);
|
|
274
323
|
};
|
|
275
324
|
function formatProblemLines(problems) {
|
|
276
325
|
return problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`);
|
|
@@ -285,7 +334,9 @@ function keyFromDir(dir) {
|
|
|
285
334
|
}
|
|
286
335
|
function nameFromKey(key) {
|
|
287
336
|
const words = key.split(/[-_]+/).filter(Boolean);
|
|
288
|
-
return words.map(
|
|
337
|
+
return words.map(
|
|
338
|
+
(word, index) => index === 0 ? word[0].toUpperCase() + word.slice(1) : word
|
|
339
|
+
).join(" ");
|
|
289
340
|
}
|
|
290
341
|
function commandInit(args) {
|
|
291
342
|
const dir = args.positional[0];
|
|
@@ -324,26 +375,34 @@ function commandValidate(args) {
|
|
|
324
375
|
const dir = resolve(args.positional[0] ?? ".");
|
|
325
376
|
const result = validateProject(dir);
|
|
326
377
|
if (!result.ok) {
|
|
327
|
-
err(
|
|
378
|
+
err(
|
|
379
|
+
`${result.problems.length} problem${result.problems.length === 1 ? "" : "s"} in ${dir}:`
|
|
380
|
+
);
|
|
328
381
|
printProblems(result.problems);
|
|
329
382
|
return 1;
|
|
330
383
|
}
|
|
331
384
|
const manifest = result.project.manifest;
|
|
332
|
-
out(
|
|
385
|
+
out(
|
|
386
|
+
`Valid: custom:${manifest.id} (${manifest.slots.length} slots, ${manifest.options.length} options) in ${dir}`
|
|
387
|
+
);
|
|
333
388
|
return 0;
|
|
334
389
|
}
|
|
335
390
|
async function commandBuild(args) {
|
|
336
391
|
const dir = resolve(args.positional[0] ?? ".");
|
|
337
392
|
const result = await buildProject(dir);
|
|
338
393
|
if (!result.ok || !result.outputs) {
|
|
339
|
-
err(
|
|
394
|
+
err(
|
|
395
|
+
`Build failed with ${result.problems.length} problem${result.problems.length === 1 ? "" : "s"}:`
|
|
396
|
+
);
|
|
340
397
|
printProblems(result.problems);
|
|
341
398
|
return 1;
|
|
342
399
|
}
|
|
343
400
|
out(`Built custom:${result.outputs.manifest.id}`);
|
|
344
401
|
for (const path of result.written) out(` ${path}`);
|
|
345
402
|
out(` contentHash ${result.outputs.contentHash}`);
|
|
346
|
-
out(
|
|
403
|
+
out(
|
|
404
|
+
` size ${result.outputs.sizeBytes} bytes (bundle ${Buffer.byteLength(result.outputs.bundleJs, "utf8")} of 2097152)`
|
|
405
|
+
);
|
|
347
406
|
return 0;
|
|
348
407
|
}
|
|
349
408
|
async function confirm(question) {
|
|
@@ -352,7 +411,9 @@ async function confirm(question) {
|
|
|
352
411
|
return false;
|
|
353
412
|
}
|
|
354
413
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
355
|
-
const answer = await new Promise(
|
|
414
|
+
const answer = await new Promise(
|
|
415
|
+
(resolveAnswer) => rl.question(question, resolveAnswer)
|
|
416
|
+
);
|
|
356
417
|
rl.close();
|
|
357
418
|
return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
|
|
358
419
|
}
|
|
@@ -360,12 +421,15 @@ async function commandUpload(args) {
|
|
|
360
421
|
const dir = resolve(args.positional[0] ?? ".");
|
|
361
422
|
const serviceToken = process.env.DATABRAIN_SERVICE_TOKEN;
|
|
362
423
|
if (!serviceToken) {
|
|
363
|
-
err(
|
|
424
|
+
err(
|
|
425
|
+
"DATABRAIN_SERVICE_TOKEN is not set. Create one in Settings > Service Tokens and export it, then try again."
|
|
426
|
+
);
|
|
364
427
|
return 1;
|
|
365
428
|
}
|
|
366
429
|
registerSecret(serviceToken);
|
|
367
430
|
const apiUrl = process.env.DATABRAIN_API_URL || "https://api.usedatabrain.com";
|
|
368
|
-
if (!process.env.DATABRAIN_API_URL)
|
|
431
|
+
if (!process.env.DATABRAIN_API_URL)
|
|
432
|
+
out(`DATABRAIN_API_URL is not set, using ${apiUrl}`);
|
|
369
433
|
const built = await buildProject(dir);
|
|
370
434
|
if (!built.ok || !built.outputs) {
|
|
371
435
|
err("Build failed:");
|
|
@@ -374,11 +438,22 @@ async function commandUpload(args) {
|
|
|
374
438
|
}
|
|
375
439
|
const { manifest, bundleJs, bundleCss, iconSvg } = built.outputs;
|
|
376
440
|
const key = manifest.id;
|
|
377
|
-
out(
|
|
378
|
-
|
|
441
|
+
out(
|
|
442
|
+
`Built custom:${key} (${built.outputs.contentHash.slice(0, 12)}, ${built.outputs.sizeBytes} bytes)`
|
|
443
|
+
);
|
|
444
|
+
const client = new DatabrainClient({
|
|
445
|
+
serviceToken,
|
|
446
|
+
apiUrl,
|
|
447
|
+
demoDomain: "https://demo.usedatabrain.com"
|
|
448
|
+
});
|
|
379
449
|
const api = createCustomChartsApi(client);
|
|
380
450
|
try {
|
|
381
|
-
const validation = await api.validate({
|
|
451
|
+
const validation = await api.validate({
|
|
452
|
+
manifest,
|
|
453
|
+
bundleJs,
|
|
454
|
+
bundleCss,
|
|
455
|
+
iconSvg
|
|
456
|
+
});
|
|
382
457
|
if (!validation.ok) {
|
|
383
458
|
err("The server refused the bundle:");
|
|
384
459
|
printProblems(validation.problems);
|
|
@@ -388,7 +463,10 @@ async function commandUpload(args) {
|
|
|
388
463
|
const existing = (await api.list()).find((chart) => chart.key === key);
|
|
389
464
|
const plan = planUpload(key, existing);
|
|
390
465
|
out(`Plan: ${plan.summary}`);
|
|
391
|
-
if (args.flags.publish === true)
|
|
466
|
+
if (args.flags.publish === true)
|
|
467
|
+
out(
|
|
468
|
+
"Then: publish that version (every dashboard using this key will render it)."
|
|
469
|
+
);
|
|
392
470
|
if (args.flags.yes !== true) {
|
|
393
471
|
const go = await confirm("Continue? [y/N] ");
|
|
394
472
|
if (!go) {
|
|
@@ -396,21 +474,37 @@ async function commandUpload(args) {
|
|
|
396
474
|
return 1;
|
|
397
475
|
}
|
|
398
476
|
}
|
|
399
|
-
const uploaded = await api.upload({
|
|
477
|
+
const uploaded = await api.upload({
|
|
478
|
+
manifest,
|
|
479
|
+
bundleJs,
|
|
480
|
+
bundleCss,
|
|
481
|
+
iconSvg
|
|
482
|
+
});
|
|
400
483
|
const versionNumber = uploaded.version?.versionNumber ?? "?";
|
|
401
|
-
if (uploaded.created === "none")
|
|
402
|
-
|
|
403
|
-
else
|
|
484
|
+
if (uploaded.created === "none")
|
|
485
|
+
out(`This exact bundle is already v${versionNumber} of custom:${key}.`);
|
|
486
|
+
else if (uploaded.created === "chart")
|
|
487
|
+
out(`Created custom:${key} (v${versionNumber}, private).`);
|
|
488
|
+
else
|
|
489
|
+
out(
|
|
490
|
+
`Uploaded v${versionNumber} of custom:${key}. It is in preview until you publish it.`
|
|
491
|
+
);
|
|
404
492
|
if (args.flags.publish === true) {
|
|
405
493
|
const published = await api.publish(key, uploaded.version?.id);
|
|
406
|
-
out(
|
|
494
|
+
out(
|
|
495
|
+
`Published v${versionNumber} of custom:${key} (status ${published.chart?.status ?? "published"}).`
|
|
496
|
+
);
|
|
407
497
|
} else if (uploaded.created !== "none") {
|
|
408
|
-
out(
|
|
498
|
+
out(
|
|
499
|
+
`Publish it with: databrain custom-chart upload --publish, the publish_custom_chart tool, or Settings > Developers > Custom charts.`
|
|
500
|
+
);
|
|
409
501
|
}
|
|
410
502
|
return 0;
|
|
411
503
|
} catch (error) {
|
|
412
504
|
if (error instanceof DatabrainApiError) {
|
|
413
|
-
err(
|
|
505
|
+
err(
|
|
506
|
+
`Error ${error.status || ""} ${error.code}: ${error.message}`.replace(/\s+/g, " ").trim()
|
|
507
|
+
);
|
|
414
508
|
printProblems(apiProblems(error));
|
|
415
509
|
return 1;
|
|
416
510
|
}
|
|
@@ -432,7 +526,9 @@ async function commandDev(args) {
|
|
|
432
526
|
return 1;
|
|
433
527
|
}
|
|
434
528
|
const server = await startDevServer({ dir, port });
|
|
435
|
-
out(
|
|
529
|
+
out(
|
|
530
|
+
`Custom chart dev page: http://127.0.0.1:${server.port}/ (Ctrl+C to stop)`
|
|
531
|
+
);
|
|
436
532
|
out("The page rebuilds on every reload. Edit chart.ts, save, reload.");
|
|
437
533
|
await new Promise((resolveDone) => {
|
|
438
534
|
const stop = () => {
|
|
@@ -473,7 +569,9 @@ async function runCli(argv) {
|
|
|
473
569
|
case "dev":
|
|
474
570
|
return commandDev(sub);
|
|
475
571
|
default:
|
|
476
|
-
err(
|
|
572
|
+
err(
|
|
573
|
+
command ? `Unknown custom-chart command "${command}".` : "custom-chart needs a command."
|
|
574
|
+
);
|
|
477
575
|
out(USAGE);
|
|
478
576
|
return 1;
|
|
479
577
|
}
|
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 {\n apiProblems,\n createCustomChartsApi,\n planUpload,\n} from '../custom-chart/api.js';\nimport { buildProject, validateProject } from '../custom-chart/bundle.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 (\n ['key', 'name', 'port'].includes(name) &&\n i + 1 < argv.length &&\n !argv[i + 1].startsWith('--')\n ) {\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))\n .toLowerCase()\n .replace(/[^a-z0-9_-]+/g, '-')\n .replace(/^-+|-+$/g, '');\n const key =\n 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\n .map((word, index) =>\n index === 0 ? word[0].toUpperCase() + word.slice(1) : word\n )\n .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 (\n existsSync(target) &&\n readdirSync(target).length > 0 &&\n args.flags.force !== true\n ) {\n err(`${target} is not empty. Pick another folder or pass --force.`);\n return 1;\n }\n const key =\n typeof args.flags.key === 'string' ? args.flags.key : keyFromDir(target);\n const name =\n 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(\n `${result.problems.length} problem${result.problems.length === 1 ? '' : 's'} in ${dir}:`\n );\n printProblems(result.problems);\n return 1;\n }\n const manifest = result.project.manifest as {\n id: string;\n slots: unknown[];\n options: unknown[];\n };\n out(\n `Valid: custom:${manifest.id} (${manifest.slots.length} slots, ${manifest.options.length} options) in ${dir}`\n );\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(\n `Build failed with ${result.problems.length} problem${result.problems.length === 1 ? '' : 's'}:`\n );\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(\n ` size ${result.outputs.sizeBytes} bytes (bundle ${Buffer.byteLength(result.outputs.bundleJs, 'utf8')} of 2097152)`\n );\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) =>\n rl.question(question, resolveAnswer)\n );\n rl.close();\n return (\n answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes'\n );\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(\n 'DATABRAIN_SERVICE_TOKEN is not set. Create one in Settings > Service Tokens and export it, then try again.'\n );\n return 1;\n }\n registerSecret(serviceToken);\n const apiUrl =\n process.env.DATABRAIN_API_URL || 'https://api.usedatabrain.com';\n if (!process.env.DATABRAIN_API_URL)\n 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(\n `Built custom:${key} (${built.outputs.contentHash.slice(0, 12)}, ${built.outputs.sizeBytes} bytes)`\n );\n\n const client = new DatabrainClient({\n serviceToken,\n apiUrl,\n demoDomain: 'https://demo.usedatabrain.com',\n });\n const api = createCustomChartsApi(client);\n try {\n const validation = await api.validate({\n manifest,\n bundleJs,\n bundleCss,\n iconSvg,\n });\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)\n out(\n 'Then: publish that version (every dashboard using this key will render it).'\n );\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({\n manifest,\n bundleJs,\n bundleCss,\n iconSvg,\n });\n const versionNumber = uploaded.version?.versionNumber ?? '?';\n if (uploaded.created === 'none')\n out(`This exact bundle is already v${versionNumber} of custom:${key}.`);\n else if (uploaded.created === 'chart')\n out(`Created custom:${key} (v${versionNumber}, private).`);\n else\n out(\n `Uploaded v${versionNumber} of custom:${key}. It is in preview until you publish it.`\n );\n\n if (args.flags.publish === true) {\n const published = await api.publish(key, uploaded.version?.id);\n out(\n `Published v${versionNumber} of custom:${key} (status ${published.chart?.status ?? 'published'}).`\n );\n } else if (uploaded.created !== 'none') {\n out(\n `Publish it with: databrain custom-chart upload --publish, the publish_custom_chart tool, or Settings > Developers > Custom charts.`\n );\n }\n return 0;\n } catch (error) {\n if (error instanceof DatabrainApiError) {\n err(\n `Error ${error.status || ''} ${error.code}: ${error.message}`\n .replace(/\\s+/g, ' ')\n .trim()\n );\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 =\n 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(\n `Custom chart dev page: http://127.0.0.1:${server.port}/ (Ctrl+C to stop)`\n );\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(\n command\n ? `Unknown custom-chart command \"${command}\".`\n : 'custom-chart needs a command.'\n );\n out(USAGE);\n return 1;\n }\n}\n\nconst invokedDirectly =\n typeof process.argv[1] === 'string' &&\n /(^|[\\\\/])(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 {\n CustomChartManifest,\n CustomChartOption,\n} 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: [\n '#3366cc',\n '#dc3912',\n '#ff9900',\n '#109618',\n '#990099',\n '#0099c6',\n '#dd4477',\n '#66aa00',\n ],\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: [\n '#60a5fa',\n '#f87171',\n '#fbbf24',\n '#34d399',\n '#c084fc',\n '#22d3ee',\n '#f472b6',\n '#a3e635',\n ],\n};\n\nfunction defaultOptions(\n manifest: CustomChartManifest\n): Record<string, string | number | boolean> {\n const options: Record<string, string | number | boolean> = {};\n for (const option of manifest.options as CustomChartOption[])\n 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): {\n columns: unknown[];\n rows: unknown[][];\n} {\n const labels = [\n 'North',\n 'South',\n 'East',\n 'West',\n 'Central',\n 'Coast',\n 'Island',\n 'Valley',\n ];\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(\n 1000 - row * 97 + column * 53 + ((row * 31 + column * 17) % 90)\n );\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(\n options: DevServerOptions\n): 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, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n });\n res.end(JSON.stringify(body));\n };\n try {\n if (url.pathname === '/') {\n res.writeHead(200, {\n 'Content-Type': 'text/html; charset=utf-8',\n 'Cache-Control': 'no-store',\n });\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, {\n 'Content-Type': 'text/plain; charset=utf-8',\n 'Cache-Control': 'no-store',\n });\n res.end(\n result.problems\n .map((p) => `${p.path || '/'} ${p.code} ${p.message}`)\n .join('\\n')\n );\n return;\n }\n res.writeHead(200, {\n 'Content-Type': 'text/javascript; charset=utf-8',\n 'Cache-Control': 'no-store',\n });\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 =\n (project.sampleData as\n | { columns?: unknown[]; rows?: unknown[][]; options?: unknown }\n | 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, {\n 'Content-Type': 'text/css; charset=utf-8',\n 'Cache-Control': 'no-store',\n });\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 =\n 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"]}
|