@databrainhq/mcp-server 0.3.0-rc.1 → 0.3.0-rc.10
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 +152 -7
- package/dist/apps/server.d.ts +6 -6
- package/dist/apps/server.js +4 -2
- package/dist/apps/stdio.js +5 -3
- package/dist/apps/stdio.js.map +1 -1
- package/dist/chunk-5IGP6A6T.js +496 -0
- package/dist/chunk-5IGP6A6T.js.map +1 -0
- package/dist/{chunk-RF3HTNO7.js → chunk-CUPL7CAN.js} +2 -2
- package/dist/{chunk-7DS7OLWP.js → chunk-ERTAKB2H.js} +1877 -1182
- package/dist/chunk-ERTAKB2H.js.map +1 -0
- package/dist/chunk-N7X762VU.js +34 -0
- package/dist/chunk-N7X762VU.js.map +1 -0
- package/dist/chunk-OCBTNAAO.js +1946 -0
- package/dist/chunk-OCBTNAAO.js.map +1 -0
- package/dist/{chunk-JCXUZ3QZ.js → chunk-Z3ZHN4UW.js} +34 -6
- package/dist/chunk-Z3ZHN4UW.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +495 -0
- package/dist/cli.js.map +1 -0
- package/dist/custom-chart/runtime-shim.js +146 -0
- package/dist/custom-chart/template/AGENTS.md +374 -0
- package/dist/custom-chart/template/README.md +374 -0
- package/dist/custom-chart/template/chart.css +28 -0
- package/dist/custom-chart/template/chart.ts +192 -0
- package/dist/custom-chart/template/custom-chart.d.ts +78 -0
- package/dist/custom-chart/template/icon.svg +1 -0
- package/dist/custom-chart/template/manifest.json +23 -0
- package/dist/custom-chart/template/package.json +14 -0
- package/dist/custom-chart/template/sample-data.json +18 -0
- package/dist/custom-chart/template/tsconfig.json +12 -0
- package/dist/index.js +14 -8
- package/dist/index.js.map +1 -1
- package/dist/main-KZRHWE3K.js +857 -0
- package/dist/main-KZRHWE3K.js.map +1 -0
- package/dist/{test-only-EJ5O4WN5.js → test-only-K7QTSVO4.js} +7 -6
- package/dist/test-only-K7QTSVO4.js.map +1 -0
- package/package.json +9 -4
- package/dist/chunk-7DS7OLWP.js.map +0 -1
- package/dist/chunk-JCXUZ3QZ.js.map +0 -1
- package/dist/chunk-NDVW3ZSJ.js +0 -310
- package/dist/chunk-NDVW3ZSJ.js.map +0 -1
- package/dist/main-7QNRH6X4.js +0 -438
- package/dist/main-7QNRH6X4.js.map +0 -1
- package/dist/test-only-EJ5O4WN5.js.map +0 -1
- /package/dist/{chunk-RF3HTNO7.js.map → chunk-CUPL7CAN.js.map} +0 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DatabrainApiError,
|
|
4
|
+
DatabrainClient,
|
|
5
|
+
MCP_SERVER_VERSION,
|
|
6
|
+
apiProblems,
|
|
7
|
+
buildProject,
|
|
8
|
+
createCustomChartsApi,
|
|
9
|
+
planUpload,
|
|
10
|
+
readProject,
|
|
11
|
+
redactSecrets,
|
|
12
|
+
registerSecret,
|
|
13
|
+
scaffoldProject,
|
|
14
|
+
validateProject
|
|
15
|
+
} from "./chunk-OCBTNAAO.js";
|
|
16
|
+
|
|
17
|
+
// src/cli/index.ts
|
|
18
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
19
|
+
import { basename, join, resolve } from "path";
|
|
20
|
+
import { createInterface } from "readline";
|
|
21
|
+
|
|
22
|
+
// src/cli/dev-server.ts
|
|
23
|
+
import { createServer } from "http";
|
|
24
|
+
var LIGHT_THEME = {
|
|
25
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
26
|
+
fontSize: 12,
|
|
27
|
+
textColor: "#1f2937",
|
|
28
|
+
textSecondaryColor: "#6b7280",
|
|
29
|
+
background: "#ffffff",
|
|
30
|
+
surfaceColor: "#f9fafb",
|
|
31
|
+
borderColor: "#e5e7eb",
|
|
32
|
+
borderRadius: 6,
|
|
33
|
+
primaryColor: "#3366cc",
|
|
34
|
+
palette: ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00"]
|
|
35
|
+
};
|
|
36
|
+
var DARK_THEME = {
|
|
37
|
+
...LIGHT_THEME,
|
|
38
|
+
textColor: "#f3f4f6",
|
|
39
|
+
textSecondaryColor: "#9ca3af",
|
|
40
|
+
background: "#111827",
|
|
41
|
+
surfaceColor: "#1f2937",
|
|
42
|
+
borderColor: "#374151",
|
|
43
|
+
primaryColor: "#60a5fa",
|
|
44
|
+
palette: ["#60a5fa", "#f87171", "#fbbf24", "#34d399", "#c084fc", "#22d3ee", "#f472b6", "#a3e635"]
|
|
45
|
+
};
|
|
46
|
+
function defaultOptions(manifest) {
|
|
47
|
+
const options = {};
|
|
48
|
+
for (const option of manifest.options) options[option.id] = option.default;
|
|
49
|
+
return options;
|
|
50
|
+
}
|
|
51
|
+
function generateSampleData(manifest) {
|
|
52
|
+
const labels = ["North", "South", "East", "West", "Central", "Coast", "Island", "Valley"];
|
|
53
|
+
const columns = manifest.slots.map((slot, index) => ({
|
|
54
|
+
id: `${slot.id}_${index}`,
|
|
55
|
+
label: slot.label,
|
|
56
|
+
type: slot.types[0],
|
|
57
|
+
filterable: slot.role === "dimension",
|
|
58
|
+
slotId: slot.id
|
|
59
|
+
}));
|
|
60
|
+
const rows = labels.map(
|
|
61
|
+
(label, row) => manifest.slots.map((slot, column) => {
|
|
62
|
+
const type = slot.types[0];
|
|
63
|
+
if (type === "string") return `${label}${column > 0 ? ` ${column}` : ""}`;
|
|
64
|
+
if (type === "boolean") return (row + column) % 2 === 0;
|
|
65
|
+
return Math.round(1e3 - row * 97 + column * 53 + (row * 31 + column * 17) % 90);
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
return { columns, rows };
|
|
69
|
+
}
|
|
70
|
+
var PAGE = `<!doctype html>
|
|
71
|
+
<html lang="en">
|
|
72
|
+
<head>
|
|
73
|
+
<meta charset="utf-8">
|
|
74
|
+
<title>Custom chart dev</title>
|
|
75
|
+
<style>
|
|
76
|
+
:root { color-scheme: light dark; }
|
|
77
|
+
body { margin: 0; font: 14px system-ui, sans-serif; display: grid; grid-template-rows: auto 1fr; height: 100vh; }
|
|
78
|
+
header { display: flex; gap: 12px; align-items: center; padding: 10px 16px; border-bottom: 1px solid #8884; }
|
|
79
|
+
header code { font-size: 12px; opacity: 0.8; }
|
|
80
|
+
main { display: grid; grid-template-columns: 1fr 320px; min-height: 0; }
|
|
81
|
+
#stage { padding: 16px; display: flex; align-items: flex-start; justify-content: center; overflow: auto; }
|
|
82
|
+
#frame { border: 1px solid #8886; border-radius: 8px; background: transparent; }
|
|
83
|
+
aside { border-left: 1px solid #8884; padding: 12px 16px; overflow: auto; }
|
|
84
|
+
aside h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 8px; opacity: 0.7; }
|
|
85
|
+
#log { font: 12px ui-monospace, monospace; white-space: pre-wrap; margin: 0; }
|
|
86
|
+
select, button { font: inherit; }
|
|
87
|
+
</style>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<header>
|
|
91
|
+
<strong id="title">Custom chart</strong>
|
|
92
|
+
<code id="key"></code>
|
|
93
|
+
<label>Theme <select id="theme"><option value="light">light</option><option value="dark">dark</option></select></label>
|
|
94
|
+
<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>
|
|
95
|
+
<button id="reload">Rebuild and reload</button>
|
|
96
|
+
</header>
|
|
97
|
+
<main>
|
|
98
|
+
<div id="stage"><iframe id="frame" sandbox="allow-scripts" width="600" height="320" title="Custom chart"></iframe></div>
|
|
99
|
+
<aside><h2>Events</h2><pre id="log">Waiting for ready...</pre></aside>
|
|
100
|
+
</main>
|
|
101
|
+
<script>
|
|
102
|
+
(async () => {
|
|
103
|
+
const log = document.getElementById('log');
|
|
104
|
+
const frame = document.getElementById('frame');
|
|
105
|
+
const themeSelect = document.getElementById('theme');
|
|
106
|
+
const sizeSelect = document.getElementById('size');
|
|
107
|
+
const say = (line) => { log.textContent = (line + '\\n' + log.textContent).slice(0, 8000); };
|
|
108
|
+
const [manifest, bundle, sample, themes, css] = await Promise.all([
|
|
109
|
+
fetch('/manifest.json').then((r) => r.json()),
|
|
110
|
+
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; }),
|
|
111
|
+
fetch('/sample-data.json').then((r) => r.json()),
|
|
112
|
+
fetch('/themes.json').then((r) => r.json()),
|
|
113
|
+
fetch('/index.css').then((r) => r.text()),
|
|
114
|
+
]);
|
|
115
|
+
document.getElementById('title').textContent = manifest.name;
|
|
116
|
+
document.getElementById('key').textContent = 'custom:' + manifest.id;
|
|
117
|
+
if (!bundle) return;
|
|
118
|
+
let revision = 0;
|
|
119
|
+
const data = () => ({
|
|
120
|
+
columns: sample.columns, rows: sample.rows,
|
|
121
|
+
options: Object.assign({}, sample.defaults, sample.options || {}),
|
|
122
|
+
theme: themes[themeSelect.value], language: 'en'
|
|
123
|
+
});
|
|
124
|
+
const send = () => frame.contentWindow.postMessage({ protocol: 'dbn-chart-sandbox', version: 1, channel: 'dev', type: 'data', revision: ++revision, data: data() }, '*');
|
|
125
|
+
addEventListener('message', (event) => {
|
|
126
|
+
if (event.source !== frame.contentWindow) return;
|
|
127
|
+
const m = event.data || {};
|
|
128
|
+
if (m.type === 'ready') { say('ready'); send(); return; }
|
|
129
|
+
if (m.type === 'resize' && Number.isFinite(m.height)) frame.height = Math.max(80, Math.min(1200, m.height));
|
|
130
|
+
say(m.type + ' ' + JSON.stringify(m, (k, v) => (k === 'protocol' || k === 'version' || k === 'channel' || k === 'type') ? undefined : v));
|
|
131
|
+
});
|
|
132
|
+
const boot = '(' + (function () {
|
|
133
|
+
var channel = 'dev'; var revision = -1; var latest = null; var listener = null;
|
|
134
|
+
var send = function (payload) { parent.postMessage(Object.assign({ protocol: 'dbn-chart-sandbox', version: 1, channel: channel }, payload), '*'); };
|
|
135
|
+
window.dbnSandbox = Object.freeze({
|
|
136
|
+
onData: function (cb) { listener = cb; send({ type: 'ready' }); if (latest) cb(latest); },
|
|
137
|
+
click: function (rowIndex, columnId) { send({ type: 'click', revision: revision, rowIndex: rowIndex, columnId: columnId }); },
|
|
138
|
+
filter: function (columnId, values) { send({ type: 'filter', revision: revision, columnId: columnId, values: values }); },
|
|
139
|
+
resize: function (height) { send({ type: 'resize', height: height }); }
|
|
140
|
+
});
|
|
141
|
+
addEventListener('error', function (e) { send({ type: 'error', code: 'runtime', message: String(e.message || e) }); });
|
|
142
|
+
addEventListener('message', function (event) {
|
|
143
|
+
var m = event.data; if (!m || m.type !== 'data' || m.revision <= revision) return;
|
|
144
|
+
revision = m.revision; latest = m.data; if (listener) listener(latest);
|
|
145
|
+
});
|
|
146
|
+
}).toString() + ')();';
|
|
147
|
+
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>';
|
|
148
|
+
const mount = () => { frame.srcdoc = child; };
|
|
149
|
+
themeSelect.addEventListener('change', () => { if (revision > 0) send(); });
|
|
150
|
+
sizeSelect.addEventListener('change', () => { const [w, h] = sizeSelect.value.split('x'); frame.width = w; frame.height = h; if (revision > 0) send(); });
|
|
151
|
+
document.getElementById('reload').addEventListener('click', () => location.reload());
|
|
152
|
+
mount();
|
|
153
|
+
})();
|
|
154
|
+
</script>
|
|
155
|
+
</body>
|
|
156
|
+
</html>
|
|
157
|
+
`;
|
|
158
|
+
async function startDevServer(options) {
|
|
159
|
+
const server = createServer(async (req, res) => {
|
|
160
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
161
|
+
const json = (status, body) => {
|
|
162
|
+
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store" });
|
|
163
|
+
res.end(JSON.stringify(body));
|
|
164
|
+
};
|
|
165
|
+
try {
|
|
166
|
+
if (url.pathname === "/") {
|
|
167
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
168
|
+
res.end(PAGE);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (url.pathname === "/bundle.js") {
|
|
172
|
+
const result = await buildProject(options.dir, { write: false });
|
|
173
|
+
if (!result.ok || !result.outputs) {
|
|
174
|
+
res.writeHead(422, { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-store" });
|
|
175
|
+
res.end(result.problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`).join("\n"));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
res.writeHead(200, { "Content-Type": "text/javascript; charset=utf-8", "Cache-Control": "no-store" });
|
|
179
|
+
res.end(result.outputs.bundleJs);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const project = readProject(options.dir);
|
|
183
|
+
const manifest = project.manifest;
|
|
184
|
+
if (url.pathname === "/manifest.json") {
|
|
185
|
+
json(manifest ? 200 : 422, manifest ?? { problems: project.problems });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (url.pathname === "/sample-data.json") {
|
|
189
|
+
if (!manifest) {
|
|
190
|
+
json(422, { problems: project.problems });
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const sample = project.sampleData ?? generateSampleData(manifest);
|
|
194
|
+
json(200, { ...sample, defaults: defaultOptions(manifest) });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (url.pathname === "/index.css") {
|
|
198
|
+
res.writeHead(200, { "Content-Type": "text/css; charset=utf-8", "Cache-Control": "no-store" });
|
|
199
|
+
res.end(project.css ?? "");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (url.pathname === "/themes.json") {
|
|
203
|
+
json(200, { light: LIGHT_THEME, dark: DARK_THEME });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
207
|
+
res.end("not found");
|
|
208
|
+
} catch (err2) {
|
|
209
|
+
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
210
|
+
res.end(err2.message);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
await new Promise((resolve2, reject) => {
|
|
214
|
+
server.once("error", reject);
|
|
215
|
+
server.listen(options.port, "127.0.0.1", () => resolve2());
|
|
216
|
+
});
|
|
217
|
+
const address = server.address();
|
|
218
|
+
const port = typeof address === "object" && address ? address.port : options.port;
|
|
219
|
+
return { port, server, close: () => server.close() };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/cli/index.ts
|
|
223
|
+
var USAGE = `databrain ${MCP_SERVER_VERSION}
|
|
224
|
+
|
|
225
|
+
Usage:
|
|
226
|
+
databrain custom-chart init <dir> [--key <key>] [--name <name>] [--force]
|
|
227
|
+
databrain custom-chart validate [dir]
|
|
228
|
+
databrain custom-chart build [dir]
|
|
229
|
+
databrain custom-chart upload [dir] [--publish] [--yes]
|
|
230
|
+
databrain custom-chart dev [dir] [--port <port>]
|
|
231
|
+
|
|
232
|
+
Commands:
|
|
233
|
+
init Create a chart project from the template (manifest.json, chart.ts, chart.css, icon.svg, guide).
|
|
234
|
+
validate Check manifest.json, icon.svg and chart.css against the rules. No build.
|
|
235
|
+
build Compile chart.ts with esbuild and write dist/index.js, manifest.json, icon.svg, index.css, bundle.zip.
|
|
236
|
+
upload Build, validate on the server, show the plan, then upload as a private chart or a preview version.
|
|
237
|
+
Needs DATABRAIN_SERVICE_TOKEN (and DATABRAIN_API_URL for self hosted). --publish publishes it.
|
|
238
|
+
dev Serve dist/index.js with sample-data.json in a local sandbox page for a quick look.
|
|
239
|
+
|
|
240
|
+
Options:
|
|
241
|
+
--yes Skip the confirmation prompt on upload.
|
|
242
|
+
--publish Publish the uploaded version right away.
|
|
243
|
+
--force Let init write into a folder that is not empty.
|
|
244
|
+
--help Show this help.
|
|
245
|
+
--version Show the version.
|
|
246
|
+
`;
|
|
247
|
+
function parseArgs(argv) {
|
|
248
|
+
const positional = [];
|
|
249
|
+
const flags = {};
|
|
250
|
+
for (let i = 0; i < argv.length; i++) {
|
|
251
|
+
const arg = argv[i];
|
|
252
|
+
if (arg.startsWith("--")) {
|
|
253
|
+
const [name, inlineValue] = arg.slice(2).split("=", 2);
|
|
254
|
+
if (inlineValue !== void 0) {
|
|
255
|
+
flags[name] = inlineValue;
|
|
256
|
+
} else if (["key", "name", "port"].includes(name) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
257
|
+
flags[name] = argv[++i];
|
|
258
|
+
} else {
|
|
259
|
+
flags[name] = true;
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
positional.push(arg);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return { positional, flags };
|
|
266
|
+
}
|
|
267
|
+
var out = (line) => {
|
|
268
|
+
process.stdout.write(redactSecrets(line) + "\n");
|
|
269
|
+
};
|
|
270
|
+
var err = (line) => {
|
|
271
|
+
process.stderr.write(redactSecrets(line) + "\n");
|
|
272
|
+
};
|
|
273
|
+
function formatProblemLines(problems) {
|
|
274
|
+
return problems.map((p) => `${p.path || "/"} ${p.code} ${p.message}`);
|
|
275
|
+
}
|
|
276
|
+
function printProblems(problems) {
|
|
277
|
+
for (const line of formatProblemLines(problems)) err(line);
|
|
278
|
+
}
|
|
279
|
+
function keyFromDir(dir) {
|
|
280
|
+
const raw = basename(resolve(dir)).toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
281
|
+
const key = raw.length === 0 ? "my-chart" : /^[a-z]/.test(raw) ? raw : `chart-${raw}`;
|
|
282
|
+
return key.slice(0, 64);
|
|
283
|
+
}
|
|
284
|
+
function nameFromKey(key) {
|
|
285
|
+
const words = key.split(/[-_]+/).filter(Boolean);
|
|
286
|
+
return words.map((word, index) => index === 0 ? word[0].toUpperCase() + word.slice(1) : word).join(" ");
|
|
287
|
+
}
|
|
288
|
+
function commandInit(args) {
|
|
289
|
+
const dir = args.positional[0];
|
|
290
|
+
if (!dir) {
|
|
291
|
+
err("init needs a folder: databrain custom-chart init <dir>");
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
const target = resolve(dir);
|
|
295
|
+
if (existsSync(target) && readdirSync(target).length > 0 && args.flags.force !== true) {
|
|
296
|
+
err(`${target} is not empty. Pick another folder or pass --force.`);
|
|
297
|
+
return 1;
|
|
298
|
+
}
|
|
299
|
+
const key = typeof args.flags.key === "string" ? args.flags.key : keyFromDir(target);
|
|
300
|
+
const name = typeof args.flags.name === "string" ? args.flags.name : nameFromKey(key);
|
|
301
|
+
const result = scaffoldProject({ key, name });
|
|
302
|
+
if (!result.ok) {
|
|
303
|
+
printProblems(result.problems);
|
|
304
|
+
return 1;
|
|
305
|
+
}
|
|
306
|
+
mkdirSync(target, { recursive: true });
|
|
307
|
+
for (const [file, content] of Object.entries(result.files)) {
|
|
308
|
+
writeFileSync(join(target, file), content);
|
|
309
|
+
}
|
|
310
|
+
out(`Created chart project ${key} in ${target}`);
|
|
311
|
+
for (const file of Object.keys(result.files)) out(` ${file}`);
|
|
312
|
+
out("");
|
|
313
|
+
out("Next:");
|
|
314
|
+
out(` cd ${dir}`);
|
|
315
|
+
out(" edit chart.ts (read AGENTS.md first)");
|
|
316
|
+
out(" databrain custom-chart validate");
|
|
317
|
+
out(" databrain custom-chart build");
|
|
318
|
+
out(" databrain custom-chart upload");
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
function commandValidate(args) {
|
|
322
|
+
const dir = resolve(args.positional[0] ?? ".");
|
|
323
|
+
const result = validateProject(dir);
|
|
324
|
+
if (!result.ok) {
|
|
325
|
+
err(`${result.problems.length} problem${result.problems.length === 1 ? "" : "s"} in ${dir}:`);
|
|
326
|
+
printProblems(result.problems);
|
|
327
|
+
return 1;
|
|
328
|
+
}
|
|
329
|
+
const manifest = result.project.manifest;
|
|
330
|
+
out(`Valid: custom:${manifest.id} (${manifest.slots.length} slots, ${manifest.options.length} options) in ${dir}`);
|
|
331
|
+
return 0;
|
|
332
|
+
}
|
|
333
|
+
async function commandBuild(args) {
|
|
334
|
+
const dir = resolve(args.positional[0] ?? ".");
|
|
335
|
+
const result = await buildProject(dir);
|
|
336
|
+
if (!result.ok || !result.outputs) {
|
|
337
|
+
err(`Build failed with ${result.problems.length} problem${result.problems.length === 1 ? "" : "s"}:`);
|
|
338
|
+
printProblems(result.problems);
|
|
339
|
+
return 1;
|
|
340
|
+
}
|
|
341
|
+
out(`Built custom:${result.outputs.manifest.id}`);
|
|
342
|
+
for (const path of result.written) out(` ${path}`);
|
|
343
|
+
out(` contentHash ${result.outputs.contentHash}`);
|
|
344
|
+
out(` size ${result.outputs.sizeBytes} bytes (bundle ${Buffer.byteLength(result.outputs.bundleJs, "utf8")} of 2097152)`);
|
|
345
|
+
return 0;
|
|
346
|
+
}
|
|
347
|
+
async function confirm(question) {
|
|
348
|
+
if (!process.stdin.isTTY) {
|
|
349
|
+
err("stdin is not a terminal. Pass --yes to upload without a prompt.");
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
353
|
+
const answer = await new Promise((resolveAnswer) => rl.question(question, resolveAnswer));
|
|
354
|
+
rl.close();
|
|
355
|
+
return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
|
|
356
|
+
}
|
|
357
|
+
async function commandUpload(args) {
|
|
358
|
+
const dir = resolve(args.positional[0] ?? ".");
|
|
359
|
+
const serviceToken = process.env.DATABRAIN_SERVICE_TOKEN;
|
|
360
|
+
if (!serviceToken) {
|
|
361
|
+
err("DATABRAIN_SERVICE_TOKEN is not set. Create one in Settings > Service Tokens and export it, then try again.");
|
|
362
|
+
return 1;
|
|
363
|
+
}
|
|
364
|
+
registerSecret(serviceToken);
|
|
365
|
+
const apiUrl = process.env.DATABRAIN_API_URL || "https://api.usedatabrain.com";
|
|
366
|
+
if (!process.env.DATABRAIN_API_URL) out(`DATABRAIN_API_URL is not set, using ${apiUrl}`);
|
|
367
|
+
const built = await buildProject(dir);
|
|
368
|
+
if (!built.ok || !built.outputs) {
|
|
369
|
+
err("Build failed:");
|
|
370
|
+
printProblems(built.problems);
|
|
371
|
+
return 1;
|
|
372
|
+
}
|
|
373
|
+
const { manifest, bundleJs, bundleCss, iconSvg } = built.outputs;
|
|
374
|
+
const key = manifest.id;
|
|
375
|
+
out(`Built custom:${key} (${built.outputs.contentHash.slice(0, 12)}, ${built.outputs.sizeBytes} bytes)`);
|
|
376
|
+
const client = new DatabrainClient({ serviceToken, apiUrl, demoDomain: "https://demo.usedatabrain.com" });
|
|
377
|
+
const api = createCustomChartsApi(client);
|
|
378
|
+
try {
|
|
379
|
+
const validation = await api.validate({ manifest, bundleJs, bundleCss, iconSvg });
|
|
380
|
+
if (!validation.ok) {
|
|
381
|
+
err("The server refused the bundle:");
|
|
382
|
+
printProblems(validation.problems);
|
|
383
|
+
return 1;
|
|
384
|
+
}
|
|
385
|
+
out("Server validation: ok");
|
|
386
|
+
const existing = (await api.list()).find((chart) => chart.key === key);
|
|
387
|
+
const plan = planUpload(key, existing);
|
|
388
|
+
out(`Plan: ${plan.summary}`);
|
|
389
|
+
if (args.flags.publish === true) out("Then: publish that version (every dashboard using this key will render it).");
|
|
390
|
+
if (args.flags.yes !== true) {
|
|
391
|
+
const go = await confirm("Continue? [y/N] ");
|
|
392
|
+
if (!go) {
|
|
393
|
+
out("Cancelled. Nothing was uploaded.");
|
|
394
|
+
return 1;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const uploaded = await api.upload({ manifest, bundleJs, bundleCss, iconSvg });
|
|
398
|
+
const versionNumber = uploaded.version?.versionNumber ?? "?";
|
|
399
|
+
if (uploaded.created === "none") out(`This exact bundle is already v${versionNumber} of custom:${key}.`);
|
|
400
|
+
else if (uploaded.created === "chart") out(`Created custom:${key} (v${versionNumber}, private).`);
|
|
401
|
+
else out(`Uploaded v${versionNumber} of custom:${key}. It is in preview until you publish it.`);
|
|
402
|
+
if (args.flags.publish === true) {
|
|
403
|
+
const published = await api.publish(key, uploaded.version?.id);
|
|
404
|
+
out(`Published v${versionNumber} of custom:${key} (status ${published.chart?.status ?? "published"}).`);
|
|
405
|
+
} else if (uploaded.created !== "none") {
|
|
406
|
+
out(`Publish it with: databrain custom-chart upload --publish, the publish_custom_chart tool, or Settings > Developers > Custom charts.`);
|
|
407
|
+
}
|
|
408
|
+
return 0;
|
|
409
|
+
} catch (error) {
|
|
410
|
+
if (error instanceof DatabrainApiError) {
|
|
411
|
+
err(`Error ${error.status || ""} ${error.code}: ${error.message}`.replace(/\s+/g, " ").trim());
|
|
412
|
+
printProblems(apiProblems(error));
|
|
413
|
+
return 1;
|
|
414
|
+
}
|
|
415
|
+
err(`Unexpected error: ${error.message}`);
|
|
416
|
+
return 1;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function commandDev(args) {
|
|
420
|
+
const dir = resolve(args.positional[0] ?? ".");
|
|
421
|
+
const port = typeof args.flags.port === "string" ? Number(args.flags.port) : 4174;
|
|
422
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
423
|
+
err("--port must be a number between 1 and 65535.");
|
|
424
|
+
return 1;
|
|
425
|
+
}
|
|
426
|
+
const built = await buildProject(dir);
|
|
427
|
+
if (!built.ok) {
|
|
428
|
+
err("Build failed:");
|
|
429
|
+
printProblems(built.problems);
|
|
430
|
+
return 1;
|
|
431
|
+
}
|
|
432
|
+
const server = await startDevServer({ dir, port });
|
|
433
|
+
out(`Custom chart dev page: http://127.0.0.1:${server.port}/ (Ctrl+C to stop)`);
|
|
434
|
+
out("The page rebuilds on every reload. Edit chart.ts, save, reload.");
|
|
435
|
+
await new Promise((resolveDone) => {
|
|
436
|
+
const stop = () => {
|
|
437
|
+
server.close();
|
|
438
|
+
resolveDone();
|
|
439
|
+
};
|
|
440
|
+
process.once("SIGINT", stop);
|
|
441
|
+
process.once("SIGTERM", stop);
|
|
442
|
+
});
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
async function runCli(argv) {
|
|
446
|
+
const args = parseArgs(argv);
|
|
447
|
+
if (args.flags.version === true) {
|
|
448
|
+
out(MCP_SERVER_VERSION);
|
|
449
|
+
return 0;
|
|
450
|
+
}
|
|
451
|
+
const [family, command, ...rest] = args.positional;
|
|
452
|
+
if (args.flags.help === true || !family) {
|
|
453
|
+
out(USAGE);
|
|
454
|
+
return family ? 0 : 1;
|
|
455
|
+
}
|
|
456
|
+
if (family !== "custom-chart") {
|
|
457
|
+
err(`Unknown command "${family}".`);
|
|
458
|
+
out(USAGE);
|
|
459
|
+
return 1;
|
|
460
|
+
}
|
|
461
|
+
const sub = { positional: rest, flags: args.flags };
|
|
462
|
+
switch (command) {
|
|
463
|
+
case "init":
|
|
464
|
+
return commandInit(sub);
|
|
465
|
+
case "validate":
|
|
466
|
+
return commandValidate(sub);
|
|
467
|
+
case "build":
|
|
468
|
+
return commandBuild(sub);
|
|
469
|
+
case "upload":
|
|
470
|
+
return commandUpload(sub);
|
|
471
|
+
case "dev":
|
|
472
|
+
return commandDev(sub);
|
|
473
|
+
default:
|
|
474
|
+
err(command ? `Unknown custom-chart command "${command}".` : "custom-chart needs a command.");
|
|
475
|
+
out(USAGE);
|
|
476
|
+
return 1;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
var invokedDirectly = typeof process.argv[1] === "string" && /(^|[\\/])(cli\.(js|mjs|ts)|databrain)$/.test(process.argv[1]);
|
|
480
|
+
if (invokedDirectly) {
|
|
481
|
+
runCli(process.argv.slice(2)).then(
|
|
482
|
+
(code) => {
|
|
483
|
+
process.exitCode = code;
|
|
484
|
+
},
|
|
485
|
+
(error) => {
|
|
486
|
+
err(`Unexpected error: ${error.message}`);
|
|
487
|
+
process.exitCode = 1;
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
export {
|
|
492
|
+
formatProblemLines,
|
|
493
|
+
runCli
|
|
494
|
+
};
|
|
495
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +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"]}
|