@nanobpm/urban 0.30.0 → 0.31.0
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.
|
@@ -580,12 +580,20 @@ function renderText(node) {
|
|
|
580
580
|
return el(v === "heading" ? "h1" : "p", { class: cls }, node.props.text || "");
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
// A grid td cell.
|
|
584
|
-
//
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
//
|
|
588
|
-
//
|
|
583
|
+
// A grid td cell. Two column-declared linking modes, checked in order:
|
|
584
|
+
// 1. linkField — the cell text becomes a link to the URL held in that other
|
|
585
|
+
// field. Only http(s) hrefs are linked (a javascript:/other-scheme URL
|
|
586
|
+
// smuggled through row data falls back to plain text).
|
|
587
|
+
// 2. link: { kind: "processExplorer", keyField } — a structured, engine-aware
|
|
588
|
+
// link: the cell text links to the Nano console's explorer view for the
|
|
589
|
+
// process instance whose key is held in the row's keyField. The console
|
|
590
|
+
// path is constructed HERE (never taken from row data) and the key is
|
|
591
|
+
// URL-encoded, so row data can't smuggle a path or scheme; only a
|
|
592
|
+
// non-empty key (after trimming surrounding whitespace) produces a link.
|
|
593
|
+
// Unknown link kinds fall back to plain text so an unrecognised schema
|
|
594
|
+
// can't render a broken anchor.
|
|
595
|
+
// Both open in a new tab with rel=noopener noreferrer so the opened page can't
|
|
596
|
+
// reach window.opener. Shared by the top-level grid and child grids.
|
|
589
597
|
function gridCell(col, row) {
|
|
590
598
|
const text = row[col.field] == null ? "" : String(row[col.field]);
|
|
591
599
|
if (col.linkField) {
|
|
@@ -598,6 +606,18 @@ function gridCell(col, row) {
|
|
|
598
606
|
);
|
|
599
607
|
}
|
|
600
608
|
}
|
|
609
|
+
if (col.link && col.link.kind === "processExplorer" && col.link.keyField) {
|
|
610
|
+
const key = row[col.link.keyField];
|
|
611
|
+
const keyStr = key == null ? "" : String(key).trim();
|
|
612
|
+
if (text !== "" && keyStr !== "") {
|
|
613
|
+
const href = "/console/explorer?instance=" + encodeURIComponent(keyStr);
|
|
614
|
+
return el(
|
|
615
|
+
"td",
|
|
616
|
+
{},
|
|
617
|
+
el("a", { class: "pc-link", href, target: "_blank", rel: "noopener noreferrer" }, text),
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
601
621
|
return el("td", {}, text);
|
|
602
622
|
}
|
|
603
623
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/urban",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Urban: build and run code-first apps on Nano — runtime, derivation toolkit, and CLI in one. Author durable processes with @nanobpm/workflow (defineFlow), a typed datasource, forms, triggers and surfaces from a nano.app.json manifest, on Node or Deno.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -189,6 +189,23 @@ test("renderer wires a column's linkField to a new-tab anchor", async () => {
|
|
|
189
189
|
assert.match(js, /rel: "noopener noreferrer"/);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
test("renderer wires a column's processExplorer link to the console explorer", async () => {
|
|
193
|
+
const res = await dispatch("GET", "/app/runtime.js");
|
|
194
|
+
const js = res.body ?? "";
|
|
195
|
+
// A column declaring `link: { kind: "processExplorer", keyField }` renders its
|
|
196
|
+
// text as a link to the Nano console's explorer for the process instance whose
|
|
197
|
+
// key is held in that field. Guard the whole shape so the primitive can't
|
|
198
|
+
// silently regress: the discriminant + keyField are read, the key is trimmed
|
|
199
|
+
// (whitespace-only keys don't produce a link), the console path is constructed
|
|
200
|
+
// here (not from row data), the trimmed key is URL-encoded, and the anchor
|
|
201
|
+
// opens in a new tab with a hardened rel.
|
|
202
|
+
assert.match(js, /col\.link && col\.link\.kind === "processExplorer" && col\.link\.keyField/);
|
|
203
|
+
assert.match(js, /String\(key\)\.trim\(\)/);
|
|
204
|
+
assert.match(js, /"\/console\/explorer\?instance="\s*\+\s*encodeURIComponent\(keyStr\)/);
|
|
205
|
+
assert.match(js, /target:\s*"_blank"/);
|
|
206
|
+
assert.match(js, /rel:\s*"noopener noreferrer"/);
|
|
207
|
+
});
|
|
208
|
+
|
|
192
209
|
test("the renderer honours numeric actionForm fields", async () => {
|
|
193
210
|
const res = await dispatch("GET", "/app/runtime.js");
|
|
194
211
|
const js = res.body ?? "";
|
|
@@ -621,12 +621,20 @@ function renderText(node) {
|
|
|
621
621
|
return el(v === "heading" ? "h1" : "p", { class: cls }, node.props.text || "");
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
-
// A grid td cell.
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
//
|
|
628
|
-
//
|
|
629
|
-
//
|
|
624
|
+
// A grid td cell. Two column-declared linking modes, checked in order:
|
|
625
|
+
// 1. linkField — the cell text becomes a link to the URL held in that other
|
|
626
|
+
// field. Only http(s) hrefs are linked (a javascript:/other-scheme URL
|
|
627
|
+
// smuggled through row data falls back to plain text).
|
|
628
|
+
// 2. link: { kind: "processExplorer", keyField } — a structured, engine-aware
|
|
629
|
+
// link: the cell text links to the Nano console's explorer view for the
|
|
630
|
+
// process instance whose key is held in the row's keyField. The console
|
|
631
|
+
// path is constructed HERE (never taken from row data) and the key is
|
|
632
|
+
// URL-encoded, so row data can't smuggle a path or scheme; only a
|
|
633
|
+
// non-empty key (after trimming surrounding whitespace) produces a link.
|
|
634
|
+
// Unknown link kinds fall back to plain text so an unrecognised schema
|
|
635
|
+
// can't render a broken anchor.
|
|
636
|
+
// Both open in a new tab with rel=noopener noreferrer so the opened page can't
|
|
637
|
+
// reach window.opener. Shared by the top-level grid and child grids.
|
|
630
638
|
function gridCell(col, row) {
|
|
631
639
|
const text = row[col.field] == null ? "" : String(row[col.field]);
|
|
632
640
|
if (col.linkField) {
|
|
@@ -639,6 +647,18 @@ function gridCell(col, row) {
|
|
|
639
647
|
);
|
|
640
648
|
}
|
|
641
649
|
}
|
|
650
|
+
if (col.link && col.link.kind === "processExplorer" && col.link.keyField) {
|
|
651
|
+
const key = row[col.link.keyField];
|
|
652
|
+
const keyStr = key == null ? "" : String(key).trim();
|
|
653
|
+
if (text !== "" && keyStr !== "") {
|
|
654
|
+
const href = "/console/explorer?instance=" + encodeURIComponent(keyStr);
|
|
655
|
+
return el(
|
|
656
|
+
"td",
|
|
657
|
+
{},
|
|
658
|
+
el("a", { class: "pc-link", href, target: "_blank", rel: "noopener noreferrer" }, text),
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
642
662
|
return el("td", {}, text);
|
|
643
663
|
}
|
|
644
664
|
|