@ereo/server 0.2.3 → 0.2.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/dist/auth-enforcement.d.ts +25 -0
- package/dist/auth-enforcement.d.ts.map +1 -0
- package/dist/bun-server.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -63
- package/dist/streaming.d.ts +7 -8
- package/dist/streaming.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ereo/server - Auth Config Enforcement
|
|
3
|
+
*
|
|
4
|
+
* Runtime enforcement of route-level AuthConfig.
|
|
5
|
+
* Evaluates static checks (required, roles) and custom check functions
|
|
6
|
+
* before loaders/actions execute.
|
|
7
|
+
*/
|
|
8
|
+
import type { AuthConfig, AuthCheckResult, AppContext } from '@ereo/core';
|
|
9
|
+
/**
|
|
10
|
+
* Resolve an auth denial using the static AuthConfig fallbacks.
|
|
11
|
+
* Checks redirect → unauthorized → default 403.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveAuthDenial(auth: AuthConfig, request: Request): Response;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a rich AuthCheckResult denial into a Response.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveCheckResult(result: AuthCheckResult & {
|
|
18
|
+
allowed: false;
|
|
19
|
+
}): Response;
|
|
20
|
+
/**
|
|
21
|
+
* Enforce route-level auth config before running loaders/actions.
|
|
22
|
+
* Returns a Response if access is denied, or null if access is allowed.
|
|
23
|
+
*/
|
|
24
|
+
export declare function enforceAuthConfig(authConfig: AuthConfig, request: Request, context: AppContext, params: Record<string, string | string[] | undefined>): Promise<Response | null>;
|
|
25
|
+
//# sourceMappingURL=auth-enforcement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-enforcement.d.ts","sourceRoot":"","sources":["../src/auth-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,QAAQ,CAgB9E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GAAG,QAAQ,CAezF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACpD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4B1B"}
|
package/dist/bun-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bun-server.d.ts","sourceRoot":"","sources":["../src/bun-server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,KAAK,EAAmE,iBAAiB,EAA0C,MAAM,YAAY,CAAC;AAC7J,OAAO,EAAiC,OAAO,EAAiB,MAAM,YAAY,CAAC;AACnF,OAAO,EAAE,UAAU,EAAwD,MAAM,cAAc,CAAC;AAChG,OAAO,EAIL,IAAI,EACJ,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAA+C,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"bun-server.d.ts","sourceRoot":"","sources":["../src/bun-server.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,9 @@ export type { ServerOptions, ServerRenderMode } from './bun-server';
|
|
|
8
8
|
export { MiddlewareChain, createMiddlewareChain, logger, cors, securityHeaders, compress, rateLimit, } from './middleware';
|
|
9
9
|
export type { MiddlewareDefinition, CorsOptions, SecurityHeadersOptions, RateLimitOptions, } from './middleware';
|
|
10
10
|
export type { MiddlewareHandler, NextFunction, Middleware, AppContext, } from '@ereo/core';
|
|
11
|
+
export { enforceAuthConfig, resolveAuthDenial, resolveCheckResult, } from './auth-enforcement';
|
|
11
12
|
export { serveStatic, staticMiddleware, getMimeType, } from './static';
|
|
12
13
|
export type { StaticOptions } from './static';
|
|
13
|
-
export { createShell, renderToStream, renderToString, createResponse,
|
|
14
|
+
export { createShell, renderToStream, renderToString, createResponse, } from './streaming';
|
|
14
15
|
export type { RenderOptions, ShellTemplate, RenderResult, } from './streaming';
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,SAAS,EACT,YAAY,EACZ,KAAK,GACN,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,eAAe,EACf,QAAQ,EACR,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAItB,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,WAAW,GACZ,MAAM,UAAU,CAAC;AAElB,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,SAAS,EACT,YAAY,EACZ,KAAK,GACN,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,eAAe,EACf,QAAQ,EACR,SAAS,GACV,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAItB,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,WAAW,GACZ,MAAM,UAAU,CAAC;AAElB,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -484,40 +484,85 @@ async function renderToStream(element, options) {
|
|
|
484
484
|
context: options.context
|
|
485
485
|
});
|
|
486
486
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
487
|
+
const hasDeferred = hasDeferredData(loaderData);
|
|
488
|
+
const { head, tail } = createShell({
|
|
489
|
+
shell,
|
|
490
|
+
scripts: [],
|
|
491
|
+
styles,
|
|
492
|
+
loaderData: hasDeferred ? null : loaderData
|
|
493
|
+
});
|
|
491
494
|
return new Promise((resolve2, reject) => {
|
|
492
495
|
const { PassThrough } = __require("stream");
|
|
496
|
+
const DEFERRED_TIMEOUT_MS = 1e4;
|
|
497
|
+
const RENDER_TIMEOUT_MS = 1e4;
|
|
498
|
+
const timeoutId = setTimeout(() => {
|
|
499
|
+
abort();
|
|
500
|
+
}, RENDER_TIMEOUT_MS);
|
|
493
501
|
const { pipe, abort } = renderToPipeableStream(element, {
|
|
494
|
-
|
|
502
|
+
bootstrapModules: scripts,
|
|
495
503
|
onShellReady() {
|
|
496
504
|
const passThrough = new PassThrough;
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
505
|
+
const encoder = new TextEncoder;
|
|
506
|
+
const stream = new ReadableStream({
|
|
507
|
+
start(controller) {
|
|
508
|
+
controller.enqueue(encoder.encode(head));
|
|
509
|
+
passThrough.on("data", (chunk) => {
|
|
510
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
511
|
+
});
|
|
512
|
+
passThrough.on("end", () => {
|
|
513
|
+
(async () => {
|
|
514
|
+
if (hasDeferred) {
|
|
515
|
+
let resolvedData;
|
|
516
|
+
try {
|
|
517
|
+
resolvedData = await Promise.race([
|
|
518
|
+
resolveAllDeferred(loaderData),
|
|
519
|
+
new Promise((_, rejectTimeout) => setTimeout(() => rejectTimeout(new Error("Deferred data resolution timed out")), DEFERRED_TIMEOUT_MS))
|
|
520
|
+
]);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
console.error("Deferred data resolution failed:", error);
|
|
523
|
+
resolvedData = null;
|
|
524
|
+
}
|
|
525
|
+
const loaderScript = `<script>window.__EREO_DATA__=${serializeLoaderData(resolvedData)}</script>`;
|
|
526
|
+
const resolvedTail = `</div>
|
|
527
|
+
${loaderScript}
|
|
528
|
+
</body>
|
|
529
|
+
</html>`;
|
|
530
|
+
controller.enqueue(encoder.encode(resolvedTail));
|
|
531
|
+
} else {
|
|
532
|
+
controller.enqueue(encoder.encode(tail));
|
|
533
|
+
}
|
|
534
|
+
controller.close();
|
|
535
|
+
})().catch((error) => {
|
|
536
|
+
console.error("Stream finalization error:", error);
|
|
537
|
+
try {
|
|
538
|
+
controller.error(error);
|
|
539
|
+
} catch {}
|
|
540
|
+
}).finally(() => {
|
|
541
|
+
clearTimeout(timeoutId);
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
passThrough.on("error", (error) => {
|
|
545
|
+
clearTimeout(timeoutId);
|
|
546
|
+
console.error("Stream error:", error);
|
|
547
|
+
controller.error(error);
|
|
548
|
+
});
|
|
549
|
+
},
|
|
550
|
+
cancel() {
|
|
551
|
+
clearTimeout(timeoutId);
|
|
552
|
+
passThrough.destroy();
|
|
553
|
+
}
|
|
513
554
|
});
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
555
|
+
resolve2({
|
|
556
|
+
body: stream,
|
|
557
|
+
headers: new Headers({
|
|
558
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
559
|
+
}),
|
|
560
|
+
status: 200
|
|
517
561
|
});
|
|
518
562
|
pipe(passThrough);
|
|
519
563
|
},
|
|
520
564
|
onShellError(error) {
|
|
565
|
+
clearTimeout(timeoutId);
|
|
521
566
|
console.error("Shell render error:", error);
|
|
522
567
|
reject(error);
|
|
523
568
|
},
|
|
@@ -525,9 +570,6 @@ async function renderToStream(element, options) {
|
|
|
525
570
|
console.error("Render error:", error);
|
|
526
571
|
}
|
|
527
572
|
});
|
|
528
|
-
setTimeout(() => {
|
|
529
|
-
abort();
|
|
530
|
-
}, 1e4);
|
|
531
573
|
});
|
|
532
574
|
}
|
|
533
575
|
async function renderToString(element, options) {
|
|
@@ -562,29 +604,67 @@ function createResponse(result) {
|
|
|
562
604
|
headers: result.headers
|
|
563
605
|
});
|
|
564
606
|
}
|
|
565
|
-
function createSuspenseStream() {
|
|
566
|
-
const encoder = new TextEncoder;
|
|
567
|
-
let controller;
|
|
568
|
-
const stream = new ReadableStream({
|
|
569
|
-
start(c) {
|
|
570
|
-
controller = c;
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
return {
|
|
574
|
-
stream,
|
|
575
|
-
push: (chunk) => {
|
|
576
|
-
controller.enqueue(encoder.encode(chunk));
|
|
577
|
-
},
|
|
578
|
-
close: () => {
|
|
579
|
-
controller.close();
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
607
|
|
|
584
608
|
// src/bun-server.ts
|
|
585
609
|
import { serializeLoaderData as serializeLoaderData2, hasDeferredData as hasDeferredData2, resolveAllDeferred as resolveAllDeferred2 } from "@ereo/data";
|
|
586
610
|
import { createElement } from "react";
|
|
587
611
|
import { OutletProvider } from "@ereo/client";
|
|
612
|
+
|
|
613
|
+
// src/auth-enforcement.ts
|
|
614
|
+
function resolveAuthDenial(auth, request) {
|
|
615
|
+
if (auth.redirect) {
|
|
616
|
+
const pathname = new URL(request.url).pathname;
|
|
617
|
+
const url = auth.redirect.replace("{pathname}", encodeURIComponent(pathname));
|
|
618
|
+
return new Response(null, {
|
|
619
|
+
status: 302,
|
|
620
|
+
headers: { Location: url }
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
if (auth.unauthorized) {
|
|
624
|
+
return new Response(JSON.stringify(auth.unauthorized.body), {
|
|
625
|
+
status: auth.unauthorized.status,
|
|
626
|
+
headers: { "Content-Type": "application/json" }
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return new Response("Forbidden", { status: 403 });
|
|
630
|
+
}
|
|
631
|
+
function resolveCheckResult(result) {
|
|
632
|
+
if ("response" in result)
|
|
633
|
+
return result.response;
|
|
634
|
+
if ("redirect" in result) {
|
|
635
|
+
return new Response(null, {
|
|
636
|
+
status: 302,
|
|
637
|
+
headers: { Location: result.redirect }
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return new Response(result.body !== undefined ? JSON.stringify(result.body) : "Forbidden", {
|
|
641
|
+
status: result.status,
|
|
642
|
+
headers: result.body !== undefined ? { "Content-Type": "application/json" } : {}
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
async function enforceAuthConfig(authConfig, request, context, params) {
|
|
646
|
+
const authCtx = context.get("auth");
|
|
647
|
+
if (authConfig.required && !authCtx?.isAuthenticated()) {
|
|
648
|
+
return resolveAuthDenial(authConfig, request);
|
|
649
|
+
}
|
|
650
|
+
if (authConfig.roles?.length) {
|
|
651
|
+
if (!authCtx?.isAuthenticated() || !authCtx.hasAnyRole(authConfig.roles)) {
|
|
652
|
+
return resolveAuthDenial(authConfig, request);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (authConfig.check) {
|
|
656
|
+
const result = await authConfig.check({ request, context, params });
|
|
657
|
+
if (result === false) {
|
|
658
|
+
return resolveAuthDenial(authConfig, request);
|
|
659
|
+
}
|
|
660
|
+
if (result !== true && typeof result === "object" && "allowed" in result && !result.allowed) {
|
|
661
|
+
return resolveCheckResult(result);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// src/bun-server.ts
|
|
588
668
|
async function getStreamingRenderer() {
|
|
589
669
|
try {
|
|
590
670
|
const browserServer = await import("react-dom/server.browser");
|
|
@@ -766,6 +846,12 @@ class BunServer {
|
|
|
766
846
|
}
|
|
767
847
|
async handleRouteInner(request, match, context) {
|
|
768
848
|
const module = match.route.module;
|
|
849
|
+
const routeAuthConfig = match.route.config?.auth || module.config?.auth;
|
|
850
|
+
if (routeAuthConfig) {
|
|
851
|
+
const denied = await enforceAuthConfig(routeAuthConfig, request, context, match.params);
|
|
852
|
+
if (denied)
|
|
853
|
+
return denied;
|
|
854
|
+
}
|
|
769
855
|
const httpMethod = request.method.toUpperCase();
|
|
770
856
|
const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
|
|
771
857
|
if (HTTP_METHODS.includes(httpMethod)) {
|
|
@@ -938,27 +1024,31 @@ class BunServer {
|
|
|
938
1024
|
}
|
|
939
1025
|
}
|
|
940
1026
|
async renderStreamingPage(element, shell, loaderData) {
|
|
1027
|
+
let timeoutId;
|
|
941
1028
|
try {
|
|
942
1029
|
const { renderToReadableStream } = await getStreamingRenderer();
|
|
943
1030
|
if (!renderToReadableStream) {
|
|
944
1031
|
return this.renderStringPage(element, shell, loaderData);
|
|
945
1032
|
}
|
|
946
1033
|
const hasDeferred = hasDeferredData2(loaderData);
|
|
947
|
-
const
|
|
1034
|
+
const clientEntry = this.options.clientEntry;
|
|
948
1035
|
const { head, tail } = createShell({
|
|
949
1036
|
shell,
|
|
950
|
-
scripts,
|
|
1037
|
+
scripts: [],
|
|
951
1038
|
loaderData: hasDeferred ? null : loaderData
|
|
952
1039
|
});
|
|
953
1040
|
const encoder = new TextEncoder;
|
|
954
1041
|
const headBytes = encoder.encode(head);
|
|
955
1042
|
const tailBytes = hasDeferred ? null : encoder.encode(tail);
|
|
1043
|
+
const abortController = new AbortController;
|
|
1044
|
+
timeoutId = setTimeout(() => abortController.abort(), 1e4);
|
|
956
1045
|
const reactStream = await renderToReadableStream(element, {
|
|
1046
|
+
bootstrapModules: [clientEntry],
|
|
1047
|
+
signal: abortController.signal,
|
|
957
1048
|
onError(error) {
|
|
958
1049
|
console.error("Streaming render error:", error);
|
|
959
1050
|
}
|
|
960
1051
|
});
|
|
961
|
-
const clientEntry = this.options.clientEntry;
|
|
962
1052
|
const reader = reactStream.getReader();
|
|
963
1053
|
let phase = "head";
|
|
964
1054
|
const stream = new ReadableStream({
|
|
@@ -972,18 +1062,26 @@ class BunServer {
|
|
|
972
1062
|
const { done, value } = await reader.read();
|
|
973
1063
|
if (done) {
|
|
974
1064
|
if (hasDeferred) {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1065
|
+
let resolvedData;
|
|
1066
|
+
try {
|
|
1067
|
+
resolvedData = await Promise.race([
|
|
1068
|
+
resolveAllDeferred2(loaderData),
|
|
1069
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Deferred data resolution timed out")), 1e4))
|
|
1070
|
+
]);
|
|
1071
|
+
} catch (error) {
|
|
1072
|
+
console.error("Deferred data resolution failed:", error);
|
|
1073
|
+
resolvedData = null;
|
|
1074
|
+
}
|
|
1075
|
+
const loaderScript = `<script>window.__EREO_DATA__=${serializeLoaderData2(resolvedData)}</script>`;
|
|
978
1076
|
const resolvedTail = `</div>
|
|
979
1077
|
${loaderScript}
|
|
980
|
-
${scriptTag}
|
|
981
1078
|
</body>
|
|
982
1079
|
</html>`;
|
|
983
1080
|
controller.enqueue(encoder.encode(resolvedTail));
|
|
984
1081
|
} else {
|
|
985
1082
|
controller.enqueue(tailBytes);
|
|
986
1083
|
}
|
|
1084
|
+
clearTimeout(timeoutId);
|
|
987
1085
|
controller.close();
|
|
988
1086
|
phase = "done";
|
|
989
1087
|
} else {
|
|
@@ -995,6 +1093,7 @@ class BunServer {
|
|
|
995
1093
|
},
|
|
996
1094
|
cancel() {
|
|
997
1095
|
reader.cancel();
|
|
1096
|
+
clearTimeout(timeoutId);
|
|
998
1097
|
}
|
|
999
1098
|
});
|
|
1000
1099
|
return new Response(stream, {
|
|
@@ -1004,6 +1103,7 @@ class BunServer {
|
|
|
1004
1103
|
}
|
|
1005
1104
|
});
|
|
1006
1105
|
} catch (error) {
|
|
1106
|
+
clearTimeout(timeoutId);
|
|
1007
1107
|
console.error("Streaming render failed:", error);
|
|
1008
1108
|
return this.renderStringPage(element, shell, loaderData);
|
|
1009
1109
|
}
|
|
@@ -1031,20 +1131,25 @@ class BunServer {
|
|
|
1031
1131
|
}
|
|
1032
1132
|
}
|
|
1033
1133
|
async renderStreamingPageDirect(element, loaderData) {
|
|
1134
|
+
let timeoutId;
|
|
1034
1135
|
try {
|
|
1035
1136
|
const { renderToReadableStream } = await getStreamingRenderer();
|
|
1036
1137
|
if (!renderToReadableStream) {
|
|
1037
1138
|
return this.renderStringPageDirect(element, loaderData);
|
|
1038
1139
|
}
|
|
1140
|
+
const hasDeferred = hasDeferredData2(loaderData);
|
|
1141
|
+
const clientEntry = this.options.clientEntry;
|
|
1142
|
+
const encoder = new TextEncoder;
|
|
1143
|
+
const abortController = new AbortController;
|
|
1144
|
+
timeoutId = setTimeout(() => abortController.abort(), 1e4);
|
|
1039
1145
|
const reactStream = await renderToReadableStream(element, {
|
|
1146
|
+
bootstrapModules: [clientEntry],
|
|
1147
|
+
signal: abortController.signal,
|
|
1040
1148
|
onError(error) {
|
|
1041
1149
|
console.error("Streaming render error:", error);
|
|
1042
1150
|
}
|
|
1043
1151
|
});
|
|
1044
|
-
const
|
|
1045
|
-
const hasDeferred = hasDeferredData2(loaderData);
|
|
1046
|
-
const clientEntry = this.options.clientEntry;
|
|
1047
|
-
const injectedScripts = hasDeferred ? null : encoder.encode((loaderData ? `<script>window.__EREO_DATA__=${serializeLoaderData2(loaderData)}</script>` : "") + `<script type="module" src="${clientEntry}"></script>`);
|
|
1152
|
+
const loaderScript = !hasDeferred && loaderData ? encoder.encode(`<script>window.__EREO_DATA__=${serializeLoaderData2(loaderData)}</script>`) : null;
|
|
1048
1153
|
const reader = reactStream.getReader();
|
|
1049
1154
|
let done = false;
|
|
1050
1155
|
const stream = new ReadableStream({
|
|
@@ -1054,13 +1159,21 @@ class BunServer {
|
|
|
1054
1159
|
const result = await reader.read();
|
|
1055
1160
|
if (result.done) {
|
|
1056
1161
|
if (hasDeferred) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1162
|
+
let resolvedData;
|
|
1163
|
+
try {
|
|
1164
|
+
resolvedData = await Promise.race([
|
|
1165
|
+
resolveAllDeferred2(loaderData),
|
|
1166
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Deferred data resolution timed out")), 1e4))
|
|
1167
|
+
]);
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
console.error("Deferred data resolution failed:", error);
|
|
1170
|
+
resolvedData = null;
|
|
1171
|
+
}
|
|
1172
|
+
controller.enqueue(encoder.encode(`<script>window.__EREO_DATA__=${serializeLoaderData2(resolvedData)}</script>`));
|
|
1173
|
+
} else if (loaderScript) {
|
|
1174
|
+
controller.enqueue(loaderScript);
|
|
1063
1175
|
}
|
|
1176
|
+
clearTimeout(timeoutId);
|
|
1064
1177
|
controller.close();
|
|
1065
1178
|
done = true;
|
|
1066
1179
|
} else {
|
|
@@ -1069,6 +1182,7 @@ class BunServer {
|
|
|
1069
1182
|
},
|
|
1070
1183
|
cancel() {
|
|
1071
1184
|
reader.cancel();
|
|
1185
|
+
clearTimeout(timeoutId);
|
|
1072
1186
|
}
|
|
1073
1187
|
});
|
|
1074
1188
|
return new Response(stream, {
|
|
@@ -1078,6 +1192,7 @@ class BunServer {
|
|
|
1078
1192
|
}
|
|
1079
1193
|
});
|
|
1080
1194
|
} catch (error) {
|
|
1195
|
+
clearTimeout(timeoutId);
|
|
1081
1196
|
console.error("Streaming render failed:", error);
|
|
1082
1197
|
return this.renderStringPageDirect(element, loaderData);
|
|
1083
1198
|
}
|
|
@@ -1373,12 +1488,14 @@ export {
|
|
|
1373
1488
|
serveStatic,
|
|
1374
1489
|
serve,
|
|
1375
1490
|
securityHeaders,
|
|
1491
|
+
resolveCheckResult,
|
|
1492
|
+
resolveAuthDenial,
|
|
1376
1493
|
renderToString,
|
|
1377
1494
|
renderToStream,
|
|
1378
1495
|
rateLimit,
|
|
1379
1496
|
logger,
|
|
1380
1497
|
getMimeType,
|
|
1381
|
-
|
|
1498
|
+
enforceAuthConfig,
|
|
1382
1499
|
createShell,
|
|
1383
1500
|
createServer,
|
|
1384
1501
|
createResponse,
|
package/dist/streaming.d.ts
CHANGED
|
@@ -69,6 +69,13 @@ export declare function createShell(options: {
|
|
|
69
69
|
/**
|
|
70
70
|
* Render a route to a streaming response.
|
|
71
71
|
* Uses renderToPipeableStream for Node.js/Bun environments.
|
|
72
|
+
*
|
|
73
|
+
* Returns a ReadableStream body that progressively sends:
|
|
74
|
+
* shell head → React content (with $RC scripts for Suspense) → tail
|
|
75
|
+
*
|
|
76
|
+
* Deferred data is NOT resolved upfront — React streams Suspense fallbacks
|
|
77
|
+
* and resolves them out-of-order via inline $RC scripts. Loader data is
|
|
78
|
+
* serialized into the tail only after all Suspense boundaries resolve.
|
|
72
79
|
*/
|
|
73
80
|
export declare function renderToStream(element: ReactElement, options: RenderOptions): Promise<RenderResult>;
|
|
74
81
|
/**
|
|
@@ -79,12 +86,4 @@ export declare function renderToString(element: ReactElement, options: RenderOpt
|
|
|
79
86
|
* Create a Response from render result.
|
|
80
87
|
*/
|
|
81
88
|
export declare function createResponse(result: RenderResult): Response;
|
|
82
|
-
/**
|
|
83
|
-
* Stream helper for sending chunks with delays (Suspense boundaries).
|
|
84
|
-
*/
|
|
85
|
-
export declare function createSuspenseStream(): {
|
|
86
|
-
stream: ReadableStream<Uint8Array>;
|
|
87
|
-
push: (chunk: string) => void;
|
|
88
|
-
close: () => void;
|
|
89
|
-
};
|
|
90
89
|
//# sourceMappingURL=streaming.d.ts.map
|
package/dist/streaming.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../src/streaming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAS,UAAU,EAAE,UAAU,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAGhG;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,sBAAsB;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,qBAAqB;IACrB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,uDAAuD;IACvD,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1C,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA8DjC;AAED
|
|
1
|
+
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../src/streaming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAS,UAAU,EAAE,UAAU,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAGhG;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,sBAAsB;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,qBAAqB;IACrB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,uDAAuD;IACvD,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,mBAAmB;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1C,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA8DjC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAuHvB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAgCvB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,QAAQ,CAK7D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ereo/server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Ereo Team",
|
|
6
6
|
"homepage": "https://ereojs.github.io/ereoJS",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ereo/core": "^0.2.
|
|
36
|
-
"@ereo/client": "^0.2.
|
|
37
|
-
"@ereo/router": "^0.2.
|
|
38
|
-
"@ereo/data": "^0.2.
|
|
35
|
+
"@ereo/core": "^0.2.6",
|
|
36
|
+
"@ereo/client": "^0.2.6",
|
|
37
|
+
"@ereo/router": "^0.2.6",
|
|
38
|
+
"@ereo/data": "^0.2.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/bun": "^1.1.0",
|