@jsenv/server 16.0.3 → 16.0.4

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.
@@ -3600,6 +3600,13 @@ const applyPattern = ({ url, pattern }) => {
3600
3600
  };
3601
3601
  };
3602
3602
 
3603
+ /**
3604
+ * Core pattern matching function that processes patterns against strings
3605
+ * @param {string} pattern - The pattern with special syntax (*,**,/) to match against
3606
+ * @param {string} string - The string to test against the pattern
3607
+ * @returns {Object} Result containing match status and capture groups
3608
+ * @private
3609
+ */
3603
3610
  const applyMatching = (pattern, string) => {
3604
3611
  const groups = [];
3605
3612
  let patternIndex = 0;
@@ -3624,22 +3631,6 @@ const applyMatching = (pattern, string) => {
3624
3631
  return consumeString(remainingString.length);
3625
3632
  };
3626
3633
 
3627
- let matched;
3628
- const iterate = () => {
3629
- const patternIndexBefore = patternIndex;
3630
- const indexBefore = index;
3631
- matched = matchOne();
3632
- if (matched === undefined) {
3633
- consumePattern(1);
3634
- consumeString(1);
3635
- iterate();
3636
- return;
3637
- }
3638
- if (matched === false && restoreIndexes) {
3639
- patternIndex = patternIndexBefore;
3640
- index = indexBefore;
3641
- }
3642
- };
3643
3634
  const matchOne = () => {
3644
3635
  // pattern consumed
3645
3636
  if (remainingPattern === "") {
@@ -3649,12 +3640,11 @@ const applyMatching = (pattern, string) => {
3649
3640
  if (remainingString[0] === "?") {
3650
3641
  // match search params
3651
3642
  consumeRemainingString();
3652
-
3653
3643
  return true;
3654
3644
  }
3655
- // if remainingString
3656
3645
  return false; // fails because string longer than expect
3657
3646
  }
3647
+
3658
3648
  // -- from this point pattern is not consumed --
3659
3649
  // string consumed, pattern not consumed
3660
3650
  if (remainingString === "") {
@@ -3668,6 +3658,7 @@ const applyMatching = (pattern, string) => {
3668
3658
  }
3669
3659
  return false; // fail because string shorter than expect
3670
3660
  }
3661
+
3671
3662
  // -- from this point pattern and string are not consumed --
3672
3663
  // fast path trailing slash
3673
3664
  if (remainingPattern === "/") {
@@ -3679,25 +3670,30 @@ const applyMatching = (pattern, string) => {
3679
3670
  }
3680
3671
  return false;
3681
3672
  }
3673
+
3682
3674
  // fast path trailing '**'
3683
3675
  if (remainingPattern === "**") {
3684
3676
  consumePattern(2);
3685
3677
  consumeRemainingString();
3686
3678
  return true;
3687
3679
  }
3680
+
3688
3681
  if (remainingPattern.slice(0, 4) === "/**/") {
3689
3682
  consumePattern(3); // consumes "/**/"
3690
- const skipResult = skipUntilMatch({
3683
+ const skipResult = skipUntilMatchIterative({
3691
3684
  pattern: remainingPattern,
3692
3685
  string: remainingString,
3693
3686
  canSkipSlash: true,
3694
3687
  });
3695
- groups.push(...skipResult.groups);
3688
+ for (let i = 0; i < skipResult.groups.length; i++) {
3689
+ groups.push(skipResult.groups[i]);
3690
+ }
3696
3691
  consumePattern(skipResult.patternIndex);
3697
3692
  consumeRemainingString();
3698
3693
  restoreIndexes = false;
3699
3694
  return skipResult.matched;
3700
3695
  }
3696
+
3701
3697
  // pattern leading **
3702
3698
  if (remainingPattern.slice(0, 2) === "**") {
3703
3699
  consumePattern(2); // consumes "**"
@@ -3707,7 +3703,7 @@ const applyMatching = (pattern, string) => {
3707
3703
  // when remainingPattern was preceeded by "**/"
3708
3704
  // and remainingString have no "/"
3709
3705
  // then skip is not allowed, a regular match will be performed
3710
- if (!remainingString.includes("/")) {
3706
+ if (remainingString.includes("/")) {
3711
3707
  skipAllowed = false;
3712
3708
  }
3713
3709
  }
@@ -3717,18 +3713,21 @@ const applyMatching = (pattern, string) => {
3717
3713
  return true;
3718
3714
  }
3719
3715
  if (skipAllowed) {
3720
- const skipResult = skipUntilMatch({
3716
+ const skipResult = skipUntilMatchIterative({
3721
3717
  pattern: remainingPattern,
3722
3718
  string: remainingString,
3723
3719
  canSkipSlash: true,
3724
3720
  });
3725
- groups.push(...skipResult.groups);
3721
+ for (let i = 0; i < skipResult.groups.length; i++) {
3722
+ groups.push(skipResult.groups[i]);
3723
+ }
3726
3724
  consumePattern(skipResult.patternIndex);
3727
3725
  consumeRemainingString();
3728
3726
  restoreIndexes = false;
3729
3727
  return skipResult.matched;
3730
3728
  }
3731
3729
  }
3730
+
3732
3731
  if (remainingPattern[0] === "*") {
3733
3732
  consumePattern(1); // consumes "*"
3734
3733
  if (remainingPattern === "") {
@@ -3748,23 +3747,53 @@ const applyMatching = (pattern, string) => {
3748
3747
  patternIndex = patternIndex - 1;
3749
3748
  return false;
3750
3749
  }
3751
- const skipResult = skipUntilMatch({
3750
+ const skipResult = skipUntilMatchIterative({
3752
3751
  pattern: remainingPattern,
3753
3752
  string: remainingString,
3754
3753
  canSkipSlash: false,
3755
3754
  });
3756
- groups.push(skipResult.group, ...skipResult.groups);
3755
+ groups.push(skipResult.group);
3756
+ for (let i = 0; i < skipResult.groups.length; i++) {
3757
+ groups.push(skipResult.groups[i]);
3758
+ }
3757
3759
  consumePattern(skipResult.patternIndex);
3758
3760
  consumeString(skipResult.index);
3759
3761
  restoreIndexes = false;
3760
3762
  return skipResult.matched;
3761
3763
  }
3764
+
3762
3765
  if (remainingPattern[0] !== remainingString[0]) {
3763
3766
  return false;
3764
3767
  }
3768
+
3765
3769
  return undefined;
3766
3770
  };
3767
- iterate();
3771
+
3772
+ // Replace recursive iterate() with iterative approach
3773
+ let matched;
3774
+ let continueIteration = true;
3775
+
3776
+ while (continueIteration) {
3777
+ const patternIndexBefore = patternIndex;
3778
+ const indexBefore = index;
3779
+
3780
+ matched = matchOne();
3781
+
3782
+ if (matched === undefined) {
3783
+ consumePattern(1);
3784
+ consumeString(1);
3785
+ // Continue the loop instead of recursion
3786
+ continue;
3787
+ }
3788
+
3789
+ if (matched === false && restoreIndexes) {
3790
+ patternIndex = patternIndexBefore;
3791
+ index = indexBefore;
3792
+ }
3793
+
3794
+ // End the loop
3795
+ continueIteration = false;
3796
+ }
3768
3797
 
3769
3798
  return {
3770
3799
  matched,
@@ -3774,7 +3803,15 @@ const applyMatching = (pattern, string) => {
3774
3803
  };
3775
3804
  };
3776
3805
 
3777
- const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
3806
+ /**
3807
+ * Iterative version of skipUntilMatch that avoids recursion
3808
+ * @param {Object} params
3809
+ * @param {string} params.pattern - The pattern to match
3810
+ * @param {string} params.string - The string to test against
3811
+ * @param {boolean} params.canSkipSlash - Whether slash characters can be skipped
3812
+ * @returns {Object} Result of the matching attempt
3813
+ */
3814
+ const skipUntilMatchIterative = ({ pattern, string, canSkipSlash }) => {
3778
3815
  let index = 0;
3779
3816
  let remainingString = string;
3780
3817
  let longestAttemptRange = null;
@@ -3792,8 +3829,10 @@ const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
3792
3829
  };
3793
3830
  };
3794
3831
 
3795
- const tryToMatch = () => {
3832
+ // Loop until a match is found or all attempts fail
3833
+ while (true) {
3796
3834
  const matchAttempt = applyMatching(pattern, remainingString);
3835
+
3797
3836
  if (matchAttempt.matched) {
3798
3837
  return {
3799
3838
  matched: true,
@@ -3808,6 +3847,7 @@ const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
3808
3847
  },
3809
3848
  };
3810
3849
  }
3850
+
3811
3851
  const attemptIndex = matchAttempt.index;
3812
3852
  const attemptRange = {
3813
3853
  patternIndex: matchAttempt.patternIndex,
@@ -3815,19 +3855,23 @@ const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
3815
3855
  length: attemptIndex,
3816
3856
  groups: matchAttempt.groups,
3817
3857
  };
3858
+
3818
3859
  if (
3819
3860
  !longestAttemptRange ||
3820
3861
  longestAttemptRange.length < attemptRange.length
3821
3862
  ) {
3822
3863
  longestAttemptRange = attemptRange;
3823
3864
  }
3865
+
3824
3866
  if (isLastAttempt) {
3825
3867
  return failure();
3826
3868
  }
3869
+
3827
3870
  const nextIndex = attemptIndex + 1;
3828
3871
  if (nextIndex >= remainingString.length) {
3829
3872
  return failure();
3830
3873
  }
3874
+
3831
3875
  if (remainingString[0] === "/") {
3832
3876
  if (!canSkipSlash) {
3833
3877
  return failure();
@@ -3837,12 +3881,11 @@ const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
3837
3881
  isLastAttempt = true;
3838
3882
  }
3839
3883
  }
3884
+
3840
3885
  // search against the next unattempted string
3841
3886
  index += nextIndex;
3842
3887
  remainingString = remainingString.slice(nextIndex);
3843
- return tryToMatch();
3844
- };
3845
- return tryToMatch();
3888
+ }
3846
3889
  };
3847
3890
 
3848
3891
  const applyPatternMatching = ({ url, pattern }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/server",
3
- "version": "16.0.3",
3
+ "version": "16.0.4",
4
4
  "description": "A modern Node.js HTTP server with declarative routing, content negotiation, and WebSocket support",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -42,7 +42,7 @@
42
42
  "launch-editor": "2.10.0",
43
43
  "@jsenv/abort": "4.3.1",
44
44
  "@jsenv/humanize": "1.3.1",
45
- "@jsenv/url-meta": "8.5.4",
45
+ "@jsenv/url-meta": "8.5.5",
46
46
  "@jsenv/utils": "2.2.1",
47
47
  "ws": "8.18.1",
48
48
  "formidable": "3.5.2"