@yuneta/gobj-js 7.25.4 → 7.25.6

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.
package/README.md CHANGED
@@ -519,6 +519,13 @@ kw_has_key(kw, key) // → boolean
519
519
  kw_pop(kw1, kw2) // delete from kw1 the keys listed in kw2
520
520
  kw_delete(gobj, kw, path)
521
521
  kw_find_path(gobj, kw, path, verbose) // back-tick path: "a`b`c"
522
+ // kw_find_path() answers undefined for a path that is not there, as C
523
+ // answers NULL. A kw that is not a dict or a list, or a scalar in the
524
+ // middle of the path, is logged ("kw must be list or dict"); an absent
525
+ // or null middle segment is logged only when verbose. So a typed reader
526
+ // handed a bad kw answers its DEFAULT:
527
+ // kw_get_int(gobj, null, "x", 5, 0) → 5 (logged: kw must be list or dict)
528
+ // kw_get_str(gobj, {a: null}, "a`b", "d", 0) → "d" (not logged)
522
529
 
523
530
  kw_get_bool(gobj, kw, path, default_value, flag)
524
531
  kw_get_int (gobj, kw, path, default_value, flag)
@@ -540,6 +547,14 @@ kw_get_dict_value(gobj, kw, path, default_value, flag)
540
547
  // kw_get_int(gobj, {x: 3.9}, "x", 0, 0) → 3 (truncated, as C)
541
548
  // kw_get_int(gobj, {x: "12"}, "x", 5, 0) → 5 (not a number: logged)
542
549
  // kw_get_int(gobj, {x: "0x1F"}, "x", 5, KW_WILD_NUMBER) → 31 (strtoll base 0)
550
+ // kw_get_bool(gobj, {x: "0x1F"}, "x", true, KW_WILD_NUMBER) → false (atoi: decimal)
551
+ // kw_get_str() logs as C too: a value that is there and is not a
552
+ // string, required or not ("path MUST BE a json str"), never a null
553
+ // value, and with KW_REQUIRED a path that is not there:
554
+ // kw_get_str(gobj, {x: 5}, "x", "d", 0) → "d" (logged)
555
+ // kw_get_str(gobj, {x: null}, "x", "d", KW_REQUIRED) → "d" (not logged)
556
+ // kw_get_dict() / kw_get_list() log a value of another type only with
557
+ // KW_REQUIRED ("path MUST BE a json dict" / "... a json list"), as C.
543
558
  // KW_EXTRACT takes out only a value the reader answers with: a value
544
559
  // of another type stays in the kw.
545
560
 
@@ -3032,7 +3032,7 @@ function gobj_stats(gobj, stats, kw, src) {
3032
3032
  *
3033
3033
  * Licence: MIT (http://www.opensource.org/licenses/mit-license)
3034
3034
  * Copyright (c) 2014,2024 Niyamaka.
3035
- * Copyright (c) 2025, ArtGins.
3035
+ * Copyright (c) 2025-2026, ArtGins.
3036
3036
  *********************************************************************************/
3037
3037
  var kw_flag_t = Object.freeze({
3038
3038
  KW_REQUIRED: 1,
@@ -3560,30 +3560,46 @@ function kw_has_key(kw, key) {
3560
3560
  return false;
3561
3561
  }
3562
3562
  /************************************************************
3563
- *
3563
+ * The value at `path` (keys joined by a backtick), as the C
3564
+ * kw_find_path() finds it: undefined when it is not there.
3565
+ * A kw that is not a dict or a list is LOGGED ("kw must be list
3566
+ * or dict") and answers undefined, as C answers NULL; so does a
3567
+ * segment in the middle of the path whose value is a scalar. A
3568
+ * middle segment that is absent or null answers undefined, logged
3569
+ * only when `verbose`. Before gobj-js 7.25.6 a bad kw answered 0,
3570
+ * which the typed readers took for a value found (kw_get_int() and
3571
+ * kw_get_real() answered 0 instead of the default, kw_get_str() and
3572
+ * kw_get_bool() logged a second line), the log named no path and
3573
+ * called gobj_short_name() on a null gobj ("gobj bad type"), and a
3574
+ * null middle segment threw a TypeError.
3564
3575
  ************************************************************/
3565
3576
  function kw_find_path(gobj, kw, path, verbose) {
3566
3577
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
3578
+ const prefix = gobj ? `${gobj_short_name(gobj)}: ` : "";
3567
3579
  if (!(is_object(kw) || is_array(kw))) {
3568
- log_error(`${gobj_short_name(gobj)}: kw must be list or dict`);
3569
- return 0;
3580
+ log_error(`${prefix}kw must be list or dict: '${String(path)}'`);
3581
+ return;
3570
3582
  }
3571
3583
  if (!is_string(path)) {
3572
- log_error(`${gobj_short_name(gobj)}: path must be a string: ${String(path)}`);
3573
- return 0;
3584
+ log_error(`${prefix}path must be a string: ${String(path)}`);
3585
+ return;
3574
3586
  }
3575
3587
  let ss = path.split("`");
3576
- if (ss.length <= 1) return kw[path];
3577
3588
  let len = ss.length;
3578
- for (let i = 0; i < len; i++) {
3579
- let key = ss[i];
3580
- kw = kw[key];
3581
- if (kw === void 0) {
3582
- if (verbose) log_error(`${gobj_short_name(gobj)}: path not found: ${String(path)}`);
3589
+ for (let i = 0; i < len - 1; i++) {
3590
+ kw = kw[ss[i]];
3591
+ if (kw === void 0 || kw === null) {
3592
+ if (verbose) log_error(`${prefix}path not found: '${path}'`);
3593
+ return;
3594
+ }
3595
+ if (!(is_object(kw) || is_array(kw))) {
3596
+ log_error(`${prefix}kw must be list or dict: '${path}'`);
3583
3597
  return;
3584
3598
  }
3585
3599
  }
3586
- return kw;
3600
+ let value = kw[ss[len - 1]];
3601
+ if (value === void 0 && verbose) log_error(`${prefix}path not found: '${path}'`);
3602
+ return value;
3587
3603
  }
3588
3604
  /************************************************************
3589
3605
  *
@@ -3608,10 +3624,13 @@ function kw_delete(gobj, kw, path) {
3608
3624
  * anything else gives the default back, LOGGED ("path MUST BE a
3609
3625
  * json boolean", required or not) -- unless KW_WILD_NUMBER asks
3610
3626
  * to read a number (0 is false), a string ("true"/"false" in any
3611
- * case, else its integer) or null (false); a list or a dict is
3612
- * then false, logged too. Before gobj-js 7.25.2 the value went
3613
- * through Boolean(), so "false" was TRUE; before 7.25.3 a value
3614
- * of the wrong type gave the default back in silence.
3627
+ * case, else its decimal integer as atoi() reads it: "0x1F" is 0,
3628
+ * so false) or null (false); a list or a dict is then false,
3629
+ * logged too. Before gobj-js 7.25.2 the value went through
3630
+ * Boolean(), so "false" was TRUE; before 7.25.3 a value of the
3631
+ * wrong type gave the default back in silence; before 7.25.5 the
3632
+ * string went through parseInt() with no base, which reads hex:
3633
+ * "0x1F" was TRUE.
3615
3634
  ************************************************************/
3616
3635
  function kw_get_bool(gobj, kw, path, default_value, flag) {
3617
3636
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -3621,7 +3640,7 @@ function kw_get_bool(gobj, kw, path, default_value, flag) {
3621
3640
  const extract = flag && flag & kw_flag_t.KW_EXTRACT;
3622
3641
  const wild = flag && flag & kw_flag_t.KW_WILD_NUMBER;
3623
3642
  if (b === void 0) {
3624
- if (create) {
3643
+ if (create && kw) {
3625
3644
  let v = Boolean(default_value);
3626
3645
  kw_set_dict_value(gobj, kw, path, v);
3627
3646
  return v;
@@ -3637,7 +3656,7 @@ function kw_get_bool(gobj, kw, path, default_value, flag) {
3637
3656
  else if (wild && is_string(b)) {
3638
3657
  if (b.toLowerCase() === "true") value = true;
3639
3658
  else if (b.toLowerCase() === "false") value = false;
3640
- else value = (parseInt(b) || 0) !== 0;
3659
+ else value = (parseInt(b, 10) || 0) !== 0;
3641
3660
  } else if (wild && b === null) value = false;
3642
3661
  else if (wild) {
3643
3662
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
@@ -3689,7 +3708,7 @@ function kw_get_int(gobj, kw, path, default_value, flag) {
3689
3708
  const extract = flag && flag & kw_flag_t.KW_EXTRACT;
3690
3709
  const wild = flag && flag & kw_flag_t.KW_WILD_NUMBER;
3691
3710
  if (v === void 0) {
3692
- if (create) {
3711
+ if (create && kw) {
3693
3712
  let v = parseInt(default_value);
3694
3713
  kw_set_dict_value(gobj, kw, path, v);
3695
3714
  return v;
@@ -3736,7 +3755,7 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3736
3755
  const extract = flag && flag & kw_flag_t.KW_EXTRACT;
3737
3756
  const wild = flag && flag & kw_flag_t.KW_WILD_NUMBER;
3738
3757
  if (v === void 0) {
3739
- if (create) {
3758
+ if (create && kw) {
3740
3759
  let v = Number(default_value);
3741
3760
  kw_set_dict_value(gobj, kw, path, v);
3742
3761
  return v;
@@ -3766,6 +3785,21 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3766
3785
  return value;
3767
3786
  }
3768
3787
  /************************************************************
3788
+ * TRUE when the last key of `path` is a key of its dict, whatever
3789
+ * its value: kw_find_path() answers undefined both for a key that
3790
+ * is not there and for one whose value is undefined.
3791
+ ************************************************************/
3792
+ function kw_path_holds_key(kw, path) {
3793
+ let ss = is_string(path) ? path.split("`") : [];
3794
+ let parent = kw;
3795
+ for (let i = 0; i < ss.length - 1; i++) {
3796
+ if (!(is_object(parent) || is_array(parent))) return false;
3797
+ parent = parent[ss[i]];
3798
+ }
3799
+ if (!(is_object(parent) || is_array(parent)) || ss.length === 0) return false;
3800
+ return Object.prototype.hasOwnProperty.call(parent, ss[ss.length - 1]);
3801
+ }
3802
+ /************************************************************
3769
3803
  * The default comes back AS GIVEN, as in the C kw_get_str()
3770
3804
  * (`const char *default_value`, where 0 is NULL). Turned into
3771
3805
  * a string, a default of 0 or null was "0" or "null", which is
@@ -3777,6 +3811,12 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3777
3811
  * C refuses to extract a string at all ("Cannot extract a
3778
3812
  * string": its answer would point into the freed json); a JS
3779
3813
  * string outlives the kw, so here it is taken out.
3814
+ * A value that is there and is not a string gives the default
3815
+ * back, LOGGED ("path MUST BE a json str", required or not), as
3816
+ * C does; a null value is the key saying "no string" and is not
3817
+ * logged, and KW_REQUIRED logs only a path that is not there at
3818
+ * all. Before gobj-js 7.25.5 a value of another type was logged
3819
+ * only with KW_REQUIRED, and then a null one too.
3780
3820
  ************************************************************/
3781
3821
  function kw_get_str(gobj, kw, path, default_value, flag) {
3782
3822
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -3785,19 +3825,19 @@ function kw_get_str(gobj, kw, path, default_value, flag) {
3785
3825
  const create = flag && flag & kw_flag_t.KW_CREATE;
3786
3826
  const extract = flag && flag & kw_flag_t.KW_EXTRACT;
3787
3827
  if (v === void 0) {
3788
- if (create) {
3828
+ if (create && kw) {
3789
3829
  kw_set_dict_value(gobj, kw, path, is_string(default_value) ? default_value : null);
3790
3830
  return default_value;
3791
- } else if (required) {
3831
+ } else if (required && !kw_path_holds_key(kw, path)) {
3792
3832
  log_error(`path not found: '${path}'`);
3793
3833
  trace_json(kw);
3794
3834
  }
3795
3835
  return default_value;
3796
3836
  }
3797
3837
  if (!is_string(v)) {
3798
- if (required) {
3799
- log_error(`${gobj_short_name(gobj)}: path value MUST BE a string: ${path}`);
3800
- trace_msg(kw);
3838
+ if (v !== null) {
3839
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json str: '${path}'`);
3840
+ trace_json(kw);
3801
3841
  }
3802
3842
  return default_value;
3803
3843
  }
@@ -3853,8 +3893,8 @@ function kw_get_dict(gobj, kw, path, default_value, flag) {
3853
3893
  }
3854
3894
  if (!is_object(v)) {
3855
3895
  if (required) {
3856
- log_error(`${gobj_short_name(gobj)}: path value MUST BE a dict: ${path}`);
3857
- trace_msg(kw);
3896
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json dict: '${path}'`);
3897
+ trace_json(kw);
3858
3898
  }
3859
3899
  return default_value;
3860
3900
  }
@@ -3911,8 +3951,8 @@ function kw_get_list(gobj, kw, path, default_value, flag) {
3911
3951
  }
3912
3952
  if (!is_array(v)) {
3913
3953
  if (required) {
3914
- log_error(`${gobj_short_name(gobj)}: path value MUST BE an array: ${path}`);
3915
- trace_msg(kw);
3954
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json list: '${path}'`);
3955
+ trace_json(kw);
3916
3956
  }
3917
3957
  return default_value;
3918
3958
  }