effective-rsc 0.1.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.
- package/LICENSE +21 -0
- package/LLMS.md +382 -0
- package/README.md +235 -0
- package/bin/ersc.js +3 -0
- package/dist/application/component.d.ts +15 -0
- package/dist/application/component.js +20 -0
- package/dist/application/component.js.map +1 -0
- package/dist/application/definition.d.ts +31 -0
- package/dist/application/definition.js +57 -0
- package/dist/application/definition.js.map +1 -0
- package/dist/application/ersc-identity.d.ts +25 -0
- package/dist/application/ersc-identity.js +23 -0
- package/dist/application/ersc-identity.js.map +1 -0
- package/dist/application/ersc.d.ts +28 -0
- package/dist/application/ersc.js +72 -0
- package/dist/application/ersc.js.map +1 -0
- package/dist/application/layout.d.ts +18 -0
- package/dist/application/layout.js +17 -0
- package/dist/application/layout.js.map +1 -0
- package/dist/application/loading.d.ts +17 -0
- package/dist/application/loading.js +14 -0
- package/dist/application/loading.js.map +1 -0
- package/dist/application/middleware.d.ts +36 -0
- package/dist/application/middleware.js +46 -0
- package/dist/application/middleware.js.map +1 -0
- package/dist/application/page.d.ts +58 -0
- package/dist/application/page.js +50 -0
- package/dist/application/page.js.map +1 -0
- package/dist/application/render-runtime.d.ts +9 -0
- package/dist/application/render-runtime.js +28 -0
- package/dist/application/render-runtime.js.map +1 -0
- package/dist/application/route-graph.d.ts +22 -0
- package/dist/application/route-graph.js +68 -0
- package/dist/application/route-graph.js.map +1 -0
- package/dist/application/route-path.d.ts +31 -0
- package/dist/application/route-path.js +67 -0
- package/dist/application/route-path.js.map +1 -0
- package/dist/application/routes.d.ts +66 -0
- package/dist/application/routes.js +159 -0
- package/dist/application/routes.js.map +1 -0
- package/dist/application/server-fn.d.ts +30 -0
- package/dist/application/server-fn.js +58 -0
- package/dist/application/server-fn.js.map +1 -0
- package/dist/build/build.d.ts +26 -0
- package/dist/build/build.js +59 -0
- package/dist/build/build.js.map +1 -0
- package/dist/build/compiled-server.d.ts +53 -0
- package/dist/build/compiled-server.js +77 -0
- package/dist/build/compiled-server.js.map +1 -0
- package/dist/build/contract.d.ts +23 -0
- package/dist/build/contract.js +41 -0
- package/dist/build/contract.js.map +1 -0
- package/dist/build/dev-channel.d.ts +20 -0
- package/dist/build/dev-channel.js +71 -0
- package/dist/build/dev-channel.js.map +1 -0
- package/dist/build/dev.d.ts +41 -0
- package/dist/build/dev.js +186 -0
- package/dist/build/dev.js.map +1 -0
- package/dist/build/rsc-entry.d.ts +4 -0
- package/dist/build/rsc-entry.js +17 -0
- package/dist/build/rsc-entry.js.map +1 -0
- package/dist/build/rspack-config.d.ts +17 -0
- package/dist/build/rspack-config.js +259 -0
- package/dist/build/rspack-config.js.map +1 -0
- package/dist/build/rspack.d.ts +45 -0
- package/dist/build/rspack.js +231 -0
- package/dist/build/rspack.js.map +1 -0
- package/dist/build/terminal.d.ts +9 -0
- package/dist/build/terminal.js +17 -0
- package/dist/build/terminal.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/application.d.ts +2 -0
- package/dist/client/application.js +73 -0
- package/dist/client/application.js.map +1 -0
- package/dist/client/browser-capabilities.d.ts +9 -0
- package/dist/client/browser-capabilities.js +20 -0
- package/dist/client/browser-capabilities.js.map +1 -0
- package/dist/client/browser-effect-runner.d.ts +13 -0
- package/dist/client/browser-effect-runner.js +13 -0
- package/dist/client/browser-effect-runner.js.map +1 -0
- package/dist/client/browser-renderer.d.ts +70 -0
- package/dist/client/browser-renderer.js +159 -0
- package/dist/client/browser-renderer.js.map +1 -0
- package/dist/client/browser-screen.d.ts +2 -0
- package/dist/client/browser-screen.js +215 -0
- package/dist/client/browser-screen.js.map +1 -0
- package/dist/client/call-server.d.ts +8 -0
- package/dist/client/call-server.js +151 -0
- package/dist/client/call-server.js.map +1 -0
- package/dist/client/client-router.d.ts +6 -0
- package/dist/client/client-router.js +588 -0
- package/dist/client/client-router.js.map +1 -0
- package/dist/client/entry.d.ts +1 -0
- package/dist/client/entry.js +12 -0
- package/dist/client/entry.js.map +1 -0
- package/dist/client/flight-client.d.ts +83 -0
- package/dist/client/flight-client.js +146 -0
- package/dist/client/flight-client.js.map +1 -0
- package/dist/client/initial-flight-stream.d.ts +18 -0
- package/dist/client/initial-flight-stream.js +60 -0
- package/dist/client/initial-flight-stream.js.map +1 -0
- package/dist/client/navigation-api.d.ts +34 -0
- package/dist/client/navigation-api.js +25 -0
- package/dist/client/navigation-api.js.map +1 -0
- package/dist/client/navigation-routing.d.ts +3 -0
- package/dist/client/navigation-routing.js +8 -0
- package/dist/client/navigation-routing.js.map +1 -0
- package/dist/client/react-dom-renderer.d.ts +20 -0
- package/dist/client/react-dom-renderer.js +101 -0
- package/dist/client/react-dom-renderer.js.map +1 -0
- package/dist/client/route-loader.d.ts +81 -0
- package/dist/client/route-loader.js +128 -0
- package/dist/client/route-loader.js.map +1 -0
- package/dist/client/route-refresh.d.ts +31 -0
- package/dist/client/route-refresh.js +132 -0
- package/dist/client/route-refresh.js.map +1 -0
- package/dist/client/route-tree.d.ts +7 -0
- package/dist/client/route-tree.js +34 -0
- package/dist/client/route-tree.js.map +1 -0
- package/dist/dev/channel.d.ts +18 -0
- package/dist/dev/channel.js +30 -0
- package/dist/dev/channel.js.map +1 -0
- package/dist/dev/client.d.ts +3 -0
- package/dist/dev/client.js +132 -0
- package/dist/dev/client.js.map +1 -0
- package/dist/dev/hmr-update.d.ts +20 -0
- package/dist/dev/hmr-update.js +28 -0
- package/dist/dev/hmr-update.js.map +1 -0
- package/dist/dev/panel.d.ts +41 -0
- package/dist/dev/panel.js +9946 -0
- package/dist/dev/panel.js.map +1 -0
- package/dist/dev/runtime-failure.d.ts +11 -0
- package/dist/dev/runtime-failure.js +32 -0
- package/dist/dev/runtime-failure.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/rsc/flight.d.ts +16 -0
- package/dist/rsc/flight.js +6 -0
- package/dist/rsc/flight.js.map +1 -0
- package/dist/rsc/render-route-tree.d.ts +11 -0
- package/dist/rsc/render-route-tree.js +52 -0
- package/dist/rsc/render-route-tree.js.map +1 -0
- package/dist/rsc/route-tree.d.ts +6 -0
- package/dist/rsc/route-tree.js +5 -0
- package/dist/rsc/route-tree.js.map +1 -0
- package/dist/server/application.d.ts +17 -0
- package/dist/server/application.js +178 -0
- package/dist/server/application.js.map +1 -0
- package/dist/server/flight-html-stream.d.ts +19 -0
- package/dist/server/flight-html-stream.js +193 -0
- package/dist/server/flight-html-stream.js.map +1 -0
- package/dist/server/flight-renderer.d.ts +31 -0
- package/dist/server/flight-renderer.js +47 -0
- package/dist/server/flight-renderer.js.map +1 -0
- package/dist/server/html-renderer.d.ts +28 -0
- package/dist/server/html-renderer.js +82 -0
- package/dist/server/html-renderer.js.map +1 -0
- package/dist/server/request-outcome.d.ts +8 -0
- package/dist/server/request-outcome.js +5 -0
- package/dist/server/request-outcome.js.map +1 -0
- package/dist/server/server-config.d.ts +17 -0
- package/dist/server/server-config.js +18 -0
- package/dist/server/server-config.js.map +1 -0
- package/dist/server/server-fn-outcome.d.ts +13 -0
- package/dist/server/server-fn-outcome.js +24 -0
- package/dist/server/server-fn-outcome.js.map +1 -0
- package/dist/server/server-fn-request.d.ts +24 -0
- package/dist/server/server-fn-request.js +160 -0
- package/dist/server/server-fn-request.js.map +1 -0
- package/dist/unsupported.d.ts +0 -0
- package/dist/unsupported.js +5 -0
- package/dist/unsupported.js.map +1 -0
- package/docs/01-getting-started/01_first-application.tsx +26 -0
- package/docs/01-getting-started/20_styling.tsx +6 -0
- package/docs/01-getting-started/index.md +27 -0
- package/docs/01-getting-started/styles.css +1 -0
- package/docs/02-guides/01-server-functions/10_ersc.ts +6 -0
- package/docs/02-guides/01-server-functions/20_follow-author.ts +15 -0
- package/docs/02-guides/01-server-functions/30_follow-author-button.tsx +19 -0
- package/docs/02-guides/01-server-functions/40_application.tsx +30 -0
- package/docs/02-guides/01-server-functions/index.md +19 -0
- package/docs/02-guides/02-services/10_catalog.ts +22 -0
- package/docs/02-guides/02-services/20_application.tsx +44 -0
- package/docs/02-guides/02-services/index.md +17 -0
- package/docs/02-guides/03-routing/10_ersc.ts +6 -0
- package/docs/02-guides/03-routing/10_layouts.tsx +36 -0
- package/docs/02-guides/03-routing/20_pages.tsx +23 -0
- package/docs/02-guides/03-routing/30_routes.tsx +17 -0
- package/docs/02-guides/03-routing/40_application.ts +7 -0
- package/docs/02-guides/03-routing/index.md +22 -0
- package/docs/02-guides/04-middleware/10_auth.ts +27 -0
- package/docs/02-guides/04-middleware/20_account-page.tsx +13 -0
- package/docs/02-guides/04-middleware/30_update-profile.ts +16 -0
- package/docs/02-guides/04-middleware/40_application.tsx +20 -0
- package/docs/02-guides/04-middleware/index.md +25 -0
- package/docs/02-guides/05-http/10_application-layer.tsx +60 -0
- package/docs/02-guides/05-http/index.md +23 -0
- package/docs/02-guides/index.md +11 -0
- package/docs/03-advanced/01-request-runtime-and-lifetimes/index.md +13 -0
- package/docs/03-advanced/02-client-navigation/index.md +61 -0
- package/docs/03-advanced/03-server-function-execution-and-refresh/index.md +17 -0
- package/docs/03-advanced/index.md +11 -0
- package/docs/04-api-reference/01-application/index.md +8 -0
- package/docs/04-api-reference/02-page/index.md +12 -0
- package/docs/04-api-reference/03-layout/index.md +4 -0
- package/docs/04-api-reference/04-loading/index.md +4 -0
- package/docs/04-api-reference/05-component/10_component.tsx +18 -0
- package/docs/04-api-reference/05-component/index.md +10 -0
- package/docs/04-api-reference/06-middleware/index.md +33 -0
- package/docs/04-api-reference/07-routes/index.md +20 -0
- package/docs/04-api-reference/08-server-fn/index.md +43 -0
- package/docs/04-api-reference/index.md +17 -0
- package/docs/environment.d.ts +1 -0
- package/docs/index.md +15 -0
- package/logo-dark.svg +6 -0
- package/logo.svg +6 -0
- package/package.json +87 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
export type DevRuntimeFailure = {
|
|
3
|
+
readonly _tag: 'RuntimeError' | 'UnhandledRejection';
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
readonly stack?: string;
|
|
7
|
+
readonly componentStack?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const fromBrowserError: (event: Pick<ErrorEvent, 'error' | 'message'>) => DevRuntimeFailure;
|
|
10
|
+
export declare const fromUnhandledRejection: (event: Pick<PromiseRejectionEvent, 'reason'>) => DevRuntimeFailure;
|
|
11
|
+
export declare const reportBrowserFailures: (report: (failure: DevRuntimeFailure) => Effect.Effect<void>) => Effect.Effect<void, never, never>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect, Inspectable, Predicate, Stream } from "effect";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const fromUnknown = (_tag, value, fallbackMessage)=>{
|
|
6
|
+
if (Predicate.isError(value)) {
|
|
7
|
+
return {
|
|
8
|
+
_tag,
|
|
9
|
+
name: value.name,
|
|
10
|
+
message: value.message,
|
|
11
|
+
...value.stack === undefined ? {} : {
|
|
12
|
+
stack: value.stack
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
_tag,
|
|
18
|
+
name: _tag === 'UnhandledRejection' ? 'UnhandledRejection' : 'Error',
|
|
19
|
+
message: value === undefined ? fallbackMessage : Inspectable.toStringUnknown(value)
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const fromBrowserError = (event)=>fromUnknown('RuntimeError', event.error === null ? undefined : event.error, event.message || 'Unknown browser error.');
|
|
23
|
+
const fromUnhandledRejection = (event)=>fromUnknown('UnhandledRejection', event.reason, 'Unhandled promise rejection.');
|
|
24
|
+
const reportBrowserFailures = Effect.fnUntraced(function*(report) {
|
|
25
|
+
const errors = Stream.fromEventListener(window, 'error').pipe(Stream.map(fromBrowserError));
|
|
26
|
+
const rejections = Stream.fromEventListener(window, 'unhandledrejection').pipe(Stream.map(fromUnhandledRejection));
|
|
27
|
+
yield* Stream.merge(errors, rejections).pipe(Stream.runForEach(report));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export { fromBrowserError, fromUnhandledRejection, reportBrowserFailures };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=runtime-failure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev/runtime-failure.js","sources":["../../src/dev/runtime-failure.ts"],"sourcesContent":["import { Effect, Inspectable, Predicate, Stream } from 'effect';\n\nexport type DevRuntimeFailure = {\n readonly _tag: 'RuntimeError' | 'UnhandledRejection';\n readonly name: string;\n readonly message: string;\n readonly stack?: string;\n readonly componentStack?: string;\n};\n\nconst fromUnknown = (\n _tag: DevRuntimeFailure['_tag'],\n value: unknown,\n fallbackMessage: string,\n): DevRuntimeFailure => {\n if (Predicate.isError(value)) {\n return {\n _tag,\n name: value.name,\n message: value.message,\n ...(value.stack === undefined ? {} : { stack: value.stack }),\n };\n }\n\n return {\n _tag,\n name: _tag === 'UnhandledRejection' ? 'UnhandledRejection' : 'Error',\n message: value === undefined ? fallbackMessage : Inspectable.toStringUnknown(value),\n };\n};\n\nexport const fromBrowserError = (event: Pick<ErrorEvent, 'error' | 'message'>): DevRuntimeFailure =>\n fromUnknown(\n 'RuntimeError',\n event.error === null ? undefined : event.error,\n event.message || 'Unknown browser error.',\n );\n\nexport const fromUnhandledRejection = (\n event: Pick<PromiseRejectionEvent, 'reason'>,\n): DevRuntimeFailure =>\n fromUnknown('UnhandledRejection', event.reason, 'Unhandled promise rejection.');\n\nexport const reportBrowserFailures = Effect.fnUntraced(function* (\n report: (failure: DevRuntimeFailure) => Effect.Effect<void>,\n) {\n const errors = Stream.fromEventListener<ErrorEvent>(window, 'error').pipe(\n Stream.map(fromBrowserError),\n );\n const rejections = Stream.fromEventListener<PromiseRejectionEvent>(\n window,\n 'unhandledrejection',\n ).pipe(Stream.map(fromUnhandledRejection));\n\n yield* Stream.merge(errors, rejections).pipe(Stream.runForEach(report));\n});\n"],"names":["Effect","Inspectable","Predicate","Stream","fromUnknown","_tag","value","fallbackMessage","undefined","fromBrowserError","event","fromUnhandledRejection","reportBrowserFailures","report","errors","window","rejections"],"mappings":";;;AAAgE;AAUhE,MAAMI,WAAWA,GAAG,CAClBC,MACAC,OACAC;IAEA,IAAIL,iBAAiB,CAACI,QAAQ;QAC5B,OAAO;YACLD;YACA,MAAMC,MAAM,IAAI;YAChB,SAASA,MAAM,OAAO;YACtB,GAAIA,MAAM,KAAK,KAAKE,YAAY,CAAC,IAAI;gBAAE,OAAOF,MAAM,KAAK;YAAC,CAAC;QAC7D;IACF;IAEA,OAAO;QACLD;QACA,MAAMA,SAAS,uBAAuB,uBAAuB;QAC7D,SAASC,UAAUE,YAAYD,kBAAkBN,2BAA2B,CAACK;IAC/E;AACF;AAEO,MAAMG,gBAAgBA,GAAG,CAACC,QAC/BN,WAAWA,CACT,gBACAM,MAAM,KAAK,KAAK,OAAOF,YAAYE,MAAM,KAAK,EAC9CA,MAAM,OAAO,IAAI,0BACjB;AAEG,MAAMC,sBAAsBA,GAAG,CACpCD,QAEAN,WAAWA,CAAC,sBAAsBM,MAAM,MAAM,EAAE,gCAAgC;AAE3E,MAAME,qBAAqBA,GAAGZ,iBAAiB,CAAC,UACrDa,MAA2D;IAE3D,MAAMC,SAASX,wBAAwB,CAAaY,QAAQ,SAAS,IAAI,CACvEZ,UAAU,CAACM,gBAAgBA;IAE7B,MAAMO,aAAab,wBAAwB,CACzCY,QACA,sBACA,IAAI,CAACZ,UAAU,CAACQ,sBAAsBA;IAExC,OAAOR,YAAY,CAACW,QAAQE,YAAY,IAAI,CAACb,iBAAiB,CAACU;AACjE,GAAG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Application } from './application/ersc.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["export { Application } from './application/ersc';\n"],"names":["Application"],"mappings":";AAAiD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactFormState } from 'react-dom/client';
|
|
2
|
+
import type { RouteTreeModel } from './route-tree.js';
|
|
3
|
+
export type FlightPayload = {
|
|
4
|
+
readonly formState: ReactFormState | null;
|
|
5
|
+
readonly routeTree: RouteTreeModel;
|
|
6
|
+
readonly serverFnResult: ServerFnResult | null;
|
|
7
|
+
};
|
|
8
|
+
export type ServerFnResult = {
|
|
9
|
+
readonly _tag: 'Success';
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
} | {
|
|
12
|
+
readonly _tag: 'Failure';
|
|
13
|
+
readonly error: unknown;
|
|
14
|
+
};
|
|
15
|
+
export declare const FlightMediaType = "text/x-component";
|
|
16
|
+
export declare const ServerFnIdHeader = "x-ersc-server-fn";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsc/flight.js","sources":["../../src/rsc/flight.ts"],"sourcesContent":["import type { ReactFormState } from 'react-dom/client';\n\nimport type { RouteTreeModel } from './route-tree';\n\nexport type FlightPayload = {\n readonly formState: ReactFormState | null;\n readonly routeTree: RouteTreeModel;\n readonly serverFnResult: ServerFnResult | null;\n};\n\nexport type ServerFnResult =\n | { readonly _tag: 'Success'; readonly value: unknown }\n | { readonly _tag: 'Failure'; readonly error: unknown };\n\nexport const FlightMediaType = 'text/x-component';\nexport const ServerFnIdHeader = 'x-ersc-server-fn';\n"],"names":["FlightMediaType","ServerFnIdHeader"],"mappings":"AAcO,MAAMA,eAAeA,GAAG,mBAAmB;AAC3C,MAAMC,gBAAgBA,GAAG,mBAAmB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PagePathParams } from '../application/page.js';
|
|
2
|
+
import type { CompiledDestination } from '../application/route-graph.js';
|
|
3
|
+
import type { AbsolutePath } from '../application/route-path.js';
|
|
4
|
+
import type { RouteTreeModel } from './route-tree.js';
|
|
5
|
+
type RenderRouteTreeOptions<Services> = {
|
|
6
|
+
readonly destination: CompiledDestination<Services>;
|
|
7
|
+
readonly pathname: AbsolutePath;
|
|
8
|
+
readonly pathParams: PagePathParams;
|
|
9
|
+
};
|
|
10
|
+
export declare const renderRouteTree: <Services>({ destination, pathname, pathParams, }: RenderRouteTreeOptions<Services>) => RouteTreeModel;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense } from "react";
|
|
3
|
+
import { RouteOutlet } from "../client/route-tree.js";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const renderRouteTree = ({ destination, pathname, pathParams })=>{
|
|
12
|
+
const Page = destination.page.component;
|
|
13
|
+
let tree = {
|
|
14
|
+
child: null,
|
|
15
|
+
content: /*#__PURE__*/ jsx(Page, {
|
|
16
|
+
params: pathParams
|
|
17
|
+
}),
|
|
18
|
+
id: `page:${pathname}`
|
|
19
|
+
};
|
|
20
|
+
for(let index = destination.scopes.length - 1; index >= 0; index--){
|
|
21
|
+
const scope = destination.scopes[index];
|
|
22
|
+
if (scope === undefined) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (scope.loading !== null) {
|
|
26
|
+
const Loading = scope.loading;
|
|
27
|
+
tree = {
|
|
28
|
+
child: tree,
|
|
29
|
+
content: /*#__PURE__*/ jsx(Suspense, {
|
|
30
|
+
fallback: /*#__PURE__*/ jsx(Loading, {}),
|
|
31
|
+
children: /*#__PURE__*/ jsx(RouteOutlet, {})
|
|
32
|
+
}),
|
|
33
|
+
id: `loading:${scope.id}:${pathname}`
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (scope.layout !== null) {
|
|
37
|
+
const Layout = scope.layout;
|
|
38
|
+
tree = {
|
|
39
|
+
child: tree,
|
|
40
|
+
content: /*#__PURE__*/ jsx(Layout, {
|
|
41
|
+
children: /*#__PURE__*/ jsx(RouteOutlet, {})
|
|
42
|
+
}),
|
|
43
|
+
id: `layout:${scope.id}`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return tree;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { renderRouteTree };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=render-route-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsc/render-route-tree.js","sources":["../../src/rsc/render-route-tree.tsx"],"sourcesContent":["import { Suspense } from 'react';\n\nimport type { PagePathParams } from '../application/page';\nimport type { CompiledDestination } from '../application/route-graph';\nimport type { AbsolutePath } from '../application/route-path';\nimport { RouteOutlet } from '../client/route-tree';\nimport type { RouteTreeModel } from './route-tree';\n\ntype RenderRouteTreeOptions<Services> = {\n readonly destination: CompiledDestination<Services>;\n readonly pathname: AbsolutePath;\n readonly pathParams: PagePathParams;\n};\n\nexport const renderRouteTree = <Services,>({\n destination,\n pathname,\n pathParams,\n}: RenderRouteTreeOptions<Services>): RouteTreeModel => {\n const Page = destination.page.component;\n let tree: RouteTreeModel = {\n child: null,\n content: <Page params={pathParams} />,\n id: `page:${pathname}`,\n };\n\n for (let index = destination.scopes.length - 1; index >= 0; index--) {\n const scope = destination.scopes[index];\n if (scope === undefined) {\n continue;\n }\n\n if (scope.loading !== null) {\n const Loading = scope.loading;\n tree = {\n child: tree,\n content: (\n <Suspense fallback={<Loading />}>\n <RouteOutlet />\n </Suspense>\n ),\n id: `loading:${scope.id}:${pathname}`,\n };\n }\n\n if (scope.layout !== null) {\n const Layout = scope.layout;\n tree = {\n child: tree,\n content: (\n <Layout>\n <RouteOutlet />\n </Layout>\n ),\n id: `layout:${scope.id}`,\n };\n }\n }\n\n return tree;\n};\n"],"names":["Suspense","RouteOutlet","renderRouteTree","destination","pathname","pathParams","Page","tree","index","scope","undefined","Loading","Layout"],"mappings":";;;;;;;;AAAiC;AAKkB;AAS5C,MAAME,eAAeA,GAAG,CAAY,EACzCC,WAAW,EACXC,QAAQ,EACRC,UAAU,EACuB;IACjC,MAAMC,OAAOH,YAAY,IAAI,CAAC,SAAS;IACvC,IAAII,OAAuB;QACzB,OAAO;QACP,uBAAS,IAACD;YAAK,QAAQD;;QACvB,IAAI,CAAC,KAAK,EAAED,UAAU;IACxB;IAEA,IAAK,IAAII,QAAQL,YAAY,MAAM,CAAC,MAAM,GAAG,GAAGK,SAAS,GAAGA,QAAS;QACnE,MAAMC,QAAQN,YAAY,MAAM,CAACK,MAAM;QACvC,IAAIC,UAAUC,WAAW;YACvB;QACF;QAEA,IAAID,MAAM,OAAO,KAAK,MAAM;YAC1B,MAAME,UAAUF,MAAM,OAAO;YAC7BF,OAAO;gBACL,OAAOA;gBACP,uBACE,IAACP,QAAQA;oBAAC,wBAAU,IAACW;8BACnB,kBAACV,WAAWA;;gBAGhB,IAAI,CAAC,QAAQ,EAAEQ,MAAM,EAAE,CAAC,CAAC,EAAEL,UAAU;YACvC;QACF;QAEA,IAAIK,MAAM,MAAM,KAAK,MAAM;YACzB,MAAMG,SAASH,MAAM,MAAM;YAC3BF,OAAO;gBACL,OAAOA;gBACP,uBACE,IAACK;8BACC,kBAACX,WAAWA;;gBAGhB,IAAI,CAAC,OAAO,EAAEQ,MAAM,EAAE,EAAE;YAC1B;QACF;IACF;IAEA,OAAOF;AACT,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsc/route-tree.js","sources":["../../src/rsc/route-tree.ts"],"sourcesContent":["import type { ReactNode } from 'react';\n\nexport type RouteTreeModel = {\n readonly child: RouteTreeModel | null;\n readonly content: ReactNode;\n readonly id: string;\n};\n"],"names":[],"mappings":"AAME"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Layer } from 'effect';
|
|
2
|
+
import type { PlatformError } from 'effect/PlatformError';
|
|
3
|
+
import { HttpRouter, HttpStaticServer } from 'effect/unstable/http';
|
|
4
|
+
import { type ApplicationDefinition } from '../application/definition.js';
|
|
5
|
+
import { HtmlRenderError } from './html-renderer.js';
|
|
6
|
+
import { ServerConfig } from './server-config.js';
|
|
7
|
+
import { type ServerFnRequestFailure } from './server-fn-request.js';
|
|
8
|
+
type HttpApplicationRequirements = ServerConfig | Layer.Services<ReturnType<typeof HttpStaticServer.layer>> | HttpRouter.Request.From<'Error', HtmlRenderError | ServerFnRequestFailure>;
|
|
9
|
+
export type HttpApplicationLayer<ApplicationError> = Layer.Layer<never, ApplicationError | PlatformError, HttpApplicationRequirements>;
|
|
10
|
+
export type ServerApplicationLayer<ApplicationError> = Layer.Layer<never, ApplicationError | PlatformError, ServerConfig>;
|
|
11
|
+
declare const httpLayer: <Services, ApplicationError>(application: ApplicationDefinition<Services, ApplicationError>) => HttpApplicationLayer<ApplicationError>;
|
|
12
|
+
declare const serverLayer: <Services, ApplicationError>(application: ApplicationDefinition<Services, ApplicationError>) => ServerApplicationLayer<ApplicationError>;
|
|
13
|
+
export declare const ServerApplication: {
|
|
14
|
+
httpLayer: typeof httpLayer;
|
|
15
|
+
serverLayer: typeof serverLayer;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Effect, Layer, Option, Stream } from "effect";
|
|
2
|
+
import { HttpEffect, HttpRouter, HttpServerRequest, HttpServerResponse, HttpStaticServer } from "effect/unstable/http";
|
|
3
|
+
import { getApplicationState } from "../application/definition.js";
|
|
4
|
+
import { getERSCIdentity } from "../application/ersc-identity.js";
|
|
5
|
+
import { applyMiddleware, getScopedHttpMiddleware } from "../application/middleware.js";
|
|
6
|
+
import { FrameworkAssetNamespace, isAbsolutePath } from "../application/route-path.js";
|
|
7
|
+
import { FlightMediaType } from "../rsc/flight.js";
|
|
8
|
+
import { renderRouteTree } from "../rsc/render-route-tree.js";
|
|
9
|
+
import { FlightHtmlInjector } from "./flight-html-stream.js";
|
|
10
|
+
import { FlightRenderer } from "./flight-renderer.js";
|
|
11
|
+
import { HtmlRenderer } from "./html-renderer.js";
|
|
12
|
+
import { ApplicationIdleTimeoutSeconds, ApplicationMaxRequestBodySizeBytes, ServerConfig } from "./server-config.js";
|
|
13
|
+
import { prepareServerFnRequest } from "./server-fn-request.js";
|
|
14
|
+
|
|
15
|
+
import * as __rspack_external__effect_platform_bun_BunHttpServer_0e4f7bbb from "@effect/platform-bun/BunHttpServer";
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const RenderersLayer = Layer.mergeAll(FlightRenderer.layer, HtmlRenderer.layer).pipe(Layer.provide(FlightHtmlInjector.layer));
|
|
45
|
+
const StaticAssetsLayer = Layer.unwrap(Effect.map(ServerConfig, ({ clientAssetsCacheControl, clientAssetsRoot })=>HttpStaticServer.layer({
|
|
46
|
+
cacheControl: clientAssetsCacheControl,
|
|
47
|
+
prefix: FrameworkAssetNamespace,
|
|
48
|
+
root: clientAssetsRoot
|
|
49
|
+
}).pipe(Layer.provide(HttpRouter.disableLogger))));
|
|
50
|
+
const PublicAssetsLayer = Layer.unwrap(Effect.map(ServerConfig, ({ publicAssetsRoot })=>HttpStaticServer.layer({
|
|
51
|
+
cacheControl: 'public, max-age=0',
|
|
52
|
+
root: publicAssetsRoot
|
|
53
|
+
})));
|
|
54
|
+
// Bun derives `development` from NODE_ENV, which `ersc start` does not set, and its error page
|
|
55
|
+
// carries the failure message and source stack.
|
|
56
|
+
const BunServerLayer = Layer.unwrap(Effect.map(ServerConfig, ({ hostname, port })=>__rspack_external__effect_platform_bun_BunHttpServer_0e4f7bbb.layer({
|
|
57
|
+
development: false,
|
|
58
|
+
hostname,
|
|
59
|
+
idleTimeout: ApplicationIdleTimeoutSeconds,
|
|
60
|
+
maxRequestBodySize: ApplicationMaxRequestBodySizeBytes,
|
|
61
|
+
port
|
|
62
|
+
})));
|
|
63
|
+
const EmptyPathParams = Object.freeze({});
|
|
64
|
+
const DynamicResponseHeaders = {
|
|
65
|
+
'cache-control': 'private, no-store'
|
|
66
|
+
};
|
|
67
|
+
const appendAcceptVary = (vary)=>{
|
|
68
|
+
if (vary === undefined || vary.trim() === '') {
|
|
69
|
+
return 'Accept';
|
|
70
|
+
}
|
|
71
|
+
const fields = vary.split(',').map((field)=>field.trim().toLowerCase());
|
|
72
|
+
return fields.includes('*') || fields.includes('accept') ? vary : `${vary}, Accept`;
|
|
73
|
+
};
|
|
74
|
+
const acceptVaryPreResponseHandler = (_request, response)=>Effect.succeed(HttpServerResponse.setHeader(response, 'vary', appendAcceptVary(response.headers['vary'])));
|
|
75
|
+
const fromWebStream = (stream, options)=>Stream.fromReadableStream({
|
|
76
|
+
evaluate: ()=>stream,
|
|
77
|
+
onError: (cause)=>cause,
|
|
78
|
+
releaseLockOnEnd: options?.releaseLockOnEnd
|
|
79
|
+
});
|
|
80
|
+
const combinePageMiddleware = (middleware, last)=>middleware.slice(0, -1).reduceRight((combined, current)=>combined.combine(getScopedHttpMiddleware(current)), getScopedHttpMiddleware(last));
|
|
81
|
+
const httpLayer = (application)=>{
|
|
82
|
+
const applicationState = getApplicationState(application);
|
|
83
|
+
const identity = getERSCIdentity(application);
|
|
84
|
+
const render = Effect.fnUntraced(function*({ destination, formState, middleware, request, serverFnResult, status, temporaryReferences }) {
|
|
85
|
+
const requestUrl = HttpServerRequest.toURL(request);
|
|
86
|
+
if (Option.isNone(requestUrl) || !isAbsolutePath(requestUrl.value.pathname)) {
|
|
87
|
+
return yield* Effect.die(new TypeError(`Expected request URL "${request.originalUrl}" to contain an absolute path.`));
|
|
88
|
+
}
|
|
89
|
+
const pathParams = yield* destination.page.paramsSchema === null ? Effect.succeed(EmptyPathParams) : HttpRouter.params;
|
|
90
|
+
const routeTree = renderRouteTree({
|
|
91
|
+
destination,
|
|
92
|
+
pathParams,
|
|
93
|
+
pathname: requestUrl.value.pathname
|
|
94
|
+
});
|
|
95
|
+
const flightRenderer = yield* FlightRenderer;
|
|
96
|
+
const flight = yield* flightRenderer.render({
|
|
97
|
+
formState,
|
|
98
|
+
middleware,
|
|
99
|
+
renderRuntime: identity.renderRuntime,
|
|
100
|
+
routeTree,
|
|
101
|
+
serverFnResult,
|
|
102
|
+
temporaryReferences
|
|
103
|
+
});
|
|
104
|
+
if (request.headers['accept'] === FlightMediaType) {
|
|
105
|
+
return HttpServerResponse.stream(fromWebStream(flight.stream, {
|
|
106
|
+
releaseLockOnEnd: true
|
|
107
|
+
}).pipe(Stream.ensuring(flight.release)), {
|
|
108
|
+
contentType: `${FlightMediaType};charset=utf-8`,
|
|
109
|
+
headers: {
|
|
110
|
+
...DynamicResponseHeaders,
|
|
111
|
+
'content-location': requestUrl.value.href
|
|
112
|
+
},
|
|
113
|
+
status
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const htmlRenderer = yield* HtmlRenderer;
|
|
117
|
+
const htmlStream = yield* htmlRenderer.render({
|
|
118
|
+
flight,
|
|
119
|
+
formState
|
|
120
|
+
}).pipe(Effect.onError(()=>flight.release));
|
|
121
|
+
return HttpServerResponse.stream(fromWebStream(htmlStream).pipe(Stream.ensuring(flight.release)), {
|
|
122
|
+
contentType: 'text/html;charset=utf-8',
|
|
123
|
+
headers: DynamicResponseHeaders,
|
|
124
|
+
status
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const executeServerFnAndRefresh = (prepared, destination, request)=>{
|
|
128
|
+
const refreshMiddleware = destination.middleware.filter((middleware)=>!prepared.middleware.includes(middleware));
|
|
129
|
+
const renderMiddleware = refreshMiddleware.length === 0 ? prepared.middleware : Object.freeze([
|
|
130
|
+
...prepared.middleware,
|
|
131
|
+
...refreshMiddleware
|
|
132
|
+
]);
|
|
133
|
+
const response = prepared.execute.pipe(Effect.flatMap((outcome)=>{
|
|
134
|
+
const refresh = render({
|
|
135
|
+
...outcome,
|
|
136
|
+
destination,
|
|
137
|
+
middleware: renderMiddleware,
|
|
138
|
+
request
|
|
139
|
+
});
|
|
140
|
+
return applyMiddleware(refreshMiddleware, refresh);
|
|
141
|
+
}));
|
|
142
|
+
return applyMiddleware(prepared.middleware, response);
|
|
143
|
+
};
|
|
144
|
+
const RequestLayer = Layer.mergeAll(RenderersLayer, applicationState.layer);
|
|
145
|
+
const ApplicationRoutesLayer = Layer.unwrap(Effect.map(Effect.context(), (requestContext)=>{
|
|
146
|
+
const RequestContextMiddleware = HttpRouter.middleware()((httpEffect)=>httpEffect.pipe(Effect.provideContext(requestContext)));
|
|
147
|
+
const makeRouteLayer = (destination)=>{
|
|
148
|
+
const GetLayer = HttpRouter.add('GET', destination.pattern, (request)=>render({
|
|
149
|
+
destination,
|
|
150
|
+
formState: null,
|
|
151
|
+
middleware: destination.middleware,
|
|
152
|
+
request,
|
|
153
|
+
serverFnResult: null,
|
|
154
|
+
status: 200
|
|
155
|
+
}).pipe(HttpEffect.withPreResponseHandler(acceptVaryPreResponseHandler)));
|
|
156
|
+
const lastPageMiddleware = destination.middleware.at(-1);
|
|
157
|
+
const PageMiddleware = lastPageMiddleware === undefined ? RequestContextMiddleware : combinePageMiddleware(destination.middleware, lastPageMiddleware).combine(RequestContextMiddleware);
|
|
158
|
+
const PageLayer = GetLayer.pipe(Layer.provide(PageMiddleware.layer));
|
|
159
|
+
const ServerFnLayer = HttpRouter.add('POST', destination.pattern, (request)=>prepareServerFnRequest(request, identity).pipe(Effect.flatMap((prepared)=>executeServerFnAndRefresh(prepared, destination, request)), Effect.catchTag('ServerFnRequestError', (error)=>Effect.succeed(HttpServerResponse.text(error.message, {
|
|
160
|
+
headers: DynamicResponseHeaders,
|
|
161
|
+
status: error.status
|
|
162
|
+
}))), HttpEffect.withPreResponseHandler(acceptVaryPreResponseHandler))).pipe(Layer.provide(RequestContextMiddleware.layer));
|
|
163
|
+
return Layer.mergeAll(PageLayer, ServerFnLayer);
|
|
164
|
+
};
|
|
165
|
+
const [firstDestination, ...remainingDestinations] = applicationState.routes;
|
|
166
|
+
return Layer.mergeAll(makeRouteLayer(firstDestination), ...remainingDestinations.map(makeRouteLayer));
|
|
167
|
+
})).pipe(Layer.provide(RequestLayer));
|
|
168
|
+
return Layer.mergeAll(StaticAssetsLayer, PublicAssetsLayer).pipe(Layer.provideMerge(ApplicationRoutesLayer));
|
|
169
|
+
};
|
|
170
|
+
const serverLayer = (application)=>HttpRouter.serve(httpLayer(application)).pipe(Layer.provide(BunServerLayer));
|
|
171
|
+
const ServerApplication = {
|
|
172
|
+
httpLayer: httpLayer,
|
|
173
|
+
serverLayer: serverLayer
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export { ServerApplication };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=application.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/application.js","sources":["../../src/server/application.ts"],"sourcesContent":["import * as BunHttpServer from '@effect/platform-bun/BunHttpServer';\nimport { Effect, Layer, Option, Stream, type Types } from 'effect';\nimport type { PlatformError } from 'effect/PlatformError';\nimport {\n HttpEffect,\n HttpRouter,\n HttpServerRequest,\n HttpServerResponse,\n HttpStaticServer,\n} from 'effect/unstable/http';\n\nimport { type ApplicationDefinition, getApplicationState } from '../application/definition';\nimport { getERSCIdentity } from '../application/ersc-identity';\nimport {\n applyMiddleware,\n type AnyMiddleware,\n getScopedHttpMiddleware,\n} from '../application/middleware';\nimport type { PagePathParams } from '../application/page';\nimport type { CompiledDestination } from '../application/route-graph';\nimport { FrameworkAssetNamespace, isAbsolutePath } from '../application/route-path';\nimport { FlightMediaType } from '../rsc/flight';\nimport { renderRouteTree } from '../rsc/render-route-tree';\nimport { FlightHtmlInjector } from './flight-html-stream';\nimport { FlightRenderer } from './flight-renderer';\nimport { HtmlRenderError, HtmlRenderer } from './html-renderer';\nimport type { RequestOutcome } from './request-outcome';\nimport {\n ApplicationIdleTimeoutSeconds,\n ApplicationMaxRequestBodySizeBytes,\n ServerConfig,\n} from './server-config';\nimport {\n prepareServerFnRequest,\n type PreparedServerFnRequest,\n type ServerFnRequestFailure,\n} from './server-fn-request';\n\nconst RenderersLayer = Layer.mergeAll(FlightRenderer.layer, HtmlRenderer.layer).pipe(\n Layer.provide(FlightHtmlInjector.layer),\n);\n\nconst StaticAssetsLayer = Layer.unwrap(\n Effect.map(ServerConfig, ({ clientAssetsCacheControl, clientAssetsRoot }) =>\n HttpStaticServer.layer({\n cacheControl: clientAssetsCacheControl,\n prefix: FrameworkAssetNamespace,\n root: clientAssetsRoot,\n }).pipe(Layer.provide(HttpRouter.disableLogger)),\n ),\n);\n\nconst PublicAssetsLayer = Layer.unwrap(\n Effect.map(ServerConfig, ({ publicAssetsRoot }) =>\n HttpStaticServer.layer({\n cacheControl: 'public, max-age=0',\n root: publicAssetsRoot,\n }),\n ),\n);\n\n// Bun derives `development` from NODE_ENV, which `ersc start` does not set, and its error page\n// carries the failure message and source stack.\nconst BunServerLayer = Layer.unwrap(\n Effect.map(ServerConfig, ({ hostname, port }) =>\n BunHttpServer.layer({\n development: false,\n hostname,\n idleTimeout: ApplicationIdleTimeoutSeconds,\n maxRequestBodySize: ApplicationMaxRequestBodySizeBytes,\n port,\n }),\n ),\n);\n\nconst EmptyPathParams: PagePathParams = Object.freeze({});\nconst DynamicResponseHeaders = {\n 'cache-control': 'private, no-store',\n} as const;\n\nconst appendAcceptVary = (vary: string | undefined) => {\n if (vary === undefined || vary.trim() === '') {\n return 'Accept';\n }\n\n const fields = vary.split(',').map((field) => field.trim().toLowerCase());\n return fields.includes('*') || fields.includes('accept') ? vary : `${vary}, Accept`;\n};\n\nconst acceptVaryPreResponseHandler: HttpEffect.PreResponseHandler = (_request, response) =>\n Effect.succeed(\n HttpServerResponse.setHeader(response, 'vary', appendAcceptVary(response.headers['vary'])),\n );\n\ntype RenderOptions<Services> = RequestOutcome & {\n readonly destination: CompiledDestination<Services>;\n readonly middleware: ReadonlyArray<AnyMiddleware<Services>>;\n readonly request: HttpServerRequest.HttpServerRequest;\n};\n\nconst fromWebStream = (\n stream: ReadableStream<Uint8Array>,\n options?: { readonly releaseLockOnEnd?: boolean },\n) =>\n Stream.fromReadableStream({\n evaluate: () => stream,\n onError: (cause) => cause,\n releaseLockOnEnd: options?.releaseLockOnEnd,\n });\n\nconst combinePageMiddleware = <Services>(\n middleware: ReadonlyArray<AnyMiddleware<Services>>,\n last: AnyMiddleware<Services>,\n) =>\n middleware\n .slice(0, -1)\n .reduceRight(\n (combined, current) => combined.combine(getScopedHttpMiddleware(current)),\n getScopedHttpMiddleware(last),\n );\n\ntype HttpApplicationRequirements =\n | ServerConfig\n | Layer.Services<ReturnType<typeof HttpStaticServer.layer>>\n | HttpRouter.Request.From<'Error', HtmlRenderError | ServerFnRequestFailure>;\n\nexport type HttpApplicationLayer<ApplicationError> = Layer.Layer<\n never,\n ApplicationError | PlatformError,\n HttpApplicationRequirements\n>;\n\nexport type ServerApplicationLayer<ApplicationError> = Layer.Layer<\n never,\n ApplicationError | PlatformError,\n ServerConfig\n>;\n\nconst httpLayer = <Services, ApplicationError>(\n application: ApplicationDefinition<Services, ApplicationError>,\n): HttpApplicationLayer<ApplicationError> => {\n const applicationState = getApplicationState(application);\n const identity = getERSCIdentity(application);\n const render = Effect.fnUntraced(function* ({\n destination,\n formState,\n middleware,\n request,\n serverFnResult,\n status,\n temporaryReferences,\n }: RenderOptions<Services>) {\n const requestUrl = HttpServerRequest.toURL(request);\n if (Option.isNone(requestUrl) || !isAbsolutePath(requestUrl.value.pathname)) {\n return yield* Effect.die(\n new TypeError(`Expected request URL \"${request.originalUrl}\" to contain an absolute path.`),\n );\n }\n\n const pathParams = yield* destination.page.paramsSchema === null\n ? Effect.succeed(EmptyPathParams)\n : HttpRouter.params;\n const routeTree = renderRouteTree({\n destination,\n pathParams,\n pathname: requestUrl.value.pathname,\n });\n const flightRenderer = yield* FlightRenderer;\n const flight = yield* flightRenderer.render({\n formState,\n middleware,\n renderRuntime: identity.renderRuntime,\n routeTree,\n serverFnResult,\n temporaryReferences,\n });\n\n if (request.headers['accept'] === FlightMediaType) {\n return HttpServerResponse.stream(\n fromWebStream(flight.stream, { releaseLockOnEnd: true }).pipe(\n Stream.ensuring(flight.release),\n ),\n {\n contentType: `${FlightMediaType};charset=utf-8`,\n headers: {\n ...DynamicResponseHeaders,\n 'content-location': requestUrl.value.href,\n },\n status,\n },\n );\n }\n\n const htmlRenderer = yield* HtmlRenderer;\n const htmlStream = yield* htmlRenderer\n .render({ flight, formState })\n .pipe(Effect.onError(() => flight.release));\n\n return HttpServerResponse.stream(\n fromWebStream(htmlStream).pipe(Stream.ensuring(flight.release)),\n {\n contentType: 'text/html;charset=utf-8',\n headers: DynamicResponseHeaders,\n status,\n },\n );\n });\n\n const executeServerFnAndRefresh = (\n prepared: PreparedServerFnRequest<Services>,\n destination: CompiledDestination<Services>,\n request: HttpServerRequest.HttpServerRequest,\n ) => {\n const refreshMiddleware = destination.middleware.filter(\n (middleware) => !prepared.middleware.includes(middleware),\n );\n const renderMiddleware =\n refreshMiddleware.length === 0\n ? prepared.middleware\n : Object.freeze([...prepared.middleware, ...refreshMiddleware]);\n const response = prepared.execute.pipe(\n Effect.flatMap((outcome) => {\n const refresh = render({\n ...outcome,\n destination,\n middleware: renderMiddleware,\n request,\n });\n return applyMiddleware(refreshMiddleware, refresh);\n }),\n );\n\n return applyMiddleware(prepared.middleware, response);\n };\n\n const RequestLayer = Layer.mergeAll(RenderersLayer, applicationState.layer);\n const ApplicationRoutesLayer = Layer.unwrap(\n Effect.map(Effect.context<Services | FlightRenderer | HtmlRenderer>(), (requestContext) => {\n const RequestContextMiddleware = HttpRouter.middleware<{\n provides: Services | FlightRenderer | HtmlRenderer;\n }>()((httpEffect): Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled> =>\n httpEffect.pipe(Effect.provideContext(requestContext)),\n );\n const makeRouteLayer = (destination: CompiledDestination<Services>) => {\n const GetLayer = HttpRouter.add('GET', destination.pattern, (request) =>\n render({\n destination,\n formState: null,\n middleware: destination.middleware,\n request,\n serverFnResult: null,\n status: 200,\n }).pipe(HttpEffect.withPreResponseHandler(acceptVaryPreResponseHandler)),\n );\n const lastPageMiddleware = destination.middleware.at(-1);\n const PageMiddleware =\n lastPageMiddleware === undefined\n ? RequestContextMiddleware\n : combinePageMiddleware(destination.middleware, lastPageMiddleware).combine(\n RequestContextMiddleware,\n );\n const PageLayer = GetLayer.pipe(Layer.provide(PageMiddleware.layer));\n const ServerFnLayer = HttpRouter.add('POST', destination.pattern, (request) =>\n prepareServerFnRequest(request, identity).pipe(\n Effect.flatMap((prepared) => executeServerFnAndRefresh(prepared, destination, request)),\n Effect.catchTag('ServerFnRequestError', (error) =>\n Effect.succeed(\n HttpServerResponse.text(error.message, {\n headers: DynamicResponseHeaders,\n status: error.status,\n }),\n ),\n ),\n HttpEffect.withPreResponseHandler(acceptVaryPreResponseHandler),\n ),\n ).pipe(Layer.provide(RequestContextMiddleware.layer));\n\n return Layer.mergeAll(PageLayer, ServerFnLayer);\n };\n const [firstDestination, ...remainingDestinations] = applicationState.routes;\n return Layer.mergeAll(\n makeRouteLayer(firstDestination),\n ...remainingDestinations.map(makeRouteLayer),\n );\n }),\n ).pipe(Layer.provide(RequestLayer));\n\n return Layer.mergeAll(StaticAssetsLayer, PublicAssetsLayer).pipe(\n Layer.provideMerge(ApplicationRoutesLayer),\n );\n};\n\nconst serverLayer = <Services, ApplicationError>(\n application: ApplicationDefinition<Services, ApplicationError>,\n): ServerApplicationLayer<ApplicationError> =>\n HttpRouter.serve(httpLayer(application)).pipe(Layer.provide(BunServerLayer));\n\nexport const ServerApplication = { httpLayer, serverLayer };\n"],"names":["BunHttpServer","Effect","Layer","Option","Stream","HttpEffect","HttpRouter","HttpServerRequest","HttpServerResponse","HttpStaticServer","getApplicationState","getERSCIdentity","applyMiddleware","getScopedHttpMiddleware","FrameworkAssetNamespace","isAbsolutePath","FlightMediaType","renderRouteTree","FlightHtmlInjector","FlightRenderer","HtmlRenderer","ApplicationIdleTimeoutSeconds","ApplicationMaxRequestBodySizeBytes","ServerConfig","prepareServerFnRequest","RenderersLayer","StaticAssetsLayer","clientAssetsCacheControl","clientAssetsRoot","PublicAssetsLayer","publicAssetsRoot","BunServerLayer","hostname","port","EmptyPathParams","Object","DynamicResponseHeaders","appendAcceptVary","vary","undefined","fields","field","acceptVaryPreResponseHandler","_request","response","fromWebStream","stream","options","cause","combinePageMiddleware","middleware","last","combined","current","httpLayer","application","applicationState","identity","render","destination","formState","request","serverFnResult","status","temporaryReferences","requestUrl","TypeError","pathParams","routeTree","flightRenderer","flight","htmlRenderer","htmlStream","executeServerFnAndRefresh","prepared","refreshMiddleware","renderMiddleware","outcome","refresh","RequestLayer","ApplicationRoutesLayer","requestContext","RequestContextMiddleware","httpEffect","makeRouteLayer","GetLayer","lastPageMiddleware","PageMiddleware","PageLayer","ServerFnLayer","error","firstDestination","remainingDestinations","serverLayer","ServerApplication"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
export type FlightHtmlStreamOptions = {
|
|
3
|
+
readonly nonce?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const injectFlightPayload: (flightStream: ReadableStream<Uint8Array>, options?: FlightHtmlStreamOptions) => TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>;
|
|
6
|
+
declare const FlightHtmlInjector_base: Context.ServiceClass<FlightHtmlInjector, "ersc/server/flight-html-stream/FlightHtmlInjector", {
|
|
7
|
+
inject: typeof injectFlightPayload;
|
|
8
|
+
}> & {
|
|
9
|
+
readonly make: Effect.Effect<{
|
|
10
|
+
inject: typeof injectFlightPayload;
|
|
11
|
+
}, never, never>;
|
|
12
|
+
};
|
|
13
|
+
export declare class FlightHtmlInjector extends FlightHtmlInjector_base {
|
|
14
|
+
static readonly layer: Layer.Layer<FlightHtmlInjector, never, never>;
|
|
15
|
+
static readonly layerTest: (implementation: {
|
|
16
|
+
inject: typeof injectFlightPayload;
|
|
17
|
+
}) => Layer.Layer<FlightHtmlInjector, never, never>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// Adapted from rsc-html-stream by Devon Govett.
|
|
5
|
+
// Copyright (c) 2024-present Devon Govett. Licensed under the MIT License; see vendor/rsc-html-stream/LICENSE.
|
|
6
|
+
|
|
7
|
+
const Encoder = new TextEncoder();
|
|
8
|
+
const HtmlTrailer = Encoder.encode('</body></html>');
|
|
9
|
+
const EmptyBytes = new Uint8Array();
|
|
10
|
+
const trailerPrefixLength = (bytes)=>{
|
|
11
|
+
const maximumLength = Math.min(bytes.byteLength, HtmlTrailer.byteLength);
|
|
12
|
+
for(let length = maximumLength; length > 0; length -= 1){
|
|
13
|
+
const offset = bytes.byteLength - length;
|
|
14
|
+
let matches = true;
|
|
15
|
+
for(let index = 0; index < length; index += 1){
|
|
16
|
+
if (bytes[offset + index] !== HtmlTrailer[index]) {
|
|
17
|
+
matches = false;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (matches) {
|
|
22
|
+
return length;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
};
|
|
27
|
+
const escapeInlineScript = (script)=>script.replace(/<!--|<\/script/gi, (match)=>match === '<!--' ? '<\\!--' : `</\\${match.slice(2)}`);
|
|
28
|
+
const writeFlightValue = (value, controller, nonce)=>{
|
|
29
|
+
const script = escapeInlineScript(`(self.__FLIGHT_DATA||=[]).push(${value})`);
|
|
30
|
+
const nonceAttribute = nonce === undefined ? '' : ` nonce="${nonce}"`;
|
|
31
|
+
controller.enqueue(Encoder.encode(`<script${nonceAttribute}>${script}</script>`));
|
|
32
|
+
};
|
|
33
|
+
const writeFlightChunk = (decoder, chunk, controller, nonce)=>{
|
|
34
|
+
try {
|
|
35
|
+
const text = decoder.decode(chunk);
|
|
36
|
+
if (text.length > 0) {
|
|
37
|
+
writeFlightValue(JSON.stringify(text), controller, nonce);
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
const base64 = JSON.stringify(chunk.toBase64());
|
|
41
|
+
writeFlightValue(`Uint8Array.from(atob(${base64}),character=>character.codePointAt(0))`, controller, nonce);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const writeFlightStream = (stream, controller, nonce)=>{
|
|
45
|
+
const decoder = new TextDecoder('utf-8', {
|
|
46
|
+
fatal: true,
|
|
47
|
+
ignoreBOM: true
|
|
48
|
+
});
|
|
49
|
+
const reader = stream.getReader();
|
|
50
|
+
let released = false;
|
|
51
|
+
const release = ()=>{
|
|
52
|
+
if (!released) {
|
|
53
|
+
released = true;
|
|
54
|
+
reader.releaseLock();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const cancel = (cause)=>{
|
|
58
|
+
let cancellation;
|
|
59
|
+
try {
|
|
60
|
+
cancellation = reader.cancel(cause);
|
|
61
|
+
} catch {
|
|
62
|
+
release();
|
|
63
|
+
return Promise.reject(cause);
|
|
64
|
+
}
|
|
65
|
+
return cancellation.then(()=>{
|
|
66
|
+
release();
|
|
67
|
+
return Promise.reject(cause);
|
|
68
|
+
}, ()=>{
|
|
69
|
+
release();
|
|
70
|
+
return Promise.reject(cause);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const handle = (result)=>{
|
|
74
|
+
if (result.done) {
|
|
75
|
+
release();
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
for (const chunk of result.value){
|
|
80
|
+
writeFlightChunk(decoder, chunk, controller, nonce);
|
|
81
|
+
}
|
|
82
|
+
} catch (cause) {
|
|
83
|
+
return cancel(cause);
|
|
84
|
+
}
|
|
85
|
+
return read();
|
|
86
|
+
};
|
|
87
|
+
const read = ()=>{
|
|
88
|
+
let result;
|
|
89
|
+
try {
|
|
90
|
+
result = reader.readMany();
|
|
91
|
+
} catch (cause) {
|
|
92
|
+
return cancel(cause);
|
|
93
|
+
}
|
|
94
|
+
return 'then' in result ? result.then(handle, cancel) : handle(result);
|
|
95
|
+
};
|
|
96
|
+
return read();
|
|
97
|
+
};
|
|
98
|
+
const makeHtmlWriter = ()=>{
|
|
99
|
+
let tail = EmptyBytes;
|
|
100
|
+
return {
|
|
101
|
+
finish (controller) {
|
|
102
|
+
if (tail.byteLength !== HtmlTrailer.byteLength && tail.byteLength > 0) {
|
|
103
|
+
controller.enqueue(tail);
|
|
104
|
+
}
|
|
105
|
+
controller.enqueue(HtmlTrailer);
|
|
106
|
+
},
|
|
107
|
+
write (chunks, controller) {
|
|
108
|
+
if (chunks.length === 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const byteLength = chunks.reduce((total, chunk)=>total + chunk.byteLength, tail.byteLength);
|
|
112
|
+
const combined = new Uint8Array(byteLength);
|
|
113
|
+
combined.set(tail);
|
|
114
|
+
let offset = tail.byteLength;
|
|
115
|
+
for (const chunk of chunks){
|
|
116
|
+
combined.set(chunk, offset);
|
|
117
|
+
offset += chunk.byteLength;
|
|
118
|
+
}
|
|
119
|
+
const bodyLength = combined.byteLength - trailerPrefixLength(combined);
|
|
120
|
+
if (bodyLength > 0) {
|
|
121
|
+
controller.enqueue(combined.subarray(0, bodyLength));
|
|
122
|
+
}
|
|
123
|
+
tail = combined.slice(bodyLength);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
const injectFlightPayload = (flightStream, options)=>{
|
|
128
|
+
const htmlWriter = makeHtmlWriter();
|
|
129
|
+
const bufferedHtml = [];
|
|
130
|
+
const flightCompletion = Promise.withResolvers();
|
|
131
|
+
let flightStarted = false;
|
|
132
|
+
let flushScheduled = false;
|
|
133
|
+
const fail = (controller, cause)=>{
|
|
134
|
+
try {
|
|
135
|
+
controller.error(cause);
|
|
136
|
+
} finally{
|
|
137
|
+
flightCompletion.resolve();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const flushHtml = (controller)=>{
|
|
141
|
+
htmlWriter.write(bufferedHtml, controller);
|
|
142
|
+
bufferedHtml.length = 0;
|
|
143
|
+
};
|
|
144
|
+
const startFlight = (controller)=>{
|
|
145
|
+
if (flightStarted) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
flightStarted = true;
|
|
149
|
+
writeFlightStream(flightStream, controller, options?.nonce).then(flightCompletion.resolve, (cause)=>fail(controller, cause));
|
|
150
|
+
};
|
|
151
|
+
const flushAndStartFlight = (controller)=>{
|
|
152
|
+
try {
|
|
153
|
+
flushHtml(controller);
|
|
154
|
+
startFlight(controller);
|
|
155
|
+
} catch (cause) {
|
|
156
|
+
fail(controller, cause);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return new TransformStream({
|
|
160
|
+
flush (controller) {
|
|
161
|
+
if (flushScheduled) {
|
|
162
|
+
flushScheduled = false;
|
|
163
|
+
flushHtml(controller);
|
|
164
|
+
}
|
|
165
|
+
startFlight(controller);
|
|
166
|
+
return flightCompletion.promise.then(()=>htmlWriter.finish(controller));
|
|
167
|
+
},
|
|
168
|
+
transform (chunk, controller) {
|
|
169
|
+
bufferedHtml.push(chunk);
|
|
170
|
+
if (!flushScheduled) {
|
|
171
|
+
flushScheduled = true;
|
|
172
|
+
void Bun.sleep(0).then(()=>{
|
|
173
|
+
if (flushScheduled) {
|
|
174
|
+
flushScheduled = false;
|
|
175
|
+
flushAndStartFlight(controller);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
class FlightHtmlInjector extends Context.Service()('ersc/server/flight-html-stream/FlightHtmlInjector', {
|
|
183
|
+
make: Effect.succeed({
|
|
184
|
+
inject: injectFlightPayload
|
|
185
|
+
})
|
|
186
|
+
}) {
|
|
187
|
+
static layer = Layer.effect(this, this.make);
|
|
188
|
+
static layerTest = Layer.mock(this);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { FlightHtmlInjector, injectFlightPayload };
|
|
192
|
+
|
|
193
|
+
//# sourceMappingURL=flight-html-stream.js.map
|