@jsenv/server 16.0.3 → 16.0.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.
- package/dist/{html → client/route_inspector}/route_inspector.html +75 -818
- package/dist/jsenv_server.js +111 -64
- package/package.json +2 -2
- /package/dist/{html → client/4xx}/4xx.html +0 -0
- /package/dist/{html → client/500}/500.html +0 -0
- /package/dist/{js → formidable_index}/formidable_index.js +0 -0
- /package/dist/{js → ws}/ws.js +0 -0
package/dist/jsenv_server.js
CHANGED
|
@@ -1718,7 +1718,7 @@ const fromNodeRequest = (
|
|
|
1718
1718
|
`formData() called on a request with content-type: "${contentType}". multipart/form-data was expected.`,
|
|
1719
1719
|
);
|
|
1720
1720
|
}
|
|
1721
|
-
const { formidable } = await import("./
|
|
1721
|
+
const { formidable } = await import("./formidable_index/formidable_index.js");
|
|
1722
1722
|
const form = formidable({});
|
|
1723
1723
|
nodeRequest.resume(); // was paused in line #53
|
|
1724
1724
|
const [fields, files] = await form.parse(nodeRequest);
|
|
@@ -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 =
|
|
3683
|
+
const skipResult = skipUntilMatchIterative({
|
|
3691
3684
|
pattern: remainingPattern,
|
|
3692
3685
|
string: remainingString,
|
|
3693
3686
|
canSkipSlash: true,
|
|
3694
3687
|
});
|
|
3695
|
-
|
|
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 (
|
|
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 =
|
|
3716
|
+
const skipResult = skipUntilMatchIterative({
|
|
3721
3717
|
pattern: remainingPattern,
|
|
3722
3718
|
string: remainingString,
|
|
3723
3719
|
canSkipSlash: true,
|
|
3724
3720
|
});
|
|
3725
|
-
|
|
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 =
|
|
3750
|
+
const skipResult = skipUntilMatchIterative({
|
|
3752
3751
|
pattern: remainingPattern,
|
|
3753
3752
|
string: remainingString,
|
|
3754
3753
|
canSkipSlash: false,
|
|
3755
3754
|
});
|
|
3756
|
-
groups.push(skipResult.group
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3844
|
-
};
|
|
3845
|
-
return tryToMatch();
|
|
3888
|
+
}
|
|
3846
3889
|
};
|
|
3847
3890
|
|
|
3848
3891
|
const applyPatternMatching = ({ url, pattern }) => {
|
|
@@ -3852,9 +3895,19 @@ const applyPatternMatching = ({ url, pattern }) => {
|
|
|
3852
3895
|
return applyPattern({ url, pattern });
|
|
3853
3896
|
};
|
|
3854
3897
|
|
|
3855
|
-
const resolveAssociations = (associations,
|
|
3856
|
-
|
|
3857
|
-
|
|
3898
|
+
const resolveAssociations = (associations, resolver) => {
|
|
3899
|
+
let resolve = () => {};
|
|
3900
|
+
if (typeof resolver === "function") {
|
|
3901
|
+
resolve = resolver;
|
|
3902
|
+
} else if (typeof resolver === "string") {
|
|
3903
|
+
const baseUrl = resolver;
|
|
3904
|
+
assertUrlLike(baseUrl, "baseUrl");
|
|
3905
|
+
resolve = (pattern) => new URL(pattern, baseUrl).href;
|
|
3906
|
+
} else if (resolver && typeof resolver.href === "string") {
|
|
3907
|
+
const baseUrl = resolver.href;
|
|
3908
|
+
assertUrlLike(baseUrl, "baseUrl");
|
|
3909
|
+
resolve = (pattern) => new URL(pattern, baseUrl).href;
|
|
3910
|
+
}
|
|
3858
3911
|
|
|
3859
3912
|
const associationsResolved = {};
|
|
3860
3913
|
for (const key of Object.keys(associations)) {
|
|
@@ -3865,12 +3918,11 @@ const resolveAssociations = (associations, baseUrl) => {
|
|
|
3865
3918
|
const valueAssociated = value[pattern];
|
|
3866
3919
|
let patternResolved;
|
|
3867
3920
|
try {
|
|
3868
|
-
patternResolved =
|
|
3921
|
+
patternResolved = resolve(pattern);
|
|
3869
3922
|
} catch {
|
|
3870
3923
|
// it's not really an url, no need to perform url resolution nor encoding
|
|
3871
3924
|
patternResolved = pattern;
|
|
3872
3925
|
}
|
|
3873
|
-
|
|
3874
3926
|
valueMapResolved[patternResolved] = valueAssociated;
|
|
3875
3927
|
}
|
|
3876
3928
|
associationsResolved[key] = valueMapResolved;
|
|
@@ -3993,34 +4045,29 @@ const urlChildMayMatch = ({ url, associations, predicate }) => {
|
|
|
3993
4045
|
};
|
|
3994
4046
|
|
|
3995
4047
|
const applyAliases = ({ url, aliases }) => {
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
const aliasMatchResult = applyPatternMatching({
|
|
4048
|
+
for (const key of Object.keys(aliases)) {
|
|
4049
|
+
const matchResult = applyPatternMatching({
|
|
3999
4050
|
pattern: key,
|
|
4000
4051
|
url,
|
|
4001
4052
|
});
|
|
4002
|
-
if (
|
|
4003
|
-
|
|
4004
|
-
return true;
|
|
4053
|
+
if (!matchResult.matched) {
|
|
4054
|
+
continue;
|
|
4005
4055
|
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
newUrl += `${part}`;
|
|
4018
|
-
if (index < parts.length - 1) {
|
|
4019
|
-
newUrl += matchGroups[index];
|
|
4056
|
+
const { matchGroups } = matchResult;
|
|
4057
|
+
const alias = aliases[key];
|
|
4058
|
+
const parts = alias.split("*");
|
|
4059
|
+
let newUrl = "";
|
|
4060
|
+
let index = 0;
|
|
4061
|
+
for (const part of parts) {
|
|
4062
|
+
newUrl += `${part}`;
|
|
4063
|
+
if (index < parts.length - 1) {
|
|
4064
|
+
newUrl += matchGroups[index];
|
|
4065
|
+
}
|
|
4066
|
+
index++;
|
|
4020
4067
|
}
|
|
4021
|
-
|
|
4068
|
+
return newUrl;
|
|
4022
4069
|
}
|
|
4023
|
-
return
|
|
4070
|
+
return url;
|
|
4024
4071
|
};
|
|
4025
4072
|
|
|
4026
4073
|
const matches = (url, patterns) => {
|
|
@@ -6446,7 +6493,7 @@ const replacePlaceholdersInHtml = (html, replacers) => {
|
|
|
6446
6493
|
});
|
|
6447
6494
|
};
|
|
6448
6495
|
|
|
6449
|
-
const clientErrorHtmlTemplateFileUrl = import.meta.resolve("./
|
|
6496
|
+
const clientErrorHtmlTemplateFileUrl = import.meta.resolve("./client/4xx/4xx.html");
|
|
6450
6497
|
|
|
6451
6498
|
const jsenvServiceDefaultBody4xx5xx = () => {
|
|
6452
6499
|
return {
|
|
@@ -6525,7 +6572,7 @@ const generateBadStatusResponse = (
|
|
|
6525
6572
|
);
|
|
6526
6573
|
};
|
|
6527
6574
|
|
|
6528
|
-
const jsenvServerRootDirectoryUrl = import.meta.resolve("
|
|
6575
|
+
const jsenvServerRootDirectoryUrl = import.meta.resolve("./");
|
|
6529
6576
|
|
|
6530
6577
|
const jsenvServiceInternalClientFiles = () => {
|
|
6531
6578
|
return {
|
|
@@ -6593,7 +6640,7 @@ const jsenvServiceOpenFile = () => {
|
|
|
6593
6640
|
};
|
|
6594
6641
|
|
|
6595
6642
|
const routeInspectorHtmlFileUrl = import.meta.resolve(
|
|
6596
|
-
"./
|
|
6643
|
+
"./client/route_inspector/route_inspector.html",
|
|
6597
6644
|
);
|
|
6598
6645
|
|
|
6599
6646
|
const jsenvServiceRouteInspector = () => {
|
|
@@ -7584,7 +7631,7 @@ const startServer = async ({
|
|
|
7584
7631
|
const webSocketSet = new Set();
|
|
7585
7632
|
let upgradeRequestToWebSocket;
|
|
7586
7633
|
const loadUpgradeRequestToWebSocket = async () => {
|
|
7587
|
-
const { WebSocketServer } = await import("./
|
|
7634
|
+
const { WebSocketServer } = await import("./ws/ws.js");
|
|
7588
7635
|
let webSocketServer = new WebSocketServer({ noServer: true });
|
|
7589
7636
|
stopCallbackSet.add(() => {
|
|
7590
7637
|
webSocketServer.close();
|
|
@@ -7769,7 +7816,7 @@ const PROCESS_TEARDOWN_EVENTS_MAP = {
|
|
|
7769
7816
|
exit: STOP_REASON_PROCESS_EXIT,
|
|
7770
7817
|
};
|
|
7771
7818
|
|
|
7772
|
-
const internalErrorHtmlFileUrl = import.meta.resolve("./
|
|
7819
|
+
const internalErrorHtmlFileUrl = import.meta.resolve("./client/500/500.html");
|
|
7773
7820
|
|
|
7774
7821
|
const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
7775
7822
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/server",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.5",
|
|
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.
|
|
45
|
+
"@jsenv/url-meta": "8.5.6",
|
|
46
46
|
"@jsenv/utils": "2.2.1",
|
|
47
47
|
"ws": "8.18.1",
|
|
48
48
|
"formidable": "3.5.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dist/{js → ws}/ws.js
RENAMED
|
File without changes
|