@yuneta/gobj-ui 7.23.187 → 7.23.188

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.
@@ -30399,54 +30399,57 @@ function plan_graph_saves(live, saved) {
30399
30399
  * treedb_node_label.js
30400
30400
  *
30401
30401
  * Pure, testable logic behind the label of a node in the treedb
30402
- * graph: WHAT A RECORD IS CALLED, which is not always what it is
30403
- * keyed by. Kept out of the gclass so it can be unit-tested with
30404
- * no DOM and no G6.
30402
+ * graph: WHAT A RECORD IS CALLED. Kept out of the gclass so it can
30403
+ * be unit-tested with no DOM and no G6.
30405
30404
  *
30406
30405
  * Copyright (c) 2026, ArtGins.
30407
30406
  * All Rights Reserved.
30408
30407
  ***********************************************************************/
30409
- /************************************************************
30410
- * What a node is CALLED, which is not always what it is
30411
- * keyed by.
30412
- *
30413
- * A topic whose id column is flagged `rowid`, `uuid` or
30414
- * `qualified` keys its records by something that is not the
30415
- * name — a counter, a random string, or the name with every
30416
- * ancestor in front of it — and the name a human knows the
30417
- * record by lives in the secondary key the topic declares
30418
- * (`pkey2s`, carried in the desc since SDK > 7.13.0). The
30419
- * `topics` and `cols` topics of treedb_system_schema are the
30420
- * case that forced this: named in `value`, so every card in
30421
- * the graph read "181", "225", "193" while they were keyed by
30422
- * rowid, and reads the whole path now that they are
30423
- * qualified.
30424
- *
30425
- * The pkey stays reachable: it is the card's tooltip.
30426
- *
30427
- * Falls back to the id whenever the schema does not say
30428
- * otherwise — an older node whose desc carries no `pkey2s`
30429
- * included.
30408
+ var SEP = " · ";
30409
+ /************************************************************
30410
+ * What a node is CALLED: **its id, and the secondary keys it
30411
+ * carries**.
30412
+ *
30413
+ * A record is one thing and an INSTANCE of it is another: the
30414
+ * topic keys the record by `id` and the instance by the
30415
+ * columns it declares in `pkey2s`. A card that shows one
30416
+ * without the other is a card that cannot be identified --
30417
+ * and it used to show one INSTEAD of the other, so the yunos
30418
+ * of an agent read `7.23.0-1`, `7.23.0-1`, `7.23.0-1` (the
30419
+ * release, which they share) and never said which yuno each
30420
+ * one was, while a `configurations` card read `1`.
30421
+ *
30422
+ * It came from the opposite case, and that one still works:
30423
+ * the `topics` and `cols` of treedb_system_schema are keyed
30424
+ * by a rowid, so the card said "181" and the name lived in
30425
+ * the pkey2. Now it says both -- `181 · yuno_role` -- which
30426
+ * is what the record is: that name, under that key.
30427
+ *
30428
+ * Rules:
30429
+ * - the id always opens the label;
30430
+ * - every pkey2 the record carries follows it, in the
30431
+ * order the topic declares them, separated by `·`;
30432
+ * - a pkey2 whose value is empty, absent or equal to the
30433
+ * id adds nothing;
30434
+ * - a desc with no `pkey2s` (an older node's schema
30435
+ * included) gives the bare id.
30430
30436
  ************************************************************/
30431
30437
  function node_label(desc, record) {
30432
30438
  let id = record.id;
30433
- let id_col = null;
30434
- if (Array.isArray(desc.cols)) {
30435
- for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
30436
- id_col = col;
30437
- break;
30438
- }
30439
- }
30440
- if (!id_col || !Array.isArray(id_col.flag)) return id;
30441
- if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0 && id_col.flag.indexOf("qualified") < 0) return id;
30442
- let pkey2s = desc.pkey2s;
30439
+ let pkey2s = desc ? desc.pkey2s : null;
30443
30440
  if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
30444
- if (!Array.isArray(pkey2s)) return id;
30441
+ if (!Array.isArray(pkey2s) || pkey2s.length === 0) return id;
30442
+ let parts = [];
30445
30443
  for (let name of pkey2s) {
30444
+ if (!name) continue;
30446
30445
  let value = record[name];
30447
- if (typeof value === "string" && value.length > 0) return value;
30446
+ if (typeof value === "number") value = String(value);
30447
+ if (typeof value !== "string" || value.length === 0) continue;
30448
+ if (value === String(id)) continue;
30449
+ parts.push(value);
30448
30450
  }
30449
- return id;
30451
+ if (parts.length === 0) return id;
30452
+ return [id, ...parts].join(SEP);
30450
30453
  }
30451
30454
  //#endregion
30452
30455
  //#region src/treedb_fold_model.js
@@ -30394,54 +30394,57 @@ function plan_graph_saves(live, saved) {
30394
30394
  * treedb_node_label.js
30395
30395
  *
30396
30396
  * Pure, testable logic behind the label of a node in the treedb
30397
- * graph: WHAT A RECORD IS CALLED, which is not always what it is
30398
- * keyed by. Kept out of the gclass so it can be unit-tested with
30399
- * no DOM and no G6.
30397
+ * graph: WHAT A RECORD IS CALLED. Kept out of the gclass so it can
30398
+ * be unit-tested with no DOM and no G6.
30400
30399
  *
30401
30400
  * Copyright (c) 2026, ArtGins.
30402
30401
  * All Rights Reserved.
30403
30402
  ***********************************************************************/
30404
- /************************************************************
30405
- * What a node is CALLED, which is not always what it is
30406
- * keyed by.
30407
- *
30408
- * A topic whose id column is flagged `rowid`, `uuid` or
30409
- * `qualified` keys its records by something that is not the
30410
- * name — a counter, a random string, or the name with every
30411
- * ancestor in front of it — and the name a human knows the
30412
- * record by lives in the secondary key the topic declares
30413
- * (`pkey2s`, carried in the desc since SDK > 7.13.0). The
30414
- * `topics` and `cols` topics of treedb_system_schema are the
30415
- * case that forced this: named in `value`, so every card in
30416
- * the graph read "181", "225", "193" while they were keyed by
30417
- * rowid, and reads the whole path now that they are
30418
- * qualified.
30419
- *
30420
- * The pkey stays reachable: it is the card's tooltip.
30421
- *
30422
- * Falls back to the id whenever the schema does not say
30423
- * otherwise — an older node whose desc carries no `pkey2s`
30424
- * included.
30403
+ var SEP = " · ";
30404
+ /************************************************************
30405
+ * What a node is CALLED: **its id, and the secondary keys it
30406
+ * carries**.
30407
+ *
30408
+ * A record is one thing and an INSTANCE of it is another: the
30409
+ * topic keys the record by `id` and the instance by the
30410
+ * columns it declares in `pkey2s`. A card that shows one
30411
+ * without the other is a card that cannot be identified --
30412
+ * and it used to show one INSTEAD of the other, so the yunos
30413
+ * of an agent read `7.23.0-1`, `7.23.0-1`, `7.23.0-1` (the
30414
+ * release, which they share) and never said which yuno each
30415
+ * one was, while a `configurations` card read `1`.
30416
+ *
30417
+ * It came from the opposite case, and that one still works:
30418
+ * the `topics` and `cols` of treedb_system_schema are keyed
30419
+ * by a rowid, so the card said "181" and the name lived in
30420
+ * the pkey2. Now it says both -- `181 · yuno_role` -- which
30421
+ * is what the record is: that name, under that key.
30422
+ *
30423
+ * Rules:
30424
+ * - the id always opens the label;
30425
+ * - every pkey2 the record carries follows it, in the
30426
+ * order the topic declares them, separated by `·`;
30427
+ * - a pkey2 whose value is empty, absent or equal to the
30428
+ * id adds nothing;
30429
+ * - a desc with no `pkey2s` (an older node's schema
30430
+ * included) gives the bare id.
30425
30431
  ************************************************************/
30426
30432
  function node_label(desc, record) {
30427
30433
  let id = record.id;
30428
- let id_col = null;
30429
- if (Array.isArray(desc.cols)) {
30430
- for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
30431
- id_col = col;
30432
- break;
30433
- }
30434
- }
30435
- if (!id_col || !Array.isArray(id_col.flag)) return id;
30436
- if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0 && id_col.flag.indexOf("qualified") < 0) return id;
30437
- let pkey2s = desc.pkey2s;
30434
+ let pkey2s = desc ? desc.pkey2s : null;
30438
30435
  if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
30439
- if (!Array.isArray(pkey2s)) return id;
30436
+ if (!Array.isArray(pkey2s) || pkey2s.length === 0) return id;
30437
+ let parts = [];
30440
30438
  for (let name of pkey2s) {
30439
+ if (!name) continue;
30441
30440
  let value = record[name];
30442
- if (typeof value === "string" && value.length > 0) return value;
30441
+ if (typeof value === "number") value = String(value);
30442
+ if (typeof value !== "string" || value.length === 0) continue;
30443
+ if (value === String(id)) continue;
30444
+ parts.push(value);
30443
30445
  }
30444
- return id;
30446
+ if (parts.length === 0) return id;
30447
+ return [id, ...parts].join(SEP);
30445
30448
  }
30446
30449
  //#endregion
30447
30450
  //#region src/treedb_fold_model.js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.187",
3
+ "version": "7.23.188",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -2,78 +2,78 @@
2
2
  * treedb_node_label.js
3
3
  *
4
4
  * Pure, testable logic behind the label of a node in the treedb
5
- * graph: WHAT A RECORD IS CALLED, which is not always what it is
6
- * keyed by. Kept out of the gclass so it can be unit-tested with
7
- * no DOM and no G6.
5
+ * graph: WHAT A RECORD IS CALLED. Kept out of the gclass so it can
6
+ * be unit-tested with no DOM and no G6.
8
7
  *
9
8
  * Copyright (c) 2026, ArtGins.
10
9
  * All Rights Reserved.
11
10
  ***********************************************************************/
12
11
 
12
+ const SEP = " · "; /* the middle dot the shell titles use */
13
+
13
14
  /************************************************************
14
- * What a node is CALLED, which is not always what it is
15
- * keyed by.
15
+ * What a node is CALLED: **its id, and the secondary keys it
16
+ * carries**.
16
17
  *
17
- * A topic whose id column is flagged `rowid`, `uuid` or
18
- * `qualified` keys its records by something that is not the
19
- * name — a counter, a random string, or the name with every
20
- * ancestor in front of it — and the name a human knows the
21
- * record by lives in the secondary key the topic declares
22
- * (`pkey2s`, carried in the desc since SDK > 7.13.0). The
23
- * `topics` and `cols` topics of treedb_system_schema are the
24
- * case that forced this: named in `value`, so every card in
25
- * the graph read "181", "225", "193" while they were keyed by
26
- * rowid, and reads the whole path now that they are
27
- * qualified.
18
+ * A record is one thing and an INSTANCE of it is another: the
19
+ * topic keys the record by `id` and the instance by the
20
+ * columns it declares in `pkey2s`. A card that shows one
21
+ * without the other is a card that cannot be identified --
22
+ * and it used to show one INSTEAD of the other, so the yunos
23
+ * of an agent read `7.23.0-1`, `7.23.0-1`, `7.23.0-1` (the
24
+ * release, which they share) and never said which yuno each
25
+ * one was, while a `configurations` card read `1`.
28
26
  *
29
- * The pkey stays reachable: it is the card's tooltip.
27
+ * It came from the opposite case, and that one still works:
28
+ * the `topics` and `cols` of treedb_system_schema are keyed
29
+ * by a rowid, so the card said "181" and the name lived in
30
+ * the pkey2. Now it says both -- `181 · yuno_role` -- which
31
+ * is what the record is: that name, under that key.
30
32
  *
31
- * Falls back to the id whenever the schema does not say
32
- * otherwise — an older node whose desc carries no `pkey2s`
33
- * included.
33
+ * Rules:
34
+ * - the id always opens the label;
35
+ * - every pkey2 the record carries follows it, in the
36
+ * order the topic declares them, separated by `·`;
37
+ * - a pkey2 whose value is empty, absent or equal to the
38
+ * id adds nothing;
39
+ * - a desc with no `pkey2s` (an older node's schema
40
+ * included) gives the bare id.
34
41
  ************************************************************/
35
42
  export function node_label(desc, record)
36
43
  {
37
- /* treedb stores every record under `id` (the pkey name is fixed in
38
- * tranger2_create_topic), so the fallback reads `id` while the FLAGS
39
- * are read from whatever column the desc names as the pkey. */
44
+ /* treedb stores every record under `id`: the pkey name is fixed in
45
+ * tranger2_create_topic, whatever the desc calls it. */
40
46
  let id = record.id;
41
47
 
42
- let id_col = null;
43
- if(Array.isArray(desc.cols)) {
44
- for(let col of desc.cols) {
45
- if(col && col.id === (desc.pkey || "id")) {
46
- id_col = col;
47
- break;
48
- }
49
- }
50
- }
51
- if(!id_col || !Array.isArray(id_col.flag)) {
52
- return id;
53
- }
54
- if(id_col.flag.indexOf("rowid") < 0 &&
55
- id_col.flag.indexOf("uuid") < 0 &&
56
- id_col.flag.indexOf("qualified") < 0
57
- ) {
58
- return id;
59
- }
60
-
48
+ let pkey2s = desc? desc.pkey2s : null;
61
49
  /* `pkey2s` is a list in the desc, but the schema literal may
62
- * declare it as a bare string — take either. */
63
- let pkey2s = desc.pkey2s;
50
+ * declare it as a bare string -- take either. */
64
51
  if(typeof pkey2s === "string") {
65
52
  pkey2s = pkey2s? [pkey2s] : [];
66
53
  }
67
- if(!Array.isArray(pkey2s)) {
54
+ if(!Array.isArray(pkey2s) || pkey2s.length === 0) {
68
55
  return id;
69
56
  }
57
+
58
+ let parts = [];
70
59
  for(let name of pkey2s) {
60
+ if(!name) {
61
+ continue;
62
+ }
71
63
  let value = record[name];
72
- if(typeof value === "string" && value.length > 0) {
73
- return value;
64
+ if(typeof value === "number") {
65
+ value = String(value);
66
+ }
67
+ if(typeof value !== "string" || value.length === 0) {
68
+ continue;
69
+ }
70
+ if(value === String(id)) {
71
+ continue; /* the same thing twice says nothing */
74
72
  }
73
+ parts.push(value);
75
74
  }
76
-
77
- return id;
75
+ if(parts.length === 0) {
76
+ return id;
77
+ }
78
+ return [id, ...parts].join(SEP);
78
79
  }
79
-
@@ -1,8 +1,10 @@
1
1
  /***********************************************************************
2
2
  * treedb_node_label.test.js
3
3
  *
4
- * The label of a treedb graph node: the pkey when it names the
5
- * record, the secondary key when the pkey is synthetic.
4
+ * The label of a treedb graph node: its id, and the secondary
5
+ * keys it carries. A record is one thing and an INSTANCE of it
6
+ * another, and a card that shows one without the other cannot be
7
+ * identified.
6
8
  *
7
9
  * Copyright (c) 2026, ArtGins.
8
10
  * All Rights Reserved.
@@ -35,44 +37,67 @@ const REALMS_DESC = {
35
37
  };
36
38
 
37
39
  describe("node_label", () => {
38
- it("uses the secondary key when the pkey is a rowid", () => {
40
+ const DOT = " \u00b7 ";
41
+
42
+ it("says the id AND the secondary key that names the record", () => {
39
43
  expect(node_label(COLS_DESC, {id: "181", value: "yuno_role"}))
40
- .toBe("yuno_role");
44
+ .toBe("181" + DOT + "yuno_role");
45
+ });
46
+
47
+ it("says the id AND the instance: the case that brought it", () => {
48
+ /* Three yunos of one agent, all of release 7.23.0-1: the cards
49
+ * read `7.23.0-1` three times and never said which yuno. */
50
+ let yunos = {
51
+ topic_name: "yunos",
52
+ pkey: "id",
53
+ pkey2s: ["yuno_release"],
54
+ cols: [
55
+ {id: "id", type: "string", flag: ["persistent", "rowid"]},
56
+ {id: "yuno_release", type: "string", flag: ["persistent"]},
57
+ ],
58
+ };
59
+ expect(node_label(yunos, {id: "1", yuno_release: "7.23.0-1"}))
60
+ .toBe("1" + DOT + "7.23.0-1");
61
+ expect(node_label(yunos, {id: "1600", yuno_release: "1.8.1.0-2"}))
62
+ .toBe("1600" + DOT + "1.8.1.0-2");
41
63
  });
42
64
 
43
- it("uses the pkey when it names the record", () => {
65
+ it("gives the bare id when the topic declares no secondary key", () => {
44
66
  expect(node_label(REALMS_DESC, {id: "artgins.utilities.all", realm_name: "all"}))
45
67
  .toBe("artgins.utilities.all");
46
68
  });
47
69
 
48
70
  it("accepts a pkey2s declared as a bare string", () => {
49
71
  let desc = {...COLS_DESC, pkey2s: "value"};
50
- expect(node_label(desc, {id: "181", value: "yuno_role"})).toBe("yuno_role");
72
+ expect(node_label(desc, {id: "181", value: "yuno_role"}))
73
+ .toBe("181" + DOT + "yuno_role");
51
74
  });
52
75
 
53
- it("treats a qualified pkey like a rowid one", () => {
54
- let desc = {
55
- ...COLS_DESC,
56
- cols: [
57
- {id: "id", type: "string", flag: ["persistent", "qualified"]},
58
- {id: "value", type: "string", flag: ["persistent", "required"]},
59
- ],
60
- };
61
- expect(node_label(desc, {
62
- id: "treedb_yunovatioscodb.yunos.yuno_role",
63
- value: "yuno_role",
64
- })).toBe("yuno_role");
76
+ it("does not care what the id column is flagged", () => {
77
+ /* rowid, uuid, qualified or nothing at all: the id is shown
78
+ * either way, and the flags decide nothing here any more. */
79
+ for(let flag of [["persistent", "uuid"], ["persistent", "qualified"], ["persistent"]]) {
80
+ let desc = {
81
+ ...COLS_DESC,
82
+ cols: [
83
+ {id: "id", type: "string", flag: flag},
84
+ {id: "value", type: "string", flag: ["persistent", "required"]},
85
+ ],
86
+ };
87
+ expect(node_label(desc, {id: "k", value: "named"})).toBe("k" + DOT + "named");
88
+ }
65
89
  });
66
90
 
67
- it("treats a uuid pkey like a rowid one", () => {
68
- let desc = {
69
- ...COLS_DESC,
70
- cols: [
71
- {id: "id", type: "string", flag: ["persistent", "uuid"]},
72
- {id: "value", type: "string", flag: ["persistent", "required"]},
73
- ],
74
- };
75
- expect(node_label(desc, {id: "3f2a…", value: "named"})).toBe("named");
91
+ it("says every secondary key the topic declares, in its order", () => {
92
+ let desc = {topic_name: "x", pkey: "id", pkey2s: ["a", "b"], cols: []};
93
+ expect(node_label(desc, {id: "7", a: "one", b: "two"}))
94
+ .toBe("7" + DOT + "one" + DOT + "two");
95
+ });
96
+
97
+ it("skips a secondary key that is empty, absent or the id again", () => {
98
+ expect(node_label(COLS_DESC, {id: "181", value: ""})).toBe("181");
99
+ expect(node_label(COLS_DESC, {id: "181"})).toBe("181");
100
+ expect(node_label(COLS_DESC, {id: "181", value: "181"})).toBe("181");
76
101
  });
77
102
 
78
103
  it("falls back to the id when the desc carries no pkey2s (older node)", () => {
@@ -80,27 +105,13 @@ describe("node_label", () => {
80
105
  expect(node_label(desc, {id: "181", value: "yuno_role"})).toBe("181");
81
106
  });
82
107
 
83
- it("falls back to the id when the secondary key is empty in the record", () => {
84
- expect(node_label(COLS_DESC, {id: "181", value: ""})).toBe("181");
85
- expect(node_label(COLS_DESC, {id: "181"})).toBe("181");
86
- });
87
-
88
- it("honours a pkey that is not called 'id'", () => {
89
- let desc = {
90
- topic_name: "x",
91
- pkey: "key",
92
- pkey2s: ["name"],
93
- cols: [
94
- {id: "key", type: "string", flag: ["persistent", "rowid"]},
95
- {id: "name", type: "string", flag: ["persistent", "required"]},
96
- /* A col called `id` that is NOT the pkey must not decide. */
97
- {id: "id", type: "string", flag: ["persistent"]},
98
- ],
99
- };
100
- expect(node_label(desc, {id: "7", key: "7", name: "seven"})).toBe("seven");
108
+ it("takes a numeric secondary key as the text it is", () => {
109
+ expect(node_label(COLS_DESC, {id: "emailsender.artgins", value: 1}))
110
+ .toBe("emailsender.artgins" + DOT + "1");
101
111
  });
102
112
 
103
- it("survives a desc with no cols", () => {
113
+ it("survives a desc with no cols, and no desc at all", () => {
104
114
  expect(node_label({topic_name: "x", pkey: "id"}, {id: "42"})).toBe("42");
115
+ expect(node_label(null, {id: "42"})).toBe("42");
105
116
  });
106
117
  });