@yuneta/gobj-js 7.25.9 → 7.26.1

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.
@@ -158,7 +158,7 @@ function command_parser(gobj, command, kw, src) {}
158
158
  function build_command_response(gobj, result, comment, schema, data) {
159
159
  return {
160
160
  result,
161
- comment: comment || {},
161
+ comment: comment || "",
162
162
  schema: schema || {},
163
163
  data: data || []
164
164
  };
@@ -212,7 +212,7 @@ var __global_command_parser_fn__ = null;
212
212
  var __global_stats_parser_fn__ = null;
213
213
  var dl_global_event_types = [];
214
214
  var _gclass_register = Object.create(null);
215
- var __jn_services__ = {};
215
+ var __jn_services__ = Object.create(null);
216
216
  var __yuno__ = null;
217
217
  var __default_service__ = null;
218
218
  var __inside__ = 0;
@@ -821,7 +821,8 @@ function set_default(gobj, sdata, it) {
821
821
  if (!is_object(jn_value)) jn_value = {};
822
822
  break;
823
823
  case data_type_t.DTP_JSON:
824
- if (is_string(svalue)) try {
824
+ if (svalue === "" || svalue === void 0) jn_value = null;
825
+ else if (is_string(svalue)) try {
825
826
  jn_value = JSON.parse(svalue);
826
827
  } catch (error) {
827
828
  log_error(`JSON parse error: ${error.message}`);
@@ -856,11 +857,17 @@ function json2item(gobj, sdata, it, jn_value_) {
856
857
  break;
857
858
  case data_type_t.DTP_INTEGER:
858
859
  jn_value2 = parseInt(jn_value_);
859
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
860
+ if (isNaN(jn_value2)) {
861
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
862
+ return -1;
863
+ }
860
864
  break;
861
865
  case data_type_t.DTP_REAL:
862
866
  jn_value2 = Number(jn_value_);
863
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
867
+ if (isNaN(jn_value2)) {
868
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
869
+ return -1;
870
+ }
864
871
  break;
865
872
  case data_type_t.DTP_LIST:
866
873
  if (is_string(jn_value_)) try {
@@ -887,15 +894,14 @@ function json2item(gobj, sdata, it, jn_value_) {
887
894
  }
888
895
  break;
889
896
  case data_type_t.DTP_JSON:
890
- if (is_string(jn_value_)) try {
891
- jn_value2 = JSON.parse(jn_value_);
892
- } catch (error) {
893
- log_error(`JSON parse error: ${error.message}`);
894
- }
895
- else jn_value2 = jn_value_;
896
- if (!is_array(jn_value2) && !is_object(jn_value2)) {
897
- log_error(`attr must be a json dict/list: ${it.name}`);
898
- return -1;
897
+ jn_value2 = jn_value_;
898
+ if (is_string(jn_value_)) {
899
+ const t = jn_value_.trim();
900
+ if (t.startsWith("{") || t.startsWith("[")) try {
901
+ jn_value2 = JSON.parse(t);
902
+ } catch (error) {
903
+ jn_value2 = jn_value_;
904
+ }
899
905
  }
900
906
  break;
901
907
  case data_type_t.DTP_POINTER: jn_value2 = jn_value_;
@@ -1200,6 +1206,10 @@ function gobj_default_service() {
1200
1206
  * find a service
1201
1207
  ************************************************************/
1202
1208
  function gobj_find_service(service_name, verbose) {
1209
+ if (!is_string(service_name)) {
1210
+ log_error(`gobj_find_service(): service name must be a string: ${service_name}`);
1211
+ return null;
1212
+ }
1203
1213
  service_name = service_name.toLowerCase();
1204
1214
  let service_gobj = __jn_services__[service_name];
1205
1215
  if (!service_gobj) {
@@ -1357,9 +1367,10 @@ function gobj_create2(gobj_name, gclass_name, kw, parent, gobj_flag) {
1357
1367
  return null;
1358
1368
  }
1359
1369
  if (is_string(parent)) {
1360
- parent = gobj_find_service(parent);
1370
+ const parent_name = parent;
1371
+ parent = gobj_find_service(parent_name);
1361
1372
  if (!parent) {
1362
- log_warning(`parent service not found: ${parent}`);
1373
+ log_warning(`parent service not found: ${parent_name}`);
1363
1374
  return null;
1364
1375
  }
1365
1376
  }
@@ -1472,6 +1483,7 @@ function gobj_destroy(gobj) {
1472
1483
  parent.gclass.gmt.mt_child_removed(parent, gobj);
1473
1484
  }
1474
1485
  if (gobj.gobj_flag & gobj_flag_t.gobj_flag_service) _deregister_service(gobj);
1486
+ if (gobj === __default_service__) __default_service__ = null;
1475
1487
  gobj_unsubscribe_list(gobj, gobj.dl_subscriptions, true);
1476
1488
  gobj_unsubscribe_list(gobj, gobj.dl_subscribings, true);
1477
1489
  if (parent) {
@@ -1817,7 +1829,7 @@ function gobj_set_bottom_gobj(gobj, bottom_gobj) {
1817
1829
  log_error(`gobj NULL of bad type`);
1818
1830
  return null;
1819
1831
  }
1820
- if (gobj.bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1832
+ if (gobj.bottom_gobj && bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1821
1833
  gobj.bottom_gobj = bottom_gobj;
1822
1834
  }
1823
1835
  /***************************************************************************
@@ -2232,7 +2244,7 @@ function gobj_write_attr(gobj, path, value, src) {
2232
2244
  jn_attrs[key] = value;
2233
2245
  if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(key)) gobj.priv[key] = value;
2234
2246
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2235
- if (gobj.mt_writing) gobj.mt_writing(gobj, key);
2247
+ if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2236
2248
  }
2237
2249
  return 0;
2238
2250
  }
@@ -2256,8 +2268,8 @@ function sdata_write_default_values(gobj, include_flag, exclude_flag) {
2256
2268
  if (!it.name) continue;
2257
2269
  if (exclude_flag && it.flag & exclude_flag) continue;
2258
2270
  if (include_flag === -1 || it.flag & include_flag) {
2259
- const value = set_default(gobj, gobj.jn_attrs, it);
2260
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = value;
2271
+ set_default(gobj, gobj.jn_attrs, it);
2272
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = gobj.jn_attrs[it.name];
2261
2273
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, it.name);
2262
2274
  }
2263
2275
  }
@@ -2291,7 +2303,7 @@ function gobj_write_bool_attr(gobj, name, value) {
2291
2303
  let hs = gobj_hsdata2(gobj, name, false);
2292
2304
  if (hs) {
2293
2305
  hs[name] = parseBoolean(value);
2294
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2306
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2295
2307
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2296
2308
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2297
2309
  }
@@ -2307,7 +2319,7 @@ function gobj_write_integer_attr(gobj, name, value) {
2307
2319
  let hs = gobj_hsdata2(gobj, name, false);
2308
2320
  if (hs) {
2309
2321
  hs[name] = parseInt(value);
2310
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2322
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2311
2323
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2312
2324
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2313
2325
  }
@@ -2323,7 +2335,7 @@ function gobj_write_str_attr(gobj, name, value) {
2323
2335
  let hs = gobj_hsdata2(gobj, name, false);
2324
2336
  if (hs) {
2325
2337
  hs[name] = String(value);
2326
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2338
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2327
2339
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2328
2340
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2329
2341
  }
@@ -2377,15 +2389,15 @@ function gobj_change_parent(gobj, parent) {
2377
2389
  log_error(`${gobj_short_name(gobj)}: gobj_change_parent() BAD TYPE of parent`);
2378
2390
  return -1;
2379
2391
  }
2380
- if (gobj.parent && gobj.parent.mt_child_removed) gobj.parent.mt_child_removed(gobj.parent, gobj);
2392
+ if (gobj.parent && gobj.parent.gclass.gmt.mt_child_removed) gobj.parent.gclass.gmt.mt_child_removed(gobj.parent, gobj);
2381
2393
  if (gobj.parent) {
2382
2394
  _remove_child(gobj.parent, gobj);
2383
2395
  gobj.parent = null;
2384
2396
  }
2385
2397
  _add_child(parent, gobj);
2386
- if (parent.mt_child_added) {
2398
+ if (parent.gclass.gmt.mt_child_added) {
2387
2399
  if (__trace_gobj_create_delete__(gobj)) log_debug(sprintf("👦👦🔵 child_added(%s): %s", gobj_full_name(parent), gobj_short_name(gobj)));
2388
- parent.mt_child_added(parent, gobj);
2400
+ parent.gclass.gmt.mt_child_added(parent, gobj);
2389
2401
  }
2390
2402
  return 0;
2391
2403
  }
@@ -2497,9 +2509,19 @@ function gobj_deliver_posted_events() {
2497
2509
  let snapshot = __dl_posted_events__;
2498
2510
  __dl_posted_events__ = [];
2499
2511
  let delivered = 0;
2500
- for (let msg of snapshot) {
2512
+ for (let i = 0; i < snapshot.length; i++) {
2513
+ const msg = snapshot[i];
2501
2514
  if (msg.dst.obflag & (obflag_t.obflag_destroyed | obflag_t.obflag_destroying)) continue;
2502
- gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2515
+ try {
2516
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2517
+ } catch (e) {
2518
+ __dl_posted_events__ = snapshot.slice(i + 1).concat(__dl_posted_events__);
2519
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2520
+ __posted_events_scheduled__ = true;
2521
+ setTimeout(gobj_deliver_posted_events, 0);
2522
+ }
2523
+ throw e;
2524
+ }
2503
2525
  delivered++;
2504
2526
  }
2505
2527
  if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
@@ -2577,7 +2599,16 @@ function gobj_send_event(dst, event, kw, src) {
2577
2599
  let ret = -1;
2578
2600
  try {
2579
2601
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
2580
- } finally {}
2602
+ } catch (e) {
2603
+ __inside__--;
2604
+ if (!(e && e.__gobj_logged__)) {
2605
+ log_error(`${gobj_short_name(dst)}: action of ${event} in ${state.state_name} THREW: ${e && e.stack ? e.stack : e}`);
2606
+ try {
2607
+ e.__gobj_logged__ = true;
2608
+ } catch (e2) {}
2609
+ }
2610
+ throw e;
2611
+ }
2581
2612
  if (tracea && __trace_machine_format__ !== 1 && !(dst.obflag & obflag_t.obflag_destroyed)) trace_machine(sprintf("<- mach(%s%s^%s), st: %s, ev: %s, ret: %d", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), dst.current_state.state_name, event ? event : "", is_number(ret) ? ret : 0));
2582
2613
  __inside__--;
2583
2614
  return ret;
@@ -3248,10 +3279,22 @@ function set_remote_log_functions(remote_log_fn) {
3248
3279
  f_warning = _console.warn;
3249
3280
  }
3250
3281
  }
3282
+ /************************************************************
3283
+ * JSON.stringify for a log line: a circular object (a gobj,
3284
+ * a DOM node) must not make the log call itself throw.
3285
+ ************************************************************/
3286
+ function log_stringify(v) {
3287
+ try {
3288
+ return JSON.stringify(v);
3289
+ } catch (e) {
3290
+ return String(v);
3291
+ }
3292
+ }
3251
3293
  function format_log(format) {
3252
3294
  const args = Array.prototype.slice.call(arguments);
3253
3295
  let output = "";
3254
3296
  if (typeof format === "string") {
3297
+ if (args.length === 1) return format;
3255
3298
  let i = 1;
3256
3299
  output = format.replace(/%[sdifoO]/g, (match) => {
3257
3300
  const val = args[i++];
@@ -3261,17 +3304,17 @@ function format_log(format) {
3261
3304
  case "%i": return parseInt(val);
3262
3305
  case "%f": return parseFloat(val);
3263
3306
  case "%o":
3264
- case "%O": return typeof val === "object" ? JSON.stringify(val) : String(val);
3307
+ case "%O": return typeof val === "object" ? log_stringify(val) : String(val);
3265
3308
  default: return match;
3266
3309
  }
3267
3310
  });
3268
- const rest = args.slice(i).map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg));
3311
+ const rest = args.slice(i).map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg));
3269
3312
  if (rest.length > 0) output += " " + rest.join(" ");
3270
- } else output = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
3313
+ } else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg)).join(" ");
3271
3314
  return output;
3272
3315
  }
3273
- function log_error(format) {
3274
- let msg = format_log(format);
3316
+ function log_error(format, ...args) {
3317
+ let msg = format_log(format, ...args);
3275
3318
  let hora = current_timestamp();
3276
3319
  if (f_error) {
3277
3320
  if (f_error === _console.error) {
@@ -3283,8 +3326,8 @@ function log_error(format) {
3283
3326
  }
3284
3327
  emit_log_callback("error", msg, hora);
3285
3328
  }
3286
- function log_warning(format) {
3287
- let msg = format_log(format);
3329
+ function log_warning(format, ...args) {
3330
+ let msg = format_log(format, ...args);
3288
3331
  let hora = current_timestamp();
3289
3332
  if (f_warning) {
3290
3333
  if (f_warning === _console.warn) {
@@ -3296,24 +3339,24 @@ function log_warning(format) {
3296
3339
  }
3297
3340
  emit_log_callback("warning", msg, hora);
3298
3341
  }
3299
- function log_info(format) {
3300
- let msg = format_log(format);
3342
+ function log_info(format, ...args) {
3343
+ let msg = format_log(format, ...args);
3301
3344
  let hora = current_timestamp();
3302
3345
  if (f_info) {
3303
3346
  if (console_wants("info", msg)) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
3304
3347
  }
3305
3348
  emit_log_callback("info", msg, hora);
3306
3349
  }
3307
- function log_debug(format) {
3308
- let msg = format_log(format);
3350
+ function log_debug(format, ...args) {
3351
+ let msg = format_log(format, ...args);
3309
3352
  let hora = current_timestamp();
3310
3353
  if (f_debug) {
3311
3354
  if (console_wants("debug", msg)) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
3312
3355
  }
3313
3356
  emit_log_callback("debug", msg, hora);
3314
3357
  }
3315
- function trace_msg(format) {
3316
- let msg = format_log(format);
3358
+ function trace_msg(format, ...args) {
3359
+ let msg = format_log(format, ...args);
3317
3360
  let hora = current_timestamp();
3318
3361
  if (f_debug) {
3319
3362
  if (console_wants("msg", msg)) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
@@ -3567,7 +3610,7 @@ function _json_mask_secrets(jn, memo, depth, budget) {
3567
3610
  if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3568
3611
  else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3569
3612
  if (shown !== value) changed = true;
3570
- result[key] = shown;
3613
+ set_own_key(result, key, shown);
3571
3614
  }
3572
3615
  if (truncated) result["<more>"] = MASK_TOO_LARGE;
3573
3616
  }
@@ -3639,7 +3682,7 @@ function json_is_identical(kw1, kw2) {
3639
3682
  ************************************************************/
3640
3683
  function json_object_update(destination, source) {
3641
3684
  if (!source) return destination;
3642
- for (let property of Object.keys(source)) destination[property] = source[property];
3685
+ for (let property of Object.keys(source)) set_own_key(destination, property, source[property]);
3643
3686
  return destination;
3644
3687
  }
3645
3688
  /************************************************************
@@ -3648,7 +3691,7 @@ function json_object_update(destination, source) {
3648
3691
  ************************************************************/
3649
3692
  function json_object_update_existing(destination, source) {
3650
3693
  if (!source) return destination;
3651
- for (let property in source) if (source.hasOwnProperty(property) && destination.hasOwnProperty(property)) destination[property] = source[property];
3694
+ for (let property in source) if (Object.prototype.hasOwnProperty.call(source, property) && Object.prototype.hasOwnProperty.call(destination, property)) set_own_key(destination, property, source[property]);
3652
3695
  return destination;
3653
3696
  }
3654
3697
  /************************************************************
@@ -3657,32 +3700,32 @@ function json_object_update_existing(destination, source) {
3657
3700
  ************************************************************/
3658
3701
  function json_object_update_missing(destination, source) {
3659
3702
  if (!source) return destination;
3660
- for (let property in source) if (source.hasOwnProperty(property) && !destination.hasOwnProperty(property)) destination[property] = source[property];
3703
+ for (let property in source) if (Object.prototype.hasOwnProperty.call(source, property) && !Object.prototype.hasOwnProperty.call(destination, property)) set_own_key(destination, property, source[property]);
3661
3704
  return destination;
3662
3705
  }
3663
3706
  /************************************************************
3664
3707
  * Simulate jansson function
3665
3708
  ************************************************************/
3666
3709
  function json_object_get(o, key) {
3667
- if (o && o.hasOwnProperty(key)) return o[key];
3710
+ if (o && Object.prototype.hasOwnProperty.call(o, key)) return o[key];
3668
3711
  }
3669
3712
  /************************************************************
3670
3713
  * Simulate jansson function
3671
3714
  ************************************************************/
3672
3715
  function json_object_del(o, k) {
3673
- if (o && o.hasOwnProperty(k)) delete o[k];
3716
+ if (o && Object.prototype.hasOwnProperty.call(o, k)) delete o[k];
3674
3717
  }
3675
3718
  /************************************************************
3676
3719
  * Simulate jansson function
3677
3720
  ************************************************************/
3678
3721
  function json_object_set(o, k, v) {
3679
- o[k] = v;
3722
+ set_own_key(o, k, v);
3680
3723
  }
3681
3724
  /************************************************************
3682
3725
  * Simulate jansson function
3683
3726
  ************************************************************/
3684
3727
  function json_object_set_new(o, k, v) {
3685
- o[k] = v;
3728
+ set_own_key(o, k, v);
3686
3729
  }
3687
3730
  /************************************************************
3688
3731
  * Simulate jansson function
@@ -3862,6 +3905,8 @@ function cmp_two_simple_json(jn_var1, jn_var2) {
3862
3905
  if (is_number(jn_var1) || is_number(jn_var2)) {
3863
3906
  let val1 = Number(jn_var1);
3864
3907
  let val2 = Number(jn_var2);
3908
+ if (Number.isNaN(val1)) val1 = 0;
3909
+ if (Number.isNaN(val2)) val2 = 0;
3865
3910
  if (val1 > val2) return 1;
3866
3911
  else if (val1 < val2) return -1;
3867
3912
  else return 0;
@@ -3924,11 +3969,31 @@ function kw_pop(kw1, kw2) {
3924
3969
  ************************************************************/
3925
3970
  function kw_has_key(kw, key) {
3926
3971
  if (key in kw) {
3927
- if (kw.hasOwnProperty(key)) return true;
3972
+ if (Object.prototype.hasOwnProperty.call(kw, key)) return true;
3928
3973
  }
3929
3974
  return false;
3930
3975
  }
3931
3976
  /************************************************************
3977
+ * A key that reaches the prototype chain instead of the data:
3978
+ * any key the object does not hold as its OWN key. A walk through one of them
3979
+ * read, or WROTE, Object.prototype: the flat id
3980
+ * "__proto__`polluted" set a key on every object of the page.
3981
+ * In C they are keys like any other, so here too: read them
3982
+ * only as own keys, and write "__proto__" as an own key.
3983
+ ************************************************************/
3984
+ function is_prototype_segment(obj, key) {
3985
+ return !Object.prototype.hasOwnProperty.call(obj, key);
3986
+ }
3987
+ function set_own_key(obj, key, value) {
3988
+ if (key === "__proto__") Object.defineProperty(obj, key, {
3989
+ value,
3990
+ writable: true,
3991
+ enumerable: true,
3992
+ configurable: true
3993
+ });
3994
+ else obj[key] = value;
3995
+ }
3996
+ /************************************************************
3932
3997
  * The value at `path` (keys joined by a backtick), as the C
3933
3998
  * kw_find_path() finds it: undefined when it is not there.
3934
3999
  * A kw that is not a dict or a list is LOGGED ("kw must be list
@@ -3955,7 +4020,12 @@ function kw_find_path(gobj, kw, path, verbose) {
3955
4020
  }
3956
4021
  let ss = path.split("`");
3957
4022
  let len = ss.length;
3958
- for (let i = 0; i < len - 1; i++) {
4023
+ for (let i = 0; i < len; i++) {
4024
+ if (is_prototype_segment(kw, ss[i])) {
4025
+ if (verbose) log_error(`${prefix}path not found: '${path}'`);
4026
+ return;
4027
+ }
4028
+ if (i === len - 1) break;
3959
4029
  kw = kw[ss[i]];
3960
4030
  if (kw === void 0 || kw === null) {
3961
4031
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -4338,17 +4408,13 @@ function kw_set_dict_value(gobj, kw, path, value) {
4338
4408
  return -1;
4339
4409
  }
4340
4410
  let ss = path.split("`");
4341
- if (ss.length <= 1) {
4342
- kw[path] = value;
4343
- return 0;
4344
- }
4345
4411
  let len = ss.length;
4346
4412
  for (let i = 0; i < len; i++) {
4347
4413
  let key = ss[i];
4348
- if (i === len - 1) kw[key] = value;
4414
+ if (i === len - 1) set_own_key(kw, key, value);
4349
4415
  else {
4350
- let seg = kw[key];
4351
- if (seg === void 0) kw[key] = {};
4416
+ let seg = is_prototype_segment(kw, key) ? void 0 : kw[key];
4417
+ if (seg === void 0) set_own_key(kw, key, {});
4352
4418
  else if (!is_object(seg) && !is_array(seg)) {
4353
4419
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not a dict or a list: '${path}'`);
4354
4420
  return -1;
@@ -4403,9 +4469,9 @@ function _kw_match_simple(kw, jn_filter, prefix_path) {
4403
4469
  if (!matched) break;
4404
4470
  continue;
4405
4471
  }
4406
- let jn_record_value = kw_get_dict_value(0, kw, path, 0, 0);
4407
- if (!jn_record_value) jn_record_value = json_object_get(kw, path);
4408
- if (!jn_record_value) {
4472
+ let jn_record_value = kw_get_dict_value(0, kw, path, void 0, 0);
4473
+ if (jn_record_value === void 0) jn_record_value = json_object_get(kw, path);
4474
+ if (jn_record_value === void 0) {
4409
4475
  matched = false;
4410
4476
  break;
4411
4477
  }
@@ -4499,14 +4565,14 @@ function kw_clone_by_keys(gobj, kw, keys, verbose) {
4499
4565
  let kw_clone = {};
4500
4566
  if (is_string(keys) && !empty_string(keys)) {
4501
4567
  let key = keys;
4502
- if (kw.hasOwnProperty(key)) kw_clone[key] = kw[key];
4568
+ if (Object.prototype.hasOwnProperty.call(kw, key)) set_own_key(kw_clone, key, kw[key]);
4503
4569
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4504
4570
  } else if (is_object(keys) && json_object_size(keys) > 0) {
4505
- for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) kw_clone[key] = kw[key];
4571
+ for (let key of Object.keys(keys)) if (Object.prototype.hasOwnProperty.call(kw, key)) set_own_key(kw_clone, key, kw[key]);
4506
4572
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4507
4573
  } else if (is_array(keys) && json_array_size(keys) > 0) for (let j = 0; j < keys.length; j++) {
4508
4574
  let key = keys[j];
4509
- if (kw.hasOwnProperty(key)) kw_clone[key] = kw[key];
4575
+ if (Object.prototype.hasOwnProperty.call(kw, key)) set_own_key(kw_clone, key, kw[key]);
4510
4576
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4511
4577
  }
4512
4578
  else return kw;
@@ -4524,17 +4590,18 @@ If paths are empty return empty
4524
4590
  ***************************************************************************/
4525
4591
  function kw_clone_by_not_keys(gobj, kw, keys, verbose) {
4526
4592
  let kw_clone = {};
4593
+ if (!is_object(kw)) return kw_clone;
4527
4594
  json_object_update(kw_clone, kw);
4528
4595
  if (is_string(keys) && !empty_string(keys)) {
4529
4596
  let key = keys;
4530
- if (kw_get_dict_value(gobj, kw, key, 0, 0)) json_object_del(kw_clone, key);
4597
+ if (Object.prototype.hasOwnProperty.call(kw, key)) json_object_del(kw_clone, key);
4531
4598
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4532
4599
  } else if (is_object(keys) && json_object_size(keys) > 0) {
4533
- for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4600
+ for (let key of Object.keys(keys)) if (Object.prototype.hasOwnProperty.call(kw, key)) json_object_del(kw_clone, key);
4534
4601
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4535
4602
  } else if (is_array(keys) && json_array_size(keys) > 0) for (let j = 0; j < keys.length; j++) {
4536
4603
  let key = keys[j];
4537
- if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4604
+ if (Object.prototype.hasOwnProperty.call(kw, key)) json_object_del(kw_clone, key);
4538
4605
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4539
4606
  }
4540
4607
  else return {};
@@ -4765,7 +4832,7 @@ function kwid_get_ids(gobj, ids) {
4765
4832
  let new_ids = [];
4766
4833
  if (is_string(ids)) new_ids.push(ids);
4767
4834
  else if (is_object(ids)) {
4768
- for (let id in ids) if (ids.hasOwnProperty(id)) new_ids.push(id);
4835
+ for (let id in ids) if (Object.prototype.hasOwnProperty.call(ids, id)) new_ids.push(id);
4769
4836
  } else if (is_array(ids)) ids.forEach(function(item) {
4770
4837
  if (is_string(item)) {
4771
4838
  if (!empty_string(item)) new_ids.push(item);
@@ -5018,7 +5085,7 @@ function send_http_json_post(url, data, on_response) {
5018
5085
  function _traverse_dict(obj, callback, full_path) {
5019
5086
  if (full_path === void 0 || !is_string(full_path)) full_path = "";
5020
5087
  for (let key of Object.keys(obj)) {
5021
- if (!obj.hasOwnProperty(key)) continue;
5088
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
5022
5089
  let sufix = (full_path.length ? "`" : "") + key;
5023
5090
  full_path += sufix;
5024
5091
  callback.apply(this, [
@@ -5487,9 +5554,15 @@ parent: document.body
5487
5554
  * will be 'data-i18n' attr
5488
5555
  *
5489
5556
  * content -> string | [createElement2() or parameters of createElement2]
5557
+ * | HTMLElement | SVGElement | Text
5490
5558
  * if string begins with '<' then
5491
5559
  * it will be converted to HTMLElement
5492
5560
  * content = createOneHtml(string)
5561
+ * ⚠️ So a string is MARKUP when it starts with '<': never
5562
+ * pass DATA (a value from a backend, a name a user typed)
5563
+ * as a bare string, or "<img src=x onerror=...>" runs.
5564
+ * Pass it as a Text node, which is always text:
5565
+ * ['span', {}, document.createTextNode(value)]
5493
5566
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5494
5567
  * it will be overridden with the translation
5495
5568
  * content = translate(attrs['i18n']));
@@ -5893,7 +5966,7 @@ function json2flat(jn) {
5893
5966
  return;
5894
5967
  }
5895
5968
  if (depth === 0) return;
5896
- flat[path] = value;
5969
+ set_own_key(flat, path, value);
5897
5970
  };
5898
5971
  if (jn === void 0 || jn === null) return flat;
5899
5972
  walk(jn, "", 0);
@@ -5928,7 +6001,7 @@ function flat2json(flat) {
5928
6001
  current[seg] = flat[key];
5929
6002
  } else {
5930
6003
  if (Object.prototype.hasOwnProperty.call(current, seg)) throw new Error(`flat2json: '${key}' is written twice, or once as a value and once as a container`);
5931
- current[seg] = flat[key];
6004
+ set_own_key(current, seg, flat[key]);
5932
6005
  }
5933
6006
  break;
5934
6007
  }
@@ -5941,10 +6014,10 @@ function flat2json(flat) {
5941
6014
  current[seg] = child;
5942
6015
  }
5943
6016
  } else {
5944
- child = current[seg];
6017
+ child = is_prototype_segment(current, seg) ? void 0 : current[seg];
5945
6018
  if (child === void 0) {
5946
6019
  child = next_is_index ? [] : {};
5947
- current[seg] = child;
6020
+ set_own_key(current, seg, child);
5948
6021
  }
5949
6022
  }
5950
6023
  if (child === null || typeof child !== "object") throw new Error(`flat2json: '${key}' walks through a value: the same id is a leaf and a container at once`);
@@ -5995,11 +6068,11 @@ function flat_apply(flat, diff) {
5995
6068
  if (flat === null || typeof flat !== "object" || Array.isArray(flat)) throw new Error("flat_apply: a flat json is an object of id -> value");
5996
6069
  if (diff === null || typeof diff !== "object") throw new Error("flat_apply: a diff is the object flat_diff() answers");
5997
6070
  for (const key of Object.keys(diff.removed || {})) delete flat[key];
5998
- for (const key of Object.keys(diff.added || {})) flat[key] = diff.added[key];
6071
+ for (const key of Object.keys(diff.added || {})) set_own_key(flat, key, diff.added[key]);
5999
6072
  for (const key of Object.keys(diff.changed || {})) {
6000
6073
  const change = diff.changed[key];
6001
6074
  if (!change || !Object.prototype.hasOwnProperty.call(change, "to")) throw new Error(`flat_apply: changed '${key}' without a 'to'`);
6002
- flat[key] = change.to;
6075
+ set_own_key(flat, key, change.to);
6003
6076
  }
6004
6077
  return flat;
6005
6078
  }
@@ -6707,6 +6780,7 @@ function mt_stop$1(gobj) {
6707
6780
  ***************************************************************/
6708
6781
  function mt_play(gobj) {
6709
6782
  let default_service = gobj_default_service();
6783
+ if (!default_service) return 0;
6710
6784
  if (!gobj_is_running(default_service)) gobj_start(default_service);
6711
6785
  if (!gobj_is_playing(default_service)) gobj_play(default_service);
6712
6786
  return 0;
@@ -6715,7 +6789,8 @@ function mt_play(gobj) {
6715
6789
  * Framework Method: Pause
6716
6790
  ***************************************************************/
6717
6791
  function mt_pause(gobj) {
6718
- gobj_pause(gobj_default_service());
6792
+ let default_service = gobj_default_service();
6793
+ if (default_service && gobj_is_playing(default_service)) gobj_pause(default_service);
6719
6794
  return 0;
6720
6795
  }
6721
6796
  /***************************************************************
@@ -6971,6 +7046,7 @@ var __gclass__ = null;
6971
7046
  function mt_create(gobj) {
6972
7047
  let priv = gobj.priv;
6973
7048
  priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
7049
+ priv.ws_seq = 0;
6974
7050
  priv.url = gobj_read_str_attr(gobj, "url");
6975
7051
  priv.remote_yuno_name = gobj_read_str_attr(gobj, "remote_yuno_name");
6976
7052
  priv.remote_yuno_role = gobj_read_str_attr(gobj, "remote_yuno_role");
@@ -7005,6 +7081,16 @@ function mt_writing(gobj, path) {
7005
7081
  function mt_start(gobj) {
7006
7082
  let priv = gobj.priv;
7007
7083
  priv.retry_ms = 0;
7084
+ if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
7085
+ gobj_send_event(gobj, "EV_ON_CLOSE", {
7086
+ url: priv.url,
7087
+ ws_seq: priv.ws_seq,
7088
+ code: 1e3,
7089
+ reason: "restarted",
7090
+ was_clean: true
7091
+ }, gobj);
7092
+ clear_timeout(priv.gobj_timer);
7093
+ }
7008
7094
  priv.websocket = setup_websocket(gobj);
7009
7095
  return 0;
7010
7096
  }
@@ -7128,6 +7214,7 @@ function setup_websocket(gobj) {
7128
7214
  if (gobj.priv.websocket) log_error(`Websocket ALREADY setup`);
7129
7215
  const url = gobj_read_str_attr(gobj, "url");
7130
7216
  log_debug(`====> Starting WebSocket to '${url}' (${gobj_short_name(gobj)})`);
7217
+ const ws_seq = ++gobj.priv.ws_seq;
7131
7218
  let websocket;
7132
7219
  try {
7133
7220
  websocket = new WebSocket(url);
@@ -7140,9 +7227,14 @@ function setup_websocket(gobj) {
7140
7227
  return null;
7141
7228
  }
7142
7229
  websocket.onopen = () => {
7143
- gobj_send_event(gobj, "EV_ON_OPEN", { url }, gobj);
7230
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7231
+ gobj_send_event(gobj, "EV_ON_OPEN", {
7232
+ url,
7233
+ ws_seq
7234
+ }, gobj);
7144
7235
  };
7145
7236
  websocket.onmessage = (e) => {
7237
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7146
7238
  gobj_send_event(gobj, "EV_ON_MESSAGE", {
7147
7239
  url,
7148
7240
  data: e.data
@@ -7150,9 +7242,13 @@ function setup_websocket(gobj) {
7150
7242
  };
7151
7243
  websocket.onclose = (e) => {
7152
7244
  if (gobj_is_destroying(gobj)) return;
7245
+ if (ws_seq !== gobj.priv.ws_seq) return;
7153
7246
  if (!gobj_read_bool_attr(gobj_yuno(), "browser_beforeunload")) gobj_send_event(gobj, "EV_ON_CLOSE", {
7154
7247
  url,
7155
- e
7248
+ ws_seq,
7249
+ code: e.code,
7250
+ reason: e.reason,
7251
+ was_clean: e.wasClean
7156
7252
  }, gobj);
7157
7253
  else log_warning("Close websocket by unload page");
7158
7254
  };
@@ -7173,7 +7269,7 @@ function close_websocket(gobj, code = 1e3, reason = "") {
7173
7269
  try {
7174
7270
  websocket.close(code, reason);
7175
7271
  } catch (e) {
7176
- log_error("Error closing WebSocket:", e.message);
7272
+ log_error(`${gobj_short_name(gobj)}: Error closing WebSocket: ${e.message}`);
7177
7273
  }
7178
7274
  }
7179
7275
  /***************************************************************
@@ -7299,29 +7395,29 @@ function iev_create_from_json(gobj, data) {
7299
7395
  try {
7300
7396
  x = JSON.parse(data);
7301
7397
  } catch (e) {
7302
- log_error("parsing inter_event json: " + e);
7398
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
7303
7399
  return null;
7304
7400
  }
7305
7401
  if (!(x instanceof Object)) {
7306
- log_error("parsing inter_event: websocket data not a json object");
7402
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: websocket data not a json object`);
7307
7403
  return null;
7308
7404
  }
7309
7405
  let event = x["event"];
7310
7406
  if (!event) {
7311
- log_error("parsing inter_event: no event");
7407
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: no event`);
7312
7408
  return null;
7313
7409
  }
7314
7410
  if (typeof event !== "string") {
7315
- log_error("parsing inter_event: event not a string");
7411
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: event not a string`);
7316
7412
  return null;
7317
7413
  }
7318
7414
  let kw = x["kw"];
7319
7415
  if (!kw) {
7320
- log_error("parsing inter_kw: no kw");
7416
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_kw: no kw`);
7321
7417
  return null;
7322
7418
  }
7323
7419
  if (!(kw instanceof Object)) {
7324
- log_error("parsing inter_event: kw not a json object");
7420
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: kw not a json object`);
7325
7421
  return null;
7326
7422
  }
7327
7423
  return {
@@ -7381,10 +7477,9 @@ function ac_on_open(gobj, event, kw, src) {
7381
7477
  ***************************************************************/
7382
7478
  function ac_on_close(gobj, event, kw, src) {
7383
7479
  let priv = gobj.priv;
7384
- let e = kw.e;
7385
7480
  let url = kw.url;
7386
7481
  set_remote_log_functions();
7387
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7482
+ log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7388
7483
  if (priv.websocket) {
7389
7484
  priv.websocket.onopen = null;
7390
7485
  priv.websocket.onmessage = null;
@@ -7401,15 +7496,15 @@ function ac_on_close(gobj, event, kw, src) {
7401
7496
  remote_yuno_service: priv.remote_yuno_service
7402
7497
  });
7403
7498
  } else {
7404
- log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7499
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7405
7500
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
7406
7501
  url,
7407
7502
  remote_yuno_name: priv.remote_yuno_name,
7408
7503
  remote_yuno_role: priv.remote_yuno_role,
7409
7504
  remote_yuno_service: priv.remote_yuno_service,
7410
- code: e.code,
7411
- reason: e.reason,
7412
- was_clean: e.wasClean
7505
+ code: kw.code,
7506
+ reason: kw.reason,
7507
+ was_clean: kw.was_clean
7413
7508
  });
7414
7509
  }
7415
7510
  if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
@@ -7507,6 +7602,10 @@ function ac_identity_card_ack(gobj, event, kw, src) {
7507
7602
  function ac_on_message(gobj, event, kw, src) {
7508
7603
  let url = gobj.priv.url;
7509
7604
  let iev_msg = iev_create_from_json(gobj, kw.data);
7605
+ if (!iev_msg) {
7606
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
7607
+ return -1;
7608
+ }
7510
7609
  if (is_traffic_tracing(gobj)) {
7511
7610
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
7512
7611
  let size = kw.data.length;
@@ -7532,8 +7631,14 @@ function ac_on_message(gobj, event, kw, src) {
7532
7631
  return 0;
7533
7632
  }
7534
7633
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
7535
- if (msg_type === "__subscribing__") return -1;
7536
- if (msg_type === "__unsubscribing__") return -1;
7634
+ if (msg_type === "__subscribing__") {
7635
+ log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7636
+ return -1;
7637
+ }
7638
+ if (msg_type === "__unsubscribing__") {
7639
+ log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7640
+ return -1;
7641
+ }
7537
7642
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7538
7643
  gobj_send_event(gobj, iev_event, iev_kw, gobj);
7539
7644
  return 0;