@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.
@@ -161,7 +161,7 @@
161
161
  function build_command_response(gobj, result, comment, schema, data) {
162
162
  return {
163
163
  result,
164
- comment: comment || {},
164
+ comment: comment || "",
165
165
  schema: schema || {},
166
166
  data: data || []
167
167
  };
@@ -215,7 +215,7 @@
215
215
  var __global_stats_parser_fn__ = null;
216
216
  var dl_global_event_types = [];
217
217
  var _gclass_register = Object.create(null);
218
- var __jn_services__ = {};
218
+ var __jn_services__ = Object.create(null);
219
219
  var __yuno__ = null;
220
220
  var __default_service__ = null;
221
221
  var __inside__ = 0;
@@ -824,7 +824,8 @@
824
824
  if (!is_object(jn_value)) jn_value = {};
825
825
  break;
826
826
  case data_type_t.DTP_JSON:
827
- if (is_string(svalue)) try {
827
+ if (svalue === "" || svalue === void 0) jn_value = null;
828
+ else if (is_string(svalue)) try {
828
829
  jn_value = JSON.parse(svalue);
829
830
  } catch (error) {
830
831
  log_error(`JSON parse error: ${error.message}`);
@@ -859,11 +860,17 @@
859
860
  break;
860
861
  case data_type_t.DTP_INTEGER:
861
862
  jn_value2 = parseInt(jn_value_);
862
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
863
+ if (isNaN(jn_value2)) {
864
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
865
+ return -1;
866
+ }
863
867
  break;
864
868
  case data_type_t.DTP_REAL:
865
869
  jn_value2 = Number(jn_value_);
866
- if (isNaN(jn_value2)) log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
870
+ if (isNaN(jn_value2)) {
871
+ log_error(`${gobj_short_name(gobj)}: attr NaN: ${it.name}`);
872
+ return -1;
873
+ }
867
874
  break;
868
875
  case data_type_t.DTP_LIST:
869
876
  if (is_string(jn_value_)) try {
@@ -890,15 +897,14 @@
890
897
  }
891
898
  break;
892
899
  case data_type_t.DTP_JSON:
893
- if (is_string(jn_value_)) try {
894
- jn_value2 = JSON.parse(jn_value_);
895
- } catch (error) {
896
- log_error(`JSON parse error: ${error.message}`);
897
- }
898
- else jn_value2 = jn_value_;
899
- if (!is_array(jn_value2) && !is_object(jn_value2)) {
900
- log_error(`attr must be a json dict/list: ${it.name}`);
901
- return -1;
900
+ jn_value2 = jn_value_;
901
+ if (is_string(jn_value_)) {
902
+ const t = jn_value_.trim();
903
+ if (t.startsWith("{") || t.startsWith("[")) try {
904
+ jn_value2 = JSON.parse(t);
905
+ } catch (error) {
906
+ jn_value2 = jn_value_;
907
+ }
902
908
  }
903
909
  break;
904
910
  case data_type_t.DTP_POINTER: jn_value2 = jn_value_;
@@ -1203,6 +1209,10 @@
1203
1209
  * find a service
1204
1210
  ************************************************************/
1205
1211
  function gobj_find_service(service_name, verbose) {
1212
+ if (!is_string(service_name)) {
1213
+ log_error(`gobj_find_service(): service name must be a string: ${service_name}`);
1214
+ return null;
1215
+ }
1206
1216
  service_name = service_name.toLowerCase();
1207
1217
  let service_gobj = __jn_services__[service_name];
1208
1218
  if (!service_gobj) {
@@ -1360,9 +1370,10 @@
1360
1370
  return null;
1361
1371
  }
1362
1372
  if (is_string(parent)) {
1363
- parent = gobj_find_service(parent);
1373
+ const parent_name = parent;
1374
+ parent = gobj_find_service(parent_name);
1364
1375
  if (!parent) {
1365
- log_warning(`parent service not found: ${parent}`);
1376
+ log_warning(`parent service not found: ${parent_name}`);
1366
1377
  return null;
1367
1378
  }
1368
1379
  }
@@ -1475,6 +1486,7 @@
1475
1486
  parent.gclass.gmt.mt_child_removed(parent, gobj);
1476
1487
  }
1477
1488
  if (gobj.gobj_flag & gobj_flag_t.gobj_flag_service) _deregister_service(gobj);
1489
+ if (gobj === __default_service__) __default_service__ = null;
1478
1490
  gobj_unsubscribe_list(gobj, gobj.dl_subscriptions, true);
1479
1491
  gobj_unsubscribe_list(gobj, gobj.dl_subscribings, true);
1480
1492
  if (parent) {
@@ -1820,7 +1832,7 @@
1820
1832
  log_error(`gobj NULL of bad type`);
1821
1833
  return null;
1822
1834
  }
1823
- if (gobj.bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1835
+ if (gobj.bottom_gobj && bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1824
1836
  gobj.bottom_gobj = bottom_gobj;
1825
1837
  }
1826
1838
  /***************************************************************************
@@ -2235,7 +2247,7 @@
2235
2247
  jn_attrs[key] = value;
2236
2248
  if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(key)) gobj.priv[key] = value;
2237
2249
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2238
- if (gobj.mt_writing) gobj.mt_writing(gobj, key);
2250
+ if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2239
2251
  }
2240
2252
  return 0;
2241
2253
  }
@@ -2259,8 +2271,8 @@
2259
2271
  if (!it.name) continue;
2260
2272
  if (exclude_flag && it.flag & exclude_flag) continue;
2261
2273
  if (include_flag === -1 || it.flag & include_flag) {
2262
- const value = set_default(gobj, gobj.jn_attrs, it);
2263
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = value;
2274
+ set_default(gobj, gobj.jn_attrs, it);
2275
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = gobj.jn_attrs[it.name];
2264
2276
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, it.name);
2265
2277
  }
2266
2278
  }
@@ -2294,7 +2306,7 @@
2294
2306
  let hs = gobj_hsdata2(gobj, name, false);
2295
2307
  if (hs) {
2296
2308
  hs[name] = parseBoolean(value);
2297
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2309
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2298
2310
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2299
2311
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2300
2312
  }
@@ -2310,7 +2322,7 @@
2310
2322
  let hs = gobj_hsdata2(gobj, name, false);
2311
2323
  if (hs) {
2312
2324
  hs[name] = parseInt(value);
2313
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2325
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2314
2326
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2315
2327
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2316
2328
  }
@@ -2326,7 +2338,7 @@
2326
2338
  let hs = gobj_hsdata2(gobj, name, false);
2327
2339
  if (hs) {
2328
2340
  hs[name] = String(value);
2329
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2341
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2330
2342
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2331
2343
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2332
2344
  }
@@ -2380,15 +2392,15 @@
2380
2392
  log_error(`${gobj_short_name(gobj)}: gobj_change_parent() BAD TYPE of parent`);
2381
2393
  return -1;
2382
2394
  }
2383
- if (gobj.parent && gobj.parent.mt_child_removed) gobj.parent.mt_child_removed(gobj.parent, gobj);
2395
+ if (gobj.parent && gobj.parent.gclass.gmt.mt_child_removed) gobj.parent.gclass.gmt.mt_child_removed(gobj.parent, gobj);
2384
2396
  if (gobj.parent) {
2385
2397
  _remove_child(gobj.parent, gobj);
2386
2398
  gobj.parent = null;
2387
2399
  }
2388
2400
  _add_child(parent, gobj);
2389
- if (parent.mt_child_added) {
2401
+ if (parent.gclass.gmt.mt_child_added) {
2390
2402
  if (__trace_gobj_create_delete__(gobj)) log_debug(sprintf("👦👦🔵 child_added(%s): %s", gobj_full_name(parent), gobj_short_name(gobj)));
2391
- parent.mt_child_added(parent, gobj);
2403
+ parent.gclass.gmt.mt_child_added(parent, gobj);
2392
2404
  }
2393
2405
  return 0;
2394
2406
  }
@@ -2500,9 +2512,19 @@
2500
2512
  let snapshot = __dl_posted_events__;
2501
2513
  __dl_posted_events__ = [];
2502
2514
  let delivered = 0;
2503
- for (let msg of snapshot) {
2515
+ for (let i = 0; i < snapshot.length; i++) {
2516
+ const msg = snapshot[i];
2504
2517
  if (msg.dst.obflag & (obflag_t.obflag_destroyed | obflag_t.obflag_destroying)) continue;
2505
- gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2518
+ try {
2519
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2520
+ } catch (e) {
2521
+ __dl_posted_events__ = snapshot.slice(i + 1).concat(__dl_posted_events__);
2522
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2523
+ __posted_events_scheduled__ = true;
2524
+ setTimeout(gobj_deliver_posted_events, 0);
2525
+ }
2526
+ throw e;
2527
+ }
2506
2528
  delivered++;
2507
2529
  }
2508
2530
  if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
@@ -2580,7 +2602,16 @@
2580
2602
  let ret = -1;
2581
2603
  try {
2582
2604
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
2583
- } finally {}
2605
+ } catch (e) {
2606
+ __inside__--;
2607
+ if (!(e && e.__gobj_logged__)) {
2608
+ log_error(`${gobj_short_name(dst)}: action of ${event} in ${state.state_name} THREW: ${e && e.stack ? e.stack : e}`);
2609
+ try {
2610
+ e.__gobj_logged__ = true;
2611
+ } catch (e2) {}
2612
+ }
2613
+ throw e;
2614
+ }
2584
2615
  if (tracea && __trace_machine_format__ !== 1 && !(dst.obflag & obflag_t.obflag_destroyed)) trace_machine(sprintf("<- mach(%s%s^%s), st: %s, ev: %s, ret: %d", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), dst.current_state.state_name, event ? event : "", is_number(ret) ? ret : 0));
2585
2616
  __inside__--;
2586
2617
  return ret;
@@ -3251,10 +3282,22 @@
3251
3282
  f_warning = _console.warn;
3252
3283
  }
3253
3284
  }
3285
+ /************************************************************
3286
+ * JSON.stringify for a log line: a circular object (a gobj,
3287
+ * a DOM node) must not make the log call itself throw.
3288
+ ************************************************************/
3289
+ function log_stringify(v) {
3290
+ try {
3291
+ return JSON.stringify(v);
3292
+ } catch (e) {
3293
+ return String(v);
3294
+ }
3295
+ }
3254
3296
  function format_log(format) {
3255
3297
  const args = Array.prototype.slice.call(arguments);
3256
3298
  let output = "";
3257
3299
  if (typeof format === "string") {
3300
+ if (args.length === 1) return format;
3258
3301
  let i = 1;
3259
3302
  output = format.replace(/%[sdifoO]/g, (match) => {
3260
3303
  const val = args[i++];
@@ -3264,17 +3307,17 @@
3264
3307
  case "%i": return parseInt(val);
3265
3308
  case "%f": return parseFloat(val);
3266
3309
  case "%o":
3267
- case "%O": return typeof val === "object" ? JSON.stringify(val) : String(val);
3310
+ case "%O": return typeof val === "object" ? log_stringify(val) : String(val);
3268
3311
  default: return match;
3269
3312
  }
3270
3313
  });
3271
- const rest = args.slice(i).map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg));
3314
+ const rest = args.slice(i).map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg));
3272
3315
  if (rest.length > 0) output += " " + rest.join(" ");
3273
- } else output = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
3316
+ } else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg)).join(" ");
3274
3317
  return output;
3275
3318
  }
3276
- function log_error(format) {
3277
- let msg = format_log(format);
3319
+ function log_error(format, ...args) {
3320
+ let msg = format_log(format, ...args);
3278
3321
  let hora = current_timestamp();
3279
3322
  if (f_error) {
3280
3323
  if (f_error === _console.error) {
@@ -3286,8 +3329,8 @@
3286
3329
  }
3287
3330
  emit_log_callback("error", msg, hora);
3288
3331
  }
3289
- function log_warning(format) {
3290
- let msg = format_log(format);
3332
+ function log_warning(format, ...args) {
3333
+ let msg = format_log(format, ...args);
3291
3334
  let hora = current_timestamp();
3292
3335
  if (f_warning) {
3293
3336
  if (f_warning === _console.warn) {
@@ -3299,24 +3342,24 @@
3299
3342
  }
3300
3343
  emit_log_callback("warning", msg, hora);
3301
3344
  }
3302
- function log_info(format) {
3303
- let msg = format_log(format);
3345
+ function log_info(format, ...args) {
3346
+ let msg = format_log(format, ...args);
3304
3347
  let hora = current_timestamp();
3305
3348
  if (f_info) {
3306
3349
  if (console_wants("info", msg)) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
3307
3350
  }
3308
3351
  emit_log_callback("info", msg, hora);
3309
3352
  }
3310
- function log_debug(format) {
3311
- let msg = format_log(format);
3353
+ function log_debug(format, ...args) {
3354
+ let msg = format_log(format, ...args);
3312
3355
  let hora = current_timestamp();
3313
3356
  if (f_debug) {
3314
3357
  if (console_wants("debug", msg)) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
3315
3358
  }
3316
3359
  emit_log_callback("debug", msg, hora);
3317
3360
  }
3318
- function trace_msg(format) {
3319
- let msg = format_log(format);
3361
+ function trace_msg(format, ...args) {
3362
+ let msg = format_log(format, ...args);
3320
3363
  let hora = current_timestamp();
3321
3364
  if (f_debug) {
3322
3365
  if (console_wants("msg", msg)) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
@@ -3570,7 +3613,7 @@
3570
3613
  if ((is_secret_name(key.slice(-128)) || value_is_secret && key === "value") && value !== void 0 && value !== null && value !== "") shown = "********";
3571
3614
  else shown = _json_mask_secrets(value, memo, depth + 1, budget);
3572
3615
  if (shown !== value) changed = true;
3573
- result[key] = shown;
3616
+ set_own_key(result, key, shown);
3574
3617
  }
3575
3618
  if (truncated) result["<more>"] = MASK_TOO_LARGE;
3576
3619
  }
@@ -3642,7 +3685,7 @@
3642
3685
  ************************************************************/
3643
3686
  function json_object_update(destination, source) {
3644
3687
  if (!source) return destination;
3645
- for (let property of Object.keys(source)) destination[property] = source[property];
3688
+ for (let property of Object.keys(source)) set_own_key(destination, property, source[property]);
3646
3689
  return destination;
3647
3690
  }
3648
3691
  /************************************************************
@@ -3651,7 +3694,7 @@
3651
3694
  ************************************************************/
3652
3695
  function json_object_update_existing(destination, source) {
3653
3696
  if (!source) return destination;
3654
- for (let property in source) if (source.hasOwnProperty(property) && destination.hasOwnProperty(property)) destination[property] = source[property];
3697
+ 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]);
3655
3698
  return destination;
3656
3699
  }
3657
3700
  /************************************************************
@@ -3660,32 +3703,32 @@
3660
3703
  ************************************************************/
3661
3704
  function json_object_update_missing(destination, source) {
3662
3705
  if (!source) return destination;
3663
- for (let property in source) if (source.hasOwnProperty(property) && !destination.hasOwnProperty(property)) destination[property] = source[property];
3706
+ 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]);
3664
3707
  return destination;
3665
3708
  }
3666
3709
  /************************************************************
3667
3710
  * Simulate jansson function
3668
3711
  ************************************************************/
3669
3712
  function json_object_get(o, key) {
3670
- if (o && o.hasOwnProperty(key)) return o[key];
3713
+ if (o && Object.prototype.hasOwnProperty.call(o, key)) return o[key];
3671
3714
  }
3672
3715
  /************************************************************
3673
3716
  * Simulate jansson function
3674
3717
  ************************************************************/
3675
3718
  function json_object_del(o, k) {
3676
- if (o && o.hasOwnProperty(k)) delete o[k];
3719
+ if (o && Object.prototype.hasOwnProperty.call(o, k)) delete o[k];
3677
3720
  }
3678
3721
  /************************************************************
3679
3722
  * Simulate jansson function
3680
3723
  ************************************************************/
3681
3724
  function json_object_set(o, k, v) {
3682
- o[k] = v;
3725
+ set_own_key(o, k, v);
3683
3726
  }
3684
3727
  /************************************************************
3685
3728
  * Simulate jansson function
3686
3729
  ************************************************************/
3687
3730
  function json_object_set_new(o, k, v) {
3688
- o[k] = v;
3731
+ set_own_key(o, k, v);
3689
3732
  }
3690
3733
  /************************************************************
3691
3734
  * Simulate jansson function
@@ -3865,6 +3908,8 @@
3865
3908
  if (is_number(jn_var1) || is_number(jn_var2)) {
3866
3909
  let val1 = Number(jn_var1);
3867
3910
  let val2 = Number(jn_var2);
3911
+ if (Number.isNaN(val1)) val1 = 0;
3912
+ if (Number.isNaN(val2)) val2 = 0;
3868
3913
  if (val1 > val2) return 1;
3869
3914
  else if (val1 < val2) return -1;
3870
3915
  else return 0;
@@ -3927,11 +3972,31 @@
3927
3972
  ************************************************************/
3928
3973
  function kw_has_key(kw, key) {
3929
3974
  if (key in kw) {
3930
- if (kw.hasOwnProperty(key)) return true;
3975
+ if (Object.prototype.hasOwnProperty.call(kw, key)) return true;
3931
3976
  }
3932
3977
  return false;
3933
3978
  }
3934
3979
  /************************************************************
3980
+ * A key that reaches the prototype chain instead of the data:
3981
+ * any key the object does not hold as its OWN key. A walk through one of them
3982
+ * read, or WROTE, Object.prototype: the flat id
3983
+ * "__proto__`polluted" set a key on every object of the page.
3984
+ * In C they are keys like any other, so here too: read them
3985
+ * only as own keys, and write "__proto__" as an own key.
3986
+ ************************************************************/
3987
+ function is_prototype_segment(obj, key) {
3988
+ return !Object.prototype.hasOwnProperty.call(obj, key);
3989
+ }
3990
+ function set_own_key(obj, key, value) {
3991
+ if (key === "__proto__") Object.defineProperty(obj, key, {
3992
+ value,
3993
+ writable: true,
3994
+ enumerable: true,
3995
+ configurable: true
3996
+ });
3997
+ else obj[key] = value;
3998
+ }
3999
+ /************************************************************
3935
4000
  * The value at `path` (keys joined by a backtick), as the C
3936
4001
  * kw_find_path() finds it: undefined when it is not there.
3937
4002
  * A kw that is not a dict or a list is LOGGED ("kw must be list
@@ -3958,7 +4023,12 @@
3958
4023
  }
3959
4024
  let ss = path.split("`");
3960
4025
  let len = ss.length;
3961
- for (let i = 0; i < len - 1; i++) {
4026
+ for (let i = 0; i < len; i++) {
4027
+ if (is_prototype_segment(kw, ss[i])) {
4028
+ if (verbose) log_error(`${prefix}path not found: '${path}'`);
4029
+ return;
4030
+ }
4031
+ if (i === len - 1) break;
3962
4032
  kw = kw[ss[i]];
3963
4033
  if (kw === void 0 || kw === null) {
3964
4034
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -4341,17 +4411,13 @@
4341
4411
  return -1;
4342
4412
  }
4343
4413
  let ss = path.split("`");
4344
- if (ss.length <= 1) {
4345
- kw[path] = value;
4346
- return 0;
4347
- }
4348
4414
  let len = ss.length;
4349
4415
  for (let i = 0; i < len; i++) {
4350
4416
  let key = ss[i];
4351
- if (i === len - 1) kw[key] = value;
4417
+ if (i === len - 1) set_own_key(kw, key, value);
4352
4418
  else {
4353
- let seg = kw[key];
4354
- if (seg === void 0) kw[key] = {};
4419
+ let seg = is_prototype_segment(kw, key) ? void 0 : kw[key];
4420
+ if (seg === void 0) set_own_key(kw, key, {});
4355
4421
  else if (!is_object(seg) && !is_array(seg)) {
4356
4422
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not a dict or a list: '${path}'`);
4357
4423
  return -1;
@@ -4406,9 +4472,9 @@
4406
4472
  if (!matched) break;
4407
4473
  continue;
4408
4474
  }
4409
- let jn_record_value = kw_get_dict_value(0, kw, path, 0, 0);
4410
- if (!jn_record_value) jn_record_value = json_object_get(kw, path);
4411
- if (!jn_record_value) {
4475
+ let jn_record_value = kw_get_dict_value(0, kw, path, void 0, 0);
4476
+ if (jn_record_value === void 0) jn_record_value = json_object_get(kw, path);
4477
+ if (jn_record_value === void 0) {
4412
4478
  matched = false;
4413
4479
  break;
4414
4480
  }
@@ -4502,14 +4568,14 @@
4502
4568
  let kw_clone = {};
4503
4569
  if (is_string(keys) && !empty_string(keys)) {
4504
4570
  let key = keys;
4505
- if (kw.hasOwnProperty(key)) kw_clone[key] = kw[key];
4571
+ 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_object(keys) && json_object_size(keys) > 0) {
4508
- for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) kw_clone[key] = kw[key];
4574
+ for (let key of Object.keys(keys)) 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
  } else if (is_array(keys) && json_array_size(keys) > 0) for (let j = 0; j < keys.length; j++) {
4511
4577
  let key = keys[j];
4512
- if (kw.hasOwnProperty(key)) kw_clone[key] = kw[key];
4578
+ if (Object.prototype.hasOwnProperty.call(kw, key)) set_own_key(kw_clone, key, kw[key]);
4513
4579
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4514
4580
  }
4515
4581
  else return kw;
@@ -4527,17 +4593,18 @@
4527
4593
  ***************************************************************************/
4528
4594
  function kw_clone_by_not_keys(gobj, kw, keys, verbose) {
4529
4595
  let kw_clone = {};
4596
+ if (!is_object(kw)) return kw_clone;
4530
4597
  json_object_update(kw_clone, kw);
4531
4598
  if (is_string(keys) && !empty_string(keys)) {
4532
4599
  let key = keys;
4533
- if (kw_get_dict_value(gobj, kw, key, 0, 0)) json_object_del(kw_clone, key);
4600
+ 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_object(keys) && json_object_size(keys) > 0) {
4536
- for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4603
+ for (let key of Object.keys(keys)) 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
  } else if (is_array(keys) && json_array_size(keys) > 0) for (let j = 0; j < keys.length; j++) {
4539
4606
  let key = keys[j];
4540
- if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4607
+ if (Object.prototype.hasOwnProperty.call(kw, key)) json_object_del(kw_clone, key);
4541
4608
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4542
4609
  }
4543
4610
  else return {};
@@ -4768,7 +4835,7 @@
4768
4835
  let new_ids = [];
4769
4836
  if (is_string(ids)) new_ids.push(ids);
4770
4837
  else if (is_object(ids)) {
4771
- for (let id in ids) if (ids.hasOwnProperty(id)) new_ids.push(id);
4838
+ for (let id in ids) if (Object.prototype.hasOwnProperty.call(ids, id)) new_ids.push(id);
4772
4839
  } else if (is_array(ids)) ids.forEach(function(item) {
4773
4840
  if (is_string(item)) {
4774
4841
  if (!empty_string(item)) new_ids.push(item);
@@ -5021,7 +5088,7 @@
5021
5088
  function _traverse_dict(obj, callback, full_path) {
5022
5089
  if (full_path === void 0 || !is_string(full_path)) full_path = "";
5023
5090
  for (let key of Object.keys(obj)) {
5024
- if (!obj.hasOwnProperty(key)) continue;
5091
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
5025
5092
  let sufix = (full_path.length ? "`" : "") + key;
5026
5093
  full_path += sufix;
5027
5094
  callback.apply(this, [
@@ -5490,9 +5557,15 @@
5490
5557
  * will be 'data-i18n' attr
5491
5558
  *
5492
5559
  * content -> string | [createElement2() or parameters of createElement2]
5560
+ * | HTMLElement | SVGElement | Text
5493
5561
  * if string begins with '<' then
5494
5562
  * it will be converted to HTMLElement
5495
5563
  * content = createOneHtml(string)
5564
+ * ⚠️ So a string is MARKUP when it starts with '<': never
5565
+ * pass DATA (a value from a backend, a name a user typed)
5566
+ * as a bare string, or "<img src=x onerror=...>" runs.
5567
+ * Pass it as a Text node, which is always text:
5568
+ * ['span', {}, document.createTextNode(value)]
5496
5569
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5497
5570
  * it will be overridden with the translation
5498
5571
  * content = translate(attrs['i18n']));
@@ -5896,7 +5969,7 @@
5896
5969
  return;
5897
5970
  }
5898
5971
  if (depth === 0) return;
5899
- flat[path] = value;
5972
+ set_own_key(flat, path, value);
5900
5973
  };
5901
5974
  if (jn === void 0 || jn === null) return flat;
5902
5975
  walk(jn, "", 0);
@@ -5931,7 +6004,7 @@
5931
6004
  current[seg] = flat[key];
5932
6005
  } else {
5933
6006
  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`);
5934
- current[seg] = flat[key];
6007
+ set_own_key(current, seg, flat[key]);
5935
6008
  }
5936
6009
  break;
5937
6010
  }
@@ -5944,10 +6017,10 @@
5944
6017
  current[seg] = child;
5945
6018
  }
5946
6019
  } else {
5947
- child = current[seg];
6020
+ child = is_prototype_segment(current, seg) ? void 0 : current[seg];
5948
6021
  if (child === void 0) {
5949
6022
  child = next_is_index ? [] : {};
5950
- current[seg] = child;
6023
+ set_own_key(current, seg, child);
5951
6024
  }
5952
6025
  }
5953
6026
  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`);
@@ -5998,11 +6071,11 @@
5998
6071
  if (flat === null || typeof flat !== "object" || Array.isArray(flat)) throw new Error("flat_apply: a flat json is an object of id -> value");
5999
6072
  if (diff === null || typeof diff !== "object") throw new Error("flat_apply: a diff is the object flat_diff() answers");
6000
6073
  for (const key of Object.keys(diff.removed || {})) delete flat[key];
6001
- for (const key of Object.keys(diff.added || {})) flat[key] = diff.added[key];
6074
+ for (const key of Object.keys(diff.added || {})) set_own_key(flat, key, diff.added[key]);
6002
6075
  for (const key of Object.keys(diff.changed || {})) {
6003
6076
  const change = diff.changed[key];
6004
6077
  if (!change || !Object.prototype.hasOwnProperty.call(change, "to")) throw new Error(`flat_apply: changed '${key}' without a 'to'`);
6005
- flat[key] = change.to;
6078
+ set_own_key(flat, key, change.to);
6006
6079
  }
6007
6080
  return flat;
6008
6081
  }
@@ -6710,6 +6783,7 @@
6710
6783
  ***************************************************************/
6711
6784
  function mt_play(gobj) {
6712
6785
  let default_service = gobj_default_service();
6786
+ if (!default_service) return 0;
6713
6787
  if (!gobj_is_running(default_service)) gobj_start(default_service);
6714
6788
  if (!gobj_is_playing(default_service)) gobj_play(default_service);
6715
6789
  return 0;
@@ -6718,7 +6792,8 @@
6718
6792
  * Framework Method: Pause
6719
6793
  ***************************************************************/
6720
6794
  function mt_pause(gobj) {
6721
- gobj_pause(gobj_default_service());
6795
+ let default_service = gobj_default_service();
6796
+ if (default_service && gobj_is_playing(default_service)) gobj_pause(default_service);
6722
6797
  return 0;
6723
6798
  }
6724
6799
  /***************************************************************
@@ -6974,6 +7049,7 @@
6974
7049
  function mt_create(gobj) {
6975
7050
  let priv = gobj.priv;
6976
7051
  priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
7052
+ priv.ws_seq = 0;
6977
7053
  priv.url = gobj_read_str_attr(gobj, "url");
6978
7054
  priv.remote_yuno_name = gobj_read_str_attr(gobj, "remote_yuno_name");
6979
7055
  priv.remote_yuno_role = gobj_read_str_attr(gobj, "remote_yuno_role");
@@ -7008,6 +7084,16 @@
7008
7084
  function mt_start(gobj) {
7009
7085
  let priv = gobj.priv;
7010
7086
  priv.retry_ms = 0;
7087
+ if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
7088
+ gobj_send_event(gobj, "EV_ON_CLOSE", {
7089
+ url: priv.url,
7090
+ ws_seq: priv.ws_seq,
7091
+ code: 1e3,
7092
+ reason: "restarted",
7093
+ was_clean: true
7094
+ }, gobj);
7095
+ clear_timeout(priv.gobj_timer);
7096
+ }
7011
7097
  priv.websocket = setup_websocket(gobj);
7012
7098
  return 0;
7013
7099
  }
@@ -7131,6 +7217,7 @@
7131
7217
  if (gobj.priv.websocket) log_error(`Websocket ALREADY setup`);
7132
7218
  const url = gobj_read_str_attr(gobj, "url");
7133
7219
  log_debug(`====> Starting WebSocket to '${url}' (${gobj_short_name(gobj)})`);
7220
+ const ws_seq = ++gobj.priv.ws_seq;
7134
7221
  let websocket;
7135
7222
  try {
7136
7223
  websocket = new WebSocket(url);
@@ -7143,9 +7230,14 @@
7143
7230
  return null;
7144
7231
  }
7145
7232
  websocket.onopen = () => {
7146
- gobj_send_event(gobj, "EV_ON_OPEN", { url }, gobj);
7233
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7234
+ gobj_send_event(gobj, "EV_ON_OPEN", {
7235
+ url,
7236
+ ws_seq
7237
+ }, gobj);
7147
7238
  };
7148
7239
  websocket.onmessage = (e) => {
7240
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7149
7241
  gobj_send_event(gobj, "EV_ON_MESSAGE", {
7150
7242
  url,
7151
7243
  data: e.data
@@ -7153,9 +7245,13 @@
7153
7245
  };
7154
7246
  websocket.onclose = (e) => {
7155
7247
  if (gobj_is_destroying(gobj)) return;
7248
+ if (ws_seq !== gobj.priv.ws_seq) return;
7156
7249
  if (!gobj_read_bool_attr(gobj_yuno(), "browser_beforeunload")) gobj_send_event(gobj, "EV_ON_CLOSE", {
7157
7250
  url,
7158
- e
7251
+ ws_seq,
7252
+ code: e.code,
7253
+ reason: e.reason,
7254
+ was_clean: e.wasClean
7159
7255
  }, gobj);
7160
7256
  else log_warning("Close websocket by unload page");
7161
7257
  };
@@ -7176,7 +7272,7 @@
7176
7272
  try {
7177
7273
  websocket.close(code, reason);
7178
7274
  } catch (e) {
7179
- log_error("Error closing WebSocket:", e.message);
7275
+ log_error(`${gobj_short_name(gobj)}: Error closing WebSocket: ${e.message}`);
7180
7276
  }
7181
7277
  }
7182
7278
  /***************************************************************
@@ -7302,29 +7398,29 @@
7302
7398
  try {
7303
7399
  x = JSON.parse(data);
7304
7400
  } catch (e) {
7305
- log_error("parsing inter_event json: " + e);
7401
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
7306
7402
  return null;
7307
7403
  }
7308
7404
  if (!(x instanceof Object)) {
7309
- log_error("parsing inter_event: websocket data not a json object");
7405
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: websocket data not a json object`);
7310
7406
  return null;
7311
7407
  }
7312
7408
  let event = x["event"];
7313
7409
  if (!event) {
7314
- log_error("parsing inter_event: no event");
7410
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: no event`);
7315
7411
  return null;
7316
7412
  }
7317
7413
  if (typeof event !== "string") {
7318
- log_error("parsing inter_event: event not a string");
7414
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: event not a string`);
7319
7415
  return null;
7320
7416
  }
7321
7417
  let kw = x["kw"];
7322
7418
  if (!kw) {
7323
- log_error("parsing inter_kw: no kw");
7419
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_kw: no kw`);
7324
7420
  return null;
7325
7421
  }
7326
7422
  if (!(kw instanceof Object)) {
7327
- log_error("parsing inter_event: kw not a json object");
7423
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: kw not a json object`);
7328
7424
  return null;
7329
7425
  }
7330
7426
  return {
@@ -7384,10 +7480,9 @@
7384
7480
  ***************************************************************/
7385
7481
  function ac_on_close(gobj, event, kw, src) {
7386
7482
  let priv = gobj.priv;
7387
- let e = kw.e;
7388
7483
  let url = kw.url;
7389
7484
  set_remote_log_functions();
7390
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7485
+ log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7391
7486
  if (priv.websocket) {
7392
7487
  priv.websocket.onopen = null;
7393
7488
  priv.websocket.onmessage = null;
@@ -7404,15 +7499,15 @@
7404
7499
  remote_yuno_service: priv.remote_yuno_service
7405
7500
  });
7406
7501
  } else {
7407
- log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7502
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7408
7503
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
7409
7504
  url,
7410
7505
  remote_yuno_name: priv.remote_yuno_name,
7411
7506
  remote_yuno_role: priv.remote_yuno_role,
7412
7507
  remote_yuno_service: priv.remote_yuno_service,
7413
- code: e.code,
7414
- reason: e.reason,
7415
- was_clean: e.wasClean
7508
+ code: kw.code,
7509
+ reason: kw.reason,
7510
+ was_clean: kw.was_clean
7416
7511
  });
7417
7512
  }
7418
7513
  if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
@@ -7510,6 +7605,10 @@
7510
7605
  function ac_on_message(gobj, event, kw, src) {
7511
7606
  let url = gobj.priv.url;
7512
7607
  let iev_msg = iev_create_from_json(gobj, kw.data);
7608
+ if (!iev_msg) {
7609
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
7610
+ return -1;
7611
+ }
7513
7612
  if (is_traffic_tracing(gobj)) {
7514
7613
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
7515
7614
  let size = kw.data.length;
@@ -7535,8 +7634,14 @@
7535
7634
  return 0;
7536
7635
  }
7537
7636
  let iev_dst_service = kw_get_str(gobj, event_id, "dst_service", "");
7538
- if (msg_type === "__subscribing__") return -1;
7539
- if (msg_type === "__unsubscribing__") return -1;
7637
+ if (msg_type === "__subscribing__") {
7638
+ log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7639
+ return -1;
7640
+ }
7641
+ if (msg_type === "__unsubscribing__") {
7642
+ log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7643
+ return -1;
7644
+ }
7540
7645
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7541
7646
  gobj_send_event(gobj, iev_event, iev_kw, gobj);
7542
7647
  return 0;