@yuneta/gobj-js 7.26.20 → 7.26.22

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.
@@ -219,6 +219,13 @@
219
219
  var __yuno__ = null;
220
220
  var __default_service__ = null;
221
221
  var __inside__ = 0;
222
+ var frame_kind_t = Object.freeze({
223
+ FRAME_ACTION: 0,
224
+ FRAME_PUBLICATION: 1,
225
+ FRAME_METHOD: 2,
226
+ FRAME_WATCH: 3
227
+ });
228
+ var __dispatch_top__ = null;
222
229
  /**************************************************************************
223
230
  * SData - Structured Data
224
231
  **************************************************************************/
@@ -232,7 +239,15 @@
232
239
  DTP_JSON: 7,
233
240
  DTP_POINTER: 8
234
241
  });
242
+ var DTP_IS_STRING = (type) => type === data_type_t.DTP_STRING;
243
+ var DTP_IS_BOOLEAN = (type) => type === data_type_t.DTP_BOOLEAN;
244
+ var DTP_IS_INTEGER = (type) => type === data_type_t.DTP_INTEGER;
245
+ var DTP_IS_REAL = (type) => type === data_type_t.DTP_REAL;
246
+ var DTP_IS_LIST = (type) => type === data_type_t.DTP_LIST;
247
+ var DTP_IS_DICT = (type) => type === data_type_t.DTP_DICT;
248
+ var DTP_IS_JSON = (type) => type === data_type_t.DTP_JSON;
235
249
  data_type_t.DTP_POINTER;
250
+ var DTP_IS_NUMBER = (type) => DTP_IS_INTEGER(type) || DTP_IS_REAL(type) || DTP_IS_BOOLEAN(type);
236
251
  var sdata_flag_t = Object.freeze({
237
252
  SDF_NOTACCESS: 1,
238
253
  SDF_RD: 2,
@@ -254,7 +269,8 @@
254
269
  SDF_AUTHZ_X: 131072,
255
270
  SDF_AUTHZ_P: 262144,
256
271
  SDF_AUTHZ_S: 524288,
257
- SDF_AUTHZ_RS: 1048576
272
+ SDF_AUTHZ_RS: 1048576,
273
+ SDF_SECRET: 2097152
258
274
  });
259
275
  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;
260
276
  sdata_flag_t.SDF_WR | sdata_flag_t.SDF_PERSIST;
@@ -842,6 +858,260 @@
842
858
  return 0;
843
859
  }
844
860
  /***************************************************************************
861
+ * A value of a type, written as text or given as json, read as the C
862
+ * command parser reads a parameter (command_parser.c): the same grammar,
863
+ * the same refusals, so that a C client and a JS client read an attribute
864
+ * value the same way (json2item()). A refusal says why in a short phrase,
865
+ * never the value: it may be a secret.
866
+ ***************************************************************************/
867
+ var JSON_INTEGER_TEXT = /^-?(0|[1-9][0-9]*)$/;
868
+ var JSON_NUMBER_TEXT = /^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?$/;
869
+ var INT64_MIN = -(2n ** 63n);
870
+ var INT64_MAX = 2n ** 63n - 1n;
871
+ var REFUSED_TEXT = "a text that does not read as one";
872
+ function value_has_shape(type, value) {
873
+ if (DTP_IS_STRING(type)) return is_string(value);
874
+ if (DTP_IS_DICT(type)) return is_object(value);
875
+ if (DTP_IS_LIST(type)) return is_array(value);
876
+ if (DTP_IS_NUMBER(type)) return typeof value === "number" || typeof value === "boolean";
877
+ return true;
878
+ }
879
+ function shape_of_value(value) {
880
+ if (is_array(value)) return "an array";
881
+ if (is_object(value)) return "an object";
882
+ if (is_string(value)) return REFUSED_TEXT;
883
+ if (typeof value === "number") return Number.isInteger(value) ? "an integer" : "a real";
884
+ if (typeof value === "boolean") return "a boolean";
885
+ return "null";
886
+ }
887
+ function number_as_type(type, value) {
888
+ if (typeof value === "number" && !Number.isFinite(value)) return {
889
+ value: void 0,
890
+ refused: "a number out of its range"
891
+ };
892
+ if (DTP_IS_BOOLEAN(type)) return {
893
+ value: typeof value === "boolean" ? value : value !== 0,
894
+ refused: null
895
+ };
896
+ if (DTP_IS_REAL(type)) return {
897
+ value: typeof value === "boolean" ? value ? 1 : 0 : value,
898
+ refused: null
899
+ };
900
+ if (typeof value === "boolean") return {
901
+ value: value ? 1 : 0,
902
+ refused: null
903
+ };
904
+ if (!(value >= -0x8000000000000000 && value < 0x8000000000000000)) return {
905
+ value: void 0,
906
+ refused: "a number out of its range"
907
+ };
908
+ if (!Number.isInteger(value)) return {
909
+ value: void 0,
910
+ refused: "a real with decimals"
911
+ };
912
+ return {
913
+ value,
914
+ refused: null
915
+ };
916
+ }
917
+ function text2parameter(type, s, from_kw) {
918
+ if (!is_string(s)) s = "";
919
+ if (DTP_IS_STRING(type)) return {
920
+ value: s,
921
+ refused: null
922
+ };
923
+ if (DTP_IS_NUMBER(type)) {
924
+ if (s.length === 0) {
925
+ if (from_kw) return {
926
+ value: void 0,
927
+ refused: null
928
+ };
929
+ return {
930
+ value: DTP_IS_BOOLEAN(type) ? false : 0,
931
+ refused: null
932
+ };
933
+ }
934
+ if (DTP_IS_BOOLEAN(type)) {
935
+ const lower = s.toLowerCase();
936
+ if (lower === "true" || lower === "yes" || lower === "on") return {
937
+ value: true,
938
+ refused: null
939
+ };
940
+ if (lower === "false" || lower === "no" || lower === "off") return {
941
+ value: false,
942
+ refused: null
943
+ };
944
+ }
945
+ if (!(DTP_IS_REAL(type) ? JSON_NUMBER_TEXT : JSON_INTEGER_TEXT).test(s)) return {
946
+ value: void 0,
947
+ refused: REFUSED_TEXT
948
+ };
949
+ if (DTP_IS_REAL(type)) {
950
+ const d = Number(s);
951
+ if (!Number.isFinite(d)) return {
952
+ value: void 0,
953
+ refused: "a number out of its range"
954
+ };
955
+ return {
956
+ value: d,
957
+ refused: null
958
+ };
959
+ }
960
+ const n = BigInt(s);
961
+ if (n < INT64_MIN || n > INT64_MAX) return {
962
+ value: void 0,
963
+ refused: "a number out of its range"
964
+ };
965
+ if (DTP_IS_BOOLEAN(type)) return {
966
+ value: n !== 0n,
967
+ refused: null
968
+ };
969
+ return {
970
+ value: Number(n),
971
+ refused: null
972
+ };
973
+ }
974
+ if (DTP_IS_DICT(type) || DTP_IS_LIST(type)) {
975
+ let jn;
976
+ try {
977
+ jn = JSON.parse(s);
978
+ } catch (e) {
979
+ return {
980
+ value: void 0,
981
+ refused: REFUSED_TEXT
982
+ };
983
+ }
984
+ if (!value_has_shape(type, jn)) return {
985
+ value: void 0,
986
+ refused: REFUSED_TEXT
987
+ };
988
+ return {
989
+ value: jn,
990
+ refused: null
991
+ };
992
+ }
993
+ if (DTP_IS_JSON(type)) try {
994
+ return {
995
+ value: JSON.parse(s),
996
+ refused: null
997
+ };
998
+ } catch (e) {
999
+ const t = s.replace(/^[ \t]+/, "");
1000
+ if (t.startsWith("{") || t.startsWith("[") || t.startsWith("\"")) return {
1001
+ value: void 0,
1002
+ refused: REFUSED_TEXT
1003
+ };
1004
+ return {
1005
+ value: s,
1006
+ refused: null
1007
+ };
1008
+ }
1009
+ return {
1010
+ value: void 0,
1011
+ refused: REFUSED_TEXT
1012
+ };
1013
+ }
1014
+ /***************************************************************************
1015
+ * A value of `type` written as TEXT, read as the C parser reads a
1016
+ * parameter on the command line (command_parameter_from_text()): an empty
1017
+ * text is 0 or false. Return {value, refused}: `value` undefined when the
1018
+ * text does not read as one, `refused` saying why.
1019
+ ***************************************************************************/
1020
+ function command_parameter_from_text(type, text) {
1021
+ const r = text2parameter(type, text, false);
1022
+ if (r.value === void 0 && !r.refused) r.refused = REFUSED_TEXT;
1023
+ return r;
1024
+ }
1025
+ /***************************************************************************
1026
+ * A value of `type` given as JSON, read as the C parser reads a
1027
+ * parameter given in kw (command_parameter_from_json()): a string as
1028
+ * command_parameter_from_text() reads a text (a string with a NUL inside
1029
+ * refused), a number or a boolean as the EXACT type (an integer to a
1030
+ * DTP_REAL is its real, a boolean to a DTP_INTEGER 1 or 0, 2.7 to a
1031
+ * DTP_INTEGER refused), an object or a list of its shape as it is, any
1032
+ * other shape refused. Return {value, refused}: `value` undefined and
1033
+ * `refused` saying why, or `refused` null when there is NO value (a null,
1034
+ * undefined, or an empty text given to a number).
1035
+ ***************************************************************************/
1036
+ function command_parameter_from_json(type, value) {
1037
+ if (value === void 0 || value === null) {
1038
+ if (DTP_IS_JSON(type) && value === null) return {
1039
+ value: null,
1040
+ refused: null
1041
+ };
1042
+ return {
1043
+ value: void 0,
1044
+ refused: null
1045
+ };
1046
+ }
1047
+ const nul_inside = is_string(value) && value.indexOf("\0") >= 0;
1048
+ if (DTP_IS_JSON(type)) {
1049
+ if (!is_string(value) || nul_inside) return {
1050
+ value,
1051
+ refused: null
1052
+ };
1053
+ const r = text2parameter(type, value, true);
1054
+ return r.value === void 0 ? {
1055
+ value,
1056
+ refused: null
1057
+ } : r;
1058
+ }
1059
+ if (!(DTP_IS_STRING(type) || DTP_IS_DICT(type) || DTP_IS_LIST(type) || DTP_IS_NUMBER(type))) return {
1060
+ value,
1061
+ refused: null
1062
+ };
1063
+ if (nul_inside) return {
1064
+ value: void 0,
1065
+ refused: "a text with a NUL in it"
1066
+ };
1067
+ if (is_string(value) && !DTP_IS_STRING(type)) return text2parameter(type, value, true);
1068
+ if (!value_has_shape(type, value)) return {
1069
+ value: void 0,
1070
+ refused: shape_of_value(value)
1071
+ };
1072
+ if (DTP_IS_NUMBER(type)) return number_as_type(type, value);
1073
+ return {
1074
+ value,
1075
+ refused: null
1076
+ };
1077
+ }
1078
+ /***************************************************************************
1079
+ * A value json2item() refuses: the attr, its type and why, and the value
1080
+ * as a log may show it (a SDF_SECRET one never; any other masked, then
1081
+ * capped on a whole character, as in C)
1082
+ ***************************************************************************/
1083
+ var ATTR_VALUE_SHOWN = 64;
1084
+ var TYPES_READ = [
1085
+ data_type_t.DTP_BOOLEAN,
1086
+ data_type_t.DTP_INTEGER,
1087
+ data_type_t.DTP_REAL,
1088
+ data_type_t.DTP_LIST,
1089
+ data_type_t.DTP_DICT
1090
+ ];
1091
+ function log_attr_value_refused(gobj, it, value, refused) {
1092
+ const types = {
1093
+ [data_type_t.DTP_BOOLEAN]: "a boolean",
1094
+ [data_type_t.DTP_INTEGER]: "an integer",
1095
+ [data_type_t.DTP_REAL]: "a real",
1096
+ [data_type_t.DTP_LIST]: "a json array",
1097
+ [data_type_t.DTP_DICT]: "a json object"
1098
+ };
1099
+ let shown;
1100
+ if (it.flag & sdata_flag_t.SDF_SECRET) shown = "********";
1101
+ else {
1102
+ shown = is_string(value) ? value : String(JSON.stringify(value === void 0 ? null : value));
1103
+ const masked = mask_secrets_inline(shown);
1104
+ if (masked !== null) shown = masked;
1105
+ if (shown.length > ATTR_VALUE_SHOWN) {
1106
+ let n = ATTR_VALUE_SHOWN;
1107
+ const code = shown.charCodeAt(n);
1108
+ if (code >= 56320 && code <= 57343) n--;
1109
+ shown = shown.substring(0, n);
1110
+ }
1111
+ }
1112
+ 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}'`);
1113
+ }
1114
+ /***************************************************************************
845
1115
  * Set array values to sdata, from json or binary
846
1116
  ***************************************************************************/
847
1117
  function json2item(gobj, sdata, it, jn_value_) {
@@ -856,44 +1126,18 @@
856
1126
  }
857
1127
  break;
858
1128
  case data_type_t.DTP_BOOLEAN:
859
- jn_value2 = parseBoolean(jn_value_);
860
- break;
861
1129
  case data_type_t.DTP_INTEGER:
862
- jn_value2 = parseInt(jn_value_);
863
- if (isNaN(jn_value2)) {
864
- log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
865
- return -1;
866
- }
867
- break;
868
1130
  case data_type_t.DTP_REAL:
869
- jn_value2 = Number(jn_value_);
870
- if (isNaN(jn_value2)) {
871
- log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
872
- return -1;
873
- }
874
- break;
875
1131
  case data_type_t.DTP_LIST:
876
- if (is_string(jn_value_)) try {
877
- jn_value2 = JSON.parse(jn_value_);
878
- } catch (error) {
879
- log_error(`JSON parse error: ${error.message}`);
880
- }
881
- else jn_value2 = jn_value_;
882
- if (!is_array(jn_value2)) {
883
- log_error(`attr must be an array: ${it.name}`);
884
- return -1;
885
- }
886
- break;
887
1132
  case data_type_t.DTP_DICT:
888
- if (is_string(jn_value_)) try {
889
- jn_value2 = JSON.parse(jn_value_);
890
- } catch (error) {
891
- log_error(`JSON parse error: ${error.message}`);
892
- }
893
- else jn_value2 = jn_value_;
894
- if (!is_object(jn_value2)) {
895
- log_error(`attr must be an object: ${it.name}`);
896
- return -1;
1133
+ {
1134
+ if (jn_value_ === void 0) return 0;
1135
+ const r = command_parameter_from_json(it.type, jn_value_);
1136
+ if (r.value === void 0) {
1137
+ log_attr_value_refused(gobj, it, jn_value_, r.refused);
1138
+ return -1;
1139
+ }
1140
+ jn_value2 = r.value;
897
1141
  }
898
1142
  break;
899
1143
  case data_type_t.DTP_JSON:
@@ -1468,6 +1712,8 @@
1468
1712
  return;
1469
1713
  }
1470
1714
  if (gobj.__refs__ > 0) log_error(`${gobj_short_name(gobj)}: gobj DESTROYING with references: ${gobj.__refs__}`);
1715
+ if (!(gobj.parent && gobj_is_destroying(gobj.parent))) check_destroy_under_running_code(gobj);
1716
+ mark_dispatch_frames_destroyed(gobj);
1471
1717
  if (gobj.playing) {
1472
1718
  log_error(`Destroying a PLAYING gobj: ${gobj_full_name(gobj)}`);
1473
1719
  gobj_pause(gobj);
@@ -1532,10 +1778,44 @@
1532
1778
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏺ ⏺ start: %s", gobj_full_name(gobj)));
1533
1779
  gobj.running = true;
1534
1780
  let ret = 0;
1535
- if (gobj.gclass.gmt.mt_start) ret = gobj.gclass.gmt.mt_start(gobj);
1781
+ if (gobj.gclass.gmt.mt_start) {
1782
+ const frame = push_dispatch_frame(gobj, "mt_start", frame_kind_t.FRAME_METHOD);
1783
+ try {
1784
+ ret = gobj.gclass.gmt.mt_start(gobj);
1785
+ } finally {
1786
+ pop_dispatch_frame(frame);
1787
+ }
1788
+ }
1536
1789
  return ret;
1537
1790
  }
1538
1791
  /***************************************************************************
1792
+ * Start a gobj in a walk of its tree, and say whether that destroyed it.
1793
+ *
1794
+ * A gclass may end itself in its own mt_start or mt_stop, and a walk of
1795
+ * the tree must not go down into the children of a gobj that is gone.
1796
+ ***************************************************************************/
1797
+ function start_destroys_it(gobj) {
1798
+ const frame = push_dispatch_frame(gobj, "gobj_start", frame_kind_t.FRAME_WATCH);
1799
+ try {
1800
+ gobj_start(gobj);
1801
+ } finally {
1802
+ pop_dispatch_frame(frame);
1803
+ }
1804
+ return frame.destroyed;
1805
+ }
1806
+ /***************************************************************************
1807
+ * Stop a gobj in a walk of its tree, and say whether that destroyed it.
1808
+ ***************************************************************************/
1809
+ function stop_destroys_it(gobj) {
1810
+ const frame = push_dispatch_frame(gobj, "gobj_stop", frame_kind_t.FRAME_WATCH);
1811
+ try {
1812
+ gobj_stop(gobj);
1813
+ } finally {
1814
+ pop_dispatch_frame(frame);
1815
+ }
1816
+ return frame.destroyed;
1817
+ }
1818
+ /***************************************************************************
1539
1819
  * Start all children of the gobj.
1540
1820
  ***************************************************************************/
1541
1821
  function cb_start_child(child, user_data, user_data2) {
@@ -1556,7 +1836,9 @@
1556
1836
  function cb_start_child_tree(child, user_data, user_data2) {
1557
1837
  if (child.gclass.gclass_flag & gclass_flag_t.gcflag_manual_start) return 1;
1558
1838
  if (gobj_is_disabled(child)) return 1;
1559
- if (!gobj_is_running(child)) gobj_start(child);
1839
+ if (!gobj_is_running(child)) {
1840
+ if (start_destroys_it(child)) return 1;
1841
+ }
1560
1842
  return 0;
1561
1843
  }
1562
1844
  function gobj_start_tree(gobj) {
@@ -1567,7 +1849,9 @@
1567
1849
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏺ ⏺ ⏺ ⏺ start_tree: %s", gobj_full_name(gobj)));
1568
1850
  if (gobj.gclass.gclass_flag & gclass_flag_t.gcflag_manual_start) return 0;
1569
1851
  else if (gobj.disabled) return 0;
1570
- else if (!gobj.running) gobj_start(gobj);
1852
+ else if (!gobj.running) {
1853
+ if (start_destroys_it(gobj)) return 0;
1854
+ }
1571
1855
  return gobj_walk_gobj_children_tree(gobj, walk_type_t.WALK_TOP2BOTTOM, cb_start_child_tree, 0, 0);
1572
1856
  }
1573
1857
  /************************************************************
@@ -1589,14 +1873,23 @@
1589
1873
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏹ ⏹ stop: %s", gobj_full_name(gobj)));
1590
1874
  gobj.running = false;
1591
1875
  let ret = 0;
1592
- if (gobj.gclass.gmt.mt_stop) ret = gobj.gclass.gmt.mt_stop(gobj);
1876
+ if (gobj.gclass.gmt.mt_stop) {
1877
+ const frame = push_dispatch_frame(gobj, "mt_stop", frame_kind_t.FRAME_METHOD);
1878
+ try {
1879
+ ret = gobj.gclass.gmt.mt_stop(gobj);
1880
+ } finally {
1881
+ pop_dispatch_frame(frame);
1882
+ }
1883
+ }
1593
1884
  return ret;
1594
1885
  }
1595
1886
  /***************************************************************************
1596
1887
  * Stop all children of the gobj.
1597
1888
  ***************************************************************************/
1598
1889
  function cb_stop_child(child, user_data, user_data2) {
1599
- if (gobj_is_running(child)) gobj_stop(child);
1890
+ if (gobj_is_running(child)) {
1891
+ if (stop_destroys_it(child)) return 1;
1892
+ }
1600
1893
  return 0;
1601
1894
  }
1602
1895
  function gobj_stop_children(gobj) {
@@ -1615,7 +1908,9 @@
1615
1908
  return -1;
1616
1909
  }
1617
1910
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏹ ⏹ ⏹ ⏹ stop_tree: %s", gobj_full_name(gobj)));
1618
- if (gobj_is_running(gobj)) gobj_stop(gobj);
1911
+ if (gobj_is_running(gobj)) {
1912
+ if (stop_destroys_it(gobj)) return 0;
1913
+ }
1619
1914
  return gobj_walk_gobj_children_tree(gobj, walk_type_t.WALK_TOP2BOTTOM, cb_stop_child, 0, 0);
1620
1915
  }
1621
1916
  /************************************************************
@@ -1647,8 +1942,13 @@
1647
1942
  gobj.playing = true;
1648
1943
  let ret = 0;
1649
1944
  if (gobj.gclass.gmt.mt_play) {
1650
- ret = gobj.gclass.gmt.mt_play(gobj);
1651
- if (ret < 0 || ret === false) gobj.playing = false;
1945
+ const frame = push_dispatch_frame(gobj, "mt_play", frame_kind_t.FRAME_METHOD);
1946
+ try {
1947
+ ret = gobj.gclass.gmt.mt_play(gobj);
1948
+ } finally {
1949
+ pop_dispatch_frame(frame);
1950
+ }
1951
+ if ((ret < 0 || ret === false) && !frame.destroyed) gobj.playing = false;
1652
1952
  }
1653
1953
  return ret;
1654
1954
  }
@@ -1666,8 +1966,14 @@
1666
1966
  }
1667
1967
  if (__trace_gobj_start_stop__(gobj)) trace_machine(sprintf("⏸ ⏸ pause: %s", gobj_full_name(gobj)));
1668
1968
  gobj.playing = false;
1669
- if (gobj.gclass.gmt.mt_pause) return gobj.gclass.gmt.mt_pause(gobj);
1670
- else return 0;
1969
+ if (gobj.gclass.gmt.mt_pause) {
1970
+ const frame = push_dispatch_frame(gobj, "mt_pause", frame_kind_t.FRAME_METHOD);
1971
+ try {
1972
+ return gobj.gclass.gmt.mt_pause(gobj);
1973
+ } finally {
1974
+ pop_dispatch_frame(frame);
1975
+ }
1976
+ } else return 0;
1671
1977
  }
1672
1978
  /************************************************************
1673
1979
  *
@@ -2220,14 +2526,7 @@
2220
2526
  if (ss.length <= 1) {
2221
2527
  let key = path;
2222
2528
  if (key in jn_attrs) {
2223
- if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) {
2224
- jn_attrs[key] = value;
2225
- if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, key)) gobj.priv[key] = value;
2226
- if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2227
- if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2228
- }
2229
- return 0;
2230
- }
2529
+ if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) return write_attr_value(gobj, key, value);
2231
2530
  }
2232
2531
  log_error(sprintf("gobj_write_attr(%s): attr not found '%s'", gobj_short_name(gobj), key));
2233
2532
  return -1;
@@ -2243,14 +2542,7 @@
2243
2542
  }
2244
2543
  let jn_attrs = gobj.jn_attrs;
2245
2544
  if (key in jn_attrs) {
2246
- if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) {
2247
- jn_attrs[key] = value;
2248
- if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, key)) gobj.priv[key] = value;
2249
- if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2250
- if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2251
- }
2252
- return 0;
2253
- }
2545
+ if (Object.prototype.hasOwnProperty.call(jn_attrs, key)) return write_attr_value(gobj, key, value);
2254
2546
  }
2255
2547
  break;
2256
2548
  }
@@ -2258,6 +2550,49 @@
2258
2550
  return -1;
2259
2551
  }
2260
2552
  /***************************************************************************
2553
+ * The value written to the attr `key` of `gobj`, read as its type as the C
2554
+ * kernel reads it (json2item(): it writes priv and calls mt_writing too).
2555
+ * Up to 7.26.21 gobj_write_attr() wrote it raw.
2556
+ ***************************************************************************/
2557
+ function write_attr_value(gobj, key, value) {
2558
+ const it = gobj_attr_desc(gobj, key, false);
2559
+ if (!it) {
2560
+ log_error(sprintf("gobj_write_attr(%s): attr without description '%s'", gobj_short_name(gobj), key));
2561
+ return -1;
2562
+ }
2563
+ if (value === void 0 && TYPES_READ.includes(it.type)) {
2564
+ log_attr_value_refused(gobj, it, value, null);
2565
+ return -1;
2566
+ }
2567
+ return json2item(gobj, gobj.jn_attrs, it, value);
2568
+ }
2569
+ /***************************************************************************
2570
+ * The value a typed writer writes, read as ITS type by the grammar of
2571
+ * json2item() (a C writer takes its C type: a string is never read as a
2572
+ * number there). Return {value} or {refused} (logged): the attr keeps its
2573
+ * value. A string writer never refuses: null and undefined are "", any
2574
+ * other json its text, as a DTP_STRING attr reads them.
2575
+ ***************************************************************************/
2576
+ function typed_writer_value(gobj, name, type, value) {
2577
+ if (DTP_IS_STRING(type)) {
2578
+ if (value === null || value === void 0) return { value: "" };
2579
+ return { value: is_string(value) ? value : String(JSON.stringify(value)) };
2580
+ }
2581
+ const r = command_parameter_from_json(type, value);
2582
+ if (r.value === void 0) {
2583
+ let holder = gobj;
2584
+ while (holder && !gobj_has_attr(holder, name)) holder = holder.bottom_gobj;
2585
+ const it = holder ? gobj_attr_desc(holder, name, false) : null;
2586
+ log_attr_value_refused(gobj, {
2587
+ name,
2588
+ type,
2589
+ flag: it ? it.flag : 0
2590
+ }, value, r.refused);
2591
+ return { refused: true };
2592
+ }
2593
+ return { value: r.value };
2594
+ }
2595
+ /***************************************************************************
2261
2596
  * WRITE - high level
2262
2597
  ***************************************************************************/
2263
2598
  function sdata_write_default_values(gobj, include_flag, exclude_flag) {
@@ -2305,7 +2640,9 @@
2305
2640
  function gobj_write_bool_attr(gobj, name, value) {
2306
2641
  let hs = gobj_hsdata2(gobj, name, false);
2307
2642
  if (hs) {
2308
- hs[name] = parseBoolean(value);
2643
+ const v = typed_writer_value(gobj, name, data_type_t.DTP_BOOLEAN, value);
2644
+ if (v.refused) return -1;
2645
+ hs[name] = v.value;
2309
2646
  if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, name)) gobj.priv[name] = hs[name];
2310
2647
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2311
2648
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
@@ -2321,7 +2658,9 @@
2321
2658
  function gobj_write_integer_attr(gobj, name, value) {
2322
2659
  let hs = gobj_hsdata2(gobj, name, false);
2323
2660
  if (hs) {
2324
- hs[name] = parseInt(value);
2661
+ const v = typed_writer_value(gobj, name, data_type_t.DTP_INTEGER, value);
2662
+ if (v.refused) return -1;
2663
+ hs[name] = v.value;
2325
2664
  if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, name)) gobj.priv[name] = hs[name];
2326
2665
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2327
2666
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
@@ -2337,7 +2676,9 @@
2337
2676
  function gobj_write_str_attr(gobj, name, value) {
2338
2677
  let hs = gobj_hsdata2(gobj, name, false);
2339
2678
  if (hs) {
2340
- hs[name] = String(value);
2679
+ const v = typed_writer_value(gobj, name, data_type_t.DTP_STRING, value);
2680
+ if (v.refused) return -1;
2681
+ hs[name] = v.value;
2341
2682
  if (is_object(gobj.priv) && Object.prototype.hasOwnProperty.call(gobj.priv, name)) gobj.priv[name] = hs[name];
2342
2683
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2343
2684
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
@@ -2547,6 +2888,84 @@
2547
2888
  __dl_posted_events__ = __dl_posted_events__.filter((msg) => msg.dst !== gobj);
2548
2889
  }
2549
2890
  /************************************************************
2891
+ * A dispatch begins: its frame goes on top of the chain.
2892
+ * Every push is paired with a pop in a `finally`: an action
2893
+ * that throws must not leave its frame on the chain.
2894
+ ************************************************************/
2895
+ function push_dispatch_frame(gobj, event, kind) {
2896
+ const frame = {
2897
+ prev: __dispatch_top__,
2898
+ gobj,
2899
+ event,
2900
+ kind,
2901
+ destroyed: false
2902
+ };
2903
+ __dispatch_top__ = frame;
2904
+ return frame;
2905
+ }
2906
+ function pop_dispatch_frame(frame) {
2907
+ __dispatch_top__ = frame.prev;
2908
+ }
2909
+ /************************************************************
2910
+ * Every dispatch in progress of `gobj` learns that it is
2911
+ * destroyed. Only its own frames: each child destroyed with
2912
+ * it marks its own, in its own gobj_destroy().
2913
+ ************************************************************/
2914
+ function mark_dispatch_frames_destroyed(gobj) {
2915
+ for (let f = __dispatch_top__; f; f = f.prev) if (f.gobj === gobj) f.destroyed = true;
2916
+ }
2917
+ /************************************************************
2918
+ * Is the innermost live publication of `gobj` its EV_STOPPED?
2919
+ * Then the stop contract holds for it: that is the last thing
2920
+ * it does with itself.
2921
+ ************************************************************/
2922
+ function said_farewell(gobj) {
2923
+ 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";
2924
+ return false;
2925
+ }
2926
+ /************************************************************
2927
+ * A gobj must not be destroyed under code that is still
2928
+ * running on it -- the C kernel's check, with its exemptions
2929
+ * and its fields, so both runtimes report the same defect the
2930
+ * same way.
2931
+ *
2932
+ * `gobj` is about to be destroyed, and with it its whole
2933
+ * subtree. Every live frame of a gobj of that subtree is code
2934
+ * that goes on when the destroy returns, on a gobj that is gone
2935
+ * (C_TIMER right after its EV_TIMEOUT). It is an ERROR, except
2936
+ * for:
2937
+ *
2938
+ * - the innermost frame: it is the code that destroys, and
2939
+ * knows it (a gobj that destroys itself in its own action);
2940
+ *
2941
+ * - the frames of a gobj whose innermost publication is
2942
+ * EV_STOPPED: the stop contract, its host destroys it there;
2943
+ *
2944
+ * - the watch frames, which run no gclass code of their own.
2945
+ *
2946
+ * The destroy goes on: this only says it, once. The fix is to
2947
+ * defer the destroy with gobj_post_event().
2948
+ ************************************************************/
2949
+ function check_destroy_under_running_code(gobj) {
2950
+ const top = __dispatch_top__;
2951
+ if (!top) return;
2952
+ for (let f = top.prev; f; f = f.prev) {
2953
+ if (f.destroyed || f.kind === frame_kind_t.FRAME_WATCH) continue;
2954
+ let in_tree = false;
2955
+ for (let p = f.gobj; p; p = p.parent) if (p === gobj) {
2956
+ in_tree = true;
2957
+ break;
2958
+ }
2959
+ if (!in_tree) continue;
2960
+ if (said_farewell(f.gobj)) continue;
2961
+ const destroyer = top.destroyed ? "(destroyed)" : gobj_full_name(top.gobj);
2962
+ const stack = String((/* @__PURE__ */ new Error()).stack || "").split("\n").slice(2).join("\n");
2963
+ 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}`);
2964
+ 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}`);
2965
+ return;
2966
+ }
2967
+ }
2968
+ /************************************************************
2550
2969
  * send_event
2551
2970
  ************************************************************/
2552
2971
  function gobj_send_event(dst, event, kw, src) {
@@ -2578,7 +2997,12 @@
2578
2997
  }
2579
2998
  }
2580
2999
  }
2581
- return dst.gclass.gmt.mt_inject_event(dst, event, kw, src);
3000
+ const frame = push_dispatch_frame(dst, event, frame_kind_t.FRAME_ACTION);
3001
+ try {
3002
+ return dst.gclass.gmt.mt_inject_event(dst, event, kw, src);
3003
+ } finally {
3004
+ pop_dispatch_frame(frame);
3005
+ }
2582
3006
  }
2583
3007
  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)));
2584
3008
  log_error(`📛📛 Event NOT DEFINED in state 📛📛: ${gobj_short_name(dst)}, ${state.state_name}, ${event}`);
@@ -2598,6 +3022,7 @@
2598
3022
  }
2599
3023
  }
2600
3024
  }
3025
+ const frame = push_dispatch_frame(dst, event, frame_kind_t.FRAME_ACTION);
2601
3026
  if (event_action.next_state) gobj_change_state(dst, event_action.next_state);
2602
3027
  let ret = -1;
2603
3028
  try {
@@ -2611,8 +3036,10 @@
2611
3036
  } catch (e2) {}
2612
3037
  }
2613
3038
  throw e;
3039
+ } finally {
3040
+ pop_dispatch_frame(frame);
2614
3041
  }
2615
- 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));
3042
+ 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));
2616
3043
  __inside__--;
2617
3044
  return ret;
2618
3045
  }
@@ -3075,62 +3502,67 @@
3075
3502
  let dl_subs = [...publisher.dl_subscriptions];
3076
3503
  let sent_count = 0;
3077
3504
  let ret = 0;
3078
- for (let idx = 0; idx < dl_subs.length; idx++) {
3079
- let subs = dl_subs[idx];
3080
- if (publisher.gclass.gmt.mt_publication_pre_filter) {
3081
- let topublish = publisher.gclass.gmt.mt_publication_pre_filter(publisher, subs, event, kw);
3082
- if (topublish < 0) break;
3083
- else if (!topublish) continue;
3084
- }
3085
- let subscriber = kw_get_pointer(publisher, subs, "subscriber", null, kw_flag_t.KW_REQUIRED);
3086
- if (!(subscriber && !gobj_is_destroying(subscriber))) continue;
3087
- let subs_flag = kw_get_int(publisher, subs, "subs_flag", 0, kw_flag_t.KW_REQUIRED);
3088
- let event_ = kw_get_str(publisher, subs, "event", "", kw_flag_t.KW_REQUIRED);
3089
- if (empty_string(event_) || strcasecmp(event_, event) === 0) {
3090
- let __config__ = kw_get_dict(publisher, subs, "__config__", null, 0);
3091
- let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
3092
- let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
3093
- let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
3094
- let event_name = subs.renamed_event;
3095
- if (empty_string(event_name)) event_name = event;
3096
- let topublish = 1;
3097
- if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
3098
- else if (json_size(__filter__) > 0) {
3099
- topublish = kw_match_simple(kw, __filter__) ? 1 : 0;
3505
+ const frame = push_dispatch_frame(publisher, event, frame_kind_t.FRAME_PUBLICATION);
3506
+ try {
3507
+ for (let idx = 0; idx < dl_subs.length; idx++) {
3508
+ let subs = dl_subs[idx];
3509
+ if (publisher.gclass.gmt.mt_publication_pre_filter) {
3510
+ let topublish = publisher.gclass.gmt.mt_publication_pre_filter(publisher, subs, event, kw);
3511
+ if (topublish < 0) break;
3512
+ else if (!topublish) continue;
3513
+ }
3514
+ let subscriber = kw_get_pointer(publisher, subs, "subscriber", null, kw_flag_t.KW_REQUIRED);
3515
+ if (!(subscriber && !gobj_is_destroying(subscriber))) continue;
3516
+ let subs_flag = kw_get_int(publisher, subs, "subs_flag", 0, kw_flag_t.KW_REQUIRED);
3517
+ let event_ = kw_get_str(publisher, subs, "event", "", kw_flag_t.KW_REQUIRED);
3518
+ if (empty_string(event_) || strcasecmp(event_, event) === 0) {
3519
+ let __config__ = kw_get_dict(publisher, subs, "__config__", null, 0);
3520
+ let __global__ = kw_get_dict(publisher, subs, "__global__", null, 0);
3521
+ let __local__ = kw_get_dict(publisher, subs, "__local__", null, 0);
3522
+ let __filter__ = kw_get_dict_value(publisher, subs, "__filter__", null, 0);
3523
+ let event_name = subs.renamed_event;
3524
+ if (empty_string(event_name)) event_name = event;
3525
+ let topublish = 1;
3526
+ if (publisher.gclass.gmt.mt_publication_filter) topublish = publisher.gclass.gmt.mt_publication_filter(publisher, event, kw, subscriber);
3527
+ else if (json_size(__filter__) > 0) {
3528
+ topublish = kw_match_simple(kw, __filter__) ? 1 : 0;
3529
+ if (tracea) {
3530
+ trace_machine(sprintf("💜💜🔄%s publishing with filter, event '%s', subscriber'%s', publisher %s", topublish ? "👍" : "👎", event ? event : "", gobj_short_name(subscriber), gobj_short_name(publisher)));
3531
+ trace_json(__filter__);
3532
+ }
3533
+ }
3534
+ if (topublish < 0) break;
3535
+ else if (!topublish) continue;
3536
+ let ev_ = gobj_event_type(subscriber, event, true);
3537
+ if (ev_) {
3538
+ if (ev_.event_flag & event_flag_t.EVF_SYSTEM_EVENT) {
3539
+ if (!gobj_has_event(subscriber, ev_.event_name, 0)) continue;
3540
+ }
3541
+ }
3542
+ let kw2publish = kw;
3543
+ if (json_size(__local__) > 0 || json_size(__global__) > 0) kw2publish = json_object_update({}, kw);
3544
+ if (json_size(__local__) > 0) kw_pop(kw2publish, __local__);
3545
+ if (__config__) {}
3546
+ if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
3100
3547
  if (tracea) {
3101
- trace_machine(sprintf("💜💜🔄%s publishing with filter, event '%s', subscriber'%s', publisher %s", topublish ? "👍" : "👎", event ? event : "", gobj_short_name(subscriber), gobj_short_name(publisher)));
3102
- trace_json(__filter__);
3548
+ if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3549
+ 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)));
3550
+ if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
3103
3551
  }
3104
- }
3105
- if (topublish < 0) break;
3106
- else if (!topublish) continue;
3107
- let ev_ = gobj_event_type(subscriber, event, true);
3108
- if (ev_) {
3109
- if (ev_.event_flag & event_flag_t.EVF_SYSTEM_EVENT) {
3110
- if (!gobj_has_event(subscriber, ev_.event_name, 0)) continue;
3552
+ let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
3553
+ if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
3554
+ sent_count = -1;
3555
+ break;
3111
3556
  }
3557
+ ret += ret_;
3558
+ sent_count++;
3559
+ if (frame.destroyed || gobj_is_destroying(publisher)) break;
3112
3560
  }
3113
- let kw2publish = kw;
3114
- if (json_size(__local__) > 0 || json_size(__global__) > 0) kw2publish = json_object_update({}, kw);
3115
- if (json_size(__local__) > 0) kw_pop(kw2publish, __local__);
3116
- if (__config__) {}
3117
- if (json_size(__global__) > 0) json_object_update(kw2publish, json_deep_copy(__global__));
3118
- if (tracea) {
3119
- if (__trace_machine_format__ === 1) trace_machine(sprintf("🔝🔄 %s (%s) %s%s", event ? event : "", event_name ? event_name : "", !subscriber.running ? "!!" : "", gobj_short_name(subscriber)));
3120
- 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)));
3121
- if (__trace_gobj_ev_kw__(publisher) && json_object_size(kw2publish)) trace_json_masked(kw2publish);
3122
- }
3123
- let ret_ = gobj_send_event(subscriber, event_name, kw2publish, publisher);
3124
- if (ret_ < 0 && subs_flag & subs_flag_t.__own_event__) {
3125
- sent_count = -1;
3126
- break;
3127
- }
3128
- ret += ret_;
3129
- sent_count++;
3130
- if (gobj_is_destroying(publisher)) break;
3131
3561
  }
3562
+ } finally {
3563
+ pop_dispatch_frame(frame);
3132
3564
  }
3133
- if (!sent_count) {
3565
+ if (!sent_count && !frame.destroyed) {
3134
3566
  if (!ev || !(ev.event_flag & event_flag_t.EVF_NO_WARN_SUBS)) log_warning(`${gobj_short_name(publisher)}: Publish event WITHOUT subscribers, ev ${event}`);
3135
3567
  }
3136
3568
  return ret;
@@ -3439,12 +3871,12 @@
3439
3871
  "sessionkey",
3440
3872
  "authdata"
3441
3873
  ];
3442
- var SECRET_NAME_SEGMENTS = /* @__PURE__ */ new Set([
3874
+ var SECRET_NAME_SEGMENTS = [
3443
3875
  "pass",
3444
3876
  "pin",
3445
3877
  "otp"
3446
- ]);
3447
- var NOT_SECRET_SEGMENTS = /* @__PURE__ */ new Set([
3878
+ ];
3879
+ var NOT_SECRET_SEGMENTS = [
3448
3880
  "endpoint",
3449
3881
  "url",
3450
3882
  "uri",
@@ -3467,36 +3899,130 @@
3467
3899
  "expiry",
3468
3900
  "expires",
3469
3901
  "mode"
3470
- ]);
3902
+ ];
3903
+ function words_by_letter(list) {
3904
+ const by_letter = new Array(128).fill(null);
3905
+ for (const word of list) {
3906
+ const c = word.charCodeAt(0);
3907
+ if (!by_letter[c]) by_letter[c] = [];
3908
+ by_letter[c].push(word);
3909
+ }
3910
+ return by_letter;
3911
+ }
3912
+ var SECRET_PARTS_BY_LETTER = words_by_letter(SECRET_NAME_PARTS);
3913
+ var SECRET_JOINED_BY_LETTER = words_by_letter(SECRET_NAME_JOINED);
3914
+ var SECRET_SEGMENTS_BY_LETTER = words_by_letter(SECRET_NAME_SEGMENTS);
3915
+ var NOT_SECRET_BY_LETTER = words_by_letter(NOT_SECRET_SEGMENTS);
3916
+ function ascii_lower_code(code) {
3917
+ return code >= 65 && code <= 90 ? code + 32 : code;
3918
+ }
3919
+ function is_name_joiner_code(code) {
3920
+ return code === 95 || code === 45 || code === 46 || code === 32 || code === 9;
3921
+ }
3922
+ function name_segment_in(str, from, to, by_letter) {
3923
+ const n = to - from;
3924
+ if (n === 0) return false;
3925
+ const c0 = ascii_lower_code(str.charCodeAt(from));
3926
+ const words = c0 < 128 ? by_letter[c0] : null;
3927
+ if (!words) return false;
3928
+ for (let w = 0; w < words.length; w++) {
3929
+ const word = words[w];
3930
+ if (word.length !== n) continue;
3931
+ let i = 1;
3932
+ while (i < n && ascii_lower_code(str.charCodeAt(from + i)) === word.charCodeAt(i)) i++;
3933
+ if (i === n) return true;
3934
+ }
3935
+ return false;
3936
+ }
3937
+ function name_part_at(str, to, i, part, joined) {
3938
+ let j = i;
3939
+ let k = 0;
3940
+ while (k < part.length) {
3941
+ if (j >= to) return false;
3942
+ const code = str.charCodeAt(j);
3943
+ if (joined && k > 0 && is_name_joiner_code(code)) {
3944
+ j++;
3945
+ continue;
3946
+ }
3947
+ if (ascii_lower_code(code) !== part.charCodeAt(k)) return false;
3948
+ j++;
3949
+ k++;
3950
+ }
3951
+ return true;
3952
+ }
3953
+ function secret_name_check(str, from, to, about_wins, by_segments) {
3954
+ if (to - from < 3) return false;
3955
+ if (about_wins) {
3956
+ let seg = from;
3957
+ for (let i = from; i <= to; i++) {
3958
+ if (i < to && !is_name_joiner_code(str.charCodeAt(i))) continue;
3959
+ if (name_segment_in(str, seg, i, NOT_SECRET_BY_LETTER)) return false;
3960
+ seg = i + 1;
3961
+ }
3962
+ }
3963
+ if (by_segments) {
3964
+ let seg = from;
3965
+ for (let i = from; i <= to; i++) {
3966
+ if (i < to && is_ascii_alnum_code(str.charCodeAt(i))) continue;
3967
+ if (name_segment_in(str, seg, i, SECRET_SEGMENTS_BY_LETTER)) return true;
3968
+ seg = i + 1;
3969
+ }
3970
+ }
3971
+ let has_priv = false;
3972
+ let has_key = false;
3973
+ for (let i = from; i < to; i++) {
3974
+ const c = ascii_lower_code(str.charCodeAt(i));
3975
+ if (c >= 128) continue;
3976
+ const parts = SECRET_PARTS_BY_LETTER[c];
3977
+ for (let k = 0; parts && k < parts.length; k++) if (name_part_at(str, to, i, parts[k], false)) return true;
3978
+ const joined = SECRET_JOINED_BY_LETTER[c];
3979
+ for (let k = 0; joined && k < joined.length; k++) if (name_part_at(str, to, i, joined[k], true)) return true;
3980
+ if (c === 112 && !has_priv) has_priv = name_part_at(str, to, i, "priv", false);
3981
+ else if (c === 107 && !has_key) has_key = name_part_at(str, to, i, "key", false);
3982
+ }
3983
+ return has_priv && has_key;
3984
+ }
3471
3985
  /************************************************************
3472
3986
  * TRUE if `name` is the name of a secret: a password, a token,
3473
3987
  * a key... Any case. As the C kernel's is_secret_name().
3474
3988
  ************************************************************/
3475
3989
  function is_secret_name(name) {
3476
- if (typeof name !== "string" || name.length < 3) return false;
3477
- let lower = name.toLowerCase();
3478
- let hit = false;
3479
- for (const segment of lower.split(/[^a-z0-9]+/)) if (SECRET_NAME_SEGMENTS.has(segment)) {
3480
- hit = true;
3481
- break;
3482
- }
3483
- if (!hit) {
3484
- for (const part of SECRET_NAME_PARTS) if (lower.indexOf(part) >= 0) {
3485
- hit = true;
3486
- break;
3990
+ if (typeof name !== "string") return false;
3991
+ return secret_name_check(name, 0, name.length, true, true);
3992
+ }
3993
+ var NAMES_NOW = 0;
3994
+ var NAMES_7_26_9 = 1;
3995
+ function is_secret_name_tail(str, from, to, names) {
3996
+ const by_segments = names === NAMES_NOW;
3997
+ if ((to - from) * 3 <= MASK_MAX_NAME) return secret_name_check(str, from, to, true, by_segments);
3998
+ let bytes = 0;
3999
+ let i = to;
4000
+ let partial = false;
4001
+ while (i > from) {
4002
+ const code = str.charCodeAt(i - 1);
4003
+ let n = 3;
4004
+ let units = 1;
4005
+ if (code < 128) n = 1;
4006
+ else if (code < 2048) n = 2;
4007
+ else if (code >= 56320 && code <= 57343 && i - 2 >= from) {
4008
+ const high = str.charCodeAt(i - 2);
4009
+ if (high >= 55296 && high <= 56319) {
4010
+ n = 4;
4011
+ units = 2;
4012
+ }
3487
4013
  }
3488
- }
3489
- if (!hit) {
3490
- let joined = lower.replace(/[_\-. \t]/g, "");
3491
- for (const part of SECRET_NAME_JOINED) if (joined.indexOf(part) >= 0) {
3492
- hit = true;
4014
+ if (bytes + n > MASK_MAX_NAME) {
4015
+ partial = bytes < MASK_MAX_NAME;
3493
4016
  break;
3494
4017
  }
4018
+ bytes += n;
4019
+ i -= units;
3495
4020
  }
3496
- if (!hit) hit = lower.indexOf("priv") >= 0 && lower.indexOf("key") >= 0;
3497
- if (!hit) return false;
3498
- for (const segment of lower.split(/[_\-. \t]/)) if (NOT_SECRET_SEGMENTS.has(segment)) return false;
3499
- return true;
4021
+ if (partial) {
4022
+ const name = "€" + str.substring(i, to);
4023
+ return secret_name_check(name, 0, name.length, true, by_segments);
4024
+ }
4025
+ return secret_name_check(str, i, to, true, by_segments);
3500
4026
  }
3501
4027
  /************************************************************
3502
4028
  * Where an unquoted secret value that begins at `p` ends: at
@@ -3524,77 +4050,255 @@
3524
4050
  }
3525
4051
  /************************************************************
3526
4052
  * A credential by its SHAPE, whatever names it -- the C kernel's
3527
- * jwt_len() / bearer_token_len(), the shapes the agent's audit
3528
- * record redacts:
3529
- * - a JWT: "eyJ" (the base64url of '{"') at the start of a
3530
- * word, and three parts of base64url joined by '.'; the dots
3531
- * at the end of the run are not its own (a JWT ends a
3532
- * sentence: "... eyJ.eyJ.sig.");
3533
- * - the token after "Bearer " (any case, a word of its own).
3534
- ************************************************************/
4053
+ * jwt_len() / scheme_credential(), the shapes the agent's audit
4054
+ * record redacts, and the schemes of an HTTP Authorization:
4055
+ * - a JWT or a JWE: "eyJ" (the base64url of '{"') at the start
4056
+ * of a word, base64url, and a '.' of its own after it -- three
4057
+ * parts joined by '.' (a JWS), five (a JWE), or fewer when a cap
4058
+ * cut it; the dots at the end of the run are not its own (a JWT
4059
+ * ends a sentence: "... eyJ.eyJ.sig."), so a run whose only
4060
+ * dots end it is a header and no JWT ("eyJhIjoxfQ.");
4061
+ * - the credential after the scheme word "Bearer" or "Basic"
4062
+ * (any case, a word of its own): scheme_credential().
4063
+ ************************************************************/
4064
+ var MASK_TOKEN_MIN = 8;
4065
+ var MASK_PROSE_MAX = 24;
3535
4066
  function is_ascii_alnum_code(code) {
3536
4067
  return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
3537
4068
  }
4069
+ function is_ascii_letter_code(code) {
4070
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
4071
+ }
4072
+ function is_ascii_upper_code(code) {
4073
+ return code >= 65 && code <= 90;
4074
+ }
3538
4075
  function is_base64url_code(code) {
3539
4076
  return is_ascii_alnum_code(code) || code === 45 || code === 95;
3540
4077
  }
4078
+ function ascii_case_equal(str, p, word) {
4079
+ if (str.length - p < word.length) return false;
4080
+ for (let i = 0; i < word.length; i++) {
4081
+ let code = str.charCodeAt(p + i);
4082
+ if (code >= 65 && code <= 90) code += 32;
4083
+ if (code !== word.charCodeAt(i)) return false;
4084
+ }
4085
+ return true;
4086
+ }
3541
4087
  function jwt_len(str, p) {
3542
4088
  if (str.length - p < 3 || str[p] !== "e" || str[p + 1] !== "y" || str[p + 2] !== "J") return 0;
3543
4089
  if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return 0;
3544
4090
  let e = p;
3545
- let dots = 0;
4091
+ let dotted = false;
3546
4092
  while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
3547
- if (str[e] === ".") dots++;
4093
+ if (e > p && str[e] !== "." && str[e - 1] === ".") dotted = true;
3548
4094
  e++;
3549
4095
  }
3550
- while (str[e - 1] === ".") {
3551
- e--;
3552
- dots--;
4096
+ if (!dotted) return 0;
4097
+ while (str[e - 1] === ".") e--;
4098
+ return e - p;
4099
+ }
4100
+ function scheme_word_len(str, p) {
4101
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return 0;
4102
+ for (const scheme of ["bearer", "basic"]) {
4103
+ const n = scheme.length;
4104
+ if (str.length - p > n && ascii_case_equal(str, p, scheme) && (str[p + n] === " " || str[p + n] === " ")) return n;
4105
+ }
4106
+ return 0;
4107
+ }
4108
+ function code_points(str, w, n) {
4109
+ let count = 0;
4110
+ for (let i = w; i < w + n; i++) {
4111
+ const code = str.charCodeAt(i);
4112
+ if (code < 56320 || code > 57343) count++;
4113
+ }
4114
+ return count;
4115
+ }
4116
+ function is_prose_word(str, w, n) {
4117
+ if (n === 0 || n >= MASK_PROSE_MAX) return false;
4118
+ let i = w;
4119
+ const end = w + n;
4120
+ while (true) {
4121
+ let r = i;
4122
+ let upper_rest = true;
4123
+ let lower_rest = true;
4124
+ while (r < end && is_ascii_letter_code(str.charCodeAt(r))) {
4125
+ if (r > i) {
4126
+ if (is_ascii_upper_code(str.charCodeAt(r))) lower_rest = false;
4127
+ else upper_rest = false;
4128
+ }
4129
+ r++;
4130
+ }
4131
+ if (r === i) return false;
4132
+ if (!lower_rest && !(upper_rest && is_ascii_upper_code(str.charCodeAt(i)))) return false;
4133
+ if (r === end) return true;
4134
+ if (str[r] !== "-" && str[r] !== "." && str[r] !== "/" || r + 1 === end) return false;
4135
+ i = r + 1;
4136
+ }
4137
+ }
4138
+ function base64_digit(code) {
4139
+ if (code >= 65 && code <= 90) return code - 65;
4140
+ if (code >= 97 && code <= 122) return code - 97 + 26;
4141
+ if (code >= 48 && code <= 57) return code - 48 + 52;
4142
+ if (code === 43) return 62;
4143
+ if (code === 47) return 63;
4144
+ return -1;
4145
+ }
4146
+ function is_basic_credential(str, w, n) {
4147
+ let bits = 0;
4148
+ let n_bits = 0;
4149
+ for (let i = 0; i < n; i++) {
4150
+ const v = base64_digit(str.charCodeAt(w + i));
4151
+ if (v < 0) return false;
4152
+ bits = (bits << 6 | v) & 16383;
4153
+ n_bits += 6;
4154
+ if (n_bits >= 8) {
4155
+ n_bits -= 8;
4156
+ if ((bits >> n_bits & 255) === 58) return true;
4157
+ }
3553
4158
  }
3554
- return dots === 2 ? e - p : 0;
4159
+ return false;
3555
4160
  }
3556
- var BEARER_STOPS = " \r\n'\",;}]\0";
3557
- function bearer_token(str, p) {
3558
- const word_len = 6;
3559
- if (str.length - p <= word_len || str.slice(p, p + word_len).toLowerCase() !== "bearer") return [0, 0];
3560
- if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
3561
- let t = p + word_len;
3562
- if (str[t] !== " " && str[t] !== " ") return [0, 0];
4161
+ var SCHEME_WORD_STOPS = " \r\n\"',;}]\0";
4162
+ function scheme_credential(str, p) {
4163
+ const n = scheme_word_len(str, p);
4164
+ if (!n) return [0, 0];
4165
+ const scheme = n === 5 ? "basic" : "bearer";
4166
+ let context = false;
4167
+ let t = p + n;
3563
4168
  while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
4169
+ if (t < str.length && scheme_word_len(str, t) === n && ascii_case_equal(str, t, scheme)) {
4170
+ t += n;
4171
+ context = true;
4172
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
4173
+ }
4174
+ if (t >= str.length) return [0, 0];
4175
+ if (str[t] === "\"" || str[t] === "'") {
4176
+ let q = t + 1;
4177
+ while (q < str.length && str[q] !== str[t] && str[q] !== " " && str[q] !== " " && str[q] !== "\r" && str[q] !== "\n" && str[q] !== "\0") q++;
4178
+ if (q < str.length && str[q] === str[t] && q > t + 1) return [t - p, q + 1 - t];
4179
+ return [0, 0];
4180
+ }
3564
4181
  let e = t;
3565
- while (e < str.length && BEARER_STOPS.indexOf(str[e]) < 0) e++;
4182
+ while (e < str.length && SCHEME_WORD_STOPS.indexOf(str[e]) < 0) e++;
4183
+ if (e === t) return [0, 0];
4184
+ if (!context) {
4185
+ let w_end = e;
4186
+ while (w_end > t && str[w_end - 1] === ".") w_end--;
4187
+ const len = w_end - t;
4188
+ if (len === 0 || is_prose_word(str, t, len)) return [0, 0];
4189
+ if (n === 5) {
4190
+ if (!is_basic_credential(str, t, len)) return [0, 0];
4191
+ } else if (code_points(str, t, len) < MASK_TOKEN_MIN) return [0, 0];
4192
+ }
3566
4193
  return [t - p, e - t];
3567
4194
  }
3568
- /************************************************************
3569
- * A text (a command line) with the value of every "name=value"
3570
- * whose name is a secret's written as "********", quoted or not
3571
- * (and the "value" of a write-attr whose attribute names a
3572
- * secret); and a JWT or the token after "Bearer ", wherever they
3573
- * are, whatever names them. The rest as it is. Return null when
3574
- * there was nothing to mask. As the C kernel's
3575
- * mask_secrets_inline().
3576
- ************************************************************/
4195
+ function shape_len(str, p) {
4196
+ const c = str[p];
4197
+ if (c === "e") return [0, jwt_len(str, p)];
4198
+ if (c === "b" || c === "B") return scheme_credential(str, p);
4199
+ return [0, 0];
4200
+ }
4201
+ function shape_len_js_7_26_20(str, p) {
4202
+ const c = str[p];
4203
+ if (c === "e") {
4204
+ if (str.length - p < 3 || str[p + 1] !== "y" || str[p + 2] !== "J") return [0, 0];
4205
+ if (p > 0 && (is_base64url_code(str.charCodeAt(p - 1)) || str[p - 1] === ".")) return [0, 0];
4206
+ let e = p;
4207
+ let dots = 0;
4208
+ while (e < str.length && (is_base64url_code(str.charCodeAt(e)) || str[e] === ".")) {
4209
+ if (str[e] === ".") dots++;
4210
+ e++;
4211
+ }
4212
+ while (str[e - 1] === ".") {
4213
+ e--;
4214
+ dots--;
4215
+ }
4216
+ return dots === 2 ? [0, e - p] : [0, 0];
4217
+ }
4218
+ if (c === "b" || c === "B") {
4219
+ if (str.length - p <= 6 || !ascii_case_equal(str, p, "bearer")) return [0, 0];
4220
+ if (p > 0 && is_ascii_alnum_code(str.charCodeAt(p - 1))) return [0, 0];
4221
+ let t = p + 6;
4222
+ if (str[t] !== " " && str[t] !== " ") return [0, 0];
4223
+ while (t < str.length && (str[t] === " " || str[t] === " ")) t++;
4224
+ let e = t;
4225
+ while (e < str.length && " \r\n'\",;}]\0".indexOf(str[e]) < 0) e++;
4226
+ return [t - p, e - t];
4227
+ }
4228
+ return [0, 0];
4229
+ }
4230
+ var COLON_VALUE_STOPS = " \r\n,;&)]}<>\"'\0";
4231
+ function colon_value_end(str, v) {
4232
+ if (v >= str.length) return v;
4233
+ const c = str[v];
4234
+ if (c === "\"" || c === "'") {
4235
+ let e = v + 1;
4236
+ while (e < str.length && str[e] !== c) {
4237
+ if (str[e] === "\\" && e + 1 < str.length) e++;
4238
+ e++;
4239
+ }
4240
+ return e < str.length ? e + 1 : str.length;
4241
+ }
4242
+ if (c === "{" || c === "[") {
4243
+ let depth = 0;
4244
+ let in_string = false;
4245
+ for (let e = v; e < str.length; e++) if (in_string) {
4246
+ if (str[e] === "\\" && e + 1 < str.length) e++;
4247
+ else if (str[e] === "\"") in_string = false;
4248
+ } else if (str[e] === "\"") in_string = true;
4249
+ else if (str[e] === "{" || str[e] === "[") depth++;
4250
+ else if (str[e] === "}" || str[e] === "]") {
4251
+ depth--;
4252
+ if (depth === 0) return e + 1;
4253
+ }
4254
+ return str.length;
4255
+ }
4256
+ if (c === "\\") {
4257
+ let q = v;
4258
+ while (q < str.length && str[q] === "\\") q++;
4259
+ if (q < str.length && (str[q] === "\"" || str[q] === "'")) {
4260
+ const level = q - v;
4261
+ let run = 0;
4262
+ for (let e = q + 1; e < str.length; e++) {
4263
+ if (str[e] === "\\") {
4264
+ run++;
4265
+ continue;
4266
+ }
4267
+ if (str[e] === str[q] && run === level) return e + 1;
4268
+ run = 0;
4269
+ }
4270
+ return str.length;
4271
+ }
4272
+ }
4273
+ let e = v;
4274
+ const n = scheme_word_len(str, v);
4275
+ if (n) {
4276
+ e = v + n;
4277
+ while (e < str.length && (str[e] === " " || str[e] === " ")) e++;
4278
+ if (e < str.length && (str[e] === "\"" || str[e] === "'")) return colon_value_end(str, e);
4279
+ }
4280
+ while (e < str.length && COLON_VALUE_STOPS.indexOf(str[e]) < 0) e++;
4281
+ return e;
4282
+ }
3577
4283
  var MASK_MAX_NAME = 128;
3578
4284
  var MASK_MAX_BYTES = 4194304;
3579
4285
  var MASK_TOO_LARGE = "<not shown: too large to mask>";
3580
- function mask_secrets_inline(str) {
3581
- if (typeof str !== "string") return null;
3582
- if (str.indexOf("=") < 0 && str.indexOf("eyJ") < 0 && !/bearer/i.test(str)) return null;
3583
- if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
3584
- let value_is_secret = false;
3585
- let re_attr = /(^|[ \t])attribute=["']?([^ \t"']*)/g;
3586
- let m;
3587
- while ((m = re_attr.exec(str)) !== null) if (is_secret_name(m[2].slice(-128))) value_is_secret = true;
3588
- let parts = [];
3589
- let copied = 0;
3590
- let changed = false;
4286
+ function mark_range(marks, from, to) {
4287
+ for (let i = from; i < to; i++) marks[i] = 1;
4288
+ }
4289
+ var READ_NOW = 0;
4290
+ var READ_AS_GOBJ_JS_7_26_20 = 1;
4291
+ var READ_AS_7_26_9 = 2;
4292
+ function mark_params(str, value_is_secret, reading, marks, judged) {
4293
+ const names = reading === READ_AS_7_26_9 ? NAMES_7_26_9 : NAMES_NOW;
4294
+ let not_7_26_9 = false;
4295
+ const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " " || str[i] === "\0";
3591
4296
  let outer = "";
3592
4297
  let word = 0;
3593
4298
  let p = 0;
3594
- const is_blank_or_end = (i) => i >= str.length || str[i] === " " || str[i] === " ";
3595
4299
  while (p < str.length) {
3596
- let c = str[p];
3597
- if (c === " " || c === " ") {
4300
+ const c = str[p];
4301
+ if (c === " " || c === " " || c === "\0") {
3598
4302
  p++;
3599
4303
  word = p;
3600
4304
  continue;
@@ -3603,48 +4307,205 @@
3603
4307
  if (!outer && (p === 0 || str[p - 1] === " " || str[p - 1] === " " || str[p - 1] === "=")) outer = c;
3604
4308
  else if (c === outer && is_blank_or_end(p + 1)) outer = "";
3605
4309
  }
3606
- let shape = c === "e" ? jwt_len(str, p) : 0;
3607
- let token_offset = 0;
3608
- if (c === "b" || c === "B") [token_offset, shape] = bearer_token(str, p);
3609
- if (shape) {
3610
- parts.push(str.substring(copied, p + token_offset));
3611
- parts.push("********");
3612
- p += token_offset + shape;
3613
- copied = p;
3614
- changed = true;
3615
- word = p;
3616
- continue;
4310
+ if (reading === READ_AS_GOBJ_JS_7_26_20 && (c === "e" || c === "b" || c === "B")) {
4311
+ const [offset, n] = shape_len_js_7_26_20(str, p);
4312
+ if (n) {
4313
+ p += offset + n;
4314
+ word = p;
4315
+ continue;
4316
+ }
3617
4317
  }
3618
4318
  if (c !== "=") {
3619
4319
  p++;
3620
4320
  continue;
3621
4321
  }
3622
- let name = str.substring(Math.max(word, p - MASK_MAX_NAME), p);
4322
+ const name_from = word;
3623
4323
  word = p + 1;
3624
- if (!(is_secret_name(name) || value_is_secret && name.toLowerCase() === "value")) {
4324
+ const secret = is_secret_name_tail(str, name_from, p, names);
4325
+ if (secret && names === NAMES_NOW && !is_secret_name_tail(str, name_from, p, NAMES_7_26_9)) not_7_26_9 = true;
4326
+ if (!(secret || value_is_secret && p - name_from === 5 && ascii_case_equal(str, name_from, "value"))) {
4327
+ if (judged && query_name_from(str, p) === name_from) judged[p] = 1;
3625
4328
  p++;
3626
4329
  continue;
3627
4330
  }
3628
4331
  p++;
3629
- let quote = str[p] === "\"" || str[p] === "'" ? str[p] : "";
3630
4332
  let end;
4333
+ const quote = p < str.length && (str[p] === "\"" || str[p] === "'") ? str[p] : "";
3631
4334
  if (quote) {
3632
4335
  end = p + 1;
3633
- while (end < str.length && str[end] !== quote) end++;
3634
- if (str[end] === quote) end++;
4336
+ while (end < str.length && str[end] !== quote && str[end] !== "\0") end++;
4337
+ if (end < str.length && str[end] === quote) end++;
3635
4338
  } else end = inline_secret_value_end(str, p, outer);
3636
- if (end > p) {
3637
- parts.push(str.substring(copied, p));
4339
+ mark_range(marks, p, end);
4340
+ p = end;
4341
+ word = p;
4342
+ }
4343
+ return not_7_26_9;
4344
+ }
4345
+ function has_shape_js_7_26_20(str) {
4346
+ for (let p = 0; p < str.length; p++) {
4347
+ const code = str.charCodeAt(p);
4348
+ if ((code === 101 || code === 98 || code === 66) && shape_len_js_7_26_20(str, p)[1]) return true;
4349
+ }
4350
+ return false;
4351
+ }
4352
+ function stop_table(chars) {
4353
+ const table = /* @__PURE__ */ new Uint8Array(128);
4354
+ for (const c of chars + "\r\n\0") table[c.charCodeAt(0)] = 1;
4355
+ return table;
4356
+ }
4357
+ var PARAM_VALUE_STOPS = stop_table("&; ,\"'<>");
4358
+ var QUOTE_STOPS = {
4359
+ "\"": stop_table("\""),
4360
+ "'": stop_table("'")
4361
+ };
4362
+ function mark_run(str, p, stops, marks) {
4363
+ let e = p;
4364
+ while (e < str.length) {
4365
+ const code = str.charCodeAt(e);
4366
+ if (code < 128 && stops[code]) break;
4367
+ e++;
4368
+ }
4369
+ mark_range(marks, p, e);
4370
+ return e;
4371
+ }
4372
+ function mark_scheme_value(str, v, stops, marks) {
4373
+ const n = scheme_word_len(str, v);
4374
+ if (!n) return -1;
4375
+ let w = v + n;
4376
+ while (w < str.length && (str[w] === " " || str[w] === " ")) w++;
4377
+ mark_range(marks, v, w);
4378
+ if (w < str.length && (str[w] === "\"" || str[w] === "'")) {
4379
+ const e = colon_value_end(str, w);
4380
+ mark_range(marks, w, e);
4381
+ return e;
4382
+ }
4383
+ return mark_run(str, w, stops, marks);
4384
+ }
4385
+ function is_name_char_code(code) {
4386
+ return is_ascii_alnum_code(code) || code === 95 || code === 45 || code === 46;
4387
+ }
4388
+ function query_name_from(str, eq) {
4389
+ let n = eq;
4390
+ while (n > 0 && eq - n < MASK_MAX_NAME && is_name_char_code(str.charCodeAt(n - 1))) n--;
4391
+ return n;
4392
+ }
4393
+ function mark_query_params(str, marks, judged) {
4394
+ let frontier = 0;
4395
+ for (let p = 1; p < str.length; p++) {
4396
+ if (p < frontier) {
4397
+ p = frontier - 1;
4398
+ continue;
4399
+ }
4400
+ if (str.charCodeAt(p) !== 61) continue;
4401
+ if (judged[p]) continue;
4402
+ const n = query_name_from(str, p);
4403
+ if (n === p || !secret_name_check(str, n, p, true, true)) continue;
4404
+ const v = p + 1;
4405
+ if (v < str.length && (str[v] === "\"" || str[v] === "'")) frontier = mark_run(str, v + 1, QUOTE_STOPS[str[v]], marks);
4406
+ else {
4407
+ mark_scheme_value(str, v, PARAM_VALUE_STOPS, marks);
4408
+ frontier = mark_run(str, v, PARAM_VALUE_STOPS, marks);
4409
+ }
4410
+ }
4411
+ }
4412
+ function mark_shapes(str, marks) {
4413
+ for (let p = 0; p < str.length; p++) {
4414
+ const code = str.charCodeAt(p);
4415
+ if (code !== 101 && code !== 98 && code !== 66) continue;
4416
+ const [offset, n] = shape_len(str, p);
4417
+ if (n) mark_range(marks, p + offset, p + offset + n);
4418
+ }
4419
+ }
4420
+ function mark_labels(str, marks) {
4421
+ let word = 0;
4422
+ let p = 0;
4423
+ while (p < str.length) {
4424
+ const code = str.charCodeAt(p);
4425
+ if (code === 32 || code === 9 || code === 0 || code === 61) {
4426
+ p++;
4427
+ word = p;
4428
+ continue;
4429
+ }
4430
+ if (code !== 58) {
4431
+ p++;
4432
+ continue;
4433
+ }
4434
+ if (!is_secret_name_tail(str, word, p, NAMES_NOW)) {
4435
+ p++;
4436
+ word = p;
4437
+ continue;
4438
+ }
4439
+ let v = p + 1;
4440
+ while (v < str.length && (str[v] === " " || str[v] === " ")) v++;
4441
+ const e = colon_value_end(str, v);
4442
+ mark_range(marks, v, e);
4443
+ const n_scheme = scheme_word_len(str, v);
4444
+ if (n_scheme) p = v + n_scheme;
4445
+ else p = e > v ? e : p + 1;
4446
+ word = p;
4447
+ }
4448
+ }
4449
+ function inline_value_is_secret(str, names) {
4450
+ let p = str.indexOf("attribute=");
4451
+ while (p >= 0) {
4452
+ if (p === 0 || str[p - 1] === " " || str[p - 1] === " ") {
4453
+ let v = p + 10;
4454
+ if (v < str.length && (str[v] === "\"" || str[v] === "'")) v++;
4455
+ let e = v;
4456
+ while (e < str.length && " \"'\0".indexOf(str[e]) < 0) e++;
4457
+ if (is_secret_name_tail(str, v, e, names)) return true;
4458
+ }
4459
+ p = str.indexOf("attribute=", p + 1);
4460
+ }
4461
+ return false;
4462
+ }
4463
+ function mark_inline_secrets(str, marks) {
4464
+ const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4465
+ const value_is_secret_7_26_9 = inline_value_is_secret(str, NAMES_7_26_9);
4466
+ const judged = new Uint8Array(str.length);
4467
+ 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);
4468
+ if (has_shape_js_7_26_20(str)) mark_params(str, value_is_secret, READ_AS_GOBJ_JS_7_26_20, marks, null);
4469
+ mark_query_params(str, marks, judged);
4470
+ mark_shapes(str, marks);
4471
+ mark_labels(str, marks);
4472
+ }
4473
+ /************************************************************
4474
+ * A text (a command line) with the secrets that can be told
4475
+ * written as "********" (each run of them as one), the rest as it
4476
+ * is: the value of every "name=value" whose name is a secret's,
4477
+ * quoted or not (and the "value" of a write-attr whose attribute
4478
+ * names a secret) -- the name is the word before the '=', and also
4479
+ * the run of name characters before it, as in a query string; the
4480
+ * same for a "name: value", its name in quotes or not (a json, a
4481
+ * header, a label: "password": x, Authorization: Basic x, bearer:
4482
+ * tok); and a JWT or the credential after "Bearer " or "Basic ",
4483
+ * wherever they are, whatever names them. Each rule judges the text
4484
+ * as it came. Return null when there was nothing to mask. As the C
4485
+ * kernel's mask_secrets_inline().
4486
+ ************************************************************/
4487
+ function mask_secrets_inline(str) {
4488
+ if (typeof str !== "string") return null;
4489
+ if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4490
+ if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
4491
+ const marks = new Uint8Array(str.length);
4492
+ mark_inline_secrets(str, marks);
4493
+ let parts = [];
4494
+ let changed = false;
4495
+ let i = 0;
4496
+ while (i < str.length) {
4497
+ let j = i;
4498
+ if (marks[i]) {
4499
+ while (j < str.length && marks[j]) j++;
3638
4500
  parts.push("********");
3639
- copied = end;
3640
4501
  changed = true;
4502
+ } else {
4503
+ while (j < str.length && !marks[j]) j++;
4504
+ parts.push(str.substring(i, j));
3641
4505
  }
3642
- p = end;
3643
- word = p;
4506
+ i = j;
3644
4507
  }
3645
- if (!changed) return null;
3646
- parts.push(str.substring(copied));
3647
- return parts.join("");
4508
+ return changed ? parts.join("") : null;
3648
4509
  }
3649
4510
  /************************************************************
3650
4511
  * A json as a log or a trace may show it: at any depth, the
@@ -3712,7 +4573,12 @@
3712
4573
  if (truncated) result.push(MASK_TOO_LARGE);
3713
4574
  } else {
3714
4575
  result = Object.create(Object.getPrototypeOf(jn));
3715
- let value_is_secret = typeof jn.attribute === "string" && is_secret_name(jn.attribute.slice(-128));
4576
+ let value_is_secret = false;
4577
+ if (typeof jn.attribute === "string") {
4578
+ let nul = jn.attribute.indexOf("\0");
4579
+ let attribute = nul >= 0 ? jn.attribute.substring(0, nul) : jn.attribute;
4580
+ value_is_secret = is_secret_name_tail(attribute, 0, attribute.length, NAMES_NOW);
4581
+ }
3716
4582
  for (const key in jn) {
3717
4583
  if (!Object.prototype.hasOwnProperty.call(jn, key)) continue;
3718
4584
  if (budget.left <= 0) {
@@ -3722,7 +4588,7 @@
3722
4588
  budget.left -= 1 + key.length;
3723
4589
  let value = jn[key];
3724
4590
  let shown;
3725
- if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
4591
+ if ((is_secret_name_tail(key, 0, key.length, NAMES_NOW) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3726
4592
  else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3727
4593
  if (shown !== value) changed = true;
3728
4594
  set_own_key(result, key, shown);
@@ -6871,8 +7737,12 @@
6871
7737
  ***************************************************************/
6872
7738
  function mt_create$2(gobj) {
6873
7739
  let priv = gobj.priv;
6874
- const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
7740
+ let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
6875
7741
  if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
7742
+ else if (gobj_is_pure_child(gobj)) {
7743
+ subscriber = gobj_parent(gobj);
7744
+ gobj_subscribe_event(gobj, null, {}, subscriber);
7745
+ }
6876
7746
  priv.periodic = gobj_read_bool_attr(gobj, "periodic");
6877
7747
  priv.msec = gobj_read_integer_attr(gobj, "msec");
6878
7748
  }
@@ -6929,8 +7799,7 @@
6929
7799
  gobj_send_event(gobj, "EV_TIMEOUT", {}, gobj);
6930
7800
  }, priv.msec);
6931
7801
  else priv.timer_id = -1;
6932
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), ev, {}, gobj);
6933
- else gobj_publish_event(gobj, ev, {});
7802
+ gobj_publish_event(gobj, ev, {});
6934
7803
  if (!priv.periodic && priv.timer_id === -1) {
6935
7804
  if (gobj_is_running(gobj)) gobj_stop(gobj);
6936
7805
  }
@@ -6977,6 +7846,15 @@
6977
7846
  * Helpers
6978
7847
  ***************************/
6979
7848
  /***************************************************************************
7849
+ * A number of milliseconds as the integer `msec` holds it: C takes an
7850
+ * integer, and a time computed in JS can carry a float error (0.3 * 1000
7851
+ * is 300.00000000000006, which an integer attr refuses). Anything that is
7852
+ * not a number goes as it is, to be refused loudly by the attr.
7853
+ ***************************************************************************/
7854
+ function whole_msec(msec) {
7855
+ return typeof msec === "number" && Number.isFinite(msec) ? Math.round(msec) : msec;
7856
+ }
7857
+ /***************************************************************************
6980
7858
  * Set timeout
6981
7859
  ***************************************************************************/
6982
7860
  function set_timeout(gobj, msec) {
@@ -6989,7 +7867,7 @@
6989
7867
  return -1;
6990
7868
  }
6991
7869
  gobj_write_bool_attr(gobj, "periodic", false);
6992
- gobj_write_integer_attr(gobj, "msec", msec);
7870
+ gobj_write_integer_attr(gobj, "msec", whole_msec(msec));
6993
7871
  }
6994
7872
  /***************************************************************************
6995
7873
  * Set periodic timeout
@@ -7004,7 +7882,7 @@
7004
7882
  return -1;
7005
7883
  }
7006
7884
  gobj_write_bool_attr(gobj, "periodic", true);
7007
- gobj_write_integer_attr(gobj, "msec", msec);
7885
+ gobj_write_integer_attr(gobj, "msec", whole_msec(msec));
7008
7886
  }
7009
7887
  /***************************************************************************
7010
7888
  * Clear timeout
@@ -7391,8 +8269,12 @@
7391
8269
  gobj_write_str_attr(gobj, "wanted_yuno_name", priv.remote_yuno_name);
7392
8270
  gobj_write_str_attr(gobj, "wanted_yuno_role", priv.remote_yuno_role);
7393
8271
  gobj_write_str_attr(gobj, "wanted_yuno_service", priv.remote_yuno_service);
7394
- const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
8272
+ let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
7395
8273
  if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
8274
+ else if (gobj_is_pure_child(gobj)) {
8275
+ subscriber = gobj_parent(gobj);
8276
+ gobj_subscribe_event(gobj, null, {}, subscriber);
8277
+ }
7396
8278
  }
7397
8279
  /***************************************************************
7398
8280
  * Framework Method: Writing
@@ -7617,6 +8499,27 @@
7617
8499
  }
7618
8500
  }
7619
8501
  /***************************************************************
8502
+ * A text of the server -- a frame, the name of an event, of a
8503
+ * service, of a role -- as a log shows it: masked, then capped on a
8504
+ * whole character (capped first, a credential the cap cut would
8505
+ * show its first characters). As the C kernel shows the name of an
8506
+ * event that is not public (mask_secrets_inline_capped()).
8507
+ ***************************************************************/
8508
+ var PEER_NAME_SHOWN = 64;
8509
+ var PEER_FRAME_SHOWN = 256;
8510
+ function peer_text_shown(text, max) {
8511
+ let s = String(text);
8512
+ let masked = mask_secrets_inline(s);
8513
+ if (masked !== null) s = masked;
8514
+ if (s.length > max) {
8515
+ let n = max;
8516
+ let code = s.charCodeAt(n);
8517
+ if (code >= 56320 && code <= 57343) n--;
8518
+ s = s.substring(0, n);
8519
+ }
8520
+ return s;
8521
+ }
8522
+ /***************************************************************
7620
8523
  * Trace intra event
7621
8524
  ***************************************************************/
7622
8525
  function trace_inter_event(gobj, prefix, iev) {
@@ -7739,7 +8642,8 @@
7739
8642
  try {
7740
8643
  x = JSON.parse(data);
7741
8644
  } catch (e) {
7742
- log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
8645
+ let at = /position (\d+)/.exec(String(e && e.message));
8646
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: frame is not json${at ? ", at position " + at[1] : ""}, ${String(data).length} characters`);
7743
8647
  return null;
7744
8648
  }
7745
8649
  if (!(x instanceof Object)) {
@@ -7824,7 +8728,7 @@
7824
8728
  let url = kw.url;
7825
8729
  if (kw.ws_seq !== void 0 && kw.ws_seq !== priv.ws_seq) return 0;
7826
8730
  set_remote_log_functions();
7827
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
8731
+ log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${peer_text_shown(kw.reason, PEER_FRAME_SHOWN)}", Clean: ${kw.was_clean})`);
7828
8732
  if (priv.websocket) {
7829
8733
  priv.websocket.onopen = null;
7830
8734
  priv.websocket.onmessage = null;
@@ -7909,7 +8813,7 @@
7909
8813
  let services_roles = kw_get_dict_value(gobj, kw, "services_roles", {});
7910
8814
  if (result < 0) {
7911
8815
  close_websocket(gobj);
7912
- let kw_nak = {
8816
+ gobj_publish_event(gobj, "EV_ON_ID_NAK", {
7913
8817
  "url": gobj_read_str_attr(gobj, "url"),
7914
8818
  "result": result,
7915
8819
  "comment": comment,
@@ -7917,24 +8821,20 @@
7917
8821
  "remote_yuno_name": src_yuno,
7918
8822
  "remote_yuno_role": src_role,
7919
8823
  "remote_yuno_service": src_service
7920
- };
7921
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), "EV_ON_ID_NAK", kw_nak, gobj);
7922
- else gobj_publish_event(gobj, "EV_ON_ID_NAK", kw_nak);
8824
+ });
7923
8825
  } else {
7924
8826
  gobj_change_state(gobj, "ST_SESSION");
7925
8827
  priv.inside_on_open = true;
7926
8828
  priv.retry_ms = 0;
7927
8829
  if (!priv.inform_on_close) {
7928
8830
  priv.inform_on_close = true;
7929
- let kw_on_open = {
8831
+ gobj_publish_event(gobj, "EV_ON_OPEN", {
7930
8832
  "username": username_,
7931
8833
  "remote_yuno_name": src_yuno,
7932
8834
  "remote_yuno_role": src_role,
7933
8835
  "remote_yuno_service": src_service,
7934
8836
  "services_roles": services_roles
7935
- };
7936
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), "EV_ON_OPEN", kw_on_open, gobj);
7937
- else gobj_publish_event(gobj, "EV_ON_OPEN", kw_on_open);
8837
+ });
7938
8838
  }
7939
8839
  priv.inside_on_open = false;
7940
8840
  resend_subscriptions(gobj);
@@ -7948,7 +8848,7 @@
7948
8848
  let url = gobj.priv.url;
7949
8849
  let iev_msg = iev_create_from_json(gobj, kw.data);
7950
8850
  if (!iev_msg) {
7951
- log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
8851
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${peer_text_shown(kw.data, PEER_FRAME_SHOWN)}`);
7952
8852
  return -1;
7953
8853
  }
7954
8854
  if (is_traffic_tracing(gobj)) {
@@ -7963,7 +8863,7 @@
7963
8863
  if (gobj_current_state(gobj) !== "ST_SESSION") {
7964
8864
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7965
8865
  if (!(gobj_send_event(gobj, iev_event, iev_kw, gobj) < 0)) return 0;
7966
- } else log_error(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${iev_event}`);
8866
+ } else log_error(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
7967
8867
  close_websocket(gobj);
7968
8868
  return -1;
7969
8869
  }
@@ -7972,16 +8872,16 @@
7972
8872
  let event_id = msg_iev_get_stack(gobj, iev_kw, IEVENT_STACK_ID);
7973
8873
  let iev_dst_role = kw_get_str(gobj, event_id, "dst_role", "");
7974
8874
  if (iev_dst_role.toLowerCase() !== gobj_yuno_role().toLowerCase()) {
7975
- log_error(`"It's not my role, dst_role: ${iev_dst_role}, my_role: ${gobj_yuno_role()}`);
8875
+ log_error(`"It's not my role, dst_role: ${peer_text_shown(iev_dst_role, PEER_NAME_SHOWN)}, my_role: ${gobj_yuno_role()}`);
7976
8876
  return 0;
7977
8877
  }
7978
8878
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
7979
8879
  if (msg_type === "__subscribing__") {
7980
- log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
8880
+ 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)})`);
7981
8881
  return -1;
7982
8882
  }
7983
8883
  if (msg_type === "__unsubscribing__") {
7984
- log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
8884
+ 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)})`);
7985
8885
  return -1;
7986
8886
  }
7987
8887
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
@@ -7991,18 +8891,17 @@
7991
8891
  if (!empty_string(iev_dst_service)) {
7992
8892
  let gobj_service = gobj_find_service(iev_dst_service, false);
7993
8893
  if (!gobj_service) {
7994
- log_warning(`${gobj_short_name(gobj)}: event for a service that is gone, DROPPED: service '${iev_dst_service}', ev '${iev_event}'`);
8894
+ 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)}'`);
7995
8895
  return 0;
7996
8896
  }
7997
8897
  if (!gobj_has_event(gobj_service, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7998
- log_error(`${gobj_short_name(gobj)}: Service found but no event or not public, service '${gobj_short_name(gobj_service)}', ev '${iev_event}'`);
8898
+ 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)}'`);
7999
8899
  return -1;
8000
8900
  }
8001
8901
  gobj_send_event(gobj_service, iev_event, iev_kw, gobj);
8002
8902
  return 0;
8003
8903
  }
8004
- if (gobj_is_pure_child(gobj)) gobj_send_event(gobj_parent(gobj), iev_event, iev_kw, gobj);
8005
- else gobj_publish_event(gobj, iev_event, iev_kw);
8904
+ gobj_publish_event(gobj, iev_event, iev_kw);
8006
8905
  return 0;
8007
8906
  }
8008
8907
  /***************************************************************************
@@ -8164,6 +9063,8 @@
8164
9063
  exports.clean_name = clean_name;
8165
9064
  exports.clear_timeout = clear_timeout;
8166
9065
  exports.cmp_two_simple_json = cmp_two_simple_json;
9066
+ exports.command_parameter_from_json = command_parameter_from_json;
9067
+ exports.command_parameter_from_text = command_parameter_from_text;
8167
9068
  exports.command_parser = command_parser;
8168
9069
  exports.createElement2 = createElement2;
8169
9070
  exports.createOneHtml = createOneHtml;