@yuneta/gobj-js 7.25.3 → 7.25.5

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.
@@ -3031,7 +3031,7 @@ function gobj_stats(gobj, stats, kw, src) {
3031
3031
  *
3032
3032
  * Licence: MIT (http://www.opensource.org/licenses/mit-license)
3033
3033
  * Copyright (c) 2014,2024 Niyamaka.
3034
- * Copyright (c) 2025, ArtGins.
3034
+ * Copyright (c) 2025-2026, ArtGins.
3035
3035
  *********************************************************************************/
3036
3036
  var kw_flag_t = Object.freeze({
3037
3037
  KW_REQUIRED: 1,
@@ -3607,10 +3607,13 @@ function kw_delete(gobj, kw, path) {
3607
3607
  * anything else gives the default back, LOGGED ("path MUST BE a
3608
3608
  * json boolean", required or not) -- unless KW_WILD_NUMBER asks
3609
3609
  * to read a number (0 is false), a string ("true"/"false" in any
3610
- * case, else its integer) or null (false); a list or a dict is
3611
- * then false, logged too. Before gobj-js 7.25.2 the value went
3612
- * through Boolean(), so "false" was TRUE; before 7.25.3 a value
3613
- * of the wrong type gave the default back in silence.
3610
+ * case, else its decimal integer as atoi() reads it: "0x1F" is 0,
3611
+ * so false) or null (false); a list or a dict is then false,
3612
+ * logged too. Before gobj-js 7.25.2 the value went through
3613
+ * Boolean(), so "false" was TRUE; before 7.25.3 a value of the
3614
+ * wrong type gave the default back in silence; before 7.25.5 the
3615
+ * string went through parseInt() with no base, which reads hex:
3616
+ * "0x1F" was TRUE.
3614
3617
  ************************************************************/
3615
3618
  function kw_get_bool(gobj, kw, path, default_value, flag) {
3616
3619
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -3636,7 +3639,7 @@ function kw_get_bool(gobj, kw, path, default_value, flag) {
3636
3639
  else if (wild && is_string(b)) {
3637
3640
  if (b.toLowerCase() === "true") value = true;
3638
3641
  else if (b.toLowerCase() === "false") value = false;
3639
- else value = (parseInt(b) || 0) !== 0;
3642
+ else value = (parseInt(b, 10) || 0) !== 0;
3640
3643
  } else if (wild && b === null) value = false;
3641
3644
  else if (wild) {
3642
3645
  log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
@@ -3651,7 +3654,34 @@ function kw_get_bool(gobj, kw, path, default_value, flag) {
3651
3654
  return value;
3652
3655
  }
3653
3656
  /************************************************************
3654
- *
3657
+ * The integer at the start of a string, as C's strtoll(s, 0, 0)
3658
+ * reads it: leading spaces, a sign, then "0x" for hex, "0" for
3659
+ * octal, else decimal, up to the first character that is not a
3660
+ * digit of that base. 0 when there is no digit.
3661
+ ************************************************************/
3662
+ function strtoll_base0(s) {
3663
+ const m = /^\s*([+-]?)(0[xX][0-9a-fA-F]+|0[0-7]*|[1-9][0-9]*)/.exec(s);
3664
+ if (!m) return 0;
3665
+ let digits = m[2];
3666
+ let value;
3667
+ if (digits.length > 1 && (digits[1] === "x" || digits[1] === "X")) value = parseInt(digits.substring(2), 16);
3668
+ else if (digits[0] === "0") value = parseInt(digits, 8);
3669
+ else value = parseInt(digits, 10);
3670
+ return m[1] === "-" ? -value : value;
3671
+ }
3672
+ /************************************************************
3673
+ * As the C kw_get_int(): a number found is the answer,
3674
+ * truncated toward zero as C casts a real, and anything else
3675
+ * gives the default back (as an integer), LOGGED ("path MUST BE
3676
+ * a json integer", required or not) -- unless KW_WILD_NUMBER
3677
+ * asks to read a boolean (1/0), a string (as strtoll() with base
3678
+ * 0: "0x1F" is 31, "017" is 15, "12abc" is 12, "abc" is 0) or
3679
+ * null (0); a list or a dict is then 0, logged too. KW_EXTRACT
3680
+ * takes out only a value the reader answers with.
3681
+ * Before gobj-js 7.25.4 KW_EXTRACT deleted the value before its
3682
+ * type was looked at, a value of another type was logged only
3683
+ * with KW_REQUIRED, KW_WILD_NUMBER was ignored, and a number went
3684
+ * through parseInt(), which reads it as a string: 1e-7 gave 1.
3655
3685
  ************************************************************/
3656
3686
  function kw_get_int(gobj, kw, path, default_value, flag) {
3657
3687
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -3659,6 +3689,7 @@ function kw_get_int(gobj, kw, path, default_value, flag) {
3659
3689
  const required = flag && flag & kw_flag_t.KW_REQUIRED;
3660
3690
  const create = flag && flag & kw_flag_t.KW_CREATE;
3661
3691
  const extract = flag && flag & kw_flag_t.KW_EXTRACT;
3692
+ const wild = flag && flag & kw_flag_t.KW_WILD_NUMBER;
3662
3693
  if (v === void 0) {
3663
3694
  if (create) {
3664
3695
  let v = parseInt(default_value);
@@ -3670,18 +3701,34 @@ function kw_get_int(gobj, kw, path, default_value, flag) {
3670
3701
  }
3671
3702
  return parseInt(default_value);
3672
3703
  }
3673
- if (extract) kw_delete(gobj, kw, path);
3674
- if (!is_number(v)) {
3675
- if (required) {
3676
- log_error(`path value MUST BE a number: ${path}`);
3677
- trace_msg(kw);
3678
- }
3704
+ let value;
3705
+ if (is_number(v)) value = Math.trunc(v);
3706
+ else if (!wild) {
3707
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json integer: '${path}'`);
3708
+ trace_json(kw);
3679
3709
  return parseInt(default_value);
3710
+ } else if (is_boolean(v)) value = v ? 1 : 0;
3711
+ else if (is_string(v)) value = strtoll_base0(v);
3712
+ else if (v === null) value = 0;
3713
+ else {
3714
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3715
+ trace_json(kw);
3716
+ return 0;
3680
3717
  }
3681
- return parseInt(v);
3718
+ if (extract) kw_delete(gobj, kw, path);
3719
+ return value;
3682
3720
  }
3683
3721
  /************************************************************
3684
- *
3722
+ * As the C kw_get_real(): a number found is the answer, and
3723
+ * anything else gives the default back (as a number), LOGGED
3724
+ * ("path MUST BE a json real", required or not) -- unless
3725
+ * KW_WILD_NUMBER asks to read a boolean (1/0), a string (as
3726
+ * parseFloat() reads it, 0 when it does not start with a number)
3727
+ * or null (0); a list or a dict is then 0, logged too.
3728
+ * KW_EXTRACT takes out only a value the reader answers with.
3729
+ * Before gobj-js 7.25.4 KW_EXTRACT deleted the value before its
3730
+ * type was looked at, a value of another type was logged only
3731
+ * with KW_REQUIRED, and KW_WILD_NUMBER was ignored.
3685
3732
  ************************************************************/
3686
3733
  function kw_get_real(gobj, kw, path, default_value, flag) {
3687
3734
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -3689,6 +3736,7 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3689
3736
  const required = flag && flag & kw_flag_t.KW_REQUIRED;
3690
3737
  const create = flag && flag & kw_flag_t.KW_CREATE;
3691
3738
  const extract = flag && flag & kw_flag_t.KW_EXTRACT;
3739
+ const wild = flag && flag & kw_flag_t.KW_WILD_NUMBER;
3692
3740
  if (v === void 0) {
3693
3741
  if (create) {
3694
3742
  let v = Number(default_value);
@@ -3700,15 +3748,39 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3700
3748
  }
3701
3749
  return Number(default_value);
3702
3750
  }
3703
- if (extract) kw_delete(gobj, kw, path);
3704
- if (!is_number(v)) {
3705
- if (required) {
3706
- log_error(`path value MUST BE a number: ${path}`);
3707
- trace_msg(kw);
3708
- }
3751
+ let value;
3752
+ if (is_number(v)) value = v;
3753
+ else if (!wild) {
3754
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json real: '${path}'`);
3755
+ trace_json(kw);
3709
3756
  return Number(default_value);
3757
+ } else if (is_boolean(v)) value = v ? 1 : 0;
3758
+ else if (is_string(v)) {
3759
+ value = parseFloat(v);
3760
+ if (Number.isNaN(value)) value = 0;
3761
+ } else if (v === null) value = 0;
3762
+ else {
3763
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a simple json element: '${path}'`);
3764
+ trace_json(kw);
3765
+ return 0;
3766
+ }
3767
+ if (extract) kw_delete(gobj, kw, path);
3768
+ return value;
3769
+ }
3770
+ /************************************************************
3771
+ * TRUE when the last key of `path` is a key of its dict, whatever
3772
+ * its value: kw_find_path() answers undefined both for a key that
3773
+ * is not there and for one whose value is undefined.
3774
+ ************************************************************/
3775
+ function kw_path_holds_key(kw, path) {
3776
+ let ss = is_string(path) ? path.split("`") : [];
3777
+ let parent = kw;
3778
+ for (let i = 0; i < ss.length - 1; i++) {
3779
+ if (!(is_object(parent) || is_array(parent))) return false;
3780
+ parent = parent[ss[i]];
3710
3781
  }
3711
- return Number(v);
3782
+ if (!(is_object(parent) || is_array(parent)) || ss.length === 0) return false;
3783
+ return Object.prototype.hasOwnProperty.call(parent, ss[ss.length - 1]);
3712
3784
  }
3713
3785
  /************************************************************
3714
3786
  * The default comes back AS GIVEN, as in the C kw_get_str()
@@ -3717,6 +3789,17 @@ function kw_get_real(gobj, kw, path, default_value, flag) {
3717
3789
  * TRUE: an `if(value)` on the answer took an absent key for a
3718
3790
  * present one. KW_CREATE stores a string default, and null for
3719
3791
  * any other, as C stores json_null() for a NULL default.
3792
+ * KW_EXTRACT takes out only a string (before gobj-js 7.25.4 it
3793
+ * deleted any value, and a value that was not a string was lost).
3794
+ * C refuses to extract a string at all ("Cannot extract a
3795
+ * string": its answer would point into the freed json); a JS
3796
+ * string outlives the kw, so here it is taken out.
3797
+ * A value that is there and is not a string gives the default
3798
+ * back, LOGGED ("path MUST BE a json str", required or not), as
3799
+ * C does; a null value is the key saying "no string" and is not
3800
+ * logged, and KW_REQUIRED logs only a path that is not there at
3801
+ * all. Before gobj-js 7.25.5 a value of another type was logged
3802
+ * only with KW_REQUIRED, and then a null one too.
3720
3803
  ************************************************************/
3721
3804
  function kw_get_str(gobj, kw, path, default_value, flag) {
3722
3805
  if (gobj && !is_gobj(gobj)) log_error(`GObj bad instanceof`);
@@ -3728,20 +3811,20 @@ function kw_get_str(gobj, kw, path, default_value, flag) {
3728
3811
  if (create) {
3729
3812
  kw_set_dict_value(gobj, kw, path, is_string(default_value) ? default_value : null);
3730
3813
  return default_value;
3731
- } else if (required) {
3814
+ } else if (required && !kw_path_holds_key(kw, path)) {
3732
3815
  log_error(`path not found: '${path}'`);
3733
3816
  trace_json(kw);
3734
3817
  }
3735
3818
  return default_value;
3736
3819
  }
3737
- if (extract) kw_delete(gobj, kw, path);
3738
3820
  if (!is_string(v)) {
3739
- if (required) {
3740
- log_error(`${gobj_short_name(gobj)}: path value MUST BE a string: ${path}`);
3741
- trace_msg(kw);
3821
+ if (v !== null) {
3822
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json str: '${path}'`);
3823
+ trace_json(kw);
3742
3824
  }
3743
3825
  return default_value;
3744
3826
  }
3827
+ if (extract) kw_delete(gobj, kw, path);
3745
3828
  return v;
3746
3829
  }
3747
3830
  /************************************************************
@@ -3793,8 +3876,8 @@ function kw_get_dict(gobj, kw, path, default_value, flag) {
3793
3876
  }
3794
3877
  if (!is_object(v)) {
3795
3878
  if (required) {
3796
- log_error(`${gobj_short_name(gobj)}: path value MUST BE a dict: ${path}`);
3797
- trace_msg(kw);
3879
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json dict: '${path}'`);
3880
+ trace_json(kw);
3798
3881
  }
3799
3882
  return default_value;
3800
3883
  }
@@ -3851,8 +3934,8 @@ function kw_get_list(gobj, kw, path, default_value, flag) {
3851
3934
  }
3852
3935
  if (!is_array(v)) {
3853
3936
  if (required) {
3854
- log_error(`${gobj_short_name(gobj)}: path value MUST BE an array: ${path}`);
3855
- trace_msg(kw);
3937
+ log_error(`${gobj ? gobj_short_name(gobj) + ": " : ""}path MUST BE a json list: '${path}'`);
3938
+ trace_json(kw);
3856
3939
  }
3857
3940
  return default_value;
3858
3941
  }