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