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