@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.
@@ -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,11 @@
890
897
  }
891
898
  break;
892
899
  case data_type_t.DTP_JSON:
900
+ jn_value2 = jn_value_;
893
901
  if (is_string(jn_value_)) try {
894
902
  jn_value2 = JSON.parse(jn_value_);
895
903
  } 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;
904
+ jn_value2 = jn_value_;
902
905
  }
903
906
  break;
904
907
  case data_type_t.DTP_POINTER: jn_value2 = jn_value_;
@@ -1203,6 +1206,10 @@
1203
1206
  * find a service
1204
1207
  ************************************************************/
1205
1208
  function gobj_find_service(service_name, verbose) {
1209
+ if (!is_string(service_name)) {
1210
+ log_error(`gobj_find_service(): service name must be a string: ${service_name}`);
1211
+ return null;
1212
+ }
1206
1213
  service_name = service_name.toLowerCase();
1207
1214
  let service_gobj = __jn_services__[service_name];
1208
1215
  if (!service_gobj) {
@@ -1360,9 +1367,10 @@
1360
1367
  return null;
1361
1368
  }
1362
1369
  if (is_string(parent)) {
1363
- parent = gobj_find_service(parent);
1370
+ const parent_name = parent;
1371
+ parent = gobj_find_service(parent_name);
1364
1372
  if (!parent) {
1365
- log_warning(`parent service not found: ${parent}`);
1373
+ log_warning(`parent service not found: ${parent_name}`);
1366
1374
  return null;
1367
1375
  }
1368
1376
  }
@@ -1475,6 +1483,7 @@
1475
1483
  parent.gclass.gmt.mt_child_removed(parent, gobj);
1476
1484
  }
1477
1485
  if (gobj.gobj_flag & gobj_flag_t.gobj_flag_service) _deregister_service(gobj);
1486
+ if (gobj === __default_service__) __default_service__ = null;
1478
1487
  gobj_unsubscribe_list(gobj, gobj.dl_subscriptions, true);
1479
1488
  gobj_unsubscribe_list(gobj, gobj.dl_subscribings, true);
1480
1489
  if (parent) {
@@ -1820,7 +1829,7 @@
1820
1829
  log_error(`gobj NULL of bad type`);
1821
1830
  return null;
1822
1831
  }
1823
- if (gobj.bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1832
+ if (gobj.bottom_gobj && bottom_gobj) log_warning(`${gobj_short_name(gobj)}: bottom_gobj already set`);
1824
1833
  gobj.bottom_gobj = bottom_gobj;
1825
1834
  }
1826
1835
  /***************************************************************************
@@ -2235,7 +2244,7 @@
2235
2244
  jn_attrs[key] = value;
2236
2245
  if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(key)) gobj.priv[key] = value;
2237
2246
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2238
- if (gobj.mt_writing) gobj.mt_writing(gobj, key);
2247
+ if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, key);
2239
2248
  }
2240
2249
  return 0;
2241
2250
  }
@@ -2259,8 +2268,8 @@
2259
2268
  if (!it.name) continue;
2260
2269
  if (exclude_flag && it.flag & exclude_flag) continue;
2261
2270
  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;
2271
+ set_default(gobj, gobj.jn_attrs, it);
2272
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(it.name)) gobj.priv[it.name] = gobj.jn_attrs[it.name];
2264
2273
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, it.name);
2265
2274
  }
2266
2275
  }
@@ -2294,7 +2303,7 @@
2294
2303
  let hs = gobj_hsdata2(gobj, name, false);
2295
2304
  if (hs) {
2296
2305
  hs[name] = parseBoolean(value);
2297
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2306
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2298
2307
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2299
2308
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2300
2309
  }
@@ -2310,7 +2319,7 @@
2310
2319
  let hs = gobj_hsdata2(gobj, name, false);
2311
2320
  if (hs) {
2312
2321
  hs[name] = parseInt(value);
2313
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2322
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2314
2323
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2315
2324
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2316
2325
  }
@@ -2326,7 +2335,7 @@
2326
2335
  let hs = gobj_hsdata2(gobj, name, false);
2327
2336
  if (hs) {
2328
2337
  hs[name] = String(value);
2329
- if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = value;
2338
+ if (is_object(gobj.priv) && gobj.priv.hasOwnProperty(name)) gobj.priv[name] = hs[name];
2330
2339
  if (gobj.obflag & obflag_t.obflag_created && !(gobj.obflag & obflag_t.obflag_destroyed)) {
2331
2340
  if (gobj.gclass.gmt.mt_writing) gobj.gclass.gmt.mt_writing(gobj, name);
2332
2341
  }
@@ -2380,15 +2389,15 @@
2380
2389
  log_error(`${gobj_short_name(gobj)}: gobj_change_parent() BAD TYPE of parent`);
2381
2390
  return -1;
2382
2391
  }
2383
- if (gobj.parent && gobj.parent.mt_child_removed) gobj.parent.mt_child_removed(gobj.parent, gobj);
2392
+ if (gobj.parent && gobj.parent.gclass.gmt.mt_child_removed) gobj.parent.gclass.gmt.mt_child_removed(gobj.parent, gobj);
2384
2393
  if (gobj.parent) {
2385
2394
  _remove_child(gobj.parent, gobj);
2386
2395
  gobj.parent = null;
2387
2396
  }
2388
2397
  _add_child(parent, gobj);
2389
- if (parent.mt_child_added) {
2398
+ if (parent.gclass.gmt.mt_child_added) {
2390
2399
  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);
2400
+ parent.gclass.gmt.mt_child_added(parent, gobj);
2392
2401
  }
2393
2402
  return 0;
2394
2403
  }
@@ -2500,9 +2509,19 @@
2500
2509
  let snapshot = __dl_posted_events__;
2501
2510
  __dl_posted_events__ = [];
2502
2511
  let delivered = 0;
2503
- for (let msg of snapshot) {
2512
+ for (let i = 0; i < snapshot.length; i++) {
2513
+ const msg = snapshot[i];
2504
2514
  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);
2515
+ try {
2516
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2517
+ } catch (e) {
2518
+ __dl_posted_events__ = snapshot.slice(i + 1).concat(__dl_posted_events__);
2519
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2520
+ __posted_events_scheduled__ = true;
2521
+ setTimeout(gobj_deliver_posted_events, 0);
2522
+ }
2523
+ throw e;
2524
+ }
2506
2525
  delivered++;
2507
2526
  }
2508
2527
  if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
@@ -2580,7 +2599,16 @@
2580
2599
  let ret = -1;
2581
2600
  try {
2582
2601
  if (event_action.action) ret = event_action.action(dst, event, kw, src);
2583
- } finally {}
2602
+ } catch (e) {
2603
+ __inside__--;
2604
+ if (!(e && e.__gobj_logged__)) {
2605
+ log_error(`${gobj_short_name(dst)}: action of ${event} in ${dst.current_state.state_name} THREW: ${e && e.stack ? e.stack : e}`);
2606
+ try {
2607
+ e.__gobj_logged__ = true;
2608
+ } catch (e2) {}
2609
+ }
2610
+ throw e;
2611
+ }
2584
2612
  if (tracea && __trace_machine_format__ !== 1 && !(dst.obflag & obflag_t.obflag_destroyed)) trace_machine(sprintf("<- mach(%s%s^%s), st: %s, ev: %s, ret: %d", !dst.running ? "!!" : "", gobj_gclass_name(dst), gobj_name(dst), dst.current_state.state_name, event ? event : "", is_number(ret) ? ret : 0));
2585
2613
  __inside__--;
2586
2614
  return ret;
@@ -3251,10 +3279,22 @@
3251
3279
  f_warning = _console.warn;
3252
3280
  }
3253
3281
  }
3282
+ /************************************************************
3283
+ * JSON.stringify for a log line: a circular object (a gobj,
3284
+ * a DOM node) must not make the log call itself throw.
3285
+ ************************************************************/
3286
+ function log_stringify(v) {
3287
+ try {
3288
+ return JSON.stringify(v);
3289
+ } catch (e) {
3290
+ return String(v);
3291
+ }
3292
+ }
3254
3293
  function format_log(format) {
3255
3294
  const args = Array.prototype.slice.call(arguments);
3256
3295
  let output = "";
3257
3296
  if (typeof format === "string") {
3297
+ if (args.length === 1) return format;
3258
3298
  let i = 1;
3259
3299
  output = format.replace(/%[sdifoO]/g, (match) => {
3260
3300
  const val = args[i++];
@@ -3264,17 +3304,17 @@
3264
3304
  case "%i": return parseInt(val);
3265
3305
  case "%f": return parseFloat(val);
3266
3306
  case "%o":
3267
- case "%O": return typeof val === "object" ? JSON.stringify(val) : String(val);
3307
+ case "%O": return typeof val === "object" ? log_stringify(val) : String(val);
3268
3308
  default: return match;
3269
3309
  }
3270
3310
  });
3271
- const rest = args.slice(i).map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg));
3311
+ const rest = args.slice(i).map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg));
3272
3312
  if (rest.length > 0) output += " " + rest.join(" ");
3273
- } else output = args.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)).join(" ");
3313
+ } else output = args.map((arg) => typeof arg === "object" ? log_stringify(arg) : String(arg)).join(" ");
3274
3314
  return output;
3275
3315
  }
3276
- function log_error(format) {
3277
- let msg = format_log(format);
3316
+ function log_error(format, ...args) {
3317
+ let msg = format_log(format, ...args);
3278
3318
  let hora = current_timestamp();
3279
3319
  if (f_error) {
3280
3320
  if (f_error === _console.error) {
@@ -3286,8 +3326,8 @@
3286
3326
  }
3287
3327
  emit_log_callback("error", msg, hora);
3288
3328
  }
3289
- function log_warning(format) {
3290
- let msg = format_log(format);
3329
+ function log_warning(format, ...args) {
3330
+ let msg = format_log(format, ...args);
3291
3331
  let hora = current_timestamp();
3292
3332
  if (f_warning) {
3293
3333
  if (f_warning === _console.warn) {
@@ -3299,24 +3339,24 @@
3299
3339
  }
3300
3340
  emit_log_callback("warning", msg, hora);
3301
3341
  }
3302
- function log_info(format) {
3303
- let msg = format_log(format);
3342
+ function log_info(format, ...args) {
3343
+ let msg = format_log(format, ...args);
3304
3344
  let hora = current_timestamp();
3305
3345
  if (f_info) {
3306
3346
  if (console_wants("info", msg)) f_info("%c" + hora + " INFO: " + String(msg), "color:cyan");
3307
3347
  }
3308
3348
  emit_log_callback("info", msg, hora);
3309
3349
  }
3310
- function log_debug(format) {
3311
- let msg = format_log(format);
3350
+ function log_debug(format, ...args) {
3351
+ let msg = format_log(format, ...args);
3312
3352
  let hora = current_timestamp();
3313
3353
  if (f_debug) {
3314
3354
  if (console_wants("debug", msg)) f_debug("%c" + hora + " DEBUG: " + String(msg), "color:silver");
3315
3355
  }
3316
3356
  emit_log_callback("debug", msg, hora);
3317
3357
  }
3318
- function trace_msg(format) {
3319
- let msg = format_log(format);
3358
+ function trace_msg(format, ...args) {
3359
+ let msg = format_log(format, ...args);
3320
3360
  let hora = current_timestamp();
3321
3361
  if (f_debug) {
3322
3362
  if (console_wants("msg", msg)) f_debug("%c" + hora + " MSG: " + String(msg), "color:cyan");
@@ -3865,6 +3905,8 @@
3865
3905
  if (is_number(jn_var1) || is_number(jn_var2)) {
3866
3906
  let val1 = Number(jn_var1);
3867
3907
  let val2 = Number(jn_var2);
3908
+ if (Number.isNaN(val1)) val1 = 0;
3909
+ if (Number.isNaN(val2)) val2 = 0;
3868
3910
  if (val1 > val2) return 1;
3869
3911
  else if (val1 < val2) return -1;
3870
3912
  else return 0;
@@ -3932,6 +3974,28 @@
3932
3974
  return false;
3933
3975
  }
3934
3976
  /************************************************************
3977
+ * A key that reaches the prototype chain instead of the data:
3978
+ * "__proto__", "constructor" or "prototype" when the object
3979
+ * does not hold it as its OWN key. A walk through one of them
3980
+ * read, or WROTE, Object.prototype: the flat id
3981
+ * "__proto__`polluted" set a key on every object of the page.
3982
+ * In C they are keys like any other, so here too: read them
3983
+ * only as own keys, and write "__proto__" as an own key.
3984
+ ************************************************************/
3985
+ function is_prototype_segment(obj, key) {
3986
+ if (key === "__proto__" || key === "constructor" || key === "prototype") return !Object.prototype.hasOwnProperty.call(obj, key);
3987
+ return false;
3988
+ }
3989
+ function set_own_key(obj, key, value) {
3990
+ if (key === "__proto__") Object.defineProperty(obj, key, {
3991
+ value,
3992
+ writable: true,
3993
+ enumerable: true,
3994
+ configurable: true
3995
+ });
3996
+ else obj[key] = value;
3997
+ }
3998
+ /************************************************************
3935
3999
  * The value at `path` (keys joined by a backtick), as the C
3936
4000
  * kw_find_path() finds it: undefined when it is not there.
3937
4001
  * A kw that is not a dict or a list is LOGGED ("kw must be list
@@ -3958,7 +4022,12 @@
3958
4022
  }
3959
4023
  let ss = path.split("`");
3960
4024
  let len = ss.length;
3961
- for (let i = 0; i < len - 1; i++) {
4025
+ for (let i = 0; i < len; i++) {
4026
+ if (is_prototype_segment(kw, ss[i])) {
4027
+ if (verbose) log_error(`${prefix}path not found: '${path}'`);
4028
+ return;
4029
+ }
4030
+ if (i === len - 1) break;
3962
4031
  kw = kw[ss[i]];
3963
4032
  if (kw === void 0 || kw === null) {
3964
4033
  if (verbose) log_error(`${prefix}path not found: '${path}'`);
@@ -4341,17 +4410,13 @@
4341
4410
  return -1;
4342
4411
  }
4343
4412
  let ss = path.split("`");
4344
- if (ss.length <= 1) {
4345
- kw[path] = value;
4346
- return 0;
4347
- }
4348
4413
  let len = ss.length;
4349
4414
  for (let i = 0; i < len; i++) {
4350
4415
  let key = ss[i];
4351
- if (i === len - 1) kw[key] = value;
4416
+ if (i === len - 1) set_own_key(kw, key, value);
4352
4417
  else {
4353
- let seg = kw[key];
4354
- if (seg === void 0) kw[key] = {};
4418
+ let seg = is_prototype_segment(kw, key) ? void 0 : kw[key];
4419
+ if (seg === void 0) set_own_key(kw, key, {});
4355
4420
  else if (!is_object(seg) && !is_array(seg)) {
4356
4421
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}kw_set_dict_value(): segment '${key}' is not a dict or a list: '${path}'`);
4357
4422
  return -1;
@@ -4406,9 +4471,9 @@
4406
4471
  if (!matched) break;
4407
4472
  continue;
4408
4473
  }
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) {
4474
+ let jn_record_value = kw_get_dict_value(0, kw, path, void 0, 0);
4475
+ if (jn_record_value === void 0) jn_record_value = json_object_get(kw, path);
4476
+ if (jn_record_value === void 0) {
4412
4477
  matched = false;
4413
4478
  break;
4414
4479
  }
@@ -4527,10 +4592,11 @@
4527
4592
  ***************************************************************************/
4528
4593
  function kw_clone_by_not_keys(gobj, kw, keys, verbose) {
4529
4594
  let kw_clone = {};
4595
+ if (!is_object(kw)) return kw_clone;
4530
4596
  json_object_update(kw_clone, kw);
4531
4597
  if (is_string(keys) && !empty_string(keys)) {
4532
4598
  let key = keys;
4533
- if (kw_get_dict_value(gobj, kw, key, 0, 0)) json_object_del(kw_clone, key);
4599
+ if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
4534
4600
  else if (verbose) log_error(`${gobj_short_name(gobj)}: key not found: ${key}`);
4535
4601
  } else if (is_object(keys) && json_object_size(keys) > 0) {
4536
4602
  for (let key of Object.keys(keys)) if (kw.hasOwnProperty(key)) json_object_del(kw_clone, key);
@@ -5490,9 +5556,15 @@
5490
5556
  * will be 'data-i18n' attr
5491
5557
  *
5492
5558
  * content -> string | [createElement2() or parameters of createElement2]
5559
+ * | HTMLElement | SVGElement | Text
5493
5560
  * if string begins with '<' then
5494
5561
  * it will be converted to HTMLElement
5495
5562
  * content = createOneHtml(string)
5563
+ * ⚠️ So a string is MARKUP when it starts with '<': never
5564
+ * pass DATA (a value from a backend, a name a user typed)
5565
+ * as a bare string, or "<img src=x onerror=...>" runs.
5566
+ * Pass it as a Text node, which is always text:
5567
+ * ['span', {}, document.createTextNode(value)]
5496
5568
  * if attrs['i18n'] is not empty then (or 'data-i18n' or 'data_i18next')
5497
5569
  * it will be overridden with the translation
5498
5570
  * content = translate(attrs['i18n']));
@@ -5931,7 +6003,7 @@
5931
6003
  current[seg] = flat[key];
5932
6004
  } else {
5933
6005
  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];
6006
+ set_own_key(current, seg, flat[key]);
5935
6007
  }
5936
6008
  break;
5937
6009
  }
@@ -5944,10 +6016,10 @@
5944
6016
  current[seg] = child;
5945
6017
  }
5946
6018
  } else {
5947
- child = current[seg];
6019
+ child = is_prototype_segment(current, seg) ? void 0 : current[seg];
5948
6020
  if (child === void 0) {
5949
6021
  child = next_is_index ? [] : {};
5950
- current[seg] = child;
6022
+ set_own_key(current, seg, child);
5951
6023
  }
5952
6024
  }
5953
6025
  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`);
@@ -6710,6 +6782,7 @@
6710
6782
  ***************************************************************/
6711
6783
  function mt_play(gobj) {
6712
6784
  let default_service = gobj_default_service();
6785
+ if (!default_service) return 0;
6713
6786
  if (!gobj_is_running(default_service)) gobj_start(default_service);
6714
6787
  if (!gobj_is_playing(default_service)) gobj_play(default_service);
6715
6788
  return 0;
@@ -6718,7 +6791,8 @@
6718
6791
  * Framework Method: Pause
6719
6792
  ***************************************************************/
6720
6793
  function mt_pause(gobj) {
6721
- gobj_pause(gobj_default_service());
6794
+ let default_service = gobj_default_service();
6795
+ if (default_service && gobj_is_playing(default_service)) gobj_pause(default_service);
6722
6796
  return 0;
6723
6797
  }
6724
6798
  /***************************************************************
@@ -6974,6 +7048,7 @@
6974
7048
  function mt_create(gobj) {
6975
7049
  let priv = gobj.priv;
6976
7050
  priv.gobj_timer = gobj_create_pure_child(gobj_name(gobj), "C_TIMER", {}, gobj);
7051
+ priv.ws_seq = 0;
6977
7052
  priv.url = gobj_read_str_attr(gobj, "url");
6978
7053
  priv.remote_yuno_name = gobj_read_str_attr(gobj, "remote_yuno_name");
6979
7054
  priv.remote_yuno_role = gobj_read_str_attr(gobj, "remote_yuno_role");
@@ -7008,6 +7083,16 @@
7008
7083
  function mt_start(gobj) {
7009
7084
  let priv = gobj.priv;
7010
7085
  priv.retry_ms = 0;
7086
+ if (gobj_current_state(gobj) !== "ST_DISCONNECTED") {
7087
+ gobj_send_event(gobj, "EV_ON_CLOSE", {
7088
+ url: priv.url,
7089
+ ws_seq: priv.ws_seq,
7090
+ code: 1e3,
7091
+ reason: "restarted",
7092
+ was_clean: true
7093
+ }, gobj);
7094
+ clear_timeout(priv.gobj_timer);
7095
+ }
7011
7096
  priv.websocket = setup_websocket(gobj);
7012
7097
  return 0;
7013
7098
  }
@@ -7142,10 +7227,16 @@
7142
7227
  log_error(`${gobj_short_name(gobj)}: Failed to initialize WebSocket.`);
7143
7228
  return null;
7144
7229
  }
7230
+ const ws_seq = ++gobj.priv.ws_seq;
7145
7231
  websocket.onopen = () => {
7146
- gobj_send_event(gobj, "EV_ON_OPEN", { url }, gobj);
7232
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7233
+ gobj_send_event(gobj, "EV_ON_OPEN", {
7234
+ url,
7235
+ ws_seq
7236
+ }, gobj);
7147
7237
  };
7148
7238
  websocket.onmessage = (e) => {
7239
+ if (ws_seq !== gobj.priv.ws_seq || gobj_is_destroying(gobj)) return;
7149
7240
  gobj_send_event(gobj, "EV_ON_MESSAGE", {
7150
7241
  url,
7151
7242
  data: e.data
@@ -7153,9 +7244,13 @@
7153
7244
  };
7154
7245
  websocket.onclose = (e) => {
7155
7246
  if (gobj_is_destroying(gobj)) return;
7247
+ if (ws_seq !== gobj.priv.ws_seq) return;
7156
7248
  if (!gobj_read_bool_attr(gobj_yuno(), "browser_beforeunload")) gobj_send_event(gobj, "EV_ON_CLOSE", {
7157
7249
  url,
7158
- e
7250
+ ws_seq,
7251
+ code: e.code,
7252
+ reason: e.reason,
7253
+ was_clean: e.wasClean
7159
7254
  }, gobj);
7160
7255
  else log_warning("Close websocket by unload page");
7161
7256
  };
@@ -7176,7 +7271,7 @@
7176
7271
  try {
7177
7272
  websocket.close(code, reason);
7178
7273
  } catch (e) {
7179
- log_error("Error closing WebSocket:", e.message);
7274
+ log_error(`${gobj_short_name(gobj)}: Error closing WebSocket: ${e.message}`);
7180
7275
  }
7181
7276
  }
7182
7277
  /***************************************************************
@@ -7302,29 +7397,29 @@
7302
7397
  try {
7303
7398
  x = JSON.parse(data);
7304
7399
  } catch (e) {
7305
- log_error("parsing inter_event json: " + e);
7400
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event json: ${e}`);
7306
7401
  return null;
7307
7402
  }
7308
7403
  if (!(x instanceof Object)) {
7309
- log_error("parsing inter_event: websocket data not a json object");
7404
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: websocket data not a json object`);
7310
7405
  return null;
7311
7406
  }
7312
7407
  let event = x["event"];
7313
7408
  if (!event) {
7314
- log_error("parsing inter_event: no event");
7409
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: no event`);
7315
7410
  return null;
7316
7411
  }
7317
7412
  if (typeof event !== "string") {
7318
- log_error("parsing inter_event: event not a string");
7413
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: event not a string`);
7319
7414
  return null;
7320
7415
  }
7321
7416
  let kw = x["kw"];
7322
7417
  if (!kw) {
7323
- log_error("parsing inter_kw: no kw");
7418
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_kw: no kw`);
7324
7419
  return null;
7325
7420
  }
7326
7421
  if (!(kw instanceof Object)) {
7327
- log_error("parsing inter_event: kw not a json object");
7422
+ log_warning(`${gobj_short_name(gobj)}: parsing inter_event: kw not a json object`);
7328
7423
  return null;
7329
7424
  }
7330
7425
  return {
@@ -7384,10 +7479,9 @@
7384
7479
  ***************************************************************/
7385
7480
  function ac_on_close(gobj, event, kw, src) {
7386
7481
  let priv = gobj.priv;
7387
- let e = kw.e;
7388
7482
  let url = kw.url;
7389
7483
  set_remote_log_functions();
7390
- log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7484
+ log_warning(`${gobj_short_name(gobj)}: WebSocket closed ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7391
7485
  if (priv.websocket) {
7392
7486
  priv.websocket.onopen = null;
7393
7487
  priv.websocket.onmessage = null;
@@ -7404,15 +7498,15 @@
7404
7498
  remote_yuno_service: priv.remote_yuno_service
7405
7499
  });
7406
7500
  } else {
7407
- log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${e.code}, Reason: "${e.reason}", Clean: ${e.wasClean})`);
7501
+ log_warning(`${gobj_short_name(gobj)}: EV_ON_OPEN_ERROR cannot open ${url} (Code: ${kw.code}, Reason: "${kw.reason}", Clean: ${kw.was_clean})`);
7408
7502
  gobj_publish_event(gobj, "EV_ON_OPEN_ERROR", {
7409
7503
  url,
7410
7504
  remote_yuno_name: priv.remote_yuno_name,
7411
7505
  remote_yuno_role: priv.remote_yuno_role,
7412
7506
  remote_yuno_service: priv.remote_yuno_service,
7413
- code: e.code,
7414
- reason: e.reason,
7415
- was_clean: e.wasClean
7507
+ code: kw.code,
7508
+ reason: kw.reason,
7509
+ was_clean: kw.was_clean
7416
7510
  });
7417
7511
  }
7418
7512
  if (gobj_is_running(gobj)) set_timeout(priv.gobj_timer, next_retry(gobj));
@@ -7510,6 +7604,10 @@
7510
7604
  function ac_on_message(gobj, event, kw, src) {
7511
7605
  let url = gobj.priv.url;
7512
7606
  let iev_msg = iev_create_from_json(gobj, kw.data);
7607
+ if (!iev_msg) {
7608
+ log_warning(`${gobj_short_name(gobj)}: frame dropped from ${url}: ${String(kw.data).substring(0, 256)}`);
7609
+ return -1;
7610
+ }
7513
7611
  if (is_traffic_tracing(gobj)) {
7514
7612
  let prefix = gobj_name(gobj_yuno()) + " <== " + url;
7515
7613
  let size = kw.data.length;
@@ -7535,8 +7633,14 @@
7535
7633
  return 0;
7536
7634
  }
7537
7635
  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;
7636
+ if (msg_type === "__subscribing__") {
7637
+ log_warning(`${gobj_short_name(gobj)}: subscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7638
+ return -1;
7639
+ }
7640
+ if (msg_type === "__unsubscribing__") {
7641
+ log_warning(`${gobj_short_name(gobj)}: unsubscription event ignored, I'm client (service: ${iev_dst_service}, event: ${iev_event})`);
7642
+ return -1;
7643
+ }
7540
7644
  if (gobj_has_event(gobj, iev_event, event_flag_t.EVF_PUBLIC_EVENT)) {
7541
7645
  gobj_send_event(gobj, iev_event, iev_kw, gobj);
7542
7646
  return 0;