@malloydata/malloyyo 0.2.40 → 0.2.41
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/dist/frame-inpage-entry.tsx +8 -2
- package/dist/index.js +64 -39
- package/package.json +1 -1
|
@@ -35,10 +35,16 @@ const givensToUrl = (dashboard, givens) => {
|
|
|
35
35
|
|
|
36
36
|
mountInPage({
|
|
37
37
|
root: document.getElementById("root"),
|
|
38
|
-
// Governed query — the shell's trusted
|
|
38
|
+
// Governed query — the shell's trusted api/run (the same endpoint the iframe
|
|
39
39
|
// broker forwards to). Returns the raw result the runtime normalizes.
|
|
40
|
+
//
|
|
41
|
+
// RELATIVE. A leading slash resolves against the ORIGIN, so behind anything
|
|
42
|
+
// serving this page under a prefix — code-server's `/proxy/<port>/`, a
|
|
43
|
+
// Codespaces forwarded port — the POST went to the proxy's own root and came
|
|
44
|
+
// back `405 Method Not Allowed` from the proxy rather than reaching the dev
|
|
45
|
+
// server at all. Resolved against the document, it is correct at any depth.
|
|
40
46
|
run: (req, givens) =>
|
|
41
|
-
fetch("
|
|
47
|
+
fetch("api/run", {
|
|
42
48
|
method: "POST",
|
|
43
49
|
headers: { "content-type": "application/json" },
|
|
44
50
|
body: JSON.stringify({ d: name, query: req.query, malloy: req.malloy, givens }),
|
package/dist/index.js
CHANGED
|
@@ -2881,7 +2881,7 @@ function clearCreds(url6) {
|
|
|
2881
2881
|
}
|
|
2882
2882
|
|
|
2883
2883
|
// package.json
|
|
2884
|
-
var version = "0.2.
|
|
2884
|
+
var version = "0.2.41";
|
|
2885
2885
|
|
|
2886
2886
|
// src/http.ts
|
|
2887
2887
|
var USER_AGENT = `malloyyo/${version}`;
|
|
@@ -3382,6 +3382,7 @@ function siblingList(current, all, href) {
|
|
|
3382
3382
|
// src/dashboard.ts
|
|
3383
3383
|
var resolveFrameEntry = () => path6.join(resolveRuntimeDir(), "..", "frame-entry.tsx");
|
|
3384
3384
|
var resolveInPageEntry = () => path6.join(resolveRuntimeDir(), "..", "frame-inpage-entry.tsx");
|
|
3385
|
+
var inlineScript = (js) => js.replace(/<\/(script)/gi, String.raw`<\/$1`);
|
|
3385
3386
|
var esc2 = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3386
3387
|
function makeBundler() {
|
|
3387
3388
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -3457,9 +3458,26 @@ function makeInPageBundler() {
|
|
|
3457
3458
|
};
|
|
3458
3459
|
}
|
|
3459
3460
|
var html = (body, title) => `<!doctype html><html><head><meta charset="utf-8"><title>${esc2(title)}</title><meta name="viewport" content="width=device-width,initial-scale=1"><style>${NAV_CSS}</style></head><body style="margin:0">${body}</body></html>`;
|
|
3460
|
-
var
|
|
3461
|
+
var dashLink = (n) => `?d=${encodeURIComponent(n)}`;
|
|
3462
|
+
var DEV_PATHS = {
|
|
3463
|
+
/** Sibling dashboard, for the switcher in the nav bar. */
|
|
3464
|
+
dashboard: dashLink,
|
|
3465
|
+
/** In-page (tag-only) bundle. */
|
|
3466
|
+
inPage: (n) => `inpage.js?d=${encodeURIComponent(n)}`,
|
|
3467
|
+
/** Artifact bundle, for a dashboard with a Dashboard.tsx. */
|
|
3468
|
+
bundle: (n) => `bundle.js?d=${encodeURIComponent(n)}`,
|
|
3469
|
+
/** The sandboxed artifact document. Same origin as the shell, relative like
|
|
3470
|
+
everything else — the iframe is isolated by its OPAQUE origin (the
|
|
3471
|
+
`allow-scripts`-only sandbox), not by living on a second port. */
|
|
3472
|
+
frame: (n) => `frame?d=${encodeURIComponent(n)}`,
|
|
3473
|
+
/** Live-reload stream. */
|
|
3474
|
+
events: "events",
|
|
3475
|
+
/** The privileged query broker the parent shell brokers postMessage into. */
|
|
3476
|
+
run: "api/run"
|
|
3477
|
+
};
|
|
3478
|
+
var devSiblings = (dash, all) => siblingList(dash.name, all, dashLink);
|
|
3461
3479
|
function navHtml2(dash, all) {
|
|
3462
|
-
return navHtml(dash.name, all,
|
|
3480
|
+
return navHtml(dash.name, all, dashLink);
|
|
3463
3481
|
}
|
|
3464
3482
|
function inPageShell(dash, all, givenSpecs, initialGivens, initialUrlState, tileSpecs) {
|
|
3465
3483
|
const info = {
|
|
@@ -3474,21 +3492,20 @@ function inPageShell(dash, all, givenSpecs, initialGivens, initialUrlState, tile
|
|
|
3474
3492
|
autorun: dash.autorun
|
|
3475
3493
|
};
|
|
3476
3494
|
return html(
|
|
3477
|
-
navHtml2(dash, all) + `<div id="root"></div><script>window.__DASHBOARD__=${safeJson(info)};window.__DASHBOARDS__=${safeJson(devSiblings(dash, all))};window.__GIVENS__=${safeJson(givenSpecs)};window.__INITIAL_GIVENS__=${safeJson(initialGivens)};window.__INITIAL_URLSTATE__=${safeJson(initialUrlState)}</script><script>try{new EventSource('
|
|
3495
|
+
navHtml2(dash, all) + `<div id="root"></div><script>window.__DASHBOARD__=${safeJson(info)};window.__DASHBOARDS__=${safeJson(devSiblings(dash, all))};window.__GIVENS__=${safeJson(givenSpecs)};window.__INITIAL_GIVENS__=${safeJson(initialGivens)};window.__INITIAL_URLSTATE__=${safeJson(initialUrlState)}</script><script>try{new EventSource('${DEV_PATHS.events}').onmessage=()=>location.reload();}catch(e){}</script><script src="${DEV_PATHS.inPage(dash.name)}"></script>`,
|
|
3478
3496
|
dash.title
|
|
3479
3497
|
);
|
|
3480
3498
|
}
|
|
3481
|
-
function parentShell(dash,
|
|
3499
|
+
function parentShell(dash, all, initialGivens, initialUrlState) {
|
|
3482
3500
|
const givensQs = Object.entries({ ...initialGivens, ...initialUrlState }).map(([k, v]) => `&${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("");
|
|
3483
3501
|
const d = safeJson(dash.name);
|
|
3484
|
-
const fb = safeJson(frameBase);
|
|
3485
3502
|
const nav = navHtml2(dash, all);
|
|
3486
3503
|
return html(
|
|
3487
3504
|
`<div style="display:flex;flex-direction:column;height:100vh">` + nav + // allow-popups(+escape-sandbox): let a # link mark open its target in a
|
|
3488
3505
|
// normal new tab on click instead of being blocked by the sandbox.
|
|
3489
|
-
`<iframe id="f" sandbox="allow-scripts allow-
|
|
3506
|
+
`<iframe id="f" sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox" src="${DEV_PATHS.frame(dash.name)}${givensQs}" style="border:0;flex:1;width:100%"></iframe></div><script>
|
|
3490
3507
|
const f=document.getElementById('f');
|
|
3491
|
-
try{new EventSource('
|
|
3508
|
+
try{new EventSource('${DEV_PATHS.events}').onmessage=()=>location.reload();}catch(e){}
|
|
3492
3509
|
// The shareable URL has TWO namespaces the frame syncs independently:
|
|
3493
3510
|
// '$NAME' = a given (the governed query contract), '~key' = a custom
|
|
3494
3511
|
// component's useUrlState view-state. Each write must re-emit the other's
|
|
@@ -3503,7 +3520,7 @@ function shareUrl(dashboard){
|
|
|
3503
3520
|
return u.pathname+u.search;
|
|
3504
3521
|
}
|
|
3505
3522
|
window.addEventListener('message',async(e)=>{
|
|
3506
|
-
if(e.source!==f.contentWindow
|
|
3523
|
+
if(e.source!==f.contentWindow)return;
|
|
3507
3524
|
const m=e.data;
|
|
3508
3525
|
if(m&&m.type==='givens'){
|
|
3509
3526
|
G=m.givens||{};
|
|
@@ -3525,11 +3542,11 @@ window.addEventListener('message',async(e)=>{
|
|
|
3525
3542
|
if(!m||m.type!=='run')return;
|
|
3526
3543
|
let out;
|
|
3527
3544
|
try{
|
|
3528
|
-
const res=await fetch('
|
|
3545
|
+
const res=await fetch('${DEV_PATHS.run}',{method:'POST',headers:{'content-type':'application/json'},
|
|
3529
3546
|
body:JSON.stringify({d:${d},query:m.query,malloy:m.malloy,givens:m.givens,dashboard:m.dashboard})});
|
|
3530
3547
|
out=await res.json();
|
|
3531
3548
|
}catch(err){ out={ok:false,problems:[{message:String(err)}]}; }
|
|
3532
|
-
f.contentWindow.postMessage({type:'result',id:m.id,...out}
|
|
3549
|
+
f.contentWindow.postMessage({type:'result',id:m.id,...out},'*');
|
|
3533
3550
|
});
|
|
3534
3551
|
</script>`,
|
|
3535
3552
|
dash.title
|
|
@@ -3541,7 +3558,7 @@ function givensFromUrl(url6) {
|
|
|
3541
3558
|
function urlStateFromUrl(url6) {
|
|
3542
3559
|
return urlStateFromSearch(url6.search);
|
|
3543
3560
|
}
|
|
3544
|
-
function frameDoc(dash, all, givenSpecs, initialGivens, initialUrlState, tileSpecs) {
|
|
3561
|
+
function frameDoc(dash, all, givenSpecs, initialGivens, initialUrlState, bundleJs, tileSpecs) {
|
|
3545
3562
|
const info = {
|
|
3546
3563
|
name: dash.name,
|
|
3547
3564
|
query: dash.query,
|
|
@@ -3556,7 +3573,7 @@ function frameDoc(dash, all, givenSpecs, initialGivens, initialUrlState, tileSpe
|
|
|
3556
3573
|
autorun: dash.autorun
|
|
3557
3574
|
};
|
|
3558
3575
|
return html(
|
|
3559
|
-
`<div id="root"></div><script>window.__DASHBOARD__=${safeJson(info)};window.__DASHBOARDS__=${safeJson(devSiblings(dash, all))};window.__GIVENS__=${safeJson(givenSpecs)};window.__INITIAL_GIVENS__=${safeJson(initialGivens)};window.__INITIAL_URLSTATE__=${safeJson(initialUrlState)}</script><script
|
|
3576
|
+
`<div id="root"></div><script>window.__DASHBOARD__=${safeJson(info)};window.__DASHBOARDS__=${safeJson(devSiblings(dash, all))};window.__GIVENS__=${safeJson(givenSpecs)};window.__INITIAL_GIVENS__=${safeJson(initialGivens)};window.__INITIAL_URLSTATE__=${safeJson(initialUrlState)}</script><script>${inlineScript(bundleJs)}</script>`,
|
|
3560
3577
|
dash.title
|
|
3561
3578
|
);
|
|
3562
3579
|
}
|
|
@@ -3569,8 +3586,6 @@ async function serveDashboard(opts) {
|
|
|
3569
3586
|
await initConnections();
|
|
3570
3587
|
const root = path6.resolve(opts.root ?? process.cwd());
|
|
3571
3588
|
const port = opts.port ?? 4173;
|
|
3572
|
-
const framePort = port + 1;
|
|
3573
|
-
const frameBase = `http://localhost:${framePort}`;
|
|
3574
3589
|
const runner = await makeRunner(root);
|
|
3575
3590
|
if (!runner.entryExists()) {
|
|
3576
3591
|
throw new Error(`No index.malloy at ${root} \u2014 run this from a Malloy model repo.`);
|
|
@@ -3618,34 +3633,39 @@ async function serveDashboard(opts) {
|
|
|
3618
3633
|
console.error(` (file watch unavailable: ${e.message} \u2014 edits won't auto-reload)`);
|
|
3619
3634
|
}
|
|
3620
3635
|
const handler = async (req, res) => {
|
|
3621
|
-
const
|
|
3622
|
-
const url6 = new URL(req.url ?? "/", `http://localhost:${onFramePort ? framePort : port}`);
|
|
3636
|
+
const url6 = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
3623
3637
|
const send = (code, type, body, extra = {}) => {
|
|
3624
3638
|
res.writeHead(code, { "content-type": type, ...extra });
|
|
3625
3639
|
res.end(body);
|
|
3626
3640
|
};
|
|
3627
3641
|
try {
|
|
3628
|
-
if (
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
if (!g.ok) {
|
|
3633
|
-
return send(
|
|
3634
|
-
200,
|
|
3635
|
-
"text/html; charset=utf-8",
|
|
3636
|
-
html(`<pre style="color:crimson;padding:16px">model error: ${esc2(g.error)}</pre>`, dash.title)
|
|
3637
|
-
);
|
|
3638
|
-
}
|
|
3642
|
+
if (url6.pathname === "/frame") {
|
|
3643
|
+
const dash = pick(url6);
|
|
3644
|
+
const g = await resolveGivens(dash);
|
|
3645
|
+
if (!g.ok) {
|
|
3639
3646
|
return send(
|
|
3640
3647
|
200,
|
|
3641
3648
|
"text/html; charset=utf-8",
|
|
3642
|
-
|
|
3649
|
+
html(`<pre style="color:crimson;padding:16px">model error: ${esc2(g.error)}</pre>`, dash.title)
|
|
3643
3650
|
);
|
|
3644
3651
|
}
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3652
|
+
return send(
|
|
3653
|
+
200,
|
|
3654
|
+
"text/html; charset=utf-8",
|
|
3655
|
+
frameDoc(
|
|
3656
|
+
dash,
|
|
3657
|
+
dashboards,
|
|
3658
|
+
g.union,
|
|
3659
|
+
givensFromUrl(url6),
|
|
3660
|
+
urlStateFromUrl(url6),
|
|
3661
|
+
await bundle(dash),
|
|
3662
|
+
g.tiles
|
|
3663
|
+
),
|
|
3664
|
+
{ "cache-control": "no-store" }
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
if (url6.pathname === "/bundle.js") {
|
|
3668
|
+
return send(200, "application/javascript; charset=utf-8", await bundle(pick(url6)));
|
|
3649
3669
|
}
|
|
3650
3670
|
if (url6.pathname === "/events") {
|
|
3651
3671
|
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
@@ -3668,17 +3688,24 @@ async function serveDashboard(opts) {
|
|
|
3668
3688
|
return send(
|
|
3669
3689
|
200,
|
|
3670
3690
|
"text/html; charset=utf-8",
|
|
3671
|
-
inPageShell(dash, dashboards, g.union, givensFromUrl(url6), urlStateFromUrl(url6), g.tiles)
|
|
3691
|
+
inPageShell(dash, dashboards, g.union, givensFromUrl(url6), urlStateFromUrl(url6), g.tiles),
|
|
3692
|
+
{ "cache-control": "no-store" }
|
|
3672
3693
|
);
|
|
3673
3694
|
}
|
|
3674
3695
|
return send(
|
|
3675
3696
|
200,
|
|
3676
3697
|
"text/html; charset=utf-8",
|
|
3677
|
-
parentShell(dash,
|
|
3698
|
+
parentShell(dash, dashboards, givensFromUrl(url6), urlStateFromUrl(url6)),
|
|
3699
|
+
{ "cache-control": "no-store" }
|
|
3678
3700
|
);
|
|
3679
3701
|
}
|
|
3680
3702
|
if (url6.pathname === "/inpage.js") {
|
|
3681
|
-
return send(
|
|
3703
|
+
return send(
|
|
3704
|
+
200,
|
|
3705
|
+
"application/javascript; charset=utf-8",
|
|
3706
|
+
await inPageBundle(),
|
|
3707
|
+
{ "cache-control": "no-store" }
|
|
3708
|
+
);
|
|
3682
3709
|
}
|
|
3683
3710
|
if (url6.pathname === "/api/run" && req.method === "POST") {
|
|
3684
3711
|
const { d, query, malloy, givens } = JSON.parse(await readBody(req));
|
|
@@ -3694,12 +3721,10 @@ async function serveDashboard(opts) {
|
|
|
3694
3721
|
}
|
|
3695
3722
|
};
|
|
3696
3723
|
const shellServer = http2.createServer(handler);
|
|
3697
|
-
const frameServer = http2.createServer(handler);
|
|
3698
3724
|
await new Promise((r) => shellServer.listen(port, r));
|
|
3699
|
-
await new Promise((r) => frameServer.listen(framePort, r));
|
|
3700
3725
|
console.error(`
|
|
3701
3726
|
malloyyo dashboard dev \u2014 model: ${root}`);
|
|
3702
|
-
console.error(` http://localhost:${port}
|
|
3727
|
+
console.error(` http://localhost:${port}/`);
|
|
3703
3728
|
for (const d of dashboards) {
|
|
3704
3729
|
const kind = d.tsxPath ? "custom (iframe)" : "tag-only (in-page)";
|
|
3705
3730
|
console.error(` \u2022 ${d.name} (${kind}) \u2192 http://localhost:${port}/?d=${d.name}`);
|