@yuneta/gobj-js 7.26.23 → 7.26.25

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.
@@ -867,6 +867,112 @@ var gobjJs = (function(exports) {
867
867
  var INT64_MIN = -(2n ** 63n);
868
868
  var INT64_MAX = 2n ** 63n - 1n;
869
869
  var REFUSED_TEXT = "a text that does not read as one";
870
+ var JSON_NUMBER_TOKEN = /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/y;
871
+ function is_surrogate_pair(high, low) {
872
+ return high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343;
873
+ }
874
+ var JSON_MAX_DEPTH = 2048;
875
+ function json_text_refused_by_c(s) {
876
+ const n = s.length;
877
+ let i = 0;
878
+ let depth = 0;
879
+ const in_object = [];
880
+ let key_next = false;
881
+ while (i < n) {
882
+ const c = s.charCodeAt(i);
883
+ if (c === 123 || c === 91) {
884
+ depth++;
885
+ if (depth > JSON_MAX_DEPTH) return true;
886
+ in_object.push(c === 123);
887
+ key_next = c === 123;
888
+ i++;
889
+ continue;
890
+ }
891
+ if (c === 125 || c === 93) {
892
+ depth--;
893
+ in_object.pop();
894
+ key_next = false;
895
+ i++;
896
+ continue;
897
+ }
898
+ if (c === 44) {
899
+ key_next = in_object.length > 0 && in_object[in_object.length - 1];
900
+ i++;
901
+ continue;
902
+ }
903
+ if (c === 58) {
904
+ key_next = false;
905
+ i++;
906
+ continue;
907
+ }
908
+ if (c === 116 || c === 102 || c === 110) {
909
+ if (depth + 1 > JSON_MAX_DEPTH) return true;
910
+ i += c === 102 ? 5 : 4;
911
+ continue;
912
+ }
913
+ if (c === 34) {
914
+ if (!key_next && depth + 1 > JSON_MAX_DEPTH) return true;
915
+ key_next = false;
916
+ i++;
917
+ while (i < n) {
918
+ const d = s.charCodeAt(i);
919
+ if (d === 34) {
920
+ i++;
921
+ break;
922
+ }
923
+ if (d === 92 && s[i + 1] === "u") {
924
+ const u = parseInt(s.substring(i + 2, i + 6), 16);
925
+ if (u === 0) return true;
926
+ if (u >= 55296 && u <= 57343) {
927
+ if (!is_surrogate_pair(u, s[i + 6] === "\\" && s[i + 7] === "u" ? parseInt(s.substring(i + 8, i + 12), 16) : -1)) return true;
928
+ i += 12;
929
+ continue;
930
+ }
931
+ i += 6;
932
+ continue;
933
+ }
934
+ if (d === 92) {
935
+ i += 2;
936
+ continue;
937
+ }
938
+ if (d >= 55296 && d <= 57343) {
939
+ if (!is_surrogate_pair(d, s.charCodeAt(i + 1))) return true;
940
+ i += 2;
941
+ continue;
942
+ }
943
+ i++;
944
+ }
945
+ continue;
946
+ }
947
+ if (c === 45 || c >= 48 && c <= 57) {
948
+ if (depth + 1 > JSON_MAX_DEPTH) return true;
949
+ JSON_NUMBER_TOKEN.lastIndex = i;
950
+ const m = JSON_NUMBER_TOKEN.exec(s);
951
+ if (!m) return true;
952
+ const token = m[0];
953
+ if (/[.eE]/.test(token)) {
954
+ if (!Number.isFinite(Number(token))) return true;
955
+ } else {
956
+ const big = BigInt(token);
957
+ if (big < INT64_MIN || big > INT64_MAX) return true;
958
+ }
959
+ i += token.length;
960
+ continue;
961
+ }
962
+ i++;
963
+ }
964
+ return false;
965
+ }
966
+ function parse_json_text_as_c(s) {
967
+ let jn;
968
+ try {
969
+ jn = JSON.parse(s);
970
+ } catch (e) {
971
+ return;
972
+ }
973
+ if (json_text_refused_by_c(s)) return;
974
+ return jn;
975
+ }
870
976
  function value_has_shape(type, value) {
871
977
  if (DTP_IS_STRING(type)) return is_string(value);
872
978
  if (DTP_IS_DICT(type)) return is_object(value);
@@ -878,7 +984,7 @@ var gobjJs = (function(exports) {
878
984
  if (is_array(value)) return "an array";
879
985
  if (is_object(value)) return "an object";
880
986
  if (is_string(value)) return REFUSED_TEXT;
881
- if (typeof value === "number") return Number.isInteger(value) ? "an integer" : "a real";
987
+ if (typeof value === "number") return Number.isInteger(value) && Math.abs(value) <= 0x8000000000000000 ? "an integer" : "a real";
882
988
  if (typeof value === "boolean") return "a boolean";
883
989
  return "null";
884
990
  }
@@ -899,7 +1005,7 @@ var gobjJs = (function(exports) {
899
1005
  value: value ? 1 : 0,
900
1006
  refused: null
901
1007
  };
902
- if (!(value >= -0x8000000000000000 && value < 0x8000000000000000)) return {
1008
+ if (!(value >= -0x8000000000000000 && value <= 0x8000000000000000)) return {
903
1009
  value: void 0,
904
1010
  refused: "a number out of its range"
905
1011
  };
@@ -970,16 +1076,8 @@ var gobjJs = (function(exports) {
970
1076
  };
971
1077
  }
972
1078
  if (DTP_IS_DICT(type) || DTP_IS_LIST(type)) {
973
- let jn;
974
- try {
975
- jn = JSON.parse(s);
976
- } catch (e) {
977
- return {
978
- value: void 0,
979
- refused: REFUSED_TEXT
980
- };
981
- }
982
- if (!value_has_shape(type, jn)) return {
1079
+ const jn = parse_json_text_as_c(s);
1080
+ if (jn === void 0 || !value_has_shape(type, jn)) return {
983
1081
  value: void 0,
984
1082
  refused: REFUSED_TEXT
985
1083
  };
@@ -988,12 +1086,12 @@ var gobjJs = (function(exports) {
988
1086
  refused: null
989
1087
  };
990
1088
  }
991
- if (DTP_IS_JSON(type)) try {
992
- return {
993
- value: JSON.parse(s),
1089
+ if (DTP_IS_JSON(type)) {
1090
+ const jn = parse_json_text_as_c(s);
1091
+ if (jn !== void 0) return {
1092
+ value: jn,
994
1093
  refused: null
995
1094
  };
996
- } catch (e) {
997
1095
  const t = s.replace(/^[ \t]+/, "");
998
1096
  if (t.startsWith("{") || t.startsWith("[") || t.startsWith("\"")) return {
999
1097
  value: void 0,
@@ -1068,6 +1166,10 @@ var gobjJs = (function(exports) {
1068
1166
  refused: shape_of_value(value)
1069
1167
  };
1070
1168
  if (DTP_IS_NUMBER(type)) return number_as_type(type, value);
1169
+ if (!is_json_value(value)) return {
1170
+ value: void 0,
1171
+ refused: NO_JSON_TEXT
1172
+ };
1071
1173
  return {
1072
1174
  value,
1073
1175
  refused: null
@@ -1086,8 +1188,44 @@ var gobjJs = (function(exports) {
1086
1188
  data_type_t.DTP_LIST,
1087
1189
  data_type_t.DTP_DICT
1088
1190
  ];
1191
+ var LEAVE_NODE = Symbol("leave");
1192
+ function is_json_value(value) {
1193
+ const on_path = /* @__PURE__ */ new Set();
1194
+ const stack = [value];
1195
+ while (stack.length) {
1196
+ const v = stack.pop();
1197
+ if (v === LEAVE_NODE) {
1198
+ on_path.delete(stack.pop());
1199
+ continue;
1200
+ }
1201
+ if (typeof v === "bigint") return false;
1202
+ if (v === null || typeof v !== "object") continue;
1203
+ if (on_path.has(v)) return false;
1204
+ const array = Array.isArray(v);
1205
+ if (!array) {
1206
+ const proto = Object.getPrototypeOf(v);
1207
+ if (proto !== Object.prototype && proto !== null) return false;
1208
+ }
1209
+ on_path.add(v);
1210
+ stack.push(v, LEAVE_NODE);
1211
+ if (array) for (let i = 0; i < v.length; i++) stack.push(v[i]);
1212
+ else for (const key of Object.keys(v)) stack.push(v[key]);
1213
+ }
1214
+ return true;
1215
+ }
1216
+ var NO_JSON_TEXT = "a value with no json text";
1217
+ function json_text_of(value) {
1218
+ if (!is_json_value(value)) return;
1219
+ try {
1220
+ const s = JSON.stringify(value);
1221
+ return is_string(s) ? s : void 0;
1222
+ } catch (e) {
1223
+ return;
1224
+ }
1225
+ }
1089
1226
  function log_attr_value_refused(gobj, it, value, refused) {
1090
1227
  const types = {
1228
+ [data_type_t.DTP_STRING]: "a string",
1091
1229
  [data_type_t.DTP_BOOLEAN]: "a boolean",
1092
1230
  [data_type_t.DTP_INTEGER]: "an integer",
1093
1231
  [data_type_t.DTP_REAL]: "a real",
@@ -1097,7 +1235,8 @@ var gobjJs = (function(exports) {
1097
1235
  let shown;
1098
1236
  if (it.flag & sdata_flag_t.SDF_SECRET) shown = "********";
1099
1237
  else {
1100
- shown = is_string(value) ? value : String(JSON.stringify(value === void 0 ? null : value));
1238
+ shown = is_string(value) ? value : json_text_of(value === void 0 ? null : value);
1239
+ if (shown === void 0) shown = `<${NO_JSON_TEXT}>`;
1101
1240
  const masked = mask_secrets_inline(shown);
1102
1241
  if (masked !== null) shown = masked;
1103
1242
  if (shown.length > ATTR_VALUE_SHOWN) {
@@ -1119,8 +1258,11 @@ var gobjJs = (function(exports) {
1119
1258
  if (jn_value_ === null || jn_value_ === void 0) jn_value2 = "";
1120
1259
  else if (is_string(jn_value_)) jn_value2 = String(jn_value_);
1121
1260
  else {
1122
- let s = JSON.stringify(jn_value_);
1123
- if (s) jn_value2 = String(s);
1261
+ jn_value2 = json_text_of(jn_value_);
1262
+ if (jn_value2 === void 0) {
1263
+ log_attr_value_refused(gobj, it, jn_value_, NO_JSON_TEXT);
1264
+ return -1;
1265
+ }
1124
1266
  }
1125
1267
  break;
1126
1268
  case data_type_t.DTP_BOOLEAN:
@@ -2568,15 +2710,20 @@ var gobjJs = (function(exports) {
2568
2710
  * The value a typed writer writes, read as ITS type by the grammar of
2569
2711
  * json2item() (a C writer takes its C type: a string is never read as a
2570
2712
  * number there). Return {value} or {refused} (logged): the attr keeps its
2571
- * value. A string writer never refuses: null and undefined are "", any
2572
- * other json its text, as a DTP_STRING attr reads them.
2713
+ * value. A string writer takes null and undefined as "", any other json
2714
+ * as its text, as a DTP_STRING attr reads them; it refuses only a value
2715
+ * with no json text (circular, a BigInt).
2573
2716
  ***************************************************************************/
2574
2717
  function typed_writer_value(gobj, name, type, value) {
2718
+ let r;
2575
2719
  if (DTP_IS_STRING(type)) {
2576
2720
  if (value === null || value === void 0) return { value: "" };
2577
- return { value: is_string(value) ? value : String(JSON.stringify(value)) };
2578
- }
2579
- const r = command_parameter_from_json(type, value);
2721
+ if (is_string(value)) return { value };
2722
+ r = {
2723
+ value: json_text_of(value),
2724
+ refused: NO_JSON_TEXT
2725
+ };
2726
+ } else r = command_parameter_from_json(type, value);
2580
2727
  if (r.value === void 0) {
2581
2728
  let holder = gobj;
2582
2729
  while (holder && !gobj_has_attr(holder, name)) holder = holder.bottom_gobj;
@@ -3021,9 +3168,9 @@ var gobjJs = (function(exports) {
3021
3168
  }
3022
3169
  }
3023
3170
  const frame = push_dispatch_frame(dst, event, frame_kind_t.FRAME_ACTION);
3024
- if (event_action.next_state) gobj_change_state(dst, event_action.next_state);
3025
3171
  let ret = -1;
3026
3172
  try {
3173
+ if (event_action.next_state) gobj_change_state(dst, event_action.next_state);
3027
3174
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
3028
3175
  } catch (e) {
3029
3176
  __inside__--;
@@ -3047,18 +3194,23 @@ var gobjJs = (function(exports) {
3047
3194
  __own_event__: 4
3048
3195
  });
3049
3196
  /***************************************************************************
3050
- * The event type of `event_name` in any registered gclass, null if none.
3051
- * The C kernel's gobj_find_event_type(): any case, gclasses only.
3197
+ * The event type of `event_name` in any registered gclass whose flags
3198
+ * hold one of `event_flag` (0: any), null if none. The C kernel's
3199
+ * gobj_find_event_type(): any case, gclasses only. With `verbose` a miss
3200
+ * is logged, with the name as it was given: never verbose for a name
3201
+ * that came from a peer.
3052
3202
  ***************************************************************************/
3053
- function _find_event_type_in_gclasses(event_name) {
3203
+ function gobj_find_event_type(event_name, event_flag, verbose) {
3054
3204
  let lower = String(event_name).toLowerCase();
3055
3205
  for (let gclass_name in _gclass_register) {
3056
3206
  let dl = _gclass_register[gclass_name].dl_events;
3057
3207
  for (let i = 0; i < dl.length; i++) {
3058
3208
  let event_type = dl[i];
3209
+ if (event_flag && !(event_type.event_flag & event_flag)) continue;
3059
3210
  if (event_type.event_name && event_type.event_name.toLowerCase() === lower) return event_type;
3060
3211
  }
3061
3212
  }
3213
+ if (verbose) log_error(`EVENT NOT FOUND: ${event_name}`);
3062
3214
  return null;
3063
3215
  }
3064
3216
  var subscription_desc = [
@@ -3095,7 +3247,7 @@ var gobjJs = (function(exports) {
3095
3247
  json_object_set_new(subs, "__config__", kw_clone);
3096
3248
  let renamed_event = kw_get_str(publisher, kw_clone, "__rename_event_name__", "");
3097
3249
  if (!empty_string(renamed_event)) {
3098
- let event_type = _find_event_type_in_gclasses(renamed_event);
3250
+ let event_type = gobj_find_event_type(renamed_event, 0, false);
3099
3251
  if (event_type) {
3100
3252
  json_object_set_new(subs, "renamed_event", event_type.event_name);
3101
3253
  delete kw_clone.__rename_event_name__;
@@ -3156,7 +3308,7 @@ var gobjJs = (function(exports) {
3156
3308
  kw_match.__config__ = config_match;
3157
3309
  let renamed_event = "";
3158
3310
  let rename = config_match.__rename_event_name__;
3159
- let event_type = typeof rename === "string" && !empty_string(rename) ? _find_event_type_in_gclasses(rename) : null;
3311
+ let event_type = typeof rename === "string" && !empty_string(rename) ? gobj_find_event_type(rename, 0, false) : null;
3160
3312
  if (event_type) {
3161
3313
  renamed_event = event_type.event_name;
3162
3314
  delete config_match.__rename_event_name__;
@@ -4022,6 +4174,35 @@ var gobjJs = (function(exports) {
4022
4174
  }
4023
4175
  return secret_name_check(str, i, to, true, by_segments);
4024
4176
  }
4177
+ function param_name_tail_from(str, from, to) {
4178
+ let bytes = 0;
4179
+ let i = to;
4180
+ while (i > from) {
4181
+ const code = str.charCodeAt(i - 1);
4182
+ let n = 3;
4183
+ let units = 1;
4184
+ if (code < 128) n = 1;
4185
+ else if (code < 2048) n = 2;
4186
+ else if (code >= 56320 && code <= 57343 && i - 2 >= from) {
4187
+ const high = str.charCodeAt(i - 2);
4188
+ if (high >= 55296 && high <= 56319) {
4189
+ n = 4;
4190
+ units = 2;
4191
+ }
4192
+ }
4193
+ if (bytes + n > MASK_MAX_NAME) return -1;
4194
+ if (code === 38 || code === 63 || code === 59) return i;
4195
+ bytes += n;
4196
+ i -= units;
4197
+ }
4198
+ return -1;
4199
+ }
4200
+ function is_param_name_secret(str, from, to, names) {
4201
+ const tail = names === NAMES_NOW ? param_name_tail_from(str, from, to) : -1;
4202
+ if (tail < 0) return is_secret_name_tail(str, from, to, names);
4203
+ if (is_secret_name_tail(str, from, to, NAMES_7_26_9)) return true;
4204
+ return is_secret_name_tail(str, from, to, NAMES_NOW) && secret_name_check(str, tail, to, false, true);
4205
+ }
4025
4206
  /************************************************************
4026
4207
  * Where an unquoted secret value that begins at `p` ends: at
4027
4208
  * the end, at the quote that closes an outer quoted value, or
@@ -4319,13 +4500,14 @@ var gobjJs = (function(exports) {
4319
4500
  }
4320
4501
  const name_from = word;
4321
4502
  word = p + 1;
4322
- const secret = is_secret_name_tail(str, name_from, p, names);
4323
- if (secret && names === NAMES_NOW && !is_secret_name_tail(str, name_from, p, NAMES_7_26_9)) not_7_26_9 = true;
4503
+ const secret = is_param_name_secret(str, name_from, p, names);
4504
+ if (secret && names === NAMES_NOW && !not_7_26_9 && !is_secret_name_tail(str, name_from, p, NAMES_7_26_9)) not_7_26_9 = true;
4324
4505
  if (!(secret || value_is_secret && p - name_from === 5 && ascii_case_equal(str, name_from, "value"))) {
4325
- if (judged && query_name_from(str, p) === name_from) judged[p] = 1;
4506
+ if (judged && query_name_from(str, p) === name_from) judged[p] = JUDGED_NOT_SECRET;
4326
4507
  p++;
4327
4508
  continue;
4328
4509
  }
4510
+ if (judged && secret && query_name_from(str, p) === name_from) judged[p] = JUDGED_SECRET;
4329
4511
  p++;
4330
4512
  let end;
4331
4513
  const quote = p < str.length && (str[p] === "\"" || str[p] === "'") ? str[p] : "";
@@ -4388,23 +4570,99 @@ var gobjJs = (function(exports) {
4388
4570
  while (n > 0 && eq - n < MASK_MAX_NAME && is_name_char_code(str.charCodeAt(n - 1))) n--;
4389
4571
  return n;
4390
4572
  }
4573
+ var JUDGED_NOT_SECRET = 1;
4574
+ var JUDGED_SECRET = 2;
4575
+ function hex_digit_value(code) {
4576
+ if (code >= 48 && code <= 57) return code - 48;
4577
+ if (code >= 97 && code <= 102) return code - 87;
4578
+ if (code >= 65 && code <= 70) return code - 55;
4579
+ return -1;
4580
+ }
4581
+ function is_written_name_bracket_code(code) {
4582
+ return code === 91 || code === 93 || code === 39 || code === 34;
4583
+ }
4584
+ function is_written_name_code(code) {
4585
+ return is_name_char_code(code) || code === 37 || is_written_name_bracket_code(code);
4586
+ }
4587
+ function written_name_is_secret(str, eq) {
4588
+ let n = eq;
4589
+ let plain = -1;
4590
+ while (n > 0 && eq - n < MASK_MAX_NAME && is_written_name_code(str.charCodeAt(n - 1))) {
4591
+ n--;
4592
+ if (plain < 0 && !is_name_char_code(str.charCodeAt(n))) plain = n + 1;
4593
+ }
4594
+ if (plain < 0) return false;
4595
+ const to = str.charCodeAt(plain - 1) === 37 ? eq : plain - 1;
4596
+ let encoded = false;
4597
+ for (let s = n; s < to && !encoded; s++) encoded = str.charCodeAt(s) === 37;
4598
+ let name = str;
4599
+ let from = n;
4600
+ let end = to;
4601
+ if (encoded) {
4602
+ name = "";
4603
+ for (let s = n; s < to; s++) {
4604
+ const hi = str.charCodeAt(s) === 37 && to - s > 2 ? hex_digit_value(str.charCodeAt(s + 1)) : -1;
4605
+ const lo = hi >= 0 ? hex_digit_value(str.charCodeAt(s + 2)) : -1;
4606
+ if (lo >= 0) {
4607
+ name += String.fromCharCode(hi * 16 + lo);
4608
+ s += 2;
4609
+ } else name += str[s];
4610
+ }
4611
+ from = 0;
4612
+ end = name.length;
4613
+ }
4614
+ let seg = from;
4615
+ for (let i = from; i <= end; i++) {
4616
+ if (i < end && !is_written_name_bracket_code(name.charCodeAt(i))) continue;
4617
+ if (i - seg >= 3 && secret_name_check(name, seg, i, true, true)) return true;
4618
+ seg = i + 1;
4619
+ }
4620
+ return false;
4621
+ }
4622
+ function mark_query_value(str, eq, marks) {
4623
+ const v = eq + 1;
4624
+ if (v < str.length && (str[v] === "\"" || str[v] === "'")) return mark_run(str, v + 1, QUOTE_STOPS[str[v]], marks);
4625
+ mark_scheme_value(str, v, PARAM_VALUE_STOPS, marks);
4626
+ return mark_run(str, v, PARAM_VALUE_STOPS, marks);
4627
+ }
4628
+ var WRITTEN_NAME_MARKS = /[[\]%'"]/;
4391
4629
  function mark_query_params(str, marks, judged) {
4392
4630
  let frontier = 0;
4393
- for (let p = 1; p < str.length; p++) {
4394
- if (p < frontier) {
4395
- p = frontier - 1;
4396
- continue;
4397
- }
4398
- if (str.charCodeAt(p) !== 61) continue;
4399
- if (judged[p]) continue;
4400
- const n = query_name_from(str, p);
4401
- if (n === p || !secret_name_check(str, n, p, true, true)) continue;
4402
- const v = p + 1;
4403
- if (v < str.length && (str[v] === "\"" || str[v] === "'")) frontier = mark_run(str, v + 1, QUOTE_STOPS[str[v]], marks);
4404
- else {
4405
- mark_scheme_value(str, v, PARAM_VALUE_STOPS, marks);
4406
- frontier = mark_run(str, v, PARAM_VALUE_STOPS, marks);
4631
+ let frontier_written = WRITTEN_NAME_MARKS.test(str) ? 0 : str.length;
4632
+ let p = str.indexOf("=", 1);
4633
+ while (p > 0) {
4634
+ if (p >= frontier) {
4635
+ let secret = judged[p] === JUDGED_SECRET;
4636
+ if (!judged[p]) {
4637
+ const n = query_name_from(str, p);
4638
+ secret = n < p && secret_name_check(str, n, p, true, true);
4639
+ }
4640
+ if (secret) frontier = mark_query_value(str, p, marks);
4641
+ }
4642
+ if (p >= frontier_written && written_name_is_secret(str, p)) frontier_written = mark_query_value(str, p, marks);
4643
+ p = str.indexOf("=", p + 1);
4644
+ }
4645
+ }
4646
+ var URL_AUTHORITY_STOPS = stop_table("/?#\"'<> ");
4647
+ function mark_url_passwords(str, marks) {
4648
+ let p = str.indexOf("://", 1);
4649
+ while (p > 0) {
4650
+ let next = p + 1;
4651
+ if (is_ascii_alnum_code(str.charCodeAt(p - 1))) {
4652
+ let colon = -1;
4653
+ let at = -1;
4654
+ let e = p + 3;
4655
+ while (e < str.length) {
4656
+ const code = str.charCodeAt(e);
4657
+ if (code < 128 && URL_AUTHORITY_STOPS[code]) break;
4658
+ if (code === 64) at = e;
4659
+ else if (code === 58 && colon < 0) colon = e;
4660
+ e++;
4661
+ }
4662
+ if (colon >= 0 && at > colon) mark_range(marks, colon + 1, at);
4663
+ next = e - 1;
4407
4664
  }
4665
+ p = str.indexOf("://", next);
4408
4666
  }
4409
4667
  }
4410
4668
  function mark_shapes(str, marks) {
@@ -4429,7 +4687,7 @@ var gobjJs = (function(exports) {
4429
4687
  p++;
4430
4688
  continue;
4431
4689
  }
4432
- if (!is_secret_name_tail(str, word, p, NAMES_NOW)) {
4690
+ if (!is_param_name_secret(str, word, p, NAMES_NOW)) {
4433
4691
  p++;
4434
4692
  word = p;
4435
4693
  continue;
@@ -4458,6 +4716,50 @@ var gobjJs = (function(exports) {
4458
4716
  }
4459
4717
  return false;
4460
4718
  }
4719
+ function mark_inline_rules_of_now(str, marks) {
4720
+ const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4721
+ const judged = new Uint8Array(str.length);
4722
+ mark_params(str, value_is_secret, READ_NOW, marks, judged);
4723
+ mark_query_params(str, marks, judged);
4724
+ mark_url_passwords(str, marks);
4725
+ mark_shapes(str, marks);
4726
+ mark_labels(str, marks);
4727
+ }
4728
+ var MASK_MAX_DECODINGS = 2;
4729
+ function encoded_ascii_at(str, p) {
4730
+ if (p + 3 > str.length || str.charCodeAt(p) !== 37) return -1;
4731
+ const hi = hex_digit_value(str.charCodeAt(p + 1));
4732
+ const lo = hi >= 0 ? hex_digit_value(str.charCodeAt(p + 2)) : -1;
4733
+ if (lo < 0) return -1;
4734
+ const c = hi * 16 + lo;
4735
+ return c > 0 && c < 128 ? c : -1;
4736
+ }
4737
+ function mark_decoded_view(str, marks, decodings) {
4738
+ if (decodings >= MASK_MAX_DECODINGS) return;
4739
+ const pieces = [];
4740
+ let last = 0;
4741
+ let p = str.indexOf("%");
4742
+ while (p >= 0) {
4743
+ const c = encoded_ascii_at(str, p);
4744
+ if (c >= 0) {
4745
+ pieces.push(str.substring(last, p), String.fromCharCode(c));
4746
+ last = p + 3;
4747
+ p = str.indexOf("%", last);
4748
+ } else p = str.indexOf("%", p + 1);
4749
+ }
4750
+ if (pieces.length === 0) return;
4751
+ pieces.push(str.substring(last));
4752
+ const view = pieces.join("");
4753
+ const view_marks = new Uint8Array(view.length);
4754
+ mark_inline_rules_of_now(view, view_marks);
4755
+ mark_decoded_view(view, view_marks, decodings + 1);
4756
+ let q = 0;
4757
+ for (let i = 0; i < view.length; i++) {
4758
+ const n = encoded_ascii_at(str, q) >= 0 ? 3 : 1;
4759
+ if (view_marks[i]) mark_range(marks, q, q + n);
4760
+ q += n;
4761
+ }
4762
+ }
4461
4763
  function mark_inline_secrets(str, marks) {
4462
4764
  const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4463
4765
  const value_is_secret_7_26_9 = inline_value_is_secret(str, NAMES_7_26_9);
@@ -4465,8 +4767,10 @@ var gobjJs = (function(exports) {
4465
4767
  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);
4466
4768
  if (has_shape_js_7_26_20(str)) mark_params(str, value_is_secret, READ_AS_GOBJ_JS_7_26_20, marks, null);
4467
4769
  mark_query_params(str, marks, judged);
4770
+ mark_url_passwords(str, marks);
4468
4771
  mark_shapes(str, marks);
4469
4772
  mark_labels(str, marks);
4773
+ mark_decoded_view(str, marks, 0);
4470
4774
  }
4471
4775
  /************************************************************
4472
4776
  * A text (a command line) with the secrets that can be told
@@ -4478,13 +4782,14 @@ var gobjJs = (function(exports) {
4478
4782
  * same for a "name: value", its name in quotes or not (a json, a
4479
4783
  * header, a label: "password": x, Authorization: Basic x, bearer:
4480
4784
  * tok); and a JWT or the credential after "Bearer " or "Basic ",
4481
- * wherever they are, whatever names them. Each rule judges the text
4482
- * as it came. Return null when there was nothing to mask. As the C
4483
- * kernel's mask_secrets_inline().
4785
+ * wherever they are, whatever names them; in a URL or a query
4786
+ * %-encoded inside a value, the same read decoded (mark_decoded_view()).
4787
+ * Each rule judges the text as it came. Return null when there was
4788
+ * nothing to mask. As the C kernel's mask_secrets_inline().
4484
4789
  ************************************************************/
4485
4790
  function mask_secrets_inline(str) {
4486
4791
  if (typeof str !== "string") return null;
4487
- if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4792
+ if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("%") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4488
4793
  if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
4489
4794
  const marks = new Uint8Array(str.length);
4490
4795
  mark_inline_secrets(str, marks);
@@ -8247,7 +8552,8 @@ var gobjJs = (function(exports) {
8247
8552
  inform_on_close: false,
8248
8553
  websocket: null,
8249
8554
  inside_on_open: false,
8250
- retry_ms: 0
8555
+ retry_ms: 0,
8556
+ retry_ms_before_session: 0
8251
8557
  };
8252
8558
  var __gclass__ = null;
8253
8559
  /******************************
@@ -8298,6 +8604,7 @@ var gobjJs = (function(exports) {
8298
8604
  function mt_start(gobj) {
8299
8605
  let priv = gobj.priv;
8300
8606
  priv.retry_ms = 0;
8607
+ priv.retry_ms_before_session = 0;
8301
8608
  if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
8302
8609
  gobj_send_event(gobj, "EV_ON_CLOSE", {
8303
8610
  url: priv.url,
@@ -8497,13 +8804,33 @@ var gobjJs = (function(exports) {
8497
8804
  }
8498
8805
  }
8499
8806
  /***************************************************************
8807
+ * Close the channel for a fault of the server: a message for a role
8808
+ * or a yuno that is not this one, an event out of its place.
8809
+ *
8810
+ * Not with 1000: EV_ON_CLOSE would say the session ended well. A
8811
+ * browser closes a WebSocket only with 1000 or 3000-4999 (1002, the
8812
+ * protocol error, throws there), so it is 4002 -- and the server
8813
+ * echoes it, so EV_ON_CLOSE carries it.
8814
+ *
8815
+ * And a session the server broke was not a session reached: the
8816
+ * backoff the ack reset is put back, or a server that breaks every
8817
+ * session is reconnected at the first delay for ever, with an
8818
+ * EV_ON_OPEN and an EV_ON_CLOSE in the app per round.
8819
+ ***************************************************************/
8820
+ var CLOSE_PEER_FAULT = 4002;
8821
+ function close_for_peer_fault(gobj, reason) {
8822
+ let priv = gobj.priv;
8823
+ if (gobj_current_state(gobj) === "ST_SESSION") priv.retry_ms = priv.retry_ms_before_session;
8824
+ close_websocket(gobj, CLOSE_PEER_FAULT, reason);
8825
+ }
8826
+ /***************************************************************
8500
8827
  * A text of the server -- a frame, the name of an event, of a
8501
8828
  * service, of a role -- as a log shows it: masked, then capped on a
8502
8829
  * whole character (capped first, a credential the cap cut would
8503
8830
  * show its first characters). As the C kernel shows the name of an
8504
8831
  * event that is not public (mask_secrets_inline_capped()).
8505
8832
  ***************************************************************/
8506
- var PEER_NAME_SHOWN = 64;
8833
+ var PEER_NAME_SHOWN = 128;
8507
8834
  var PEER_FRAME_SHOWN = 256;
8508
8835
  function peer_text_shown(text, max) {
8509
8836
  let s = String(text);
@@ -8726,7 +9053,8 @@ var gobjJs = (function(exports) {
8726
9053
  let url = kw.url;
8727
9054
  if (kw.ws_seq !== void 0 && kw.ws_seq !== priv.ws_seq) return 0;
8728
9055
  set_remote_log_functions();
8729
- 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})`);
9056
+ const reason_shown = peer_text_shown(kw.reason === void 0 ? "" : kw.reason, PEER_FRAME_SHOWN);
9057
+ if (is_traffic_tracing(gobj)) trace_msg(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${reason_shown}", Clean: ${kw.was_clean})`);
8730
9058
  if (priv.websocket) {
8731
9059
  priv.websocket.onopen = null;
8732
9060
  priv.websocket.onmessage = null;
@@ -8736,21 +9064,25 @@ var gobjJs = (function(exports) {
8736
9064
  }
8737
9065
  if (priv.inform_on_close) {
8738
9066
  priv.inform_on_close = false;
9067
+ if ((!kw.was_clean || kw.code !== 1e3 && kw.code !== 1001) && gobj_is_running(gobj)) log_warning(`${gobj_short_name(gobj)}: session closed ${url} (Code: ${kw.code}, Reason: "${reason_shown}", Clean: ${kw.was_clean})`);
8739
9068
  gobj_publish_event(gobj, "EV_ON_CLOSE", {
8740
9069
  url,
8741
9070
  remote_yuno_name: priv.remote_yuno_name,
8742
9071
  remote_yuno_role: priv.remote_yuno_role,
8743
- remote_yuno_service: priv.remote_yuno_service
9072
+ remote_yuno_service: priv.remote_yuno_service,
9073
+ code: kw.code,
9074
+ reason: reason_shown,
9075
+ was_clean: kw.was_clean
8744
9076
  });
8745
9077
  } else if (!gobj_is_running(gobj)) {} else {
8746
- 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})`);
9078
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${reason_shown}", Clean: ${kw.was_clean})`);
8747
9079
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
8748
9080
  url,
8749
9081
  remote_yuno_name: priv.remote_yuno_name,
8750
9082
  remote_yuno_role: priv.remote_yuno_role,
8751
9083
  remote_yuno_service: priv.remote_yuno_service,
8752
9084
  code: kw.code,
8753
- reason: kw.reason,
9085
+ reason: reason_shown,
8754
9086
  was_clean: kw.was_clean
8755
9087
  });
8756
9088
  }
@@ -8773,7 +9105,8 @@ var gobjJs = (function(exports) {
8773
9105
  *
8774
9106
  * Reset to zero when a session is actually reached
8775
9107
  * (ac_identity_card_ack), so a link that flaps once does not carry a
8776
- * minute-long penalty into its next real drop.
9108
+ * minute-long penalty into its next real drop -- and put back when the
9109
+ * server breaks that session (close_for_peer_fault()).
8777
9110
  ***************************************************************/
8778
9111
  function next_retry(gobj) {
8779
9112
  let priv = gobj.priv;
@@ -8823,6 +9156,7 @@ var gobjJs = (function(exports) {
8823
9156
  } else {
8824
9157
  gobj_change_state(gobj, "ST_SESSION");
8825
9158
  priv.inside_on_open = true;
9159
+ priv.retry_ms_before_session = priv.retry_ms;
8826
9160
  priv.retry_ms = 0;
8827
9161
  if (!priv.inform_on_close) {
8828
9162
  priv.inform_on_close = true;
@@ -8859,19 +9193,26 @@ var gobjJs = (function(exports) {
8859
9193
  let iev_event = iev_msg.event;
8860
9194
  let iev_kw = iev_msg.kw;
8861
9195
  if (gobj_current_state(gobj) !== "ST_SESSION") {
8862
- if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
8863
- if (!(gobj_send_event(gobj, iev_event, iev_kw, gobj) < 0)) return 0;
8864
- } else log_error(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
8865
- close_websocket(gobj);
9196
+ if (String(iev_event).toLowerCase() === "ev_identity_card_ack") {
9197
+ if (!(gobj_send_event(gobj, "EV_IDENTITY_CARD_ACK", iev_kw, gobj) < 0)) return 0;
9198
+ } else log_warning(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9199
+ close_for_peer_fault(gobj, "event out of session");
8866
9200
  return -1;
8867
9201
  }
8868
9202
  iev_msg = null;
8869
9203
  let msg_type = msg_iev_get_msg_type(gobj, iev_kw);
8870
9204
  let event_id = msg_iev_get_stack(gobj, iev_kw, IEVENT_STACK_ID);
8871
9205
  let iev_dst_role = kw_get_str(gobj, event_id, "dst_role", "");
8872
- if (iev_dst_role.toLowerCase() !== gobj_yuno_role().toLowerCase()) {
8873
- log_error(`"It's not my role, dst_role: ${peer_text_shown(iev_dst_role, PEER_NAME_SHOWN)}, my_role: ${gobj_yuno_role()}`);
8874
- return 0;
9206
+ if (!empty_string(iev_dst_role) && iev_dst_role.toLowerCase() !== String(gobj_yuno_role()).toLowerCase()) {
9207
+ log_warning(`${gobj_short_name(gobj)}: It's not my role, dst_role: ${peer_text_shown(iev_dst_role, PEER_NAME_SHOWN)}, my_role: ${gobj_yuno_role()}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9208
+ close_for_peer_fault(gobj, "not my role");
9209
+ return -1;
9210
+ }
9211
+ let iev_dst_yuno = kw_get_str(gobj, event_id, "dst_yuno", "");
9212
+ if (!empty_string(iev_dst_yuno) && iev_dst_yuno.toLowerCase() !== String(gobj_yuno_name()).toLowerCase()) {
9213
+ log_warning(`${gobj_short_name(gobj)}: It's not my name, dst_yuno: ${peer_text_shown(iev_dst_yuno, PEER_NAME_SHOWN)}, my_name: ${gobj_yuno_name()}, event: ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9214
+ close_for_peer_fault(gobj, "not my name");
9215
+ return -1;
8875
9216
  }
8876
9217
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
8877
9218
  if (msg_type === "__subscribing__") {
@@ -8882,8 +9223,14 @@ var gobjJs = (function(exports) {
8882
9223
  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)})`);
8883
9224
  return -1;
8884
9225
  }
8885
- if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
8886
- gobj_send_event(gobj, iev_event, iev_kw, gobj);
9226
+ const declared = gobj_find_event_type(iev_event, 0, false);
9227
+ if (declared && gobj_has_event(gobj, declared.event_name, 0)) {
9228
+ const own_event = declared.event_name;
9229
+ if (!gobj_has_event(gobj, own_event, event_flag_t.EVF_PUBLIC_EVENT) || own_event === "EV_IDENTITY_CARD_ACK") {
9230
+ log_warning(`${gobj_short_name(gobj)}: event of the transport itself, or out of its place, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9231
+ return -1;
9232
+ }
9233
+ gobj_send_event(gobj, own_event, iev_kw, gobj);
8887
9234
  return 0;
8888
9235
  }
8889
9236
  if (!empty_string(iev_dst_service)) {
@@ -8893,13 +9240,18 @@ var gobjJs = (function(exports) {
8893
9240
  return 0;
8894
9241
  }
8895
9242
  if (!gobj_has_event(gobj_service, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
8896
- 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)}'`);
9243
+ log_warning(`${gobj_short_name(gobj)}: Service found but no event or not public, DROPPED: service '${gobj_short_name(gobj_service)}', ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
8897
9244
  return -1;
8898
9245
  }
8899
9246
  gobj_send_event(gobj_service, iev_event, iev_kw, gobj);
8900
9247
  return 0;
8901
9248
  }
8902
- gobj_publish_event(gobj, iev_event, iev_kw);
9249
+ let event_type = gobj_find_event_type(iev_event, event_flag_t.EVF_PUBLIC_EVENT, false);
9250
+ if (!event_type) {
9251
+ log_warning(`${gobj_short_name(gobj)}: event that no gclass declares public, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9252
+ return -1;
9253
+ }
9254
+ gobj_publish_event(gobj, event_type.event_name, iev_kw);
8903
9255
  return 0;
8904
9256
  }
8905
9257
  /***************************************************************************
@@ -9019,7 +9371,7 @@ var gobjJs = (function(exports) {
9019
9371
  __gclass__ = gclass_create(gclass_name, [
9020
9372
  ["EV_ON_MESSAGE", event_flag_t.EVF_OUTPUT_EVENT],
9021
9373
  ["EV_IDENTITY_CARD_ACK", event_flag_t.EVF_PUBLIC_EVENT],
9022
- ["EV_ON_ID_NAK", event_flag_t.EVF_PUBLIC_EVENT | event_flag_t.EVF_OUTPUT_EVENT],
9374
+ ["EV_ON_ID_NAK", event_flag_t.EVF_OUTPUT_EVENT],
9023
9375
  ["EV_MT_STATS", event_flag_t.EVF_PUBLIC_EVENT],
9024
9376
  ["EV_MT_COMMAND", event_flag_t.EVF_PUBLIC_EVENT],
9025
9377
  ["EV_ON_OPEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
@@ -9114,6 +9466,7 @@ var gobjJs = (function(exports) {
9114
9466
  exports.gobj_deliver_posted_events = gobj_deliver_posted_events;
9115
9467
  exports.gobj_destroy = gobj_destroy;
9116
9468
  exports.gobj_find_child = gobj_find_child;
9469
+ exports.gobj_find_event_type = gobj_find_event_type;
9117
9470
  exports.gobj_find_gobj = gobj_find_gobj;
9118
9471
  exports.gobj_find_service = gobj_find_service;
9119
9472
  exports.gobj_find_subscribings = gobj_find_subscribings;