@rpcbase/server 0.462.0 → 0.463.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.
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { Request } from 'express';
|
|
2
2
|
import { StaticHandlerContext } from '../../router/src';
|
|
3
|
-
export
|
|
3
|
+
export type RouterContextWithRedirect = StaticHandlerContext & {
|
|
4
|
+
redirectResponse?: Response;
|
|
5
|
+
redirectRouteId?: string | null;
|
|
6
|
+
redirectRoutePath?: string | null;
|
|
7
|
+
};
|
|
8
|
+
export declare function applyRouteLoaders(req: Request, dataRoutes: any[]): Promise<RouterContextWithRedirect>;
|
|
4
9
|
//# sourceMappingURL=applyRouteLoaders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"applyRouteLoaders.d.ts","sourceRoot":"","sources":["../src/applyRouteLoaders.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAA;AAC/B,OAAO,EACL,oBAAoB,EAMrB,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"applyRouteLoaders.d.ts","sourceRoot":"","sources":["../src/applyRouteLoaders.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAA;AAC/B,OAAO,EACL,oBAAoB,EAMrB,MAAM,iBAAiB,CAAA;AAuFxB,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,GAAG;IAC7D,gBAAgB,CAAC,EAAE,QAAQ,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,CAAA;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,GAAG,EAAE,GAChB,OAAO,CAAC,yBAAyB,CAAC,CA6KpC"}
|
package/dist/index.js
CHANGED
|
@@ -3615,6 +3615,15 @@ const getErrorStatus = (error) => {
|
|
|
3615
3615
|
if (typeof candidate?.response?.status === "number") return candidate.response.status;
|
|
3616
3616
|
return void 0;
|
|
3617
3617
|
};
|
|
3618
|
+
const isResponseLike = (value) => {
|
|
3619
|
+
return Boolean(
|
|
3620
|
+
value && typeof value === "object" && typeof value.status === "number" && value.headers && typeof value.headers.get === "function"
|
|
3621
|
+
);
|
|
3622
|
+
};
|
|
3623
|
+
const isRedirectResponse = (value) => {
|
|
3624
|
+
if (!isResponseLike(value)) return false;
|
|
3625
|
+
return value.status >= 300 && value.status < 400 && Boolean(value.headers.get("Location"));
|
|
3626
|
+
};
|
|
3618
3627
|
async function applyRouteLoaders(req, dataRoutes) {
|
|
3619
3628
|
const baseUrl = `${req.protocol}://${req.get("host")}`;
|
|
3620
3629
|
const url = new URL(req.originalUrl, baseUrl);
|
|
@@ -3659,8 +3668,12 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
3659
3668
|
timeoutId = setTimeout(() => {
|
|
3660
3669
|
const err = new Error(`Loader timeout after ${LOADER_TIMEOUT_MS}ms`);
|
|
3661
3670
|
err.status = 504;
|
|
3662
|
-
console.error("[rpcbase timeout][server loader]", {
|
|
3663
|
-
|
|
3671
|
+
console.error("[rpcbase timeout][server loader]", {
|
|
3672
|
+
routeId: route.id,
|
|
3673
|
+
ms: LOADER_TIMEOUT_MS,
|
|
3674
|
+
url: req.originalUrl
|
|
3675
|
+
});
|
|
3676
|
+
reject({ id: route.id, path: route.path, reason: err });
|
|
3664
3677
|
}, LOADER_TIMEOUT_MS);
|
|
3665
3678
|
});
|
|
3666
3679
|
const loaderPromise = (async () => {
|
|
@@ -3669,9 +3682,9 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
3669
3682
|
params,
|
|
3670
3683
|
ctx: { req }
|
|
3671
3684
|
});
|
|
3672
|
-
return { id: route.id, data };
|
|
3685
|
+
return { id: route.id, path: route.path, data };
|
|
3673
3686
|
} catch (error) {
|
|
3674
|
-
throw { id: route.id, reason: error };
|
|
3687
|
+
throw { id: route.id, path: route.path, reason: error };
|
|
3675
3688
|
} finally {
|
|
3676
3689
|
if (timeoutId) {
|
|
3677
3690
|
clearTimeout(timeoutId);
|
|
@@ -3687,15 +3700,33 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
3687
3700
|
let errors = null;
|
|
3688
3701
|
let hasNotFoundError = false;
|
|
3689
3702
|
let hasNonNotFoundError = false;
|
|
3703
|
+
let redirectResponse = null;
|
|
3704
|
+
let redirectRouteId = null;
|
|
3705
|
+
let redirectRoutePath = null;
|
|
3690
3706
|
for (const result of loaderPromisesResults) {
|
|
3691
3707
|
if (result.status === "fulfilled") {
|
|
3692
3708
|
if (result.value) {
|
|
3693
|
-
|
|
3709
|
+
if (isRedirectResponse(result.value.data)) {
|
|
3710
|
+
redirectResponse = result.value.data;
|
|
3711
|
+
redirectRouteId = result.value.id;
|
|
3712
|
+
redirectRoutePath = result.value.path ?? null;
|
|
3713
|
+
} else {
|
|
3714
|
+
loaderData[result.value.id] = result.value.data;
|
|
3715
|
+
}
|
|
3694
3716
|
}
|
|
3695
3717
|
} else if (result.status === "rejected") {
|
|
3696
3718
|
const id = result.reason?.id;
|
|
3697
3719
|
if (!id) {
|
|
3698
|
-
throw new Error(
|
|
3720
|
+
throw new Error(
|
|
3721
|
+
`missing route ID in error: ${result.reason}`
|
|
3722
|
+
);
|
|
3723
|
+
}
|
|
3724
|
+
const reasonCandidate = result.reason?.reason ?? result.reason;
|
|
3725
|
+
if (isRedirectResponse(reasonCandidate)) {
|
|
3726
|
+
redirectResponse = reasonCandidate;
|
|
3727
|
+
redirectRouteId = id;
|
|
3728
|
+
redirectRoutePath = result.reason?.path ?? null;
|
|
3729
|
+
continue;
|
|
3699
3730
|
}
|
|
3700
3731
|
if (!errors) {
|
|
3701
3732
|
errors = {};
|
|
@@ -3709,6 +3740,19 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
3709
3740
|
errors[id] = result.reason;
|
|
3710
3741
|
}
|
|
3711
3742
|
}
|
|
3743
|
+
if (redirectResponse) {
|
|
3744
|
+
return {
|
|
3745
|
+
...baseContext,
|
|
3746
|
+
matches,
|
|
3747
|
+
loaderData,
|
|
3748
|
+
actionData: null,
|
|
3749
|
+
errors: null,
|
|
3750
|
+
statusCode: redirectResponse.status,
|
|
3751
|
+
redirectResponse,
|
|
3752
|
+
redirectRouteId,
|
|
3753
|
+
redirectRoutePath
|
|
3754
|
+
};
|
|
3755
|
+
}
|
|
3712
3756
|
let statusCode = 200;
|
|
3713
3757
|
if (errors) {
|
|
3714
3758
|
if (hasNonNotFoundError) {
|
|
@@ -3729,19 +3773,46 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
3729
3773
|
};
|
|
3730
3774
|
}
|
|
3731
3775
|
async function renderSSR(req, dataRoutes) {
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3776
|
+
const routerContext = await applyRouteLoaders(req, dataRoutes);
|
|
3777
|
+
const isMatched = routerContext.matches.length > 0;
|
|
3778
|
+
if (routerContext.redirectResponse) {
|
|
3779
|
+
return {
|
|
3780
|
+
element: null,
|
|
3781
|
+
isMatched,
|
|
3782
|
+
statusCode: routerContext.statusCode ?? routerContext.redirectResponse.status ?? 302,
|
|
3783
|
+
redirectResponse: routerContext.redirectResponse,
|
|
3784
|
+
redirectRouteId: routerContext.redirectRouteId,
|
|
3785
|
+
redirectRoutePath: routerContext.redirectRoutePath
|
|
3786
|
+
};
|
|
3738
3787
|
}
|
|
3739
3788
|
if (routerContext.errors) {
|
|
3740
3789
|
if (routerContext.statusCode === 404) {
|
|
3741
|
-
console.warn(`SSR 404 ${req.method} ${req.originalUrl}`, {
|
|
3790
|
+
console.warn(`SSR 404 ${req.method} ${req.originalUrl}`, {
|
|
3791
|
+
errors: routerContext.errors
|
|
3792
|
+
});
|
|
3742
3793
|
} else {
|
|
3743
|
-
|
|
3744
|
-
|
|
3794
|
+
const matchesSummary = routerContext.matches?.map((m) => ({
|
|
3795
|
+
routeId: m.route.id,
|
|
3796
|
+
routePath: m.route.path,
|
|
3797
|
+
pathname: m.pathname,
|
|
3798
|
+
pathnameBase: m.pathnameBase
|
|
3799
|
+
}));
|
|
3800
|
+
console.error(
|
|
3801
|
+
`SSR ${routerContext.statusCode || 500} ${req.method} ${req.originalUrl}`,
|
|
3802
|
+
{
|
|
3803
|
+
errors: routerContext.errors,
|
|
3804
|
+
matches: matchesSummary
|
|
3805
|
+
}
|
|
3806
|
+
);
|
|
3807
|
+
const errorEntries = Object.entries(routerContext.errors || {});
|
|
3808
|
+
const firstErrorEntry = errorEntries[0]?.[1];
|
|
3809
|
+
const firstReason = firstErrorEntry?.reason ?? firstErrorEntry;
|
|
3810
|
+
if (errorEntries.length === 1 && firstReason instanceof Error) {
|
|
3811
|
+
firstReason.details = routerContext.errors;
|
|
3812
|
+
throw firstReason;
|
|
3813
|
+
}
|
|
3814
|
+
const extra = firstErrorEntry?.id ? ` (route ${firstErrorEntry.id}${firstErrorEntry?.path ? ` ${firstErrorEntry.path}` : ""})` : "";
|
|
3815
|
+
const error = new Error(`SSR loader error${extra}`);
|
|
3745
3816
|
error.details = routerContext.errors;
|
|
3746
3817
|
throw error;
|
|
3747
3818
|
}
|
|
@@ -3754,8 +3825,11 @@ async function renderSSR(req, dataRoutes) {
|
|
|
3754
3825
|
context: routerContext
|
|
3755
3826
|
}
|
|
3756
3827
|
) });
|
|
3757
|
-
|
|
3758
|
-
|
|
3828
|
+
return {
|
|
3829
|
+
element,
|
|
3830
|
+
isMatched,
|
|
3831
|
+
statusCode: routerContext.statusCode ?? (routerContext.errors ? 500 : 200)
|
|
3832
|
+
};
|
|
3759
3833
|
}
|
|
3760
3834
|
const ABORT_DELAY_MS = 1e4;
|
|
3761
3835
|
const APP_HTML_PLACEHOLDER = "<!--app-html-->";
|
|
@@ -3764,6 +3838,33 @@ const FALLBACK_ERROR_TEMPLATE_START = `<!doctype html><html lang="en"><head><met
|
|
|
3764
3838
|
const FALLBACK_ERROR_TEMPLATE_END = "</main></body></html>";
|
|
3765
3839
|
const isProduction = env.NODE_ENV === "production";
|
|
3766
3840
|
const templateHtml = isProduction ? readFileSync("./build/dist/client/src/client/index.html", "utf-8") : "";
|
|
3841
|
+
const handleRedirectionResponse = (res, redirectResponse, location) => {
|
|
3842
|
+
res.status(redirectResponse.status || 302);
|
|
3843
|
+
try {
|
|
3844
|
+
const headers = redirectResponse.headers;
|
|
3845
|
+
const setCookies = headers.getSetCookie?.();
|
|
3846
|
+
const fallbackSetCookies = [];
|
|
3847
|
+
for (const [key, value] of headers) {
|
|
3848
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
3849
|
+
if (!setCookies?.length) {
|
|
3850
|
+
fallbackSetCookies.push(value);
|
|
3851
|
+
}
|
|
3852
|
+
continue;
|
|
3853
|
+
}
|
|
3854
|
+
res.setHeader(key, value);
|
|
3855
|
+
}
|
|
3856
|
+
if (setCookies?.length) {
|
|
3857
|
+
res.setHeader("Set-Cookie", setCookies);
|
|
3858
|
+
} else if (fallbackSetCookies.length === 1) {
|
|
3859
|
+
res.setHeader("Set-Cookie", fallbackSetCookies[0]);
|
|
3860
|
+
} else if (fallbackSetCookies.length > 1) {
|
|
3861
|
+
res.setHeader("Set-Cookie", fallbackSetCookies);
|
|
3862
|
+
}
|
|
3863
|
+
} catch {
|
|
3864
|
+
}
|
|
3865
|
+
if (location) res.setHeader("Location", location);
|
|
3866
|
+
res.end();
|
|
3867
|
+
};
|
|
3767
3868
|
const formatErrorDetails = (error) => {
|
|
3768
3869
|
if (isProduction) return void 0;
|
|
3769
3870
|
if (!error) return void 0;
|
|
@@ -3874,7 +3975,32 @@ const ssrMiddleware = ({
|
|
|
3874
3975
|
htmlStart = templateStart;
|
|
3875
3976
|
}
|
|
3876
3977
|
htmlEnd = template.slice(placeholderIndex + APP_HTML_PLACEHOLDER.length);
|
|
3877
|
-
const {
|
|
3978
|
+
const {
|
|
3979
|
+
element,
|
|
3980
|
+
isMatched,
|
|
3981
|
+
statusCode,
|
|
3982
|
+
redirectResponse,
|
|
3983
|
+
redirectRouteId,
|
|
3984
|
+
redirectRoutePath
|
|
3985
|
+
} = await renderSSR(req, dataRoutes);
|
|
3986
|
+
if (redirectResponse) {
|
|
3987
|
+
if (!responseCommitted) {
|
|
3988
|
+
const location = redirectResponse.headers?.get?.("Location");
|
|
3989
|
+
if (!isProduction) {
|
|
3990
|
+
console.info("SSR redirect", {
|
|
3991
|
+
method: req.method,
|
|
3992
|
+
url: req.originalUrl,
|
|
3993
|
+
status: redirectResponse.status,
|
|
3994
|
+
location,
|
|
3995
|
+
routeId: redirectRouteId ?? void 0,
|
|
3996
|
+
routePath: redirectRoutePath ?? void 0
|
|
3997
|
+
});
|
|
3998
|
+
}
|
|
3999
|
+
responseCommitted = true;
|
|
4000
|
+
handleRedirectionResponse(res, redirectResponse, location);
|
|
4001
|
+
}
|
|
4002
|
+
return;
|
|
4003
|
+
}
|
|
3878
4004
|
if (!isMatched) {
|
|
3879
4005
|
next();
|
|
3880
4006
|
return;
|
package/dist/renderSSR.d.ts
CHANGED
|
@@ -2,8 +2,11 @@ import { ReactNode } from 'react';
|
|
|
2
2
|
import { StaticHandler } from '../../router/src';
|
|
3
3
|
import * as express from "express";
|
|
4
4
|
export declare function renderSSR(req: express.Request, dataRoutes: StaticHandler["dataRoutes"]): Promise<{
|
|
5
|
-
element: ReactNode;
|
|
5
|
+
element: ReactNode | null;
|
|
6
6
|
isMatched: boolean;
|
|
7
7
|
statusCode: number;
|
|
8
|
+
redirectResponse?: Response;
|
|
9
|
+
redirectRouteId?: string | null;
|
|
10
|
+
redirectRoutePath?: string | null;
|
|
8
11
|
}>;
|
|
9
12
|
//# sourceMappingURL=renderSSR.d.ts.map
|
package/dist/renderSSR.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderSSR.d.ts","sourceRoot":"","sources":["../src/renderSSR.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAC7C,OAAO,EAGL,aAAa,EACd,MAAM,iBAAiB,CAAA;AAOxB,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,GACtC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"renderSSR.d.ts","sourceRoot":"","sources":["../src/renderSSR.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAc,MAAM,OAAO,CAAA;AAC7C,OAAO,EAGL,aAAa,EACd,MAAM,iBAAiB,CAAA;AAOxB,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,GACtC,OAAO,CAAC;IACT,OAAO,EAAE,SAAS,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,OAAO,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,QAAQ,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,CAAC,CAwED"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssrMiddleware.d.ts","sourceRoot":"","sources":["../src/ssrMiddleware.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACjI,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAErE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAM3C,KAAK,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ssrMiddleware.d.ts","sourceRoot":"","sources":["../src/ssrMiddleware.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACjI,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAErE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAM3C,KAAK,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC;AAkI9D,eAAO,MAAM,aAAa,GAAI,kFAM3B;IACD,YAAY,EAAE,aAAa,CAAC;IAC5B,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACxC,mBAAmB,CAAC,EAAE,aAAa,CAAC;QAAE,KAAK,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAC,CAAC;IACtE,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/E,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC1B,MAAW,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,kBA0K1D,CAAA"}
|