@yuneta/gobj-js 7.26.21 → 7.26.23

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.
@@ -216,6 +216,13 @@ var __jn_services__ = Object.create(null);
216
216
  var __yuno__ = null;
217
217
  var __default_service__ = null;
218
218
  var __inside__ = 0;
219
+ var frame_kind_t = Object.freeze({
220
+ FRAME_ACTION: 0,
221
+ FRAME_PUBLICATION: 1,
222
+ FRAME_METHOD: 2,
223
+ FRAME_WATCH: 3
224
+ });
225
+ var __dispatch_top__ = null;
219
226
  /**************************************************************************
220
227
  * SData - Structured Data
221
228
  **************************************************************************/
@@ -229,7 +236,15 @@ var data_type_t = Object.freeze({
229
236
  DTP_JSON: 7,
230
237
  DTP_POINTER: 8
231
238
  });
239
+ var DTP_IS_STRING = (type) => type === data_type_t.DTP_STRING;
240
+ var DTP_IS_BOOLEAN = (type) => type === data_type_t.DTP_BOOLEAN;
241
+ var DTP_IS_INTEGER = (type) => type === data_type_t.DTP_INTEGER;
242
+ var DTP_IS_REAL = (type) => type === data_type_t.DTP_REAL;
243
+ var DTP_IS_LIST = (type) => type === data_type_t.DTP_LIST;
244
+ var DTP_IS_DICT = (type) => type === data_type_t.DTP_DICT;
245
+ var DTP_IS_JSON = (type) => type === data_type_t.DTP_JSON;
232
246
  data_type_t.DTP_POINTER;
247
+ var DTP_IS_NUMBER = (type) => DTP_IS_INTEGER(type) || DTP_IS_REAL(type) || DTP_IS_BOOLEAN(type);
233
248
  var sdata_flag_t = Object.freeze({
234
249
  SDF_NOTACCESS: 1,
235
250
  SDF_RD: 2,
@@ -251,7 +266,8 @@ var sdata_flag_t = Object.freeze({
251
266
  SDF_AUTHZ_X: 131072,
252
267
  SDF_AUTHZ_P: 262144,
253
268
  SDF_AUTHZ_S: 524288,
254
- SDF_AUTHZ_RS: 1048576
269
+ SDF_AUTHZ_RS: 1048576,
270
+ SDF_SECRET: 2097152
255
271
  });
256
272
  sdata_flag_t.SDF_RD | sdata_flag_t.SDF_WR | sdata_flag_t.SDF_STATS | sdata_flag_t.SDF_PERSIST | sdata_flag_t.SDF_VOLATIL | sdata_flag_t.SDF_RSTATS | sdata_flag_t.SDF_PSTATS;
257
273
  sdata_flag_t.SDF_WR | sdata_flag_t.SDF_PERSIST;
@@ -839,6 +855,260 @@ function set_default(gobj, sdata, it) {
839
855
  return 0;
840
856
  }
841
857
  /***************************************************************************
858
+ * A value of a type, written as text or given as json, read as the C
859
+ * command parser reads a parameter (command_parser.c): the same grammar,
860
+ * the same refusals, so that a C client and a JS client read an attribute
861
+ * value the same way (json2item()). A refusal says why in a short phrase,
862
+ * never the value: it may be a secret.
863
+ ***************************************************************************/
864
+ var JSON_INTEGER_TEXT = /^-?(0|[1-9][0-9]*)$/;
865
+ var JSON_NUMBER_TEXT = /^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?$/;
866
+ var INT64_MIN = -(2n ** 63n);
867
+ var INT64_MAX = 2n ** 63n - 1n;
868
+ var REFUSED_TEXT = "a text that does not read as one";
869
+ function value_has_shape(type, value) {
870
+ if (DTP_IS_STRING(type)) return is_string(value);
871
+ if (DTP_IS_DICT(type)) return is_object(value);
872
+ if (DTP_IS_LIST(type)) return is_array(value);
873
+ if (DTP_IS_NUMBER(type)) return typeof value === "number" || typeof value === "boolean";
874
+ return true;
875
+ }
876
+ function shape_of_value(value) {
877
+ if (is_array(value)) return "an array";
878
+ if (is_object(value)) return "an object";
879
+ if (is_string(value)) return REFUSED_TEXT;
880
+ if (typeof value === "number") return Number.isInteger(value) ? "an integer" : "a real";
881
+ if (typeof value === "boolean") return "a boolean";
882
+ return "null";
883
+ }
884
+ function number_as_type(type, value) {
885
+ if (typeof value === "number" && !Number.isFinite(value)) return {
886
+ value: void 0,
887
+ refused: "a number out of its range"
888
+ };
889
+ if (DTP_IS_BOOLEAN(type)) return {
890
+ value: typeof value === "boolean" ? value : value !== 0,
891
+ refused: null
892
+ };
893
+ if (DTP_IS_REAL(type)) return {
894
+ value: typeof value === "boolean" ? value ? 1 : 0 : value,
895
+ refused: null
896
+ };
897
+ if (typeof value === "boolean") return {
898
+ value: value ? 1 : 0,
899
+ refused: null
900
+ };
901
+ if (!(value >= -0x8000000000000000 && value < 0x8000000000000000)) return {
902
+ value: void 0,
903
+ refused: "a number out of its range"
904
+ };
905
+ if (!Number.isInteger(value)) return {
906
+ value: void 0,
907
+ refused: "a real with decimals"
908
+ };
909
+ return {
910
+ value,
911
+ refused: null
912
+ };
913
+ }
914
+ function text2parameter(type, s, from_kw) {
915
+ if (!is_string(s)) s = "";
916
+ if (DTP_IS_STRING(type)) return {
917
+ value: s,
918
+ refused: null
919
+ };
920
+ if (DTP_IS_NUMBER(type)) {
921
+ if (s.length === 0) {
922
+ if (from_kw) return {
923
+ value: void 0,
924
+ refused: null
925
+ };
926
+ return {
927
+ value: DTP_IS_BOOLEAN(type) ? false : 0,
928
+ refused: null
929
+ };
930
+ }
931
+ if (DTP_IS_BOOLEAN(type)) {
932
+ const lower = s.toLowerCase();
933
+ if (lower === "true" || lower === "yes" || lower === "on") return {
934
+ value: true,
935
+ refused: null
936
+ };
937
+ if (lower === "false" || lower === "no" || lower === "off") return {
938
+ value: false,
939
+ refused: null
940
+ };
941
+ }
942
+ if (!(DTP_IS_REAL(type) ? JSON_NUMBER_TEXT : JSON_INTEGER_TEXT).test(s)) return {
943
+ value: void 0,
944
+ refused: REFUSED_TEXT
945
+ };
946
+ if (DTP_IS_REAL(type)) {
947
+ const d = Number(s);
948
+ if (!Number.isFinite(d)) return {
949
+ value: void 0,
950
+ refused: "a number out of its range"
951
+ };
952
+ return {
953
+ value: d,
954
+ refused: null
955
+ };
956
+ }
957
+ const n = BigInt(s);
958
+ if (n < INT64_MIN || n > INT64_MAX) return {
959
+ value: void 0,
960
+ refused: "a number out of its range"
961
+ };
962
+ if (DTP_IS_BOOLEAN(type)) return {
963
+ value: n !== 0n,
964
+ refused: null
965
+ };
966
+ return {
967
+ value: Number(n),
968
+ refused: null
969
+ };
970
+ }
971
+ if (DTP_IS_DICT(type) || DTP_IS_LIST(type)) {
972
+ let jn;
973
+ try {
974
+ jn = JSON.parse(s);
975
+ } catch (e) {
976
+ return {
977
+ value: void 0,
978
+ refused: REFUSED_TEXT
979
+ };
980
+ }
981
+ if (!value_has_shape(type, jn)) return {
982
+ value: void 0,
983
+ refused: REFUSED_TEXT
984
+ };
985
+ return {
986
+ value: jn,
987
+ refused: null
988
+ };
989
+ }
990
+ if (DTP_IS_JSON(type)) try {
991
+ return {
992
+ value: JSON.parse(s),
993
+ refused: null
994
+ };
995
+ } catch (e) {
996
+ const t = s.replace(/^[ \t]+/, "");
997
+ if (t.startsWith("{") || t.startsWith("[") || t.startsWith("\"")) return {
998
+ value: void 0,
999
+ refused: REFUSED_TEXT
1000
+ };
1001
+ return {
1002
+ value: s,
1003
+ refused: null
1004
+ };
1005
+ }
1006
+ return {
1007
+ value: void 0,
1008
+ refused: REFUSED_TEXT
1009
+ };
1010
+ }
1011
+ /***************************************************************************
1012
+ * A value of `type` written as TEXT, read as the C parser reads a
1013
+ * parameter on the command line (command_parameter_from_text()): an empty
1014
+ * text is 0 or false. Return {value, refused}: `value` undefined when the
1015
+ * text does not read as one, `refused` saying why.
1016
+ ***************************************************************************/
1017
+ function command_parameter_from_text(type, text) {
1018
+ const r = text2parameter(type, text, false);
1019
+ if (r.value === void 0 && !r.refused) r.refused = REFUSED_TEXT;
1020
+ return r;
1021
+ }
1022
+ /***************************************************************************
1023
+ * A value of `type` given as JSON, read as the C parser reads a
1024
+ * parameter given in kw (command_parameter_from_json()): a string as
1025
+ * command_parameter_from_text() reads a text (a string with a NUL inside
1026
+ * refused), a number or a boolean as the EXACT type (an integer to a
1027
+ * DTP_REAL is its real, a boolean to a DTP_INTEGER 1 or 0, 2.7 to a
1028
+ * DTP_INTEGER refused), an object or a list of its shape as it is, any
1029
+ * other shape refused. Return {value, refused}: `value` undefined and
1030
+ * `refused` saying why, or `refused` null when there is NO value (a null,
1031
+ * undefined, or an empty text given to a number).
1032
+ ***************************************************************************/
1033
+ function command_parameter_from_json(type, value) {
1034
+ if (value === void 0 || value === null) {
1035
+ if (DTP_IS_JSON(type) && value === null) return {
1036
+ value: null,
1037
+ refused: null
1038
+ };
1039
+ return {
1040
+ value: void 0,
1041
+ refused: null
1042
+ };
1043
+ }
1044
+ const nul_inside = is_string(value) && value.indexOf("\0") >= 0;
1045
+ if (DTP_IS_JSON(type)) {
1046
+ if (!is_string(value) || nul_inside) return {
1047
+ value,
1048
+ refused: null
1049
+ };
1050
+ const r = text2parameter(type, value, true);
1051
+ return r.value === void 0 ? {
1052
+ value,
1053
+ refused: null
1054
+ } : r;
1055
+ }
1056
+ if (!(DTP_IS_STRING(type) || DTP_IS_DICT(type) || DTP_IS_LIST(type) || DTP_IS_NUMBER(type))) return {
1057
+ value,
1058
+ refused: null
1059
+ };
1060
+ if (nul_inside) return {
1061
+ value: void 0,
1062
+ refused: "a text with a NUL in it"
1063
+ };
1064
+ if (is_string(value) && !DTP_IS_STRING(type)) return text2parameter(type, value, true);
1065
+ if (!value_has_shape(type, value)) return {
1066
+ value: void 0,
1067
+ refused: shape_of_value(value)
1068
+ };
1069
+ if (DTP_IS_NUMBER(type)) return number_as_type(type, value);
1070
+ return {
1071
+ value,
1072
+ refused: null
1073
+ };
1074
+ }
1075
+ /***************************************************************************
1076
+ * A value json2item() refuses: the attr, its type and why, and the value
1077
+ * as a log may show it (a SDF_SECRET one never; any other masked, then
1078
+ * capped on a whole character, as in C)
1079
+ ***************************************************************************/
1080
+ var ATTR_VALUE_SHOWN = 64;
1081
+ var TYPES_READ = [
1082
+ data_type_t.DTP_BOOLEAN,
1083
+ data_type_t.DTP_INTEGER,
1084
+ data_type_t.DTP_REAL,
1085
+ data_type_t.DTP_LIST,
1086
+ data_type_t.DTP_DICT
1087
+ ];
1088
+ function log_attr_value_refused(gobj, it, value, refused) {
1089
+ const types = {
1090
+ [data_type_t.DTP_BOOLEAN]: "a boolean",
1091
+ [data_type_t.DTP_INTEGER]: "an integer",
1092
+ [data_type_t.DTP_REAL]: "a real",
1093
+ [data_type_t.DTP_LIST]: "a json array",
1094
+ [data_type_t.DTP_DICT]: "a json object"
1095
+ };
1096
+ let shown;
1097
+ if (it.flag & sdata_flag_t.SDF_SECRET) shown = "********";
1098
+ else {
1099
+ shown = is_string(value) ? value : String(JSON.stringify(value === void 0 ? null : value));
1100
+ const masked = mask_secrets_inline(shown);
1101
+ if (masked !== null) shown = masked;
1102
+ if (shown.length > ATTR_VALUE_SHOWN) {
1103
+ let n = ATTR_VALUE_SHOWN;
1104
+ const code = shown.charCodeAt(n);
1105
+ if (code >= 56320 && code <= 57343) n--;
1106
+ shown = shown.substring(0, n);
1107
+ }
1108
+ }
1109
+ log_error(`${gobj_short_name(gobj)}: Attribute value of the wrong type, not written: attr '${it.name}', type ${types[it.type] || "a value of its type"}, got ${refused || "no value (a null, or an empty text)"}, value '${shown}'`);
1110
+ }
1111
+ /***************************************************************************
842
1112
  * Set array values to sdata, from json or binary
843
1113
  ***************************************************************************/
844
1114
  function json2item(gobj, sdata, it, jn_value_) {
@@ -853,44 +1123,18 @@ function json2item(gobj, sdata, it, jn_value_) {
853
1123
  }
854
1124
  break;
855
1125
  case data_type_t.DTP_BOOLEAN:
856
- jn_value2 = parseBoolean(jn_value_);
857
- break;
858
1126
  case data_type_t.DTP_INTEGER:
859
- jn_value2 = parseInt(jn_value_);
860
- if (isNaN(jn_value2)) {
861
- log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
862
- return -1;
863
- }
864
- break;
865
1127
  case data_type_t.DTP_REAL:
866
- jn_value2 = Number(jn_value_);
867
- if (isNaN(jn_value2)) {
868
- log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
869
- return -1;
870
- }
871
- break;
872
1128
  case data_type_t.DTP_LIST:
873
- if (is_string(jn_value_)) try {
874
- jn_value2 = JSON.parse(jn_value_);
875
- } catch (error) {
876
- log_error(`JSON parse error: ${error.message}`);
877
- }
878
- else jn_value2 = jn_value_;
879
- if (!is_array(jn_value2)) {
880
- log_error(`attr must be an array: ${it.name}`);
881
- return -1;
882
- }
883
- break;
884
1129
  case data_type_t.DTP_DICT:
885
- if (is_string(jn_value_)) try {
886
- jn_value2 = JSON.parse(jn_value_);
887
- } catch (error) {
888
- log_error(`JSON parse error: ${error.message}`);
889
- }
890
- else jn_value2 = jn_value_;
891
- if (!is_object(jn_value2)) {
892
- log_error(`attr must be an object: ${it.name}`);
893
- return -1;
1130
+ {
1131
+ if (jn_value_ === void 0) return 0;
1132
+ const r = command_parameter_from_json(it.type, jn_value_);
1133
+ if (r.value === void 0) {
1134
+ log_attr_value_refused(gobj, it, jn_value_, r.refused);
1135
+ return -1;
1136
+ }
1137
+ jn_value2 = r.value;
894
1138
  }
895
1139
  break;
896
1140
  case data_type_t.DTP_JSON:
@@ -1465,6 +1709,8 @@ function gobj_destroy(gobj) {
1465
1709
  return;
1466
1710
  }
1467
1711
  if (gobj.__refs__ > 0) log_error(`${gobj_short_name(gobj)}: gobj DESTROYING with references: ${gobj.__refs__}`);
1712
+ if (!(gobj.parent && gobj_is_destroying(gobj.parent))) check_destroy_under_running_code(gobj);
1713
+ mark_dispatch_frames_destroyed(gobj);
1468
1714
  if (gobj.playing) {
1469
1715
  log_error(`Destroying a PLAYING gobj: ${gobj_full_name(gobj)}`);
1470
1716
  gobj_pause(gobj);
@@ -1529,10 +1775,44 @@ function gobj_start(gobj) {
1529
1775
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏺ ⏺ start: %s", gobj_full_name(gobj)));
1530
1776
  gobj.running = true;
1531
1777
  let ret = 0;
1532
- if (gobj.gclass.gmt.mt_start) ret = gobj.gclass.gmt.mt_start(gobj);
1778
+ if (gobj.gclass.gmt.mt_start) {
1779
+ const frame = push_dispatch_frame(gobj, "mt_start", frame_kind_t.FRAME_METHOD);
1780
+ try {
1781
+ ret = gobj.gclass.gmt.mt_start(gobj);
1782
+ } finally {
1783
+ pop_dispatch_frame(frame);
1784
+ }
1785
+ }
1533
1786
  return ret;
1534
1787
  }
1535
1788
  /***************************************************************************
1789
+ * Start a gobj in a walk of its tree, and say whether that destroyed it.
1790
+ *
1791
+ * A gclass may end itself in its own mt_start or mt_stop, and a walk of
1792
+ * the tree must not go down into the children of a gobj that is gone.
1793
+ ***************************************************************************/
1794
+ function start_destroys_it(gobj) {
1795
+ const frame = push_dispatch_frame(gobj, "gobj_start", frame_kind_t.FRAME_WATCH);
1796
+ try {
1797
+ gobj_start(gobj);
1798
+ } finally {
1799
+ pop_dispatch_frame(frame);
1800
+ }
1801
+ return frame.destroyed;
1802
+ }
1803
+ /***************************************************************************
1804
+ * Stop a gobj in a walk of its tree, and say whether that destroyed it.
1805
+ ***************************************************************************/
1806
+ function stop_destroys_it(gobj) {
1807
+ const frame = push_dispatch_frame(gobj, "gobj_stop", frame_kind_t.FRAME_WATCH);
1808
+ try {
1809
+ gobj_stop(gobj);
1810
+ } finally {
1811
+ pop_dispatch_frame(frame);
1812
+ }
1813
+ return frame.destroyed;
1814
+ }
1815
+ /***************************************************************************
1536
1816
  * Start all children of the gobj.
1537
1817
  ***************************************************************************/
1538
1818
  function cb_start_child(child, user_data, user_data2) {
@@ -1553,7 +1833,9 @@ function gobj_start_children(gobj) {
1553
1833
  function cb_start_child_tree(child, user_data, user_data2) {
1554
1834
  if (child.gclass.gclass_flag & gclass_flag_t.gcflag_manual_start) return 1;
1555
1835
  if (gobj_is_disabled(child)) return 1;
1556
- if (!gobj_is_running(child)) gobj_start(child);
1836
+ if (!gobj_is_running(child)) {
1837
+ if (start_destroys_it(child)) return 1;
1838
+ }
1557
1839
  return 0;
1558
1840
  }
1559
1841
  function gobj_start_tree(gobj) {
@@ -1564,7 +1846,9 @@ function gobj_start_tree(gobj) {
1564
1846
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏺ ⏺ ⏺ ⏺ start_tree: %s", gobj_full_name(gobj)));
1565
1847
  if (gobj.gclass.gclass_flag & gclass_flag_t.gcflag_manual_start) return 0;
1566
1848
  else if (gobj.disabled) return 0;
1567
- else if (!gobj.running) gobj_start(gobj);
1849
+ else if (!gobj.running) {
1850
+ if (start_destroys_it(gobj)) return 0;
1851
+ }
1568
1852
  return gobj_walk_gobj_children_tree(gobj, walk_type_t.WALK_TOP2BOTTOM, cb_start_child_tree, 0, 0);
1569
1853
  }
1570
1854
  /************************************************************
@@ -1586,14 +1870,23 @@ function gobj_stop(gobj) {
1586
1870
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏹ ⏹ stop: %s", gobj_full_name(gobj)));
1587
1871
  gobj.running = false;
1588
1872
  let ret = 0;
1589
- if (gobj.gclass.gmt.mt_stop) ret = gobj.gclass.gmt.mt_stop(gobj);
1873
+ if (gobj.gclass.gmt.mt_stop) {
1874
+ const frame = push_dispatch_frame(gobj, "mt_stop", frame_kind_t.FRAME_METHOD);
1875
+ try {
1876
+ ret = gobj.gclass.gmt.mt_stop(gobj);
1877
+ } finally {
1878
+ pop_dispatch_frame(frame);
1879
+ }
1880
+ }
1590
1881
  return ret;
1591
1882
  }
1592
1883
  /***************************************************************************
1593
1884
  * Stop all children of the gobj.
1594
1885
  ***************************************************************************/
1595
1886
  function cb_stop_child(child, user_data, user_data2) {
1596
- if (gobj_is_running(child)) gobj_stop(child);
1887
+ if (gobj_is_running(child)) {
1888
+ if (stop_destroys_it(child)) return 1;
1889
+ }
1597
1890
  return 0;
1598
1891
  }
1599
1892
  function gobj_stop_children(gobj) {
@@ -1612,7 +1905,9 @@ function gobj_stop_tree(gobj) {
1612
1905
  return -1;
1613
1906
  }
1614
1907
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏹ ⏹ ⏹ ⏹ stop_tree: %s", gobj_full_name(gobj)));
1615
- if (gobj_is_running(gobj)) gobj_stop(gobj);
1908
+ if (gobj_is_running(gobj)) {
1909
+ if (stop_destroys_it(gobj)) return 0;
1910
+ }
1616
1911
  return gobj_walk_gobj_children_tree(gobj, walk_type_t.WALK_TOP2BOTTOM, cb_stop_child, 0, 0);
1617
1912
  }
1618
1913
  /************************************************************
@@ -1644,8 +1939,13 @@ function gobj_play(gobj) {
1644
1939
  gobj.playing = true;
1645
1940
  let ret = 0;
1646
1941
  if (gobj.gclass.gmt.mt_play) {
1647
- ret = gobj.gclass.gmt.mt_play(gobj);
1648
- if (ret < 0 || ret === false) gobj.playing = false;
1942
+ const frame = push_dispatch_frame(gobj, "mt_play", frame_kind_t.FRAME_METHOD);
1943
+ try {
1944
+ ret = gobj.gclass.gmt.mt_play(gobj);
1945
+ } finally {
1946
+ pop_dispatch_frame(frame);
1947
+ }
1948
+ if ((ret < 0 || ret === false) && !frame.destroyed) gobj.playing = false;
1649
1949
  }
1650
1950
  return ret;
1651
1951
  }
@@ -1663,8 +1963,14 @@ function gobj_pause(gobj) {
1663
1963
  }
1664
1964
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏸ ⏸ pause: %s", gobj_full_name(gobj)));
1665
1965
  gobj.playing = false;
1666
- if (gobj.gclass.gmt.mt_pause) return gobj.gclass.gmt.mt_pause(gobj);
1667
- else return 0;
1966
+ if (gobj.gclass.gmt.mt_pause) {
1967
+ const frame = push_dispatch_frame(gobj, "mt_pause", frame_kind_t.FRAME_METHOD);
1968
+ try {
1969
+ return gobj.gclass.gmt.mt_pause(gobj);
1970
+ } finally {
1971
+ pop_dispatch_frame(frame);
1972
+ }
1973
+ } else return 0;
1668
1974
  }
1669
1975
  /************************************************************
1670
1976
  *
@@ -2217,14 +2523,7 @@ function gobj_write_attr(gobj, path, value, src) {
2217
2523
  if (ss.length <= 1) {
2218
2524
  let key = path;
2219
2525
  if (key in jn_attrs) {
2220
- if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) {
2221
- jn_attrs[key] = value;
2222
- if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, key)) gobj.priv[key] = value;
2223
- if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2224
- if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2225
- }
2226
- return 0;
2227
- }
2526
+ if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) return write_attr_value(gobj, key, value);
2228
2527
  }
2229
2528
  log_error(sprintf("gobj_write_attr(%s): attr not found '%s'", gobj_short_name(gobj), key));
2230
2529
  return -1;
@@ -2240,14 +2539,7 @@ function gobj_write_attr(gobj, path, value, src) {
2240
2539
  }
2241
2540
  let jn_attrs = gobj.jn_attrs;
2242
2541
  if (key in jn_attrs) {
2243
- if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) {
2244
- jn_attrs[key] = value;
2245
- if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, key)) gobj.priv[key] = value;
2246
- if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2247
- if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2248
- }
2249
- return 0;
2250
- }
2542
+ if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) return write_attr_value(gobj, key, value);
2251
2543
  }
2252
2544
  break;
2253
2545
  }
@@ -2255,6 +2547,49 @@ function gobj_write_attr(gobj, path, value, src) {
2255
2547
  return -1;
2256
2548
  }
2257
2549
  /***************************************************************************
2550
+ * The value written to the attr `key` of `gobj`, read as its type as the C
2551
+ * kernel reads it (json2item(): it writes priv and calls mt_writing too).
2552
+ * Up to 7.26.21 gobj_write_attr() wrote it raw.
2553
+ ***************************************************************************/
2554
+ function write_attr_value(gobj, key, value) {
2555
+ const it = gobj_attr_desc(gobj, key, false);
2556
+ if (!it) {
2557
+ log_error(sprintf("gobj_write_attr(%s): attr without description '%s'", gobj_short_name(gobj), key));
2558
+ return -1;
2559
+ }
2560
+ if (value === void 0 && TYPES_READ.includes(it.type)) {
2561
+ log_attr_value_refused(gobj, it, value, null);
2562
+ return -1;
2563
+ }
2564
+ return json2item(gobj, gobj.jn_attrs, it, value);
2565
+ }
2566
+ /***************************************************************************
2567
+ * The value a typed writer writes, read as ITS type by the grammar of
2568
+ * json2item() (a C writer takes its C type: a string is never read as a
2569
+ * number there). Return {value} or {refused} (logged): the attr keeps its
2570
+ * value. A string writer never refuses: null and undefined are "", any
2571
+ * other json its text, as a DTP_STRING attr reads them.
2572
+ ***************************************************************************/
2573
+ function typed_writer_value(gobj, name, type, value) {
2574
+ if (DTP_IS_STRING(type)) {
2575
+ if (value === null || value === void 0) return { value: "" };
2576
+ return { value: is_string(value) ? value : String(JSON.stringify(value)) };
2577
+ }
2578
+ const r = command_parameter_from_json(type, value);
2579
+ if (r.value === void 0) {
2580
+ let holder = gobj;
2581
+ while (holder && !gobj_has_attr(holder, name)) holder = holder.bottom_gobj;
2582
+ const it = holder ? gobj_attr_desc(holder, name, false) : null;
2583
+ log_attr_value_refused(gobj, {
2584
+ name,
2585
+ type,
2586
+ flag: it ? it.flag : 0
2587
+ }, value, r.refused);
2588
+ return { refused: true };
2589
+ }
2590
+ return { value: r.value };
2591
+ }
2592
+ /***************************************************************************
2258
2593
  * WRITE - high level
2259
2594
  ***************************************************************************/
2260
2595
  function sdata_write_default_values(gobj, include_flag, exclude_flag) {
@@ -2302,7 +2637,9 @@ function gobj_write_attrs(gobj, kw, include_flag, src) {
2302
2637
  function gobj_write_bool_attr(gobj, name, value) {
2303
2638
  let hs = gobj_hsdata2(gobj, name, false);
2304
2639
  if (hs) {
2305
- hs[name] = parseBoolean(value);
2640
+ const v = typed_writer_value(gobj, name, data_type_t.DTP_BOOLEAN, value);
2641
+ if (v.refused) return -1;
2642
+ hs[name] = v.value;
2306
2643
  if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, name)) gobj.priv[name] = hs[name];
2307
2644
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2308
2645
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
@@ -2318,7 +2655,9 @@ function gobj_write_bool_attr(gobj, name, value) {
2318
2655
  function gobj_write_integer_attr(gobj, name, value) {
2319
2656
  let hs = gobj_hsdata2(gobj, name, false);
2320
2657
  if (hs) {
2321
- hs[name] = parseInt(value);
2658
+ const v = typed_writer_value(gobj, name, data_type_t.DTP_INTEGER, value);
2659
+ if (v.refused) return -1;
2660
+ hs[name] = v.value;
2322
2661
  if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, name)) gobj.priv[name] = hs[name];
2323
2662
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2324
2663
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
@@ -2334,7 +2673,9 @@ function gobj_write_integer_attr(gobj, name, value) {
2334
2673
  function gobj_write_str_attr(gobj, name, value) {
2335
2674
  let hs = gobj_hsdata2(gobj, name, false);
2336
2675
  if (hs) {
2337
- hs[name] = String(value);
2676
+ const v = typed_writer_value(gobj, name, data_type_t.DTP_STRING, value);
2677
+ if (v.refused) return -1;
2678
+ hs[name] = v.value;
2338
2679
  if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, name)) gobj.priv[name] = hs[name];
2339
2680
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2340
2681
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
@@ -2544,6 +2885,84 @@ function purge_posted_events(gobj) {
2544
2885
  __dl_posted_events__ = __dl_posted_events__.filter((msg) => msg.dst !== gobj);
2545
2886
  }
2546
2887
  /************************************************************
2888
+ * A dispatch begins: its frame goes on top of the chain.
2889
+ * Every push is paired with a pop in a `finally`: an action
2890
+ * that throws must not leave its frame on the chain.
2891
+ ************************************************************/
2892
+ function push_dispatch_frame(gobj, event, kind) {
2893
+ const frame = {
2894
+ prev: __dispatch_top__,
2895
+ gobj,
2896
+ event,
2897
+ kind,
2898
+ destroyed: false
2899
+ };
2900
+ __dispatch_top__ = frame;
2901
+ return frame;
2902
+ }
2903
+ function pop_dispatch_frame(frame) {
2904
+ __dispatch_top__ = frame.prev;
2905
+ }
2906
+ /************************************************************
2907
+ * Every dispatch in progress of `gobj` learns that it is
2908
+ * destroyed. Only its own frames: each child destroyed with
2909
+ * it marks its own, in its own gobj_destroy().
2910
+ ************************************************************/
2911
+ function mark_dispatch_frames_destroyed(gobj) {
2912
+ for (let f = __dispatch_top__; f; f = f.prev) if (f.gobj === gobj) f.destroyed = true;
2913
+ }
2914
+ /************************************************************
2915
+ * Is the innermost live publication of `gobj` its EV_STOPPED?
2916
+ * Then the stop contract holds for it: that is the last thing
2917
+ * it does with itself.
2918
+ ************************************************************/
2919
+ function said_farewell(gobj) {
2920
+ for (let f = __dispatch_top__; f; f = f.prev) if (f.kind === frame_kind_t.FRAME_PUBLICATION && !f.destroyed && f.gobj === gobj) return f.event === "EV_STOPPED";
2921
+ return false;
2922
+ }
2923
+ /************************************************************
2924
+ * A gobj must not be destroyed under code that is still
2925
+ * running on it -- the C kernel's check, with its exemptions
2926
+ * and its fields, so both runtimes report the same defect the
2927
+ * same way.
2928
+ *
2929
+ * `gobj` is about to be destroyed, and with it its whole
2930
+ * subtree. Every live frame of a gobj of that subtree is code
2931
+ * that goes on when the destroy returns, on a gobj that is gone
2932
+ * (C_TIMER right after its EV_TIMEOUT). It is an ERROR, except
2933
+ * for:
2934
+ *
2935
+ * - the innermost frame: it is the code that destroys, and
2936
+ * knows it (a gobj that destroys itself in its own action);
2937
+ *
2938
+ * - the frames of a gobj whose innermost publication is
2939
+ * EV_STOPPED: the stop contract, its host destroys it there;
2940
+ *
2941
+ * - the watch frames, which run no gclass code of their own.
2942
+ *
2943
+ * The destroy goes on: this only says it, once. The fix is to
2944
+ * defer the destroy with gobj_post_event().
2945
+ ************************************************************/
2946
+ function check_destroy_under_running_code(gobj) {
2947
+ const top = __dispatch_top__;
2948
+ if (!top) return;
2949
+ for (let f = top.prev; f; f = f.prev) {
2950
+ if (f.destroyed || f.kind === frame_kind_t.FRAME_WATCH) continue;
2951
+ let in_tree = false;
2952
+ for (let p = f.gobj; p; p = p.parent) if (p === gobj) {
2953
+ in_tree = true;
2954
+ break;
2955
+ }
2956
+ if (!in_tree) continue;
2957
+ if (said_farewell(f.gobj)) continue;
2958
+ const destroyer = top.destroyed ? "(destroyed)" : gobj_full_name(top.gobj);
2959
+ const stack = String((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(2).join("\n");
2960
+ if (f.kind === frame_kind_t.FRAME_PUBLICATION) log_error(`Destroying a gobj while its tree is publishing: destroying ${gobj_full_name(gobj)}, publisher ${gobj_full_name(f.gobj)}, event ${f.event || ""}, destroyer ${destroyer}, inner_event ${top.event || ""}. Defer it with gobj_post_event(): a subscriber must not destroy the tree of its publisher synchronously. stack:\n${stack}`);
2961
+ else log_error(`Destroying a gobj while one of its actions is still running: destroying ${gobj_full_name(gobj)}, running ${gobj_full_name(f.gobj)}, event ${f.event || ""}, destroyer ${destroyer}, inner_event ${top.event || ""}. Defer it with gobj_post_event(): an inner dispatch must not destroy a gobj that an outer one is running on. stack:\n${stack}`);
2962
+ return;
2963
+ }
2964
+ }
2965
+ /************************************************************
2547
2966
  * send_event
2548
2967
  ************************************************************/
2549
2968
  function gobj_send_event(dst, event, kw, src) {
@@ -2575,7 +2994,12 @@ function gobj_send_event(dst, event, kw, src) {
2575
2994
  }
2576
2995
  }
2577
2996
  }
2578
- return dst.gclass.gmt.mt_inject_event(dst, event, kw, src);
2997
+ const frame = push_dispatch_frame(dst, event, frame_kind_t.FRAME_ACTION);
2998
+ try {
2999
+ return dst.gclass.gmt.mt_inject_event(dst, event, kw, src);
3000
+ } finally {
3001
+ pop_dispatch_frame(frame);
3002
+ }
2579
3003
  }
2580
3004
  if (tracea) trace_machine(sprintf("📛 mach(%s%s^%s), st: %s, ev: %s, 📛📛ERROR Event NOT DEFINED in state📛📛, from(%s%s^%s)", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), state.state_name, event ? event : "", src && !src.running ? "!!" : "", gobj_gclass_name(src), gobj_name(src)));
2581
3005
  log_error(`📛📛 Event NOT DEFINED in state 📛📛: ${gobj_short_name(dst)}, ${state.state_name}, ${event}`);
@@ -2595,6 +3019,7 @@ function gobj_send_event(dst, event, kw, src) {
2595
3019
  }
2596
3020
  }
2597
3021
  }
3022
+ const frame = push_dispatch_frame(dst, event, frame_kind_t.FRAME_ACTION);
2598
3023
  if (event_action.next_state) gobj_change_state(dst, event_action.next_state);
2599
3024
  let ret = -1;
2600
3025
  try {
@@ -2608,8 +3033,10 @@ function gobj_send_event(dst, event, kw, src) {
2608
3033
  } catch (e2) {}
2609
3034
  }
2610
3035
  throw e;
3036
+ } finally {
3037
+ pop_dispatch_frame(frame);
2611
3038
  }
2612
- if (tracea && __trace_machine_format__ !== 1 && !(dst.obflag & obflag_t.obflag_destroyed)) trace_machine(sprintf("<- mach(%s%s^%s), st: %s, ev: %s, ret: %d", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), dst.current_state.state_name, event ? event : "", is_number(ret) ? ret : 0));
3039
+ if (tracea && __trace_machine_format__ !== 1 && !frame.destroyed) trace_machine(sprintf("<- mach(%s%s^%s), st: %s, ev: %s, ret: %d", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), dst.current_state.state_name, event ? event : "", is_number(ret) ? ret : 0));
2613
3040
  __inside__--;
2614
3041
  return ret;
2615
3042
  }
@@ -3072,62 +3499,67 @@ function gobj_publish_event(publisher, event, kw) {
3072
3499
  let dl_subs = [...publisher.dl_subscriptions];
3073
3500
  let sent_count = 0;
3074
3501
  let ret = 0;
3075
- for (let idx = 0; idx < dl_subs.length; idx++) {
3076
- let subs = dl_subs[idx];
3077
- if (publisher.gclass.gmt.mt_publication_pre_filter) {
3078
- let topublish = publisher.gclass.gmt.mt_publication_pre_filter(publisher, subs, event, kw);
3079
- if (topublish < 0) break;
3080
- else if (!topublish) continue;
3081
- }
3082
- let subscriber = kw_get_pointer(publisher, subs, "subscriber", null, kw_flag_t.KW_REQUIRED);
3083
- if (!(subscriber && !gobj_is_destroying(subscriber))) continue;
3084
- let subs_flag = kw_get_int(publisher, subs, "subs_flag", 0, kw_flag_t.KW_REQUIRED);
3085
- let event_ = kw_get_str(publisher, subs, "event", "", kw_flag_t.KW_REQUIRED);
3086
- if (empty_string(event_) || strcasecmp(event_, event) === 0) {
3087
- let __config__ = kw_get_dict(publisher, subs, "__config__", null, 0);
3088
- let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
3089
- let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
3090
- let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
3091
- let event_name = subs.renamed_event;
3092
- if (empty_string(event_name)) event_name = event;
3093
- let topublish = 1;
3094
- if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
3095
- else if (json_size(__filter__) > 0) {
3096
- topublish = kw_match_simple(kw, __filter__) ? 1 : 0;
3502
+ const frame = push_dispatch_frame(publisher, event, frame_kind_t.FRAME_PUBLICATION);
3503
+ try {
3504
+ for (let idx = 0; idx < dl_subs.length; idx++) {
3505
+ let subs = dl_subs[idx];
3506
+ if (publisher.gclass.gmt.mt_publication_pre_filter) {
3507
+ let topublish = publisher.gclass.gmt.mt_publication_pre_filter(publisher, subs, event, kw);
3508
+ if (topublish < 0) break;
3509
+ else if (!topublish) continue;
3510
+ }
3511
+ let subscriber = kw_get_pointer(publisher, subs, "subscriber", null, kw_flag_t.KW_REQUIRED);
3512
+ if (!(subscriber && !gobj_is_destroying(subscriber))) continue;
3513
+ let subs_flag = kw_get_int(publisher, subs, "subs_flag", 0, kw_flag_t.KW_REQUIRED);
3514
+ let event_ = kw_get_str(publisher, subs, "event", "", kw_flag_t.KW_REQUIRED);
3515
+ if (empty_string(event_) || strcasecmp(event_, event) === 0) {
3516
+ let __config__ = kw_get_dict(publisher, subs, "__config__", null, 0);
3517
+ let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
3518
+ let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
3519
+ let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
3520
+ let event_name = subs.renamed_event;
3521
+ if (empty_string(event_name)) event_name = event;
3522
+ let topublish = 1;
3523
+ if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
3524
+ else if (json_size(__filter__) > 0) {
3525
+ topublish = kw_match_simple(kw, __filter__) ? 1 : 0;
3526
+ if (tracea) {
3527
+ trace_machine(sprintf("💜💜🔄%s publishing with filter, event '%s', subscriber'%s', publisher %s", topublish ? "👍" : "👎", event ? event : "", gobj_short_name(subscriber), gobj_short_name(publisher)));
3528
+ trace_json(__filter__);
3529
+ }
3530
+ }
3531
+ if (topublish < 0) break;
3532
+ else if (!topublish) continue;
3533
+ let ev_ = gobj_event_type(subscriber, event, true);
3534
+ if (ev_) {
3535
+ if (ev_.event_flag & event_flag_t.EVF_SYSTEM_EVENT) {
3536
+ if (!gobj_has_event(subscriber, ev_.event_name, 0)) continue;
3537
+ }
3538
+ }
3539
+ let kw2publish = kw;
3540
+ if (json_size(__local__) > 0 || json_size(__global__) > 0) kw2publish = json_object_update({}, kw);
3541
+ if (json_size(__local__) > 0) kw_pop(kw2publish, __local__);
3542
+ if (__config__) {}
3543
+ if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
3097
3544
  if (tracea) {
3098
- trace_machine(sprintf("💜💜🔄%s publishing with filter, event '%s', subscriber'%s', publisher %s", topublish ? "👍" : "👎", event ? event : "", gobj_short_name(subscriber), gobj_short_name(publisher)));
3099
- trace_json(__filter__);
3545
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3546
+ else trace_machine(sprintf("🔝🔄 mach(%s%s), st: %s, ev: %s (%s), from(%s%s)", !subscriber.running ? "!!" : "", gobj_short_name(subscriber), gobj_current_state(subscriber), event ? event : "", event_name ? event_name : "", publisher && !publisher.running ? "!!" : "", gobj_short_name(publisher)));
3547
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
3100
3548
  }
3101
- }
3102
- if (topublish < 0) break;
3103
- else if (!topublish) continue;
3104
- let ev_ = gobj_event_type(subscriber, event, true);
3105
- if (ev_) {
3106
- if (ev_.event_flag & event_flag_t.EVF_SYSTEM_EVENT) {
3107
- if (!gobj_has_event(subscriber, ev_.event_name, 0)) continue;
3549
+ let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
3550
+ if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
3551
+ sent_count = -1;
3552
+ break;
3108
3553
  }
3554
+ ret += ret_;
3555
+ sent_count++;
3556
+ if (frame.destroyed || gobj_is_destroying(publisher)) break;
3109
3557
  }
3110
- let kw2publish = kw;
3111
- if (json_size(__local__) > 0 || json_size(__global__) > 0) kw2publish = json_object_update({}, kw);
3112
- if (json_size(__local__) > 0) kw_pop(kw2publish, __local__);
3113
- if (__config__) {}
3114
- if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
3115
- if (tracea) {
3116
- if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3117
- else trace_machine(sprintf("🔝🔄 mach(%s%s), st: %s, ev: %s (%s), from(%s%s)", !subscriber.running ? "!!" : "", gobj_short_name(subscriber), gobj_current_state(subscriber), event ? event : "", event_name ? event_name : "", publisher && !publisher.running ? "!!" : "", gobj_short_name(publisher)));
3118
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
3119
- }
3120
- let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
3121
- if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
3122
- sent_count = -1;
3123
- break;
3124
- }
3125
- ret += ret_;
3126
- sent_count++;
3127
- if (gobj_is_destroying(publisher)) break;
3128
3558
  }
3559
+ } finally {
3560
+ pop_dispatch_frame(frame);
3129
3561
  }
3130
- if (!sent_count) {
3562
+ if (!sent_count && !frame.destroyed) {
3131
3563
  if (!ev || !(ev.event_flag & event_flag_t.EVF_NO_WARN_SUBS)) log_warning(`${gobj_short_name(publisher)}: Publish event WITHOUT subscribers, ev ${event}`);
3132
3564
  }
3133
3565
  return ret;
@@ -3436,12 +3868,12 @@ var SECRET_NAME_JOINED = [
3436
3868
  "sessionkey",
3437
3869
  "authdata"
3438
3870
  ];
3439
- var SECRET_NAME_SEGMENTS = /* @__PURE__ */ new Set([
3871
+ var SECRET_NAME_SEGMENTS = [
3440
3872
  "pass",
3441
3873
  "pin",
3442
3874
  "otp"
3443
- ]);
3444
- var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3875
+ ];
3876
+ var NOT_SECRET_SEGMENTS = [
3445
3877
  "endpoint",
3446
3878
  "url",
3447
3879
  "uri",
@@ -3464,36 +3896,130 @@ var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3464
3896
  "expiry",
3465
3897
  "expires",
3466
3898
  "mode"
3467
- ]);
3899
+ ];
3900
+ function words_by_letter(list) {
3901
+ const by_letter = new Array(128).fill(null);
3902
+ for (const word of list) {
3903
+ const c = word.charCodeAt(0);
3904
+ if (!by_letter[c]) by_letter[c] = [];
3905
+ by_letter[c].push(word);
3906
+ }
3907
+ return by_letter;
3908
+ }
3909
+ var SECRET_PARTS_BY_LETTER = words_by_letter(SECRET_NAME_PARTS);
3910
+ var SECRET_JOINED_BY_LETTER = words_by_letter(SECRET_NAME_JOINED);
3911
+ var SECRET_SEGMENTS_BY_LETTER = words_by_letter(SECRET_NAME_SEGMENTS);
3912
+ var NOT_SECRET_BY_LETTER = words_by_letter(NOT_SECRET_SEGMENTS);
3913
+ function ascii_lower_code(code) {
3914
+ return code >= 65 && code <= 90 ? code + 32 : code;
3915
+ }
3916
+ function is_name_joiner_code(code) {
3917
+ return code === 95 || code === 45 || code === 46 || code === 32 || code === 9;
3918
+ }
3919
+ function name_segment_in(str, from, to, by_letter) {
3920
+ const n = to - from;
3921
+ if (n === 0) return false;
3922
+ const c0 = ascii_lower_code(str.charCodeAt(from));
3923
+ const words = c0 < 128 ? by_letter[c0] : null;
3924
+ if (!words) return false;
3925
+ for (let w = 0; w < words.length; w++) {
3926
+ const word = words[w];
3927
+ if (word.length !== n) continue;
3928
+ let i = 1;
3929
+ while (i < n && ascii_lower_code(str.charCodeAt(from + i)) === word.charCodeAt(i)) i++;
3930
+ if (i === n) return true;
3931
+ }
3932
+ return false;
3933
+ }
3934
+ function name_part_at(str, to, i, part, joined) {
3935
+ let j = i;
3936
+ let k = 0;
3937
+ while (k < part.length) {
3938
+ if (j >= to) return false;
3939
+ const code = str.charCodeAt(j);
3940
+ if (joined && k > 0 && is_name_joiner_code(code)) {
3941
+ j++;
3942
+ continue;
3943
+ }
3944
+ if (ascii_lower_code(code) !== part.charCodeAt(k)) return false;
3945
+ j++;
3946
+ k++;
3947
+ }
3948
+ return true;
3949
+ }
3950
+ function secret_name_check(str, from, to, about_wins, by_segments) {
3951
+ if (to - from < 3) return false;
3952
+ if (about_wins) {
3953
+ let seg = from;
3954
+ for (let i = from; i <= to; i++) {
3955
+ if (i < to && !is_name_joiner_code(str.charCodeAt(i))) continue;
3956
+ if (name_segment_in(str, seg, i, NOT_SECRET_BY_LETTER)) return false;
3957
+ seg = i + 1;
3958
+ }
3959
+ }
3960
+ if (by_segments) {
3961
+ let seg = from;
3962
+ for (let i = from; i <= to; i++) {
3963
+ if (i < to && is_ascii_alnum_code(str.charCodeAt(i))) continue;
3964
+ if (name_segment_in(str, seg, i, SECRET_SEGMENTS_BY_LETTER)) return true;
3965
+ seg = i + 1;
3966
+ }
3967
+ }
3968
+ let has_priv = false;
3969
+ let has_key = false;
3970
+ for (let i = from; i < to; i++) {
3971
+ const c = ascii_lower_code(str.charCodeAt(i));
3972
+ if (c >= 128) continue;
3973
+ const parts = SECRET_PARTS_BY_LETTER[c];
3974
+ for (let k = 0; parts && k < parts.length; k++) if (name_part_at(str, to, i, parts[k], false)) return true;
3975
+ const joined = SECRET_JOINED_BY_LETTER[c];
3976
+ for (let k = 0; joined && k < joined.length; k++) if (name_part_at(str, to, i, joined[k], true)) return true;
3977
+ if (c === 112 && !has_priv) has_priv = name_part_at(str, to, i, "priv", false);
3978
+ else if (c === 107 && !has_key) has_key = name_part_at(str, to, i, "key", false);
3979
+ }
3980
+ return has_priv && has_key;
3981
+ }
3468
3982
  /************************************************************
3469
3983
  * TRUE if `name` is the name of a secret: a password, a token,
3470
3984
  * a key... Any case. As the C kernel's is_secret_name().
3471
3985
  ************************************************************/
3472
3986
  function is_secret_name(name) {
3473
- if (typeof name !== "string" || name.length < 3) return false;
3474
- let lower = name.toLowerCase();
3475
- let hit = false;
3476
- for (const segment of lower.split(/[^a-z0-9]+/)) if (SECRET_NAME_SEGMENTS.has(segment)) {
3477
- hit = true;
3478
- break;
3479
- }
3480
- if (!hit) {
3481
- for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3482
- hit = true;
3483
- break;
3987
+ if (typeof name !== "string") return false;
3988
+ return secret_name_check(name, 0, name.length, true, true);
3989
+ }
3990
+ var NAMES_NOW = 0;
3991
+ var NAMES_7_26_9 = 1;
3992
+ function is_secret_name_tail(str, from, to, names) {
3993
+ const by_segments = names === NAMES_NOW;
3994
+ if ((to - from) * 3 <= MASK_MAX_NAME) return secret_name_check(str, from, to, true, by_segments);
3995
+ let bytes = 0;
3996
+ let i = to;
3997
+ let partial = false;
3998
+ while (i > from) {
3999
+ const code = str.charCodeAt(i - 1);
4000
+ let n = 3;
4001
+ let units = 1;
4002
+ if (code < 128) n = 1;
4003
+ else if (code < 2048) n = 2;
4004
+ else if (code >= 56320 && code <= 57343 && i - 2 >= from) {
4005
+ const high = str.charCodeAt(i - 2);
4006
+ if (high >= 55296 && high <= 56319) {
4007
+ n = 4;
4008
+ units = 2;
4009
+ }
3484
4010
  }
3485
- }
3486
- if (!hit) {
3487
- let joined = lower.replace(/[_\-. \t]/g, "");
3488
- for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
3489
- hit = true;
4011
+ if (bytes + n > MASK_MAX_NAME) {
4012
+ partial = bytes < MASK_MAX_NAME;
3490
4013
  break;
3491
4014
  }
4015
+ bytes += n;
4016
+ i -= units;
3492
4017
  }
3493
- if (!hit) hit = lower.indexOf("priv") >= 0 && lower.indexOf("key") >= 0;
3494
- if (!hit) return false;
3495
- for (const segment of lower.split(/[_\-. \t]/)) if (NOT_SECRET_SEGMENTS.has(segment)) return false;
3496
- return true;
4018
+ if (partial) {
4019
+ const name = "€" + str.substring(i, to);
4020
+ return secret_name_check(name, 0, name.length, true, by_segments);
4021
+ }
4022
+ return secret_name_check(str, i, to, true, by_segments);
3497
4023
  }
3498
4024
  /************************************************************
3499
4025
  * Where an unquoted secret value that begins at `p` ends: at
@@ -3524,10 +4050,11 @@ function inline_secret_value_end(str, p, outer) {
3524
4050
  * jwt_len() / scheme_credential(), the shapes the agent's audit
3525
4051
  * record redacts, and the schemes of an HTTP Authorization:
3526
4052
  * - a JWT or a JWE: "eyJ" (the base64url of '{"') at the start
3527
- * of a word, base64url, and a '.' after it -- three parts
3528
- * joined by '.' (a JWS), five (a JWE), or fewer when a cap cut
3529
- * it; the dots at the end of the run are not its own (a JWT
3530
- * ends a sentence: "... eyJ.eyJ.sig.");
4053
+ * of a word, base64url, and a '.' of its own after it -- three
4054
+ * parts joined by '.' (a JWS), five (a JWE), or fewer when a cap
4055
+ * cut it; the dots at the end of the run are not its own (a JWT
4056
+ * ends a sentence: "... eyJ.eyJ.sig."), so a run whose only
4057
+ * dots end it is a header and no JWT ("eyJhIjoxfQ.");
3531
4058
  * - the credential after the scheme word "Bearer" or "Basic"
3532
4059
  * (any case, a word of its own): scheme_credential().
3533
4060
  ************************************************************/
@@ -3560,7 +4087,7 @@ function jwt_len(str, p) {
3560
4087
  let e = p;
3561
4088
  let dotted = false;
3562
4089
  while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3563
- if (str[e] === ".") dotted = true;
4090
+ if (e > p && str[e] !== "." && str[e - 1] === ".") dotted = true;
3564
4091
  e++;
3565
4092
  }
3566
4093
  if (!dotted) return 0;
@@ -3668,7 +4195,7 @@ function shape_len(str, p) {
3668
4195
  if (c === "b" || c === "B") return scheme_credential(str, p);
3669
4196
  return [0, 0];
3670
4197
  }
3671
- function shape_len_7_26_9(str, p) {
4198
+ function shape_len_js_7_26_20(str, p) {
3672
4199
  const c = str[p];
3673
4200
  if (c === "e") {
3674
4201
  if (str.length - p < 3 || str[p + 1] !== "y" || str[p + 2] !== "J") return [0, 0];
@@ -3756,7 +4283,12 @@ var MASK_TOO_LARGE = "<not shown: too large to mask>";
3756
4283
  function mark_range(marks, from, to) {
3757
4284
  for (let i = from; i < to; i++) marks[i] = 1;
3758
4285
  }
3759
- function mark_params(str, value_is_secret, as_7_26_9, marks) {
4286
+ var READ_NOW = 0;
4287
+ var READ_AS_GOBJ_JS_7_26_20 = 1;
4288
+ var READ_AS_7_26_9 = 2;
4289
+ function mark_params(str, value_is_secret, reading, marks, judged) {
4290
+ const names = reading === READ_AS_7_26_9 ? NAMES_7_26_9 : NAMES_NOW;
4291
+ let not_7_26_9 = false;
3760
4292
  const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " " || str[i] === "\0";
3761
4293
  let outer = "";
3762
4294
  let word = 0;
@@ -3772,8 +4304,8 @@ function mark_params(str, value_is_secret, as_7_26_9, marks) {
3772
4304
  if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3773
4305
  else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3774
4306
  }
3775
- if (as_7_26_9) {
3776
- const [offset, n] = shape_len_7_26_9(str, p);
4307
+ if (reading === READ_AS_GOBJ_JS_7_26_20 && (c === "e" || c === "b" || c === "B")) {
4308
+ const [offset, n] = shape_len_js_7_26_20(str, p);
3777
4309
  if (n) {
3778
4310
  p += offset + n;
3779
4311
  word = p;
@@ -3784,10 +4316,12 @@ function mark_params(str, value_is_secret, as_7_26_9, marks) {
3784
4316
  p++;
3785
4317
  continue;
3786
4318
  }
3787
- const name_from = Math.max(word, p - MASK_MAX_NAME);
3788
- const name = str.substring(name_from, p);
4319
+ const name_from = word;
3789
4320
  word = p + 1;
3790
- if (!(is_secret_name(name) || value_is_secret && name.length === 5 && ascii_case_equal(str, name_from, "value"))) {
4321
+ const secret = is_secret_name_tail(str, name_from, p, names);
4322
+ if (secret && names === NAMES_NOW && !is_secret_name_tail(str, name_from, p, NAMES_7_26_9)) not_7_26_9 = true;
4323
+ if (!(secret || value_is_secret && p - name_from === 5 && ascii_case_equal(str, name_from, "value"))) {
4324
+ if (judged && query_name_from(str, p) === name_from) judged[p] = 1;
3791
4325
  p++;
3792
4326
  continue;
3793
4327
  }
@@ -3803,11 +4337,79 @@ function mark_params(str, value_is_secret, as_7_26_9, marks) {
3803
4337
  p = end;
3804
4338
  word = p;
3805
4339
  }
4340
+ return not_7_26_9;
4341
+ }
4342
+ function has_shape_js_7_26_20(str) {
4343
+ for (let p = 0; p < str.length; p++) {
4344
+ const code = str.charCodeAt(p);
4345
+ if ((code === 101 || code === 98 || code === 66) && shape_len_js_7_26_20(str, p)[1]) return true;
4346
+ }
4347
+ return false;
4348
+ }
4349
+ function stop_table(chars) {
4350
+ const table = /* @__PURE__ */ new Uint8Array(128);
4351
+ for (const c of chars + "\r\n\0") table[c.charCodeAt(0)] = 1;
4352
+ return table;
4353
+ }
4354
+ var PARAM_VALUE_STOPS = stop_table("&; ,\"'<>");
4355
+ var QUOTE_STOPS = {
4356
+ "\"": stop_table("\""),
4357
+ "'": stop_table("'")
4358
+ };
4359
+ function mark_run(str, p, stops, marks) {
4360
+ let e = p;
4361
+ while (e < str.length) {
4362
+ const code = str.charCodeAt(e);
4363
+ if (code < 128 && stops[code]) break;
4364
+ e++;
4365
+ }
4366
+ mark_range(marks, p, e);
4367
+ return e;
4368
+ }
4369
+ function mark_scheme_value(str, v, stops, marks) {
4370
+ const n = scheme_word_len(str, v);
4371
+ if (!n) return -1;
4372
+ let w = v + n;
4373
+ while (w < str.length && (str[w] === " " || str[w] === " ")) w++;
4374
+ mark_range(marks, v, w);
4375
+ if (w < str.length && (str[w] === "\"" || str[w] === "'")) {
4376
+ const e = colon_value_end(str, w);
4377
+ mark_range(marks, w, e);
4378
+ return e;
4379
+ }
4380
+ return mark_run(str, w, stops, marks);
4381
+ }
4382
+ function is_name_char_code(code) {
4383
+ return is_ascii_alnum_code(code) || code === 95 || code === 45 || code === 46;
4384
+ }
4385
+ function query_name_from(str, eq) {
4386
+ let n = eq;
4387
+ while (n > 0 && eq - n < MASK_MAX_NAME && is_name_char_code(str.charCodeAt(n - 1))) n--;
4388
+ return n;
4389
+ }
4390
+ function mark_query_params(str, marks, judged) {
4391
+ let frontier = 0;
4392
+ for (let p = 1; p < str.length; p++) {
4393
+ if (p < frontier) {
4394
+ p = frontier - 1;
4395
+ continue;
4396
+ }
4397
+ if (str.charCodeAt(p) !== 61) continue;
4398
+ if (judged[p]) continue;
4399
+ const n = query_name_from(str, p);
4400
+ if (n === p || !secret_name_check(str, n, p, true, true)) continue;
4401
+ const v = p + 1;
4402
+ if (v < str.length && (str[v] === "\"" || str[v] === "'")) frontier = mark_run(str, v + 1, QUOTE_STOPS[str[v]], marks);
4403
+ else {
4404
+ mark_scheme_value(str, v, PARAM_VALUE_STOPS, marks);
4405
+ frontier = mark_run(str, v, PARAM_VALUE_STOPS, marks);
4406
+ }
4407
+ }
3806
4408
  }
3807
4409
  function mark_shapes(str, marks) {
3808
4410
  for (let p = 0; p < str.length; p++) {
3809
- const c = str[p];
3810
- if (c !== "e" && c !== "b" && c !== "B") continue;
4411
+ const code = str.charCodeAt(p);
4412
+ if (code !== 101 && code !== 98 && code !== 66) continue;
3811
4413
  const [offset, n] = shape_len(str, p);
3812
4414
  if (n) mark_range(marks, p + offset, p + offset + n);
3813
4415
  }
@@ -3816,17 +4418,17 @@ function mark_labels(str, marks) {
3816
4418
  let word = 0;
3817
4419
  let p = 0;
3818
4420
  while (p < str.length) {
3819
- const c = str[p];
3820
- if (c === " " || c === " " || c === "\0" || c === "=") {
4421
+ const code = str.charCodeAt(p);
4422
+ if (code === 32 || code === 9 || code === 0 || code === 61) {
3821
4423
  p++;
3822
4424
  word = p;
3823
4425
  continue;
3824
4426
  }
3825
- if (c !== ":") {
4427
+ if (code !== 58) {
3826
4428
  p++;
3827
4429
  continue;
3828
4430
  }
3829
- if (!is_secret_name(str.substring(Math.max(word, p - MASK_MAX_NAME), p))) {
4431
+ if (!is_secret_name_tail(str, word, p, NAMES_NOW)) {
3830
4432
  p++;
3831
4433
  word = p;
3832
4434
  continue;
@@ -3841,31 +4443,50 @@ function mark_labels(str, marks) {
3841
4443
  word = p;
3842
4444
  }
3843
4445
  }
4446
+ function inline_value_is_secret(str, names) {
4447
+ let p = str.indexOf("attribute=");
4448
+ while (p >= 0) {
4449
+ if (p === 0 || str[p - 1] === " " || str[p - 1] === " ") {
4450
+ let v = p + 10;
4451
+ if (v < str.length && (str[v] === "\"" || str[v] === "'")) v++;
4452
+ let e = v;
4453
+ while (e < str.length && " \"'\0".indexOf(str[e]) < 0) e++;
4454
+ if (is_secret_name_tail(str, v, e, names)) return true;
4455
+ }
4456
+ p = str.indexOf("attribute=", p + 1);
4457
+ }
4458
+ return false;
4459
+ }
4460
+ function mark_inline_secrets(str, marks) {
4461
+ const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4462
+ const value_is_secret_7_26_9 = inline_value_is_secret(str, NAMES_7_26_9);
4463
+ const judged = new Uint8Array(str.length);
4464
+ if (mark_params(str, value_is_secret, READ_NOW, marks, judged) || value_is_secret !== value_is_secret_7_26_9) mark_params(str, value_is_secret_7_26_9, READ_AS_7_26_9, marks, null);
4465
+ if (has_shape_js_7_26_20(str)) mark_params(str, value_is_secret, READ_AS_GOBJ_JS_7_26_20, marks, null);
4466
+ mark_query_params(str, marks, judged);
4467
+ mark_shapes(str, marks);
4468
+ mark_labels(str, marks);
4469
+ }
3844
4470
  /************************************************************
3845
4471
  * A text (a command line) with the secrets that can be told
3846
4472
  * written as "********" (each run of them as one), the rest as it
3847
4473
  * is: the value of every "name=value" whose name is a secret's,
3848
4474
  * quoted or not (and the "value" of a write-attr whose attribute
3849
- * names a secret); the same for a "name: value", its name in quotes
3850
- * or not (a json, a header, a label: "password": x, Authorization:
3851
- * Basic x, bearer: tok); and a JWT or the credential after "Bearer "
3852
- * or "Basic ", wherever they are, whatever names them. Each rule
3853
- * judges the text as it came. Return null when there was nothing to
3854
- * mask. As the C kernel's mask_secrets_inline().
4475
+ * names a secret) -- the name is the word before the '=', and also
4476
+ * the run of name characters before it, as in a query string; the
4477
+ * same for a "name: value", its name in quotes or not (a json, a
4478
+ * header, a label: "password": x, Authorization: Basic x, bearer:
4479
+ * tok); and a JWT or the credential after "Bearer " or "Basic ",
4480
+ * wherever they are, whatever names them. Each rule judges the text
4481
+ * as it came. Return null when there was nothing to mask. As the C
4482
+ * kernel's mask_secrets_inline().
3855
4483
  ************************************************************/
3856
4484
  function mask_secrets_inline(str) {
3857
4485
  if (typeof str !== "string") return null;
3858
4486
  if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
3859
4487
  if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3860
- let value_is_secret = false;
3861
- let re_attr = /(^|[ \t])attribute=["']?([^ \t"'\u0000]*)/g;
3862
- let m;
3863
- while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3864
4488
  const marks = new Uint8Array(str.length);
3865
- mark_params(str, value_is_secret, true, marks);
3866
- mark_params(str, value_is_secret, false, marks);
3867
- mark_shapes(str, marks);
3868
- mark_labels(str, marks);
4489
+ mark_inline_secrets(str, marks);
3869
4490
  let parts = [];
3870
4491
  let changed = false;
3871
4492
  let i = 0;
@@ -3949,7 +4570,12 @@ function _json_mask_secrets(jn, memo, depth, budget) {
3949
4570
  if (truncated) result.push(MASK_TOO_LARGE);
3950
4571
  } else {
3951
4572
  result = Object.create(Object.getPrototypeOf(jn));
3952
- let value_is_secret = typeof jn.attribute === "string" && is_secret_name(jn.attribute.slice(-128));
4573
+ let value_is_secret = false;
4574
+ if (typeof jn.attribute === "string") {
4575
+ let nul = jn.attribute.indexOf("\0");
4576
+ let attribute = nul >= 0 ? jn.attribute.substring(0, nul) : jn.attribute;
4577
+ value_is_secret = is_secret_name_tail(attribute, 0, attribute.length, NAMES_NOW);
4578
+ }
3953
4579
  for (const key in jn) {
3954
4580
  if (!Object.prototype.hasOwnProperty.call(jn, key)) continue;
3955
4581
  if (budget.left <= 0) {
@@ -3959,7 +4585,7 @@ function _json_mask_secrets(jn, memo, depth, budget) {
3959
4585
  budget.left -= 1 + key.length;
3960
4586
  let value = jn[key];
3961
4587
  let shown;
3962
- if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
4588
+ if ((is_secret_name_tail(key, 0, key.length, NAMES_NOW) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3963
4589
  else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3964
4590
  if (shown !== value) changed = true;
3965
4591
  set_own_key(result, key, shown);
@@ -7108,8 +7734,12 @@ var __gclass__$2 = null;
7108
7734
  ***************************************************************/
7109
7735
  function mt_create$2(gobj) {
7110
7736
  let priv = gobj.priv;
7111
- const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
7737
+ let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
7112
7738
  if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
7739
+ else if (gobj_is_pure_child(gobj)) {
7740
+ subscriber = gobj_parent(gobj);
7741
+ gobj_subscribe_event(gobj, null, {}, subscriber);
7742
+ }
7113
7743
  priv.periodic = gobj_read_bool_attr(gobj, "periodic");
7114
7744
  priv.msec = gobj_read_integer_attr(gobj, "msec");
7115
7745
  }
@@ -7166,8 +7796,7 @@ function ac_timeout$1(gobj, event, kw, src) {
7166
7796
  gobj_send_event(gobj, "EV_TIMEOUT", {}, gobj);
7167
7797
  }, priv.msec);
7168
7798
  else priv.timer_id = -1;
7169
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), ev, {}, gobj);
7170
- else gobj_publish_event(gobj, ev, {});
7799
+ gobj_publish_event(gobj, ev, {});
7171
7800
  if (!priv.periodic && priv.timer_id === -1) {
7172
7801
  if (gobj_is_running(gobj)) gobj_stop(gobj);
7173
7802
  }
@@ -7214,6 +7843,15 @@ function register_c_timer() {
7214
7843
  * Helpers
7215
7844
  ***************************/
7216
7845
  /***************************************************************************
7846
+ * A number of milliseconds as the integer `msec` holds it: C takes an
7847
+ * integer, and a time computed in JS can carry a float error (0.3 * 1000
7848
+ * is 300.00000000000006, which an integer attr refuses). Anything that is
7849
+ * not a number goes as it is, to be refused loudly by the attr.
7850
+ ***************************************************************************/
7851
+ function whole_msec(msec) {
7852
+ return typeof msec === "number" && Number.isFinite(msec) ? Math.round(msec) : msec;
7853
+ }
7854
+ /***************************************************************************
7217
7855
  * Set timeout
7218
7856
  ***************************************************************************/
7219
7857
  function set_timeout(gobj, msec) {
@@ -7226,7 +7864,7 @@ function set_timeout(gobj, msec) {
7226
7864
  return -1;
7227
7865
  }
7228
7866
  gobj_write_bool_attr(gobj, "periodic", false);
7229
- gobj_write_integer_attr(gobj, "msec", msec);
7867
+ gobj_write_integer_attr(gobj, "msec", whole_msec(msec));
7230
7868
  }
7231
7869
  /***************************************************************************
7232
7870
  * Set periodic timeout
@@ -7241,7 +7879,7 @@ function set_timeout_periodic(gobj, msec) {
7241
7879
  return -1;
7242
7880
  }
7243
7881
  gobj_write_bool_attr(gobj, "periodic", true);
7244
- gobj_write_integer_attr(gobj, "msec", msec);
7882
+ gobj_write_integer_attr(gobj, "msec", whole_msec(msec));
7245
7883
  }
7246
7884
  /***************************************************************************
7247
7885
  * Clear timeout
@@ -7628,8 +8266,12 @@ function mt_create(gobj) {
7628
8266
  gobj_write_str_attr(gobj, "wanted_yuno_name", priv.remote_yuno_name);
7629
8267
  gobj_write_str_attr(gobj, "wanted_yuno_role", priv.remote_yuno_role);
7630
8268
  gobj_write_str_attr(gobj, "wanted_yuno_service", priv.remote_yuno_service);
7631
- const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
8269
+ let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
7632
8270
  if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
8271
+ else if (gobj_is_pure_child(gobj)) {
8272
+ subscriber = gobj_parent(gobj);
8273
+ gobj_subscribe_event(gobj, null, {}, subscriber);
8274
+ }
7633
8275
  }
7634
8276
  /***************************************************************
7635
8277
  * Framework Method: Writing
@@ -7854,6 +8496,27 @@ function close_websocket(gobj, code = 1e3, reason = "") {
7854
8496
  }
7855
8497
  }
7856
8498
  /***************************************************************
8499
+ * A text of the server -- a frame, the name of an event, of a
8500
+ * service, of a role -- as a log shows it: masked, then capped on a
8501
+ * whole character (capped first, a credential the cap cut would
8502
+ * show its first characters). As the C kernel shows the name of an
8503
+ * event that is not public (mask_secrets_inline_capped()).
8504
+ ***************************************************************/
8505
+ var PEER_NAME_SHOWN = 64;
8506
+ var PEER_FRAME_SHOWN = 256;
8507
+ function peer_text_shown(text, max) {
8508
+ let s = String(text);
8509
+ let masked = mask_secrets_inline(s);
8510
+ if (masked !== null) s = masked;
8511
+ if (s.length > max) {
8512
+ let n = max;
8513
+ let code = s.charCodeAt(n);
8514
+ if (code >= 56320 && code <= 57343) n--;
8515
+ s = s.substring(0, n);
8516
+ }
8517
+ return s;
8518
+ }
8519
+ /***************************************************************
7857
8520
  * Trace intra event
7858
8521
  ***************************************************************/
7859
8522
  function trace_inter_event(gobj, prefix, iev) {
@@ -7976,7 +8639,8 @@ function iev_create_from_json(gobj, data) {
7976
8639
  try {
7977
8640
  x = JSON.parse(data);
7978
8641
  } catch (e) {
7979
- log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
8642
+ let at = /position (\d+)/.exec(String(e && e.message));
8643
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: frame is not json${at ? ", at position " + at[1] : ""}, ${String(data).length} characters`);
7980
8644
  return null;
7981
8645
  }
7982
8646
  if (!(x instanceof Object)) {
@@ -8061,7 +8725,7 @@ function ac_on_close(gobj, event, kw, src) {
8061
8725
  let url = kw.url;
8062
8726
  if (kw.ws_seq !== void 0 && kw.ws_seq !== priv.ws_seq) return 0;
8063
8727
  set_remote_log_functions();
8064
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
8728
+ if (is_traffic_tracing(gobj)) trace_msg(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${peer_text_shown(kw.reason, PEER_FRAME_SHOWN)}", Clean: ${kw.was_clean})`);
8065
8729
  if (priv.websocket) {
8066
8730
  priv.websocket.onopen = null;
8067
8731
  priv.websocket.onmessage = null;
@@ -8078,7 +8742,7 @@ function ac_on_close(gobj, event, kw, src) {
8078
8742
  remote_yuno_service: priv.remote_yuno_service
8079
8743
  });
8080
8744
  } else if (!gobj_is_running(gobj)) {} else {
8081
- log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
8745
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${peer_text_shown(kw.reason, PEER_FRAME_SHOWN)}", Clean: ${kw.was_clean})`);
8082
8746
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
8083
8747
  url,
8084
8748
  remote_yuno_name: priv.remote_yuno_name,
@@ -8146,7 +8810,7 @@ function ac_identity_card_ack(gobj, event, kw, src) {
8146
8810
  let services_roles = kw_get_dict_value(gobj, kw, "services_roles", {});
8147
8811
  if (result < 0) {
8148
8812
  close_websocket(gobj);
8149
- let kw_nak = {
8813
+ gobj_publish_event(gobj, "EV_ON_ID_NAK", {
8150
8814
  "url": gobj_read_str_attr(gobj, "url"),
8151
8815
  "result": result,
8152
8816
  "comment": comment,
@@ -8154,24 +8818,20 @@ function ac_identity_card_ack(gobj, event, kw, src) {
8154
8818
  "remote_yuno_name": src_yuno,
8155
8819
  "remote_yuno_role": src_role,
8156
8820
  "remote_yuno_service": src_service
8157
- };
8158
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), "EV_ON_ID_NAK", kw_nak, gobj);
8159
- else gobj_publish_event(gobj, "EV_ON_ID_NAK", kw_nak);
8821
+ });
8160
8822
  } else {
8161
8823
  gobj_change_state(gobj, "ST_SESSION");
8162
8824
  priv.inside_on_open = true;
8163
8825
  priv.retry_ms = 0;
8164
8826
  if (!priv.inform_on_close) {
8165
8827
  priv.inform_on_close = true;
8166
- let kw_on_open = {
8828
+ gobj_publish_event(gobj, "EV_ON_OPEN", {
8167
8829
  "username": username_,
8168
8830
  "remote_yuno_name": src_yuno,
8169
8831
  "remote_yuno_role": src_role,
8170
8832
  "remote_yuno_service": src_service,
8171
8833
  "services_roles": services_roles
8172
- };
8173
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), "EV_ON_OPEN", kw_on_open, gobj);
8174
- else gobj_publish_event(gobj, "EV_ON_OPEN", kw_on_open);
8834
+ });
8175
8835
  }
8176
8836
  priv.inside_on_open = false;
8177
8837
  resend_subscriptions(gobj);
@@ -8185,7 +8845,7 @@ function ac_on_message(gobj, event, kw, src) {
8185
8845
  let url = gobj.priv.url;
8186
8846
  let iev_msg = iev_create_from_json(gobj, kw.data);
8187
8847
  if (!iev_msg) {
8188
- log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
8848
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${peer_text_shown(kw.data, PEER_FRAME_SHOWN)}`);
8189
8849
  return -1;
8190
8850
  }
8191
8851
  if (is_traffic_tracing(gobj)) {
@@ -8200,7 +8860,7 @@ function ac_on_message(gobj, event, kw, src) {
8200
8860
  if (gobj_current_state(gobj) !== "ST_SESSION") {
8201
8861
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
8202
8862
  if (!(gobj_send_event(gobj, iev_event, iev_kw, gobj) < 0)) return 0;
8203
- } else log_error(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${iev_event}`);
8863
+ } else log_error(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
8204
8864
  close_websocket(gobj);
8205
8865
  return -1;
8206
8866
  }
@@ -8209,16 +8869,16 @@ function ac_on_message(gobj, event, kw, src) {
8209
8869
  let event_id = msg_iev_get_stack(gobj, iev_kw, IEVENT_STACK_ID);
8210
8870
  let iev_dst_role = kw_get_str(gobj, event_id, "dst_role", "");
8211
8871
  if (iev_dst_role.toLowerCase() !== gobj_yuno_role().toLowerCase()) {
8212
- log_error(`"It's not my role, dst_role: ${iev_dst_role}, my_role: ${gobj_yuno_role()}`);
8872
+ log_error(`"It's not my role, dst_role: ${peer_text_shown(iev_dst_role, PEER_NAME_SHOWN)}, my_role: ${gobj_yuno_role()}`);
8213
8873
  return 0;
8214
8874
  }
8215
8875
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
8216
8876
  if (msg_type === "__subscribing__") {
8217
- log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
8877
+ log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${peer_text_shown(iev_dst_service, PEER_NAME_SHOWN)}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)})`);
8218
8878
  return -1;
8219
8879
  }
8220
8880
  if (msg_type === "__unsubscribing__") {
8221
- log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
8881
+ log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${peer_text_shown(iev_dst_service, PEER_NAME_SHOWN)}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)})`);
8222
8882
  return -1;
8223
8883
  }
8224
8884
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
@@ -8228,18 +8888,17 @@ function ac_on_message(gobj, event, kw, src) {
8228
8888
  if (!empty_string(iev_dst_service)) {
8229
8889
  let gobj_service = gobj_find_service(iev_dst_service, false);
8230
8890
  if (!gobj_service) {
8231
- log_warning(`${gobj_short_name(gobj)}: event for a service that is gone, DROPPED: service '${iev_dst_service}', ev '${iev_event}'`);
8891
+ log_warning(`${gobj_short_name(gobj)}: event for a service that is gone, DROPPED: service '${peer_text_shown(iev_dst_service, PEER_NAME_SHOWN)}', ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
8232
8892
  return 0;
8233
8893
  }
8234
8894
  if (!gobj_has_event(gobj_service, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
8235
- log_error(`${gobj_short_name(gobj)}: Service found but no event or not public, service '${gobj_short_name(gobj_service)}', ev '${iev_event}'`);
8895
+ log_error(`${gobj_short_name(gobj)}: Service found but no event or not public, service '${gobj_short_name(gobj_service)}', ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
8236
8896
  return -1;
8237
8897
  }
8238
8898
  gobj_send_event(gobj_service, iev_event, iev_kw, gobj);
8239
8899
  return 0;
8240
8900
  }
8241
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), iev_event, iev_kw, gobj);
8242
- else gobj_publish_event(gobj, iev_event, iev_kw);
8901
+ gobj_publish_event(gobj, iev_event, iev_kw);
8243
8902
  return 0;
8244
8903
  }
8245
8904
  /***************************************************************************
@@ -8401,6 +9060,8 @@ exports.build_stats_response = build_stats_response;
8401
9060
  exports.clean_name = clean_name;
8402
9061
  exports.clear_timeout = clear_timeout;
8403
9062
  exports.cmp_two_simple_json = cmp_two_simple_json;
9063
+ exports.command_parameter_from_json = command_parameter_from_json;
9064
+ exports.command_parameter_from_text = command_parameter_from_text;
8404
9065
  exports.command_parser = command_parser;
8405
9066
  exports.createElement2 = createElement2;
8406
9067
  exports.createOneHtml = createOneHtml;