@yuneta/gobj-js 7.26.24 → 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.
package/README.md CHANGED
@@ -302,12 +302,31 @@ gobj_write_bool_attr(gobj, "selectable", kw.selectable === true); // false
302
302
 
303
303
  A value with no json text (a circular object such as a gobj or a DOM node, a
304
304
  `BigInt`) given to a string attribute is refused the same way, and is never
305
- thrown (7.26.24). A json TEXT is read as the C parser reads it: one that C
305
+ thrown (7.26.24). So is a value that is not json ALL THE WAY DOWN given to a
306
+ string, a `DTP_DICT` or a `DTP_LIST` (7.26.25): a cycle anywhere inside, a
307
+ `BigInt` inside (`[1n]`), a `Map`, a `Set`, a `Date`, a typed array, an instance
308
+ of a class. They were written, and burst (or turned into `"{}"`) when the value
309
+ was serialized, far from the write. A key whose value is `undefined` or a
310
+ function is dropped, as `JSON.stringify()` drops it:
311
+
312
+ ```javascript
313
+ gobj_write_attr(gobj, "settings", {a: 1, b: undefined}); // 0: b goes when serialized
314
+ gobj_write_attr(gobj, "settings", new Map([["a", 1]])); // -1, ERROR "a value with no json text"
315
+ gobj_write_str_attr(gobj, "name", new Map()); // -1: it was the text "{}"
316
+ ```
317
+
318
+ A `DTP_JSON` attribute is not checked: it takes any value, as before.
319
+
320
+ A json TEXT is read as the C parser reads it: one that C
306
321
  refuses is refused in JS too -- an escaped NUL (`"\u0000"`), a lone surrogate,
307
322
  an integer out of int64, a real that overflows (`1e309`; to a `DTP_JSON` it is
308
- the text `"1e309"`, not `Infinity`). Two differences remain, because a JS number
309
- cannot carry them: an integer beyond 2^53 is rounded, and `2.0` is the integer
310
- 2 (see `tests/fixtures/attr_types_parity.json`).
323
+ the text `"1e309"`, not `Infinity`), a text nested deeper than 2048 levels (a
324
+ scalar is a level too; 7.26.25). A number beyond int64 is "a real" to a dict,
325
+ as in C (`1e20`), and `9223372036854775807` is taken by an integer (JSON.parse()
326
+ makes it 2^63, kept as such). Two differences remain, because a JS number
327
+ cannot carry them: an integer beyond 2^53 is rounded, and `2.0` (and `-0.0`) is
328
+ the integer (see `tests/fixtures/attr_types_parity.json`, which the C test
329
+ `tests/c/attr_types` of yunetas reads too).
311
330
 
312
331
  ### Events & Pub-Sub
313
332
 
@@ -1014,7 +1033,14 @@ Key attributes: `url`, `jwt`, `wanted_yuno_role`, `wanted_yuno_name`, `wanted_yu
1014
1033
 
1015
1034
  Its events (`EV_ON_OPEN`, `EV_ON_CLOSE`, `EV_ON_OPEN_ERROR`, `EV_ON_ID_NAK` and every message that names no service) are PUBLISHED: to the `subscriber` attr, else, for a pure child, to its parent, which it subscribes in `mt_create` — one subscription, as the C transport. Up to 7.26.21 a pure child SENT three of them to its parent and subscribed nobody, so its parent never heard `EV_ON_CLOSE` / `EV_ON_OPEN_ERROR`: a host of a pure child must declare both in its FSM (BREAKING in 7.26.22).
1016
1035
 
1017
- A message that names no service is published only when its event is declared by some registered gclass (`gobj_find_event_type()`); any other is dropped with a WARNING, its name masked (7.26.24). What the server gets wrong is a WARNING, never an ERROR. Before the session only `EV_IDENTITY_CARD_ACK` is taken; a `dst_role` or `dst_yuno` that is not this yuno's closes the channel too (an empty one is legal: anybody's), as the C transport does; an event that the named service does not take as public is dropped (7.26.24).
1036
+ A message that names no service is published only when its event is declared PUBLIC by some registered gclass (`gobj_find_event_type(name, EVF_PUBLIC_EVENT)`, as C's `gclass_find_public_event()`; 7.26.24 took any declaration, 7.26.25 requires it public); any other is dropped with a WARNING, its name masked. The events the transport says itself (`EV_ON_OPEN`, `EV_ON_CLOSE`, `EV_ON_OPEN_ERROR`, `EV_ON_ID_NAK`, `EV_TIMEOUT`, `EV_ON_MESSAGE`, and `EV_IDENTITY_CARD_ACK` in session) are never taken from the server, in any case of letters and whatever service the message names: a WARNING, dropped (7.26.25). A backend event an app receives must therefore be declared `EVF_PUBLIC_EVENT` in the gclass that receives it:
1037
+
1038
+ ```javascript
1039
+ const event_types = [
1040
+ ["EV_MT_COMMAND_ANSWER", event_flag_t.EVF_PUBLIC_EVENT],
1041
+ ["EV_REALTIME_ALARM", event_flag_t.EVF_PUBLIC_EVENT],
1042
+ ];
1043
+ ``` What the server gets wrong is a WARNING, never an ERROR. Before the session only `EV_IDENTITY_CARD_ACK` is taken; a `dst_role` or `dst_yuno` that is not this yuno's closes the channel too (an empty one is legal: anybody's), as the C transport does; an event that the named service does not take as public is dropped (7.26.24). A channel closed for a fault of the server is closed with **4002**, not 1000 (a browser may send only 1000 or 3000-4999, so 1002 cannot be used), so `EV_ON_CLOSE` says the session was cut (7.26.25).
1018
1044
 
1019
1045
  `EV_ON_CLOSE` carries `code`, `reason` (masked) and `was_clean`, and so does `EV_ON_OPEN_ERROR` (7.26.24). A session that ends cleanly (a stop, a logout, the server closing with 1000 or 1001) is logged by nobody; a session CUT -- not clean, or any other code (1006, 1008, 1011) -- is one WARNING with the code and the reason masked:
1020
1046
 
@@ -1038,7 +1064,9 @@ naming services it does not host.
1038
1064
  what breaks lockstep, so N links that dropped together do not stampede a backend
1039
1065
  that is just coming back. The backoff resets when a session is actually reached,
1040
1066
  and on `mt_start` — a deliberate reconnect never inherits a previous run's
1041
- penalty. For the old fixed-interval behaviour set `timeout_retry_max` equal to
1067
+ penalty. A session the transport closes for a fault of the server (4002) does not
1068
+ count as reached: the backoff goes on from where it was (7.26.25), so a server
1069
+ that breaks every session is not reconnected at the first delay for ever. For the old fixed-interval behaviour set `timeout_retry_max` equal to
1042
1070
  `timeout_retry`.
1043
1071
 
1044
1072
  ---
@@ -870,12 +870,48 @@ var JSON_NUMBER_TOKEN = /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/y;
870
870
  function is_surrogate_pair(high, low) {
871
871
  return high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343;
872
872
  }
873
+ var JSON_MAX_DEPTH = 2048;
873
874
  function json_text_refused_by_c(s) {
874
875
  const n = s.length;
875
876
  let i = 0;
877
+ let depth = 0;
878
+ const in_object = [];
879
+ let key_next = false;
876
880
  while (i < n) {
877
881
  const c = s.charCodeAt(i);
882
+ if (c === 123 || c === 91) {
883
+ depth++;
884
+ if (depth > JSON_MAX_DEPTH) return true;
885
+ in_object.push(c === 123);
886
+ key_next = c === 123;
887
+ i++;
888
+ continue;
889
+ }
890
+ if (c === 125 || c === 93) {
891
+ depth--;
892
+ in_object.pop();
893
+ key_next = false;
894
+ i++;
895
+ continue;
896
+ }
897
+ if (c === 44) {
898
+ key_next = in_object.length > 0 && in_object[in_object.length - 1];
899
+ i++;
900
+ continue;
901
+ }
902
+ if (c === 58) {
903
+ key_next = false;
904
+ i++;
905
+ continue;
906
+ }
907
+ if (c === 116 || c === 102 || c === 110) {
908
+ if (depth + 1 > JSON_MAX_DEPTH) return true;
909
+ i += c === 102 ? 5 : 4;
910
+ continue;
911
+ }
878
912
  if (c === 34) {
913
+ if (!key_next && depth + 1 > JSON_MAX_DEPTH) return true;
914
+ key_next = false;
879
915
  i++;
880
916
  while (i < n) {
881
917
  const d = s.charCodeAt(i);
@@ -908,6 +944,7 @@ function json_text_refused_by_c(s) {
908
944
  continue;
909
945
  }
910
946
  if (c === 45 || c >= 48 && c <= 57) {
947
+ if (depth + 1 > JSON_MAX_DEPTH) return true;
911
948
  JSON_NUMBER_TOKEN.lastIndex = i;
912
949
  const m = JSON_NUMBER_TOKEN.exec(s);
913
950
  if (!m) return true;
@@ -946,7 +983,7 @@ function shape_of_value(value) {
946
983
  if (is_array(value)) return "an array";
947
984
  if (is_object(value)) return "an object";
948
985
  if (is_string(value)) return REFUSED_TEXT;
949
- if (typeof value === "number") return Number.isInteger(value) ? "an integer" : "a real";
986
+ if (typeof value === "number") return Number.isInteger(value) && Math.abs(value) <= 0x8000000000000000 ? "an integer" : "a real";
950
987
  if (typeof value === "boolean") return "a boolean";
951
988
  return "null";
952
989
  }
@@ -967,7 +1004,7 @@ function number_as_type(type, value) {
967
1004
  value: value ? 1 : 0,
968
1005
  refused: null
969
1006
  };
970
- if (!(value >= -0x8000000000000000 && value < 0x8000000000000000)) return {
1007
+ if (!(value >= -0x8000000000000000 && value <= 0x8000000000000000)) return {
971
1008
  value: void 0,
972
1009
  refused: "a number out of its range"
973
1010
  };
@@ -1128,6 +1165,10 @@ function command_parameter_from_json(type, value) {
1128
1165
  refused: shape_of_value(value)
1129
1166
  };
1130
1167
  if (DTP_IS_NUMBER(type)) return number_as_type(type, value);
1168
+ if (!is_json_value(value)) return {
1169
+ value: void 0,
1170
+ refused: NO_JSON_TEXT
1171
+ };
1131
1172
  return {
1132
1173
  value,
1133
1174
  refused: null
@@ -1146,8 +1187,34 @@ var TYPES_READ = [
1146
1187
  data_type_t.DTP_LIST,
1147
1188
  data_type_t.DTP_DICT
1148
1189
  ];
1190
+ var LEAVE_NODE = Symbol("leave");
1191
+ function is_json_value(value) {
1192
+ const on_path = /* @__PURE__ */ new Set();
1193
+ const stack = [value];
1194
+ while (stack.length) {
1195
+ const v = stack.pop();
1196
+ if (v === LEAVE_NODE) {
1197
+ on_path.delete(stack.pop());
1198
+ continue;
1199
+ }
1200
+ if (typeof v === "bigint") return false;
1201
+ if (v === null || typeof v !== "object") continue;
1202
+ if (on_path.has(v)) return false;
1203
+ const array = Array.isArray(v);
1204
+ if (!array) {
1205
+ const proto = Object.getPrototypeOf(v);
1206
+ if (proto !== Object.prototype && proto !== null) return false;
1207
+ }
1208
+ on_path.add(v);
1209
+ stack.push(v, LEAVE_NODE);
1210
+ if (array) for (let i = 0; i < v.length; i++) stack.push(v[i]);
1211
+ else for (const key of Object.keys(v)) stack.push(v[key]);
1212
+ }
1213
+ return true;
1214
+ }
1149
1215
  var NO_JSON_TEXT = "a value with no json text";
1150
1216
  function json_text_of(value) {
1217
+ if (!is_json_value(value)) return;
1151
1218
  try {
1152
1219
  const s = JSON.stringify(value);
1153
1220
  return is_string(s) ? s : void 0;
@@ -4106,6 +4173,35 @@ function is_secret_name_tail(str, from, to, names) {
4106
4173
  }
4107
4174
  return secret_name_check(str, i, to, true, by_segments);
4108
4175
  }
4176
+ function param_name_tail_from(str, from, to) {
4177
+ let bytes = 0;
4178
+ let i = to;
4179
+ while (i > from) {
4180
+ const code = str.charCodeAt(i - 1);
4181
+ let n = 3;
4182
+ let units = 1;
4183
+ if (code < 128) n = 1;
4184
+ else if (code < 2048) n = 2;
4185
+ else if (code >= 56320 && code <= 57343 && i - 2 >= from) {
4186
+ const high = str.charCodeAt(i - 2);
4187
+ if (high >= 55296 && high <= 56319) {
4188
+ n = 4;
4189
+ units = 2;
4190
+ }
4191
+ }
4192
+ if (bytes + n > MASK_MAX_NAME) return -1;
4193
+ if (code === 38 || code === 63 || code === 59) return i;
4194
+ bytes += n;
4195
+ i -= units;
4196
+ }
4197
+ return -1;
4198
+ }
4199
+ function is_param_name_secret(str, from, to, names) {
4200
+ const tail = names === NAMES_NOW ? param_name_tail_from(str, from, to) : -1;
4201
+ if (tail < 0) return is_secret_name_tail(str, from, to, names);
4202
+ if (is_secret_name_tail(str, from, to, NAMES_7_26_9)) return true;
4203
+ return is_secret_name_tail(str, from, to, NAMES_NOW) && secret_name_check(str, tail, to, false, true);
4204
+ }
4109
4205
  /************************************************************
4110
4206
  * Where an unquoted secret value that begins at `p` ends: at
4111
4207
  * the end, at the quote that closes an outer quoted value, or
@@ -4403,7 +4499,7 @@ function mark_params(str, value_is_secret, reading, marks, judged) {
4403
4499
  }
4404
4500
  const name_from = word;
4405
4501
  word = p + 1;
4406
- const secret = is_secret_name_tail(str, name_from, p, names);
4502
+ const secret = is_param_name_secret(str, name_from, p, names);
4407
4503
  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;
4408
4504
  if (!(secret || value_is_secret && p - name_from === 5 && ascii_case_equal(str, name_from, "value"))) {
4409
4505
  if (judged && query_name_from(str, p) === name_from) judged[p] = JUDGED_NOT_SECRET;
@@ -4590,7 +4686,7 @@ function mark_labels(str, marks) {
4590
4686
  p++;
4591
4687
  continue;
4592
4688
  }
4593
- if (!is_secret_name_tail(str, word, p, NAMES_NOW)) {
4689
+ if (!is_param_name_secret(str, word, p, NAMES_NOW)) {
4594
4690
  p++;
4595
4691
  word = p;
4596
4692
  continue;
@@ -4619,6 +4715,50 @@ function inline_value_is_secret(str, names) {
4619
4715
  }
4620
4716
  return false;
4621
4717
  }
4718
+ function mark_inline_rules_of_now(str, marks) {
4719
+ const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4720
+ const judged = new Uint8Array(str.length);
4721
+ mark_params(str, value_is_secret, READ_NOW, marks, judged);
4722
+ mark_query_params(str, marks, judged);
4723
+ mark_url_passwords(str, marks);
4724
+ mark_shapes(str, marks);
4725
+ mark_labels(str, marks);
4726
+ }
4727
+ var MASK_MAX_DECODINGS = 2;
4728
+ function encoded_ascii_at(str, p) {
4729
+ if (p + 3 > str.length || str.charCodeAt(p) !== 37) return -1;
4730
+ const hi = hex_digit_value(str.charCodeAt(p + 1));
4731
+ const lo = hi >= 0 ? hex_digit_value(str.charCodeAt(p + 2)) : -1;
4732
+ if (lo < 0) return -1;
4733
+ const c = hi * 16 + lo;
4734
+ return c > 0 && c < 128 ? c : -1;
4735
+ }
4736
+ function mark_decoded_view(str, marks, decodings) {
4737
+ if (decodings >= MASK_MAX_DECODINGS) return;
4738
+ const pieces = [];
4739
+ let last = 0;
4740
+ let p = str.indexOf("%");
4741
+ while (p >= 0) {
4742
+ const c = encoded_ascii_at(str, p);
4743
+ if (c >= 0) {
4744
+ pieces.push(str.substring(last, p), String.fromCharCode(c));
4745
+ last = p + 3;
4746
+ p = str.indexOf("%", last);
4747
+ } else p = str.indexOf("%", p + 1);
4748
+ }
4749
+ if (pieces.length === 0) return;
4750
+ pieces.push(str.substring(last));
4751
+ const view = pieces.join("");
4752
+ const view_marks = new Uint8Array(view.length);
4753
+ mark_inline_rules_of_now(view, view_marks);
4754
+ mark_decoded_view(view, view_marks, decodings + 1);
4755
+ let q = 0;
4756
+ for (let i = 0; i < view.length; i++) {
4757
+ const n = encoded_ascii_at(str, q) >= 0 ? 3 : 1;
4758
+ if (view_marks[i]) mark_range(marks, q, q + n);
4759
+ q += n;
4760
+ }
4761
+ }
4622
4762
  function mark_inline_secrets(str, marks) {
4623
4763
  const value_is_secret = inline_value_is_secret(str, NAMES_NOW);
4624
4764
  const value_is_secret_7_26_9 = inline_value_is_secret(str, NAMES_7_26_9);
@@ -4629,6 +4769,7 @@ function mark_inline_secrets(str, marks) {
4629
4769
  mark_url_passwords(str, marks);
4630
4770
  mark_shapes(str, marks);
4631
4771
  mark_labels(str, marks);
4772
+ mark_decoded_view(str, marks, 0);
4632
4773
  }
4633
4774
  /************************************************************
4634
4775
  * A text (a command line) with the secrets that can be told
@@ -4640,13 +4781,14 @@ function mark_inline_secrets(str, marks) {
4640
4781
  * same for a "name: value", its name in quotes or not (a json, a
4641
4782
  * header, a label: "password": x, Authorization: Basic x, bearer:
4642
4783
  * tok); and a JWT or the credential after "Bearer " or "Basic ",
4643
- * wherever they are, whatever names them. Each rule judges the text
4644
- * as it came. Return null when there was nothing to mask. As the C
4645
- * kernel's mask_secrets_inline().
4784
+ * wherever they are, whatever names them; in a URL or a query
4785
+ * %-encoded inside a value, the same read decoded (mark_decoded_view()).
4786
+ * Each rule judges the text as it came. Return null when there was
4787
+ * nothing to mask. As the C kernel's mask_secrets_inline().
4646
4788
  ************************************************************/
4647
4789
  function mask_secrets_inline(str) {
4648
4790
  if (typeof str !== "string") return null;
4649
- if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4791
+ if (str.indexOf("=") < 0 && str.indexOf(":") < 0 && str.indexOf("%") < 0 && str.indexOf("eyJ") < 0 && !/bearer|basic/i.test(str)) return null;
4650
4792
  if (str.length > MASK_MAX_BYTES) return MASK_TOO_LARGE;
4651
4793
  const marks = new Uint8Array(str.length);
4652
4794
  mark_inline_secrets(str, marks);
@@ -8409,7 +8551,8 @@ var PRIVATE_DATA = {
8409
8551
  inform_on_close: false,
8410
8552
  websocket: null,
8411
8553
  inside_on_open: false,
8412
- retry_ms: 0
8554
+ retry_ms: 0,
8555
+ retry_ms_before_session: 0
8413
8556
  };
8414
8557
  var __gclass__ = null;
8415
8558
  /******************************
@@ -8460,6 +8603,7 @@ function mt_writing(gobj, path) {
8460
8603
  function mt_start(gobj) {
8461
8604
  let priv = gobj.priv;
8462
8605
  priv.retry_ms = 0;
8606
+ priv.retry_ms_before_session = 0;
8463
8607
  if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
8464
8608
  gobj_send_event(gobj, "EV_ON_CLOSE", {
8465
8609
  url: priv.url,
@@ -8659,6 +8803,26 @@ function close_websocket(gobj, code = 1e3, reason = "") {
8659
8803
  }
8660
8804
  }
8661
8805
  /***************************************************************
8806
+ * Close the channel for a fault of the server: a message for a role
8807
+ * or a yuno that is not this one, an event out of its place.
8808
+ *
8809
+ * Not with 1000: EV_ON_CLOSE would say the session ended well. A
8810
+ * browser closes a WebSocket only with 1000 or 3000-4999 (1002, the
8811
+ * protocol error, throws there), so it is 4002 -- and the server
8812
+ * echoes it, so EV_ON_CLOSE carries it.
8813
+ *
8814
+ * And a session the server broke was not a session reached: the
8815
+ * backoff the ack reset is put back, or a server that breaks every
8816
+ * session is reconnected at the first delay for ever, with an
8817
+ * EV_ON_OPEN and an EV_ON_CLOSE in the app per round.
8818
+ ***************************************************************/
8819
+ var CLOSE_PEER_FAULT = 4002;
8820
+ function close_for_peer_fault(gobj, reason) {
8821
+ let priv = gobj.priv;
8822
+ if (gobj_current_state(gobj) === "ST_SESSION") priv.retry_ms = priv.retry_ms_before_session;
8823
+ close_websocket(gobj, CLOSE_PEER_FAULT, reason);
8824
+ }
8825
+ /***************************************************************
8662
8826
  * A text of the server -- a frame, the name of an event, of a
8663
8827
  * service, of a role -- as a log shows it: masked, then capped on a
8664
8828
  * whole character (capped first, a credential the cap cut would
@@ -8940,7 +9104,8 @@ function ac_on_close(gobj, event, kw, src) {
8940
9104
  *
8941
9105
  * Reset to zero when a session is actually reached
8942
9106
  * (ac_identity_card_ack), so a link that flaps once does not carry a
8943
- * minute-long penalty into its next real drop.
9107
+ * minute-long penalty into its next real drop -- and put back when the
9108
+ * server breaks that session (close_for_peer_fault()).
8944
9109
  ***************************************************************/
8945
9110
  function next_retry(gobj) {
8946
9111
  let priv = gobj.priv;
@@ -8990,6 +9155,7 @@ function ac_identity_card_ack(gobj, event, kw, src) {
8990
9155
  } else {
8991
9156
  gobj_change_state(gobj, "ST_SESSION");
8992
9157
  priv.inside_on_open = true;
9158
+ priv.retry_ms_before_session = priv.retry_ms;
8993
9159
  priv.retry_ms = 0;
8994
9160
  if (!priv.inform_on_close) {
8995
9161
  priv.inform_on_close = true;
@@ -9029,7 +9195,7 @@ function ac_on_message(gobj, event, kw, src) {
9029
9195
  if (String(iev_event).toLowerCase() === "ev_identity_card_ack") {
9030
9196
  if (!(gobj_send_event(gobj, "EV_IDENTITY_CARD_ACK", iev_kw, gobj) < 0)) return 0;
9031
9197
  } else log_warning(`${gobj_short_name(gobj)}: event UNKNOWN in not-session state ${peer_text_shown(iev_event, PEER_NAME_SHOWN)}`);
9032
- close_websocket(gobj);
9198
+ close_for_peer_fault(gobj, "event out of session");
9033
9199
  return -1;
9034
9200
  }
9035
9201
  iev_msg = null;
@@ -9038,13 +9204,13 @@ function ac_on_message(gobj, event, kw, src) {
9038
9204
  let iev_dst_role = kw_get_str(gobj, event_id, "dst_role", "");
9039
9205
  if (!empty_string(iev_dst_role) && iev_dst_role.toLowerCase() !== String(gobj_yuno_role()).toLowerCase()) {
9040
9206
  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)}`);
9041
- close_websocket(gobj);
9207
+ close_for_peer_fault(gobj, "not my role");
9042
9208
  return -1;
9043
9209
  }
9044
9210
  let iev_dst_yuno = kw_get_str(gobj, event_id, "dst_yuno", "");
9045
9211
  if (!empty_string(iev_dst_yuno) && iev_dst_yuno.toLowerCase() !== String(gobj_yuno_name()).toLowerCase()) {
9046
9212
  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)}`);
9047
- close_websocket(gobj);
9213
+ close_for_peer_fault(gobj, "not my name");
9048
9214
  return -1;
9049
9215
  }
9050
9216
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
@@ -9056,8 +9222,14 @@ function ac_on_message(gobj, event, kw, src) {
9056
9222
  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)})`);
9057
9223
  return -1;
9058
9224
  }
9059
- if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
9060
- gobj_send_event(gobj, iev_event, iev_kw, gobj);
9225
+ const declared = gobj_find_event_type(iev_event, 0, false);
9226
+ if (declared && gobj_has_event(gobj, declared.event_name, 0)) {
9227
+ const own_event = declared.event_name;
9228
+ if (!gobj_has_event(gobj, own_event, event_flag_t.EVF_PUBLIC_EVENT) || own_event === "EV_IDENTITY_CARD_ACK") {
9229
+ 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)}'`);
9230
+ return -1;
9231
+ }
9232
+ gobj_send_event(gobj, own_event, iev_kw, gobj);
9061
9233
  return 0;
9062
9234
  }
9063
9235
  if (!empty_string(iev_dst_service)) {
@@ -9073,9 +9245,9 @@ function ac_on_message(gobj, event, kw, src) {
9073
9245
  gobj_send_event(gobj_service, iev_event, iev_kw, gobj);
9074
9246
  return 0;
9075
9247
  }
9076
- let event_type = gobj_find_event_type(iev_event, 0, false);
9248
+ let event_type = gobj_find_event_type(iev_event, event_flag_t.EVF_PUBLIC_EVENT, false);
9077
9249
  if (!event_type) {
9078
- log_warning(`${gobj_short_name(gobj)}: event that no gclass declares, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9250
+ log_warning(`${gobj_short_name(gobj)}: event that no gclass declares public, DROPPED: ev '${peer_text_shown(iev_event, PEER_NAME_SHOWN)}'`);
9079
9251
  return -1;
9080
9252
  }
9081
9253
  gobj_publish_event(gobj, event_type.event_name, iev_kw);
@@ -9198,7 +9370,7 @@ function create_gclass(gclass_name) {
9198
9370
  __gclass__ = gclass_create(gclass_name, [
9199
9371
  ["EV_ON_MESSAGE", event_flag_t.EVF_OUTPUT_EVENT],
9200
9372
  ["EV_IDENTITY_CARD_ACK", event_flag_t.EVF_PUBLIC_EVENT],
9201
- ["EV_ON_ID_NAK", event_flag_t.EVF_PUBLIC_EVENT | event_flag_t.EVF_OUTPUT_EVENT],
9373
+ ["EV_ON_ID_NAK", event_flag_t.EVF_OUTPUT_EVENT],
9202
9374
  ["EV_MT_STATS", event_flag_t.EVF_PUBLIC_EVENT],
9203
9375
  ["EV_MT_COMMAND", event_flag_t.EVF_PUBLIC_EVENT],
9204
9376
  ["EV_ON_OPEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],