@yuneta/gobj-js 7.25.9 → 7.26.0

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,11 @@ function json2item(gobj, sdata, it, jn_value_) {
887
894
  }
888
895
  break;
889
896
  case data_type_t.DTP_JSON:
897
+ jn_value2 = jn_value_;
890
898
  if (is_string(jn_value_)) try {
891
899
  jn_value2 = JSON.parse(jn_value_);
892
900
  } 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;
901
+ jn_value2 = jn_value_;
899
902
  }
900
903
  break;
901
904
  case data_type_t.DTP_POINTER: jn_value2 = jn_value_;
@@ -1200,6 +1203,10 @@ function gobj_default_service() {
1200
1203
  * find a service
1201
1204
  ************************************************************/
1202
1205
  function gobj_find_service(service_name, verbose) {
1206
+ if (!is_string(service_name)) {
1207
+ log_error(`gobj_find_service(): service name must be a string: ${service_name}`);
1208
+ return null;
1209
+ }
1203
1210
  service_name = service_name.toLowerCase();
1204
1211
  let service_gobj = __jn_services__[service_name];
1205
1212
  if (!service_gobj) {
@@ -1357,9 +1364,10 @@ function gobj_create2(gobj_name, gclass_name, kw, parent, gobj_flag) {
1357
1364
  return null;
1358
1365
  }
1359
1366
  if (is_string(parent)) {
1360
- parent = gobj_find_service(parent);
1367
+ const parent_name = parent;
1368
+ parent = gobj_find_service(parent_name);
1361
1369
  if (!parent) {
1362
- log_warning(`parent service not found: ${parent}`);
1370
+ log_warning(`parent service not found: ${parent_name}`);
1363
1371
  return null;
1364
1372
  }
1365
1373
  }
@@ -1472,6 +1480,7 @@ function gobj_destroy(gobj) {
1472
1480
  parent.gclass.gmt.mt_child_removed(parent, gobj);
1473
1481
  }
1474
1482
  if (gobj.gobj_flag & gobj_flag_t.gobj_flag_service) _deregister_service(gobj);
1483
+ if (gobj === __default_service__) __default_service__ = null;
1475
1484
  gobj_unsubscribe_list(gobj, gobj.dl_subscriptions, true);
1476
1485
  gobj_unsubscribe_list(gobj, gobj.dl_subscribings, true);
1477
1486
  if (parent) {
@@ -1817,7 +1826,7 @@ function gobj_set_bottom_gobj(gobj, bottom_gobj) {
1817
1826
  log_error(`gobj NULL of bad type`);
1818
1827
  return null;
1819
1828
  }
1820
- if (gobj.bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1829
+ if (gobj.bottom_gobj && bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1821
1830
  gobj.bottom_gobj = bottom_gobj;
1822
1831
  }
1823
1832
  /***************************************************************************
@@ -2232,7 +2241,7 @@ function gobj_write_attr(gobj, path, value, src) {
2232
2241
  jn_attrs[key] = value;
2233
2242
  if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(key)) gobj.priv[key] = value;
2234
2243
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2235
- if (gobj.mt_writing) gobj.mt_writing(gobj, key);
2244
+ if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2236
2245
  }
2237
2246
  return 0;
2238
2247
  }
@@ -2256,8 +2265,8 @@ function sdata_write_default_values(gobj, include_flag, exclude_flag) {
2256
2265
  if (!it.name) continue;
2257
2266
  if (exclude_flag && it.flag & exclude_flag) continue;
2258
2267
  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;
2268
+ set_default(gobj, gobj.jn_attrs, it);
2269
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = gobj.jn_attrs[it.name];
2261
2270
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, it.name);
2262
2271
  }
2263
2272
  }
@@ -2291,7 +2300,7 @@ function gobj_write_bool_attr(gobj, name, value) {
2291
2300
  let hs = gobj_hsdata2(gobj, name, false);
2292
2301
  if (hs) {
2293
2302
  hs[name] = parseBoolean(value);
2294
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2303
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2295
2304
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2296
2305
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2297
2306
  }
@@ -2307,7 +2316,7 @@ function gobj_write_integer_attr(gobj, name, value) {
2307
2316
  let hs = gobj_hsdata2(gobj, name, false);
2308
2317
  if (hs) {
2309
2318
  hs[name] = parseInt(value);
2310
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2319
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2311
2320
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2312
2321
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2313
2322
  }
@@ -2323,7 +2332,7 @@ function gobj_write_str_attr(gobj, name, value) {
2323
2332
  let hs = gobj_hsdata2(gobj, name, false);
2324
2333
  if (hs) {
2325
2334
  hs[name] = String(value);
2326
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2335
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2327
2336
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2328
2337
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2329
2338
  }
@@ -2377,15 +2386,15 @@ function gobj_change_parent(gobj, parent) {
2377
2386
  log_error(`${gobj_short_name(gobj)}: gobj_change_parent() BAD TYPE of parent`);
2378
2387
  return -1;
2379
2388
  }
2380
- if (gobj.parent && gobj.parent.mt_child_removed) gobj.parent.mt_child_removed(gobj.parent, gobj);
2389
+ if (gobj.parent && gobj.parent.gclass.gmt.mt_child_removed) gobj.parent.gclass.gmt.mt_child_removed(gobj.parent, gobj);
2381
2390
  if (gobj.parent) {
2382
2391
  _remove_child(gobj.parent, gobj);
2383
2392
  gobj.parent = null;
2384
2393
  }
2385
2394
  _add_child(parent, gobj);
2386
- if (parent.mt_child_added) {
2395
+ if (parent.gclass.gmt.mt_child_added) {
2387
2396
  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);
2397
+ parent.gclass.gmt.mt_child_added(parent, gobj);
2389
2398
  }
2390
2399
  return 0;
2391
2400
  }
@@ -2497,9 +2506,19 @@ function gobj_deliver_posted_events() {
2497
2506
  let snapshot = __dl_posted_events__;
2498
2507
  __dl_posted_events__ = [];
2499
2508
  let delivered = 0;
2500
- for (let msg of snapshot) {
2509
+ for (let i = 0; i < snapshot.length; i++) {
2510
+ const msg = snapshot[i];
2501
2511
  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);
2512
+ try {
2513
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2514
+ } catch (e) {
2515
+ __dl_posted_events__ = snapshot.slice(i + 1).concat(__dl_posted_events__);
2516
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2517
+ __posted_events_scheduled__ = true;
2518
+ setTimeout(gobj_deliver_posted_events, 0);
2519
+ }
2520
+ throw e;
2521
+ }
2503
2522
  delivered++;
2504
2523
  }
2505
2524
  if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
@@ -2577,7 +2596,16 @@ function gobj_send_event(dst, event, kw, src) {
2577
2596
  let ret = -1;
2578
2597
  try {
2579
2598
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
2580
- } finally {}
2599
+ } catch (e) {
2600
+ __inside__--;
2601
+ if (!(e && e.__gobj_logged__)) {
2602
+ log_error(`${gobj_short_name(dst)}: action of ${event} in ${dst.current_state.state_name} THREW: ${e && e.stack ? e.stack : e}`);
2603
+ try {
2604
+ e.__gobj_logged__ = true;
2605
+ } catch (e2) {}
2606
+ }
2607
+ throw e;
2608
+ }
2581
2609
  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
2610
  __inside__--;
2583
2611
  return ret;
@@ -3248,10 +3276,22 @@ function set_remote_log_functions(remote_log_fn) {
3248
3276
  f_warning = _console.warn;
3249
3277
  }
3250
3278
  }
3279
+ /************************************************************
3280
+ * JSON.stringify for a log line: a circular object (a gobj,
3281
+ * a DOM node) must not make the log call itself throw.
3282
+ ************************************************************/
3283
+ function log_stringify(v) {
3284
+ try {
3285
+ return JSON.stringify(v);
3286
+ } catch (e) {
3287
+ return String(v);
3288
+ }
3289
+ }
3251
3290
  function format_log(format) {
3252
3291
  const args = Array.prototype.slice.call(arguments);
3253
3292
  let output = "";
3254
3293
  if (typeof format === "string") {
3294
+ if (args.length === 1) return format;
3255
3295
  let i = 1;
3256
3296
  output = format.replace(/%[sdifoO]/g, (match) => {
3257
3297
  const val = args[i++];
@@ -3261,17 +3301,17 @@ function format_log(format) {
3261
3301
  case "%i": return parseInt(val);
3262
3302
  case "%f": return parseFloat(val);
3263
3303
  case "%o":
3264
- case "%O": return typeof val === "object" ? JSON.stringify(val) : String(val);
3304
+ case "%O": return typeof val === "object" ? log_stringify(val) : String(val);
3265
3305
  default: return match;
3266
3306
  }
3267
3307
  });
3268
- const rest = args.slice(i).map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg));
3308
+ const rest = args.slice(i).map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg));
3269
3309
  if (rest.length > 0) output += " " + rest.join(" ");
3270
- } else output = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
3310
+ } else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg)).join(" ");
3271
3311
  return output;
3272
3312
  }
3273
- function log_error(format) {
3274
- let msg = format_log(format);
3313
+ function log_error(format, ...args) {
3314
+ let msg = format_log(format, ...args);
3275
3315
  let hora = current_timestamp();
3276
3316
  if (f_error) {
3277
3317
  if (f_error === _console.error) {
@@ -3283,8 +3323,8 @@ function log_error(format) {
3283
3323
  }
3284
3324
  emit_log_callback("error", msg, hora);
3285
3325
  }
3286
- function log_warning(format) {
3287
- let msg = format_log(format);
3326
+ function log_warning(format, ...args) {
3327
+ let msg = format_log(format, ...args);
3288
3328
  let hora = current_timestamp();
3289
3329
  if (f_warning) {
3290
3330
  if (f_warning === _console.warn) {
@@ -3296,24 +3336,24 @@ function log_warning(format) {
3296
3336
  }
3297
3337
  emit_log_callback("warning", msg, hora);
3298
3338
  }
3299
- function log_info(format) {
3300
- let msg = format_log(format);
3339
+ function log_info(format, ...args) {
3340
+ let msg = format_log(format, ...args);
3301
3341
  let hora = current_timestamp();
3302
3342
  if (f_info) {
3303
3343
  if (console_wants("info", msg)) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
3304
3344
  }
3305
3345
  emit_log_callback("info", msg, hora);
3306
3346
  }
3307
- function log_debug(format) {
3308
- let msg = format_log(format);
3347
+ function log_debug(format, ...args) {
3348
+ let msg = format_log(format, ...args);
3309
3349
  let hora = current_timestamp();
3310
3350
  if (f_debug) {
3311
3351
  if (console_wants("debug", msg)) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
3312
3352
  }
3313
3353
  emit_log_callback("debug", msg, hora);
3314
3354
  }
3315
- function trace_msg(format) {
3316
- let msg = format_log(format);
3355
+ function trace_msg(format, ...args) {
3356
+ let msg = format_log(format, ...args);
3317
3357
  let hora = current_timestamp();
3318
3358
  if (f_debug) {
3319
3359
  if (console_wants("msg", msg)) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
@@ -3862,6 +3902,8 @@ function cmp_two_simple_json(jn_var1, jn_var2) {
3862
3902
  if (is_number(jn_var1) || is_number(jn_var2)) {
3863
3903
  let val1 = Number(jn_var1);
3864
3904
  let val2 = Number(jn_var2);
3905
+ if (Number.isNaN(val1)) val1 = 0;
3906
+ if (Number.isNaN(val2)) val2 = 0;
3865
3907
  if (val1 > val2) return 1;
3866
3908
  else if (val1 < val2) return -1;
3867
3909
  else return 0;
@@ -3929,6 +3971,28 @@ function kw_has_key(kw, key) {
3929
3971
  return false;
3930
3972
  }
3931
3973
  /************************************************************
3974
+ * A key that reaches the prototype chain instead of the data:
3975
+ * "__proto__", "constructor" or "prototype" when the object
3976
+ * does not hold it as its OWN key. A walk through one of them
3977
+ * read, or WROTE, Object.prototype: the flat id
3978
+ * "__proto__`polluted" set a key on every object of the page.
3979
+ * In C they are keys like any other, so here too: read them
3980
+ * only as own keys, and write "__proto__" as an own key.
3981
+ ************************************************************/
3982
+ function is_prototype_segment(obj, key) {
3983
+ if (key === "__proto__" || key === "constructor" || key === "prototype") return !Object.prototype.hasOwnProperty.call(obj, key);
3984
+ return false;
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
+ /************************************************************
3932
3996
  * The value at `path` (keys joined by a backtick), as the C
3933
3997
  * kw_find_path() finds it: undefined when it is not there.
3934
3998
  * A kw that is not a dict or a list is LOGGED ("kw must be list
@@ -3955,7 +4019,12 @@ function kw_find_path(gobj, kw, path, verbose) {
3955
4019
  }
3956
4020
  let ss = path.split("`");
3957
4021
  let len = ss.length;
3958
- 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;
3959
4028
  kw = kw[ss[i]];
3960
4029
  if (kw === void 0 || kw === null) {
3961
4030
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -4338,17 +4407,13 @@ function kw_set_dict_value(gobj, kw, path, value) {
4338
4407
  return -1;
4339
4408
  }
4340
4409
  let ss = path.split("`");
4341
- if (ss.length <= 1) {
4342
- kw[path] = value;
4343
- return 0;
4344
- }
4345
4410
  let len = ss.length;
4346
4411
  for (let i = 0; i < len; i++) {
4347
4412
  let key = ss[i];
4348
- if (i === len - 1) kw[key] = value;
4413
+ if (i === len - 1) set_own_key(kw, key, value);
4349
4414
  else {
4350
- let seg = kw[key];
4351
- 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, {});
4352
4417
  else if (!is_object(seg) && !is_array(seg)) {
4353
4418
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not a dict or a list: '${path}'`);
4354
4419
  return -1;
@@ -4403,9 +4468,9 @@ function _kw_match_simple(kw, jn_filter, prefix_path) {
4403
4468
  if (!matched) break;
4404
4469
  continue;
4405
4470
  }
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) {
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) {
4409
4474
  matched = false;
4410
4475
  break;
4411
4476
  }
@@ -4524,10 +4589,11 @@ If paths are empty return empty
4524
4589
  ***************************************************************************/
4525
4590
  function kw_clone_by_not_keys(gobj, kw, keys, verbose) {
4526
4591
  let kw_clone = {};
4592
+ if (!is_object(kw)) return kw_clone;
4527
4593
  json_object_update(kw_clone, kw);
4528
4594
  if (is_string(keys) && !empty_string(keys)) {
4529
4595
  let key = keys;
4530
- if (kw_get_dict_value(gobj, kw, key, 0, 0)) json_object_del(kw_clone, key);
4596
+ if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4531
4597
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4532
4598
  } else if (is_object(keys) && json_object_size(keys) > 0) {
4533
4599
  for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
@@ -5487,9 +5553,15 @@ parent: document.body
5487
5553
  * will be 'data-i18n' attr
5488
5554
  *
5489
5555
  * content -> string | [createElement2() or parameters of createElement2]
5556
+ * | HTMLElement | SVGElement | Text
5490
5557
  * if string begins with '<' then
5491
5558
  * it will be converted to HTMLElement
5492
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)]
5493
5565
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5494
5566
  * it will be overridden with the translation
5495
5567
  * content = translate(attrs['i18n']));
@@ -5928,7 +6000,7 @@ function flat2json(flat) {
5928
6000
  current[seg] = flat[key];
5929
6001
  } else {
5930
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`);
5931
- current[seg] = flat[key];
6003
+ set_own_key(current, seg, flat[key]);
5932
6004
  }
5933
6005
  break;
5934
6006
  }
@@ -5941,10 +6013,10 @@ function flat2json(flat) {
5941
6013
  current[seg] = child;
5942
6014
  }
5943
6015
  } else {
5944
- child = current[seg];
6016
+ child = is_prototype_segment(current, seg) ? void 0 : current[seg];
5945
6017
  if (child === void 0) {
5946
6018
  child = next_is_index ? [] : {};
5947
- current[seg] = child;
6019
+ set_own_key(current, seg, child);
5948
6020
  }
5949
6021
  }
5950
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`);
@@ -6707,6 +6779,7 @@ function mt_stop$1(gobj) {
6707
6779
  ***************************************************************/
6708
6780
  function mt_play(gobj) {
6709
6781
  let default_service = gobj_default_service();
6782
+ if (!default_service) return 0;
6710
6783
  if (!gobj_is_running(default_service)) gobj_start(default_service);
6711
6784
  if (!gobj_is_playing(default_service)) gobj_play(default_service);
6712
6785
  return 0;
@@ -6715,7 +6788,8 @@ function mt_play(gobj) {
6715
6788
  * Framework Method: Pause
6716
6789
  ***************************************************************/
6717
6790
  function mt_pause(gobj) {
6718
- 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);
6719
6793
  return 0;
6720
6794
  }
6721
6795
  /***************************************************************
@@ -6971,6 +7045,7 @@ var __gclass__ = null;
6971
7045
  function mt_create(gobj) {
6972
7046
  let priv = gobj.priv;
6973
7047
  priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
7048
+ priv.ws_seq = 0;
6974
7049
  priv.url = gobj_read_str_attr(gobj, "url");
6975
7050
  priv.remote_yuno_name = gobj_read_str_attr(gobj, "remote_yuno_name");
6976
7051
  priv.remote_yuno_role = gobj_read_str_attr(gobj, "remote_yuno_role");
@@ -7005,6 +7080,16 @@ function mt_writing(gobj, path) {
7005
7080
  function mt_start(gobj) {
7006
7081
  let priv = gobj.priv;
7007
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
+ }
7008
7093
  priv.websocket = setup_websocket(gobj);
7009
7094
  return 0;
7010
7095
  }
@@ -7139,10 +7224,16 @@ function setup_websocket(gobj) {
7139
7224
  log_error(`${gobj_short_name(gobj)}: Failed to initialize WebSocket.`);
7140
7225
  return null;
7141
7226
  }
7227
+ const ws_seq = ++gobj.priv.ws_seq;
7142
7228
  websocket.onopen = () => {
7143
- 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);
7144
7234
  };
7145
7235
  websocket.onmessage = (e) => {
7236
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7146
7237
  gobj_send_event(gobj, "EV_ON_MESSAGE", {
7147
7238
  url,
7148
7239
  data: e.data
@@ -7150,9 +7241,13 @@ function setup_websocket(gobj) {
7150
7241
  };
7151
7242
  websocket.onclose = (e) => {
7152
7243
  if (gobj_is_destroying(gobj)) return;
7244
+ if (ws_seq !== gobj.priv.ws_seq) return;
7153
7245
  if (!gobj_read_bool_attr(gobj_yuno(), "browser_beforeunload")) gobj_send_event(gobj, "EV_ON_CLOSE", {
7154
7246
  url,
7155
- e
7247
+ ws_seq,
7248
+ code: e.code,
7249
+ reason: e.reason,
7250
+ was_clean: e.wasClean
7156
7251
  }, gobj);
7157
7252
  else log_warning("Close websocket by unload page");
7158
7253
  };
@@ -7173,7 +7268,7 @@ function close_websocket(gobj, code = 1e3, reason = "") {
7173
7268
  try {
7174
7269
  websocket.close(code, reason);
7175
7270
  } catch (e) {
7176
- log_error("Error closing WebSocket:", e.message);
7271
+ log_error(`${gobj_short_name(gobj)}: Error closing WebSocket: ${e.message}`);
7177
7272
  }
7178
7273
  }
7179
7274
  /***************************************************************
@@ -7299,29 +7394,29 @@ function iev_create_from_json(gobj, data) {
7299
7394
  try {
7300
7395
  x = JSON.parse(data);
7301
7396
  } catch (e) {
7302
- log_error("parsing inter_event json: " + e);
7397
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
7303
7398
  return null;
7304
7399
  }
7305
7400
  if (!(x instanceof Object)) {
7306
- 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`);
7307
7402
  return null;
7308
7403
  }
7309
7404
  let event = x["event"];
7310
7405
  if (!event) {
7311
- log_error("parsing inter_event: no event");
7406
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: no event`);
7312
7407
  return null;
7313
7408
  }
7314
7409
  if (typeof event !== "string") {
7315
- log_error("parsing inter_event: event not a string");
7410
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: event not a string`);
7316
7411
  return null;
7317
7412
  }
7318
7413
  let kw = x["kw"];
7319
7414
  if (!kw) {
7320
- log_error("parsing inter_kw: no kw");
7415
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_kw: no kw`);
7321
7416
  return null;
7322
7417
  }
7323
7418
  if (!(kw instanceof Object)) {
7324
- 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`);
7325
7420
  return null;
7326
7421
  }
7327
7422
  return {
@@ -7381,10 +7476,9 @@ function ac_on_open(gobj, event, kw, src) {
7381
7476
  ***************************************************************/
7382
7477
  function ac_on_close(gobj, event, kw, src) {
7383
7478
  let priv = gobj.priv;
7384
- let e = kw.e;
7385
7479
  let url = kw.url;
7386
7480
  set_remote_log_functions();
7387
- 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})`);
7388
7482
  if (priv.websocket) {
7389
7483
  priv.websocket.onopen = null;
7390
7484
  priv.websocket.onmessage = null;
@@ -7401,15 +7495,15 @@ function ac_on_close(gobj, event, kw, src) {
7401
7495
  remote_yuno_service: priv.remote_yuno_service
7402
7496
  });
7403
7497
  } else {
7404
- 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})`);
7405
7499
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
7406
7500
  url,
7407
7501
  remote_yuno_name: priv.remote_yuno_name,
7408
7502
  remote_yuno_role: priv.remote_yuno_role,
7409
7503
  remote_yuno_service: priv.remote_yuno_service,
7410
- code: e.code,
7411
- reason: e.reason,
7412
- was_clean: e.wasClean
7504
+ code: kw.code,
7505
+ reason: kw.reason,
7506
+ was_clean: kw.was_clean
7413
7507
  });
7414
7508
  }
7415
7509
  if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
@@ -7507,6 +7601,10 @@ function ac_identity_card_ack(gobj, event, kw, src) {
7507
7601
  function ac_on_message(gobj, event, kw, src) {
7508
7602
  let url = gobj.priv.url;
7509
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
+ }
7510
7608
  if (is_traffic_tracing(gobj)) {
7511
7609
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
7512
7610
  let size = kw.data.length;
@@ -7532,8 +7630,14 @@ function ac_on_message(gobj, event, kw, src) {
7532
7630
  return 0;
7533
7631
  }
7534
7632
  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;
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
+ }
7537
7641
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7538
7642
  gobj_send_event(gobj, iev_event, iev_kw, gobj);
7539
7643
  return 0;