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 @@
|
|
|
1
|
+
{"version":3,"file":"server/flight-html-stream.js","sources":["../../src/server/flight-html-stream.ts"],"sourcesContent":["// Adapted from rsc-html-stream by Devon Govett.\n// Copyright (c) 2024-present Devon Govett. Licensed under the MIT License; see vendor/rsc-html-stream/LICENSE.\nimport { Context, Effect, Layer } from 'effect';\n\nconst Encoder = new TextEncoder();\nconst HtmlTrailer = Encoder.encode('</body></html>');\nconst EmptyBytes = new Uint8Array();\n\ntype StreamController = TransformStreamDefaultController<Uint8Array>;\n\nexport type FlightHtmlStreamOptions = {\n readonly nonce?: string;\n};\n\nconst trailerPrefixLength = (bytes: Uint8Array) => {\n const maximumLength = Math.min(bytes.byteLength, HtmlTrailer.byteLength);\n for (let length = maximumLength; length > 0; length -= 1) {\n const offset = bytes.byteLength - length;\n let matches = true;\n for (let index = 0; index < length; index += 1) {\n if (bytes[offset + index] !== HtmlTrailer[index]) {\n matches = false;\n break;\n }\n }\n if (matches) {\n return length;\n }\n }\n return 0;\n};\n\nconst escapeInlineScript = (script: string) =>\n script.replace(/<!--|<\\/script/gi, (match) =>\n match === '<!--' ? '<\\\\!--' : `</\\\\${match.slice(2)}`,\n );\n\nconst writeFlightValue = (\n value: string,\n controller: StreamController,\n nonce: string | undefined,\n) => {\n const script = escapeInlineScript(`(self.__FLIGHT_DATA||=[]).push(${value})`);\n const nonceAttribute = nonce === undefined ? '' : ` nonce=\"${nonce}\"`;\n controller.enqueue(Encoder.encode(`<script${nonceAttribute}>${script}</script>`));\n};\n\nconst writeFlightChunk = (\n decoder: TextDecoder,\n chunk: Uint8Array,\n controller: StreamController,\n nonce: string | undefined,\n) => {\n try {\n const text = decoder.decode(chunk);\n if (text.length > 0) {\n writeFlightValue(JSON.stringify(text), controller, nonce);\n }\n } catch {\n const base64 = JSON.stringify(chunk.toBase64());\n writeFlightValue(\n `Uint8Array.from(atob(${base64}),character=>character.codePointAt(0))`,\n controller,\n nonce,\n );\n }\n};\n\nconst writeFlightStream = (\n stream: ReadableStream<Uint8Array>,\n controller: StreamController,\n nonce: string | undefined,\n): Promise<void> => {\n const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });\n const reader = stream.getReader();\n let released = false;\n const release = () => {\n if (!released) {\n released = true;\n reader.releaseLock();\n }\n };\n const cancel = (cause: unknown): Promise<never> => {\n let cancellation: Promise<void>;\n try {\n cancellation = reader.cancel(cause);\n } catch {\n release();\n return Promise.reject(cause);\n }\n return cancellation.then(\n () => {\n release();\n return Promise.reject(cause);\n },\n () => {\n release();\n return Promise.reject(cause);\n },\n );\n };\n const handle = (result: Bun.ReadableStreamDefaultReadManyResult<Uint8Array>): Promise<void> => {\n if (result.done) {\n release();\n return Promise.resolve();\n }\n try {\n for (const chunk of result.value) {\n writeFlightChunk(decoder, chunk, controller, nonce);\n }\n } catch (cause) {\n return cancel(cause);\n }\n return read();\n };\n const read = (): Promise<void> => {\n let result:\n | Bun.ReadableStreamDefaultReadManyResult<Uint8Array>\n | Promise<Bun.ReadableStreamDefaultReadManyResult<Uint8Array>>;\n try {\n result = reader.readMany();\n } catch (cause) {\n return cancel(cause);\n }\n return 'then' in result ? result.then(handle, cancel) : handle(result);\n };\n\n return read();\n};\n\nconst makeHtmlWriter = () => {\n let tail = EmptyBytes;\n\n return {\n finish(controller: StreamController) {\n if (tail.byteLength !== HtmlTrailer.byteLength && tail.byteLength > 0) {\n controller.enqueue(tail);\n }\n controller.enqueue(HtmlTrailer);\n },\n write(chunks: ReadonlyArray<Uint8Array>, controller: StreamController) {\n if (chunks.length === 0) {\n return;\n }\n const byteLength = chunks.reduce((total, chunk) => total + chunk.byteLength, tail.byteLength);\n const combined = new Uint8Array(byteLength);\n combined.set(tail);\n let offset = tail.byteLength;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n const bodyLength = combined.byteLength - trailerPrefixLength(combined);\n if (bodyLength > 0) {\n controller.enqueue(combined.subarray(0, bodyLength));\n }\n tail = combined.slice(bodyLength);\n },\n };\n};\n\nexport const injectFlightPayload = (\n flightStream: ReadableStream<Uint8Array>,\n options?: FlightHtmlStreamOptions,\n) => {\n const htmlWriter = makeHtmlWriter();\n const bufferedHtml: Array<Uint8Array> = [];\n const flightCompletion = Promise.withResolvers<void>();\n let flightStarted = false;\n let flushScheduled = false;\n\n const fail = (controller: StreamController, cause: unknown) => {\n try {\n controller.error(cause);\n } finally {\n flightCompletion.resolve();\n }\n };\n const flushHtml = (controller: StreamController) => {\n htmlWriter.write(bufferedHtml, controller);\n bufferedHtml.length = 0;\n };\n const startFlight = (controller: StreamController) => {\n if (flightStarted) {\n return;\n }\n flightStarted = true;\n writeFlightStream(flightStream, controller, options?.nonce).then(\n flightCompletion.resolve,\n (cause: unknown) => fail(controller, cause),\n );\n };\n const flushAndStartFlight = (controller: StreamController) => {\n try {\n flushHtml(controller);\n startFlight(controller);\n } catch (cause) {\n fail(controller, cause);\n }\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n flush(controller) {\n if (flushScheduled) {\n flushScheduled = false;\n flushHtml(controller);\n }\n startFlight(controller);\n return flightCompletion.promise.then(() => htmlWriter.finish(controller));\n },\n transform(chunk, controller) {\n bufferedHtml.push(chunk);\n if (!flushScheduled) {\n flushScheduled = true;\n void Bun.sleep(0).then(() => {\n if (flushScheduled) {\n flushScheduled = false;\n flushAndStartFlight(controller);\n }\n });\n }\n },\n });\n};\n\nexport class FlightHtmlInjector extends Context.Service<FlightHtmlInjector>()(\n 'ersc/server/flight-html-stream/FlightHtmlInjector',\n {\n make: Effect.succeed({ inject: injectFlightPayload }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n\n static readonly layerTest = Layer.mock(this);\n}\n"],"names":["Context","Effect","Layer","Encoder","TextEncoder","HtmlTrailer","EmptyBytes","Uint8Array","trailerPrefixLength","bytes","maximumLength","Math","length","offset","matches","index","escapeInlineScript","script","match","writeFlightValue","value","controller","nonce","nonceAttribute","undefined","writeFlightChunk","decoder","chunk","text","JSON","base64","writeFlightStream","stream","TextDecoder","reader","released","release","cancel","cause","cancellation","Promise","handle","result","read","makeHtmlWriter","tail","chunks","byteLength","total","combined","bodyLength","injectFlightPayload","flightStream","options","htmlWriter","bufferedHtml","flightCompletion","flightStarted","flushScheduled","fail","flushHtml","startFlight","flushAndStartFlight","TransformStream","Bun","FlightHtmlInjector"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Scope } from 'effect';
|
|
2
|
+
import type { TemporaryReferenceSet } from 'react-server-dom-rspack/server.node';
|
|
3
|
+
import type { AnyMiddleware } from '../application/middleware.js';
|
|
4
|
+
import type { RenderRuntimeContext } from '../application/render-runtime.js';
|
|
5
|
+
import type { FlightPayload, ServerFnResult } from '../rsc/flight.js';
|
|
6
|
+
import type { RouteTreeModel } from '../rsc/route-tree.js';
|
|
7
|
+
type FlightStream = ReadableStream<Uint8Array>;
|
|
8
|
+
export type FlightRender = {
|
|
9
|
+
readonly release: Effect.Effect<void>;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
readonly stream: FlightStream;
|
|
12
|
+
};
|
|
13
|
+
export type FlightRenderOptions<Services> = {
|
|
14
|
+
readonly formState: FlightPayload['formState'];
|
|
15
|
+
readonly middleware: ReadonlyArray<AnyMiddleware<Services>>;
|
|
16
|
+
readonly renderRuntime: RenderRuntimeContext;
|
|
17
|
+
readonly routeTree: RouteTreeModel;
|
|
18
|
+
readonly serverFnResult: ServerFnResult | null;
|
|
19
|
+
readonly temporaryReferences?: TemporaryReferenceSet;
|
|
20
|
+
};
|
|
21
|
+
declare const FlightRenderer_base: Context.ServiceClass<FlightRenderer, "ersc/server/flight-renderer/FlightRenderer", {
|
|
22
|
+
render: <Services>(args_0: FlightRenderOptions<Services>) => Effect.Effect<FlightRender, never, Services | Scope.Scope>;
|
|
23
|
+
}> & {
|
|
24
|
+
readonly make: Effect.Effect<{
|
|
25
|
+
render: <Services>(args_0: FlightRenderOptions<Services>) => Effect.Effect<FlightRender, never, Services | Scope.Scope>;
|
|
26
|
+
}, never, never>;
|
|
27
|
+
};
|
|
28
|
+
export declare class FlightRenderer extends FlightRenderer_base {
|
|
29
|
+
static readonly layer: Layer.Layer<FlightRenderer, never, never>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Context, Effect, Exit, FiberSet, Layer, Scope } from "effect";
|
|
2
|
+
import { renderToReadableStream } from "react-server-dom-rspack/server.node";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FlightRenderer extends Context.Service()('ersc/server/flight-renderer/FlightRenderer', {
|
|
9
|
+
make: Effect.succeed({
|
|
10
|
+
render: Effect.fn('FlightRenderer.render')(function*({ formState, middleware, renderRuntime, routeTree, serverFnResult, temporaryReferences }) {
|
|
11
|
+
const parentScope = yield* Effect.scope;
|
|
12
|
+
const renderScope = yield* Scope.fork(parentScope);
|
|
13
|
+
const release = Scope.close(renderScope, Exit["void"]);
|
|
14
|
+
return yield* Effect.gen(function*() {
|
|
15
|
+
const runtime = yield* FiberSet.makeRuntimePromise().pipe(Scope.provide(renderScope));
|
|
16
|
+
const signal = yield* Effect.abortSignal.pipe(Scope.provide(renderScope));
|
|
17
|
+
const stream = renderRuntime.bind(runtime, middleware, ()=>{
|
|
18
|
+
const payload = {
|
|
19
|
+
formState,
|
|
20
|
+
routeTree,
|
|
21
|
+
serverFnResult
|
|
22
|
+
};
|
|
23
|
+
return renderToReadableStream(payload, {
|
|
24
|
+
onError: (error)=>{
|
|
25
|
+
if (!signal.aborted) {
|
|
26
|
+
void runtime(Effect.logError(error));
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
signal,
|
|
30
|
+
temporaryReferences
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
release,
|
|
35
|
+
signal,
|
|
36
|
+
stream
|
|
37
|
+
};
|
|
38
|
+
}).pipe(Effect.onError(()=>release));
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
}) {
|
|
42
|
+
static layer = Layer.effect(this, this.make);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { FlightRenderer };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=flight-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/flight-renderer.js","sources":["../../src/server/flight-renderer.tsx"],"sourcesContent":["import { Context, Effect, Exit, FiberSet, Layer, Scope } from 'effect';\nimport type { TemporaryReferenceSet } from 'react-server-dom-rspack/server.node';\nimport { renderToReadableStream } from 'react-server-dom-rspack/server.node';\n\nimport type { AnyMiddleware } from '../application/middleware';\nimport type { RenderRuntimeContext } from '../application/render-runtime';\nimport type { FlightPayload, ServerFnResult } from '../rsc/flight';\nimport type { RouteTreeModel } from '../rsc/route-tree';\n\ntype FlightStream = ReadableStream<Uint8Array>;\n\nexport type FlightRender = {\n readonly release: Effect.Effect<void>;\n readonly signal: AbortSignal;\n readonly stream: FlightStream;\n};\n\nexport type FlightRenderOptions<Services> = {\n readonly formState: FlightPayload['formState'];\n readonly middleware: ReadonlyArray<AnyMiddleware<Services>>;\n readonly renderRuntime: RenderRuntimeContext;\n readonly routeTree: RouteTreeModel;\n readonly serverFnResult: ServerFnResult | null;\n readonly temporaryReferences?: TemporaryReferenceSet;\n};\n\nexport class FlightRenderer extends Context.Service<FlightRenderer>()(\n 'ersc/server/flight-renderer/FlightRenderer',\n {\n make: Effect.succeed({\n render: Effect.fn('FlightRenderer.render')(function* <Services>({\n formState,\n middleware,\n renderRuntime,\n routeTree,\n serverFnResult,\n temporaryReferences,\n }: FlightRenderOptions<Services>): Effect.fn.Return<\n FlightRender,\n never,\n Services | Scope.Scope\n > {\n const parentScope = yield* Effect.scope;\n const renderScope = yield* Scope.fork(parentScope);\n const release = Scope.close(renderScope, Exit.void);\n return yield* Effect.gen(function* () {\n const runtime = yield* FiberSet.makeRuntimePromise<Services>().pipe(\n Scope.provide(renderScope),\n );\n const signal = yield* Effect.abortSignal.pipe(Scope.provide(renderScope));\n const stream = renderRuntime.bind(runtime, middleware, () => {\n const payload = { formState, routeTree, serverFnResult } satisfies FlightPayload;\n return renderToReadableStream(payload, {\n onError: (error) => {\n if (!signal.aborted) {\n void runtime(Effect.logError(error));\n }\n },\n signal,\n temporaryReferences,\n });\n });\n return { release, signal, stream } satisfies FlightRender;\n }).pipe(Effect.onError(() => release));\n }),\n }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n}\n"],"names":["Context","Effect","Exit","FiberSet","Layer","Scope","renderToReadableStream","FlightRenderer","formState","middleware","renderRuntime","routeTree","serverFnResult","temporaryReferences","parentScope","renderScope","release","runtime","signal","stream","payload","error"],"mappings":";;;;;AAAuE;AAEM;AAwBtE,MAAMO,cAAcA,SAASP,eAAe,GACjD,8CACA;IACE,MAAMC,cAAc,CAAC;QACnB,QAAQA,SAAS,CAAC,yBAAyB,UAAqB,EAC9DO,SAAS,EACTC,UAAU,EACVC,aAAa,EACbC,SAAS,EACTC,cAAc,EACdC,mBAAmB,EACW;YAK9B,MAAMC,cAAc,OAAOb,YAAY;YACvC,MAAMc,cAAc,OAAOV,UAAU,CAACS;YACtC,MAAME,UAAUX,WAAW,CAACU,aAAab,YAAS;YAClD,OAAO,OAAOD,UAAU,CAAC;gBACvB,MAAMgB,UAAU,OAAOd,2BAA2B,GAAa,IAAI,CACjEE,aAAa,CAACU;gBAEhB,MAAMG,SAAS,OAAOjB,uBAAuB,CAACI,aAAa,CAACU;gBAC5D,MAAMI,SAAST,cAAc,IAAI,CAACO,SAASR,YAAY;oBACrD,MAAMW,UAAU;wBAAEZ;wBAAWG;wBAAWC;oBAAe;oBACvD,OAAON,sBAAsBA,CAACc,SAAS;wBACrC,SAAS,CAACC;4BACR,IAAI,CAACH,OAAO,OAAO,EAAE;gCACnB,KAAKD,QAAQhB,eAAe,CAACoB;4BAC/B;wBACF;wBACAH;wBACAL;oBACF;gBACF;gBACA,OAAO;oBAAEG;oBAASE;oBAAQC;gBAAO;YACnC,GAAG,IAAI,CAAClB,cAAc,CAAC,IAAMe;QAC/B;IACF;AACF;IAEA,OAAgB,QAAQZ,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACxD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Schema, type Scope } from 'effect';
|
|
2
|
+
import type { FlightPayload } from '../rsc/flight.js';
|
|
3
|
+
import { FlightHtmlInjector } from './flight-html-stream.js';
|
|
4
|
+
import type { FlightRender } from './flight-renderer.js';
|
|
5
|
+
import { ServerConfig } from './server-config.js';
|
|
6
|
+
type HtmlStream = ReadableStream<Uint8Array>;
|
|
7
|
+
declare const HtmlRenderError_base: Schema.Class<HtmlRenderError, Schema.TaggedStruct<"HtmlRenderError", {
|
|
8
|
+
readonly cause: Schema.Defect;
|
|
9
|
+
}>, import("effect/Cause").YieldableError>;
|
|
10
|
+
export declare class HtmlRenderError extends HtmlRenderError_base {
|
|
11
|
+
}
|
|
12
|
+
declare const HtmlRenderer_base: Context.ServiceClass<HtmlRenderer, "ersc/server/html-renderer/HtmlRenderer", {
|
|
13
|
+
render: (args_0: {
|
|
14
|
+
readonly flight: FlightRender;
|
|
15
|
+
readonly formState: FlightPayload['formState'];
|
|
16
|
+
}) => Effect.Effect<HtmlStream, HtmlRenderError, Scope.Scope>;
|
|
17
|
+
}> & {
|
|
18
|
+
readonly make: Effect.Effect<{
|
|
19
|
+
render: (args_0: {
|
|
20
|
+
readonly flight: FlightRender;
|
|
21
|
+
readonly formState: FlightPayload['formState'];
|
|
22
|
+
}) => Effect.Effect<HtmlStream, HtmlRenderError, Scope.Scope>;
|
|
23
|
+
}, never, FlightHtmlInjector | ServerConfig>;
|
|
24
|
+
};
|
|
25
|
+
export declare class HtmlRenderer extends HtmlRenderer_base {
|
|
26
|
+
static readonly layer: Layer.Layer<HtmlRenderer, never, FlightHtmlInjector | ServerConfig>;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Context, Effect, FiberSet, Layer, Schema } from "effect";
|
|
3
|
+
import { use } from "react";
|
|
4
|
+
import { renderToReadableStream } from "react-dom/server.bun";
|
|
5
|
+
import { createFromReadableStream } from "react-server-dom-rspack/client";
|
|
6
|
+
import { RouteTree } from "../client/route-tree.js";
|
|
7
|
+
import { FlightHtmlInjector } from "./flight-html-stream.js";
|
|
8
|
+
import { ServerConfig } from "./server-config.js";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class HtmlRenderError extends Schema.TaggedError()('HtmlRenderError', {
|
|
27
|
+
cause: Schema.Defect()
|
|
28
|
+
}) {
|
|
29
|
+
}
|
|
30
|
+
class HtmlRenderer extends Context.Service()('ersc/server/html-renderer/HtmlRenderer', {
|
|
31
|
+
make: Effect.gen(function*() {
|
|
32
|
+
const { clientBootstrapScripts, clientStylesheets } = yield* ServerConfig;
|
|
33
|
+
const flightHtmlInjector = yield* FlightHtmlInjector;
|
|
34
|
+
return {
|
|
35
|
+
render: Effect.fn('HtmlRenderer.render')(function*({ flight, formState }) {
|
|
36
|
+
const signal = yield* Effect.abortSignal;
|
|
37
|
+
const runtime = yield* FiberSet.makeRuntimePromise();
|
|
38
|
+
const [ssrFlightStream, browserFlightStream] = flight.stream.tee();
|
|
39
|
+
let payload = null;
|
|
40
|
+
function SsrRoot() {
|
|
41
|
+
const { routeTree } = use(payload ??= createFromReadableStream(ssrFlightStream));
|
|
42
|
+
return /*#__PURE__*/ jsx(RouteTree, {
|
|
43
|
+
root: routeTree
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const htmlStream = yield* Effect.tryPromise({
|
|
47
|
+
try: ()=>renderToReadableStream(/*#__PURE__*/ jsxs(Fragment, {
|
|
48
|
+
children: [
|
|
49
|
+
clientStylesheets.map((href)=>/*#__PURE__*/ jsx("link", {
|
|
50
|
+
rel: "stylesheet",
|
|
51
|
+
href: href,
|
|
52
|
+
precedence: "default"
|
|
53
|
+
}, href)),
|
|
54
|
+
/*#__PURE__*/ jsx(SsrRoot, {})
|
|
55
|
+
]
|
|
56
|
+
}), {
|
|
57
|
+
bootstrapScripts: [
|
|
58
|
+
...clientBootstrapScripts
|
|
59
|
+
],
|
|
60
|
+
formState,
|
|
61
|
+
onError: (error, errorInfo)=>{
|
|
62
|
+
if (!signal.aborted && !flight.signal.aborted) {
|
|
63
|
+
void runtime(Effect.logError('HTML render failed.', error, errorInfo.componentStack));
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
signal
|
|
67
|
+
}),
|
|
68
|
+
catch: (cause)=>new HtmlRenderError({
|
|
69
|
+
cause
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
return htmlStream.pipeThrough(flightHtmlInjector.inject(browserFlightStream));
|
|
73
|
+
})
|
|
74
|
+
};
|
|
75
|
+
})
|
|
76
|
+
}) {
|
|
77
|
+
static layer = Layer.effect(this, this.make);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { HtmlRenderError, HtmlRenderer };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=html-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/html-renderer.js","sources":["../../src/server/html-renderer.tsx"],"sourcesContent":["import { Context, Effect, FiberSet, Layer, Schema, type Scope } from 'effect';\nimport { use } from 'react';\nimport { renderToReadableStream } from 'react-dom/server.bun';\nimport { createFromReadableStream } from 'react-server-dom-rspack/client';\n\nimport { RouteTree } from '../client/route-tree';\nimport type { FlightPayload } from '../rsc/flight';\nimport { FlightHtmlInjector } from './flight-html-stream';\nimport type { FlightRender } from './flight-renderer';\nimport { ServerConfig } from './server-config';\n\ntype HtmlStream = ReadableStream<Uint8Array>;\n\nexport class HtmlRenderError extends Schema.TaggedError<HtmlRenderError>()('HtmlRenderError', {\n cause: Schema.Defect(),\n}) {}\n\nexport class HtmlRenderer extends Context.Service<HtmlRenderer>()(\n 'ersc/server/html-renderer/HtmlRenderer',\n {\n make: Effect.gen(function* () {\n const { clientBootstrapScripts, clientStylesheets } = yield* ServerConfig;\n const flightHtmlInjector = yield* FlightHtmlInjector;\n\n return {\n render: Effect.fn('HtmlRenderer.render')(function* ({\n flight,\n formState,\n }: {\n readonly flight: FlightRender;\n readonly formState: FlightPayload['formState'];\n }): Effect.fn.Return<HtmlStream, HtmlRenderError, Scope.Scope> {\n const signal = yield* Effect.abortSignal;\n const runtime = yield* FiberSet.makeRuntimePromise<never>();\n const [ssrFlightStream, browserFlightStream] = flight.stream.tee();\n let payload: PromiseLike<FlightPayload> | null = null;\n\n function SsrRoot() {\n const { routeTree } = use(\n (payload ??= createFromReadableStream<FlightPayload>(ssrFlightStream)),\n );\n return <RouteTree root={routeTree} />;\n }\n\n const htmlStream = yield* Effect.tryPromise({\n try: () =>\n renderToReadableStream(\n <>\n {clientStylesheets.map((href) => (\n <link key={href} rel='stylesheet' href={href} precedence='default' />\n ))}\n <SsrRoot />\n </>,\n {\n bootstrapScripts: [...clientBootstrapScripts],\n formState,\n onError: (error, errorInfo) => {\n if (!signal.aborted && !flight.signal.aborted) {\n void runtime(\n Effect.logError('HTML render failed.', error, errorInfo.componentStack),\n );\n }\n },\n signal,\n },\n ),\n catch: (cause) => new HtmlRenderError({ cause }),\n });\n\n return htmlStream.pipeThrough(flightHtmlInjector.inject(browserFlightStream));\n }),\n };\n }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n}\n"],"names":["Context","Effect","FiberSet","Layer","Schema","use","renderToReadableStream","createFromReadableStream","RouteTree","FlightHtmlInjector","ServerConfig","HtmlRenderError","HtmlRenderer","clientBootstrapScripts","clientStylesheets","flightHtmlInjector","flight","formState","signal","runtime","ssrFlightStream","browserFlightStream","payload","SsrRoot","routeTree","htmlStream","href","error","errorInfo","cause"],"mappings":";;;;;;;;;;;;;;;;;;AAA8E;AAClD;AACkC;AACY;AAEzB;AAES;AAEX;AAIxC,MAAMW,eAAeA,SAASP,kBAAkB,GAAoB,mBAAmB;IAC5F,OAAOA,aAAa;AACtB;AAAI;AAEG,MAAMQ,YAAYA,SAASZ,eAAe,GAC/C,0CACA;IACE,MAAMC,UAAU,CAAC;QACf,MAAM,EAAEY,sBAAsB,EAAEC,iBAAiB,EAAE,GAAG,OAAOJ,YAAYA;QACzE,MAAMK,qBAAqB,OAAON,kBAAkBA;QAEpD,OAAO;YACL,QAAQR,SAAS,CAAC,uBAAuB,UAAW,EAClDe,MAAM,EACNC,SAAS,EAIV;gBACC,MAAMC,SAAS,OAAOjB,kBAAkB;gBACxC,MAAMkB,UAAU,OAAOjB,2BAA2B;gBAClD,MAAM,CAACkB,iBAAiBC,oBAAoB,GAAGL,OAAO,MAAM,CAAC,GAAG;gBAChE,IAAIM,UAA6C;gBAEjD,SAASC;oBACP,MAAM,EAAEC,SAAS,EAAE,GAAGnB,GAAGA,CACtBiB,YAAYf,wBAAwBA,CAAgBa;oBAEvD,qBAAO,IAACZ,SAASA;wBAAC,MAAMgB;;gBAC1B;gBAEA,MAAMC,aAAa,OAAOxB,iBAAiB,CAAC;oBAC1C,KAAK,IACHK,sBAAsBA,eACpB;;gCACGQ,kBAAkB,GAAG,CAAC,CAACY,qBACtB,IAAC;wCAAgB,KAAI;wCAAa,MAAMA;wCAAM,YAAW;uCAA9CA;8CAEb,IAACH;;4BAEH;4BACE,kBAAkB;mCAAIV;6BAAuB;4BAC7CI;4BACA,SAAS,CAACU,OAAOC;gCACf,IAAI,CAACV,OAAO,OAAO,IAAI,CAACF,OAAO,MAAM,CAAC,OAAO,EAAE;oCAC7C,KAAKG,QACHlB,eAAe,CAAC,uBAAuB0B,OAAOC,UAAU,cAAc;gCAE1E;4BACF;4BACAV;wBACF;oBAEJ,OAAO,CAACW,QAAU,IAAIlB,eAAeA,CAAC;4BAAEkB;wBAAM;gBAChD;gBAEA,OAAOJ,WAAW,WAAW,CAACV,mBAAmB,MAAM,CAACM;YAC1D;QACF;IACF;AACF;IAEA,OAAgB,QAAQlB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACxD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TemporaryReferenceSet } from 'react-server-dom-rspack/server.node';
|
|
2
|
+
import type { FlightPayload } from '../rsc/flight.js';
|
|
3
|
+
export type RequestOutcome = {
|
|
4
|
+
readonly formState: FlightPayload['formState'];
|
|
5
|
+
readonly serverFnResult: FlightPayload['serverFnResult'];
|
|
6
|
+
readonly status: 200;
|
|
7
|
+
readonly temporaryReferences?: TemporaryReferenceSet;
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/request-outcome.js","sources":["../../src/server/request-outcome.ts"],"sourcesContent":["import type { TemporaryReferenceSet } from 'react-server-dom-rspack/server.node';\n\nimport type { FlightPayload } from '../rsc/flight';\n\nexport type RequestOutcome = {\n readonly formState: FlightPayload['formState'];\n readonly serverFnResult: FlightPayload['serverFnResult'];\n readonly status: 200;\n readonly temporaryReferences?: TemporaryReferenceSet;\n};\n"],"names":[],"mappings":"AASE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Context } from 'effect';
|
|
2
|
+
export declare const DefaultApplicationPort = 18193;
|
|
3
|
+
export declare const DefaultApplicationHostname = "localhost";
|
|
4
|
+
export declare const ApplicationIdleTimeoutSeconds = 0;
|
|
5
|
+
export declare const ApplicationMaxRequestBodySizeBytes: number;
|
|
6
|
+
declare const ServerConfig_base: Context.ServiceClass<ServerConfig, "ersc/server/server-config/ServerConfig", {
|
|
7
|
+
readonly clientAssetsCacheControl: string;
|
|
8
|
+
readonly clientAssetsRoot: string;
|
|
9
|
+
readonly clientBootstrapScripts: ReadonlyArray<string>;
|
|
10
|
+
readonly clientStylesheets: ReadonlyArray<string>;
|
|
11
|
+
readonly hostname: string;
|
|
12
|
+
readonly port: number;
|
|
13
|
+
readonly publicAssetsRoot: string;
|
|
14
|
+
}>;
|
|
15
|
+
export declare class ServerConfig extends ServerConfig_base {
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const DefaultApplicationPort = 18193;
|
|
6
|
+
const DefaultApplicationHostname = 'localhost';
|
|
7
|
+
// Zero disables Bun's idle timeout, which would otherwise cut a stalled Suspense boundary off at
|
|
8
|
+
// ten seconds. Connection deadlines belong to the deployment's proxy.
|
|
9
|
+
const ApplicationIdleTimeoutSeconds = 0;
|
|
10
|
+
// Bun rejects oversized bodies before they reach the framework router or React's Server Function
|
|
11
|
+
// decoder.
|
|
12
|
+
const ApplicationMaxRequestBodySizeBytes = 10 * 1024 * 1024;
|
|
13
|
+
class ServerConfig extends Context.Service()('ersc/server/server-config/ServerConfig') {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { ApplicationIdleTimeoutSeconds, ApplicationMaxRequestBodySizeBytes, DefaultApplicationHostname, DefaultApplicationPort, ServerConfig };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=server-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/server-config.js","sources":["../../src/server/server-config.ts"],"sourcesContent":["import { Context } from 'effect';\n\nexport const DefaultApplicationPort = 18193;\nexport const DefaultApplicationHostname = 'localhost';\n\n// Zero disables Bun's idle timeout, which would otherwise cut a stalled Suspense boundary off at\n// ten seconds. Connection deadlines belong to the deployment's proxy.\nexport const ApplicationIdleTimeoutSeconds = 0;\n\n// Bun rejects oversized bodies before they reach the framework router or React's Server Function\n// decoder.\nexport const ApplicationMaxRequestBodySizeBytes = 10 * 1024 * 1024;\n\nexport class ServerConfig extends Context.Service<\n ServerConfig,\n {\n readonly clientAssetsCacheControl: string;\n readonly clientAssetsRoot: string;\n readonly clientBootstrapScripts: ReadonlyArray<string>;\n readonly clientStylesheets: ReadonlyArray<string>;\n readonly hostname: string;\n readonly port: number;\n readonly publicAssetsRoot: string;\n }\n>()('ersc/server/server-config/ServerConfig') {}\n"],"names":["Context","DefaultApplicationPort","DefaultApplicationHostname","ApplicationIdleTimeoutSeconds","ApplicationMaxRequestBodySizeBytes","ServerConfig"],"mappings":";;;AAAiC;AAE1B,MAAMC,sBAAsBA,GAAG,MAAM;AACrC,MAAMC,0BAA0BA,GAAG,YAAY;AAEtD,iGAAiG;AACjG,sEAAsE;AAC/D,MAAMC,6BAA6BA,GAAG,EAAE;AAE/C,iGAAiG;AACjG,WAAW;AACJ,MAAMC,kCAAkCA,GAAG,KAAK,OAAO,KAAK;AAE5D,MAAMC,YAAYA,SAASL,eAAe,GAW7C;AAA2C"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
export declare const serverFnOutcome: <Output, Error, Requirements>(operation: Effect.Effect<Output, Error, Requirements>) => Effect.Effect<{
|
|
3
|
+
serverFnResult: {
|
|
4
|
+
_tag: "Success";
|
|
5
|
+
value: Output;
|
|
6
|
+
error?: never;
|
|
7
|
+
} | {
|
|
8
|
+
value?: never;
|
|
9
|
+
_tag: "Failure";
|
|
10
|
+
error: unknown;
|
|
11
|
+
};
|
|
12
|
+
status: 200;
|
|
13
|
+
}, never, Requirements>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Cause, Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const serverFnOutcome = Effect.fnUntraced(function*(operation) {
|
|
6
|
+
const exit = yield* Effect.exit(operation);
|
|
7
|
+
if (exit._tag === 'Failure' && Cause.hasInterrupts(exit.cause)) {
|
|
8
|
+
return yield* Effect.interrupt;
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
serverFnResult: exit._tag === 'Success' ? {
|
|
12
|
+
_tag: 'Success',
|
|
13
|
+
value: exit.value
|
|
14
|
+
} : {
|
|
15
|
+
_tag: 'Failure',
|
|
16
|
+
error: Cause.squash(exit.cause)
|
|
17
|
+
},
|
|
18
|
+
status: 200
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export { serverFnOutcome };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=server-fn-outcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/server-fn-outcome.js","sources":["../../src/server/server-fn-outcome.ts"],"sourcesContent":["import { Cause, Effect } from 'effect';\n\nimport type { ServerFnResult } from '../rsc/flight';\nimport type { RequestOutcome } from './request-outcome';\n\nexport const serverFnOutcome = Effect.fnUntraced(function* <Output, Error, Requirements>(\n operation: Effect.Effect<Output, Error, Requirements>,\n) {\n const exit = yield* Effect.exit(operation);\n if (exit._tag === 'Failure' && Cause.hasInterrupts(exit.cause)) {\n return yield* Effect.interrupt;\n }\n\n return {\n serverFnResult:\n exit._tag === 'Success'\n ? ({ _tag: 'Success', value: exit.value } satisfies ServerFnResult)\n : ({ _tag: 'Failure', error: Cause.squash(exit.cause) } satisfies ServerFnResult),\n status: 200,\n } satisfies Pick<RequestOutcome, 'serverFnResult' | 'status'>;\n});\n"],"names":["Cause","Effect","serverFnOutcome","operation","exit"],"mappings":";;;AAAuC;AAKhC,MAAME,eAAeA,GAAGD,iBAAiB,CAAC,UAC/CE,SAAqD;IAErD,MAAMC,OAAO,OAAOH,WAAW,CAACE;IAChC,IAAIC,KAAK,IAAI,KAAK,aAAaJ,mBAAmB,CAACI,KAAK,KAAK,GAAG;QAC9D,OAAO,OAAOH,gBAAgB;IAChC;IAEA,OAAO;QACL,gBACEG,KAAK,IAAI,KAAK,YACT;YAAE,MAAM;YAAW,OAAOA,KAAK,KAAK;QAAC,IACrC;YAAE,MAAM;YAAW,OAAOJ,YAAY,CAACI,KAAK,KAAK;QAAE;QAC1D,QAAQ;IACV;AACF,GAAG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Cause, Effect, Schema } from 'effect';
|
|
2
|
+
import { HttpServerRequest } from 'effect/unstable/http';
|
|
3
|
+
import type { ERSCIdentity } from '../application/ersc-identity.js';
|
|
4
|
+
import type { AnyMiddleware } from '../application/middleware.js';
|
|
5
|
+
import type { RequestOutcome } from './request-outcome.js';
|
|
6
|
+
declare const ServerFnRequestError_base: Schema.Class<ServerFnRequestError, Schema.TaggedStruct<"ServerFnRequestError", {
|
|
7
|
+
readonly cause: Schema.Defect;
|
|
8
|
+
readonly message: Schema.String;
|
|
9
|
+
readonly status: Schema.Literals<readonly [400, 403, 500]>;
|
|
10
|
+
}>, Cause.YieldableError>;
|
|
11
|
+
export declare class ServerFnRequestError extends ServerFnRequestError_base {
|
|
12
|
+
}
|
|
13
|
+
declare const ServerFnExecutionError_base: Schema.Class<ServerFnExecutionError, Schema.TaggedStruct<"ServerFnExecutionError", {
|
|
14
|
+
readonly cause: Schema.Defect;
|
|
15
|
+
}>, Cause.YieldableError>;
|
|
16
|
+
declare class ServerFnExecutionError extends ServerFnExecutionError_base {
|
|
17
|
+
}
|
|
18
|
+
export type PreparedServerFnRequest<ApplicationServices> = {
|
|
19
|
+
readonly execute: Effect.Effect<RequestOutcome, ServerFnRequestError, ApplicationServices>;
|
|
20
|
+
readonly middleware: ReadonlyArray<AnyMiddleware<ApplicationServices>>;
|
|
21
|
+
};
|
|
22
|
+
export declare const prepareServerFnRequest: <Services>(request: HttpServerRequest.HttpServerRequest, identity: ERSCIdentity<Services>) => Effect.Effect<PreparedServerFnRequest<Services>, ServerFnExecutionError | ServerFnRequestError | import("effect/unstable/http/HttpServerError").RequestError, import("effect/Scope").Scope>;
|
|
23
|
+
export type ServerFnRequestFailure = Effect.Error<ReturnType<typeof prepareServerFnRequest>>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Cause, Effect, Schema } from "effect";
|
|
2
|
+
import { HttpServerRequest } from "effect/unstable/http";
|
|
3
|
+
import { createTemporaryReferenceSet, decodeAction, decodeFormState, decodeReply, loadServerAction } from "react-server-dom-rspack/server.node";
|
|
4
|
+
import { matchServerFnInvocation } from "../application/server-fn.js";
|
|
5
|
+
import { ServerFnIdHeader } from "../rsc/flight.js";
|
|
6
|
+
import { serverFnOutcome } from "./server-fn-outcome.js";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const ServerFnArraySizeLimit = 10000;
|
|
21
|
+
const NoMiddleware = Object.freeze([]);
|
|
22
|
+
class ServerFnRequestError extends Schema.TaggedError()('ServerFnRequestError', {
|
|
23
|
+
cause: Schema.Defect(),
|
|
24
|
+
message: Schema.String,
|
|
25
|
+
status: Schema.Literals([
|
|
26
|
+
400,
|
|
27
|
+
403,
|
|
28
|
+
500
|
|
29
|
+
])
|
|
30
|
+
}) {
|
|
31
|
+
}
|
|
32
|
+
class ServerFnExecutionError extends Schema.TaggedError()('ServerFnExecutionError', {
|
|
33
|
+
cause: Schema.Defect()
|
|
34
|
+
}) {
|
|
35
|
+
}
|
|
36
|
+
const requestError = (message, status, cause)=>new ServerFnRequestError({
|
|
37
|
+
cause,
|
|
38
|
+
message,
|
|
39
|
+
status
|
|
40
|
+
});
|
|
41
|
+
const bodyReadError = (message, cause)=>requestError(message, 400, cause);
|
|
42
|
+
const normalizeServerFnFailure = (effect)=>effect.pipe(Effect.catchCause((cause)=>Cause.hasInterrupts(cause) ? Effect.interrupt : Effect.fail(new ServerFnExecutionError({
|
|
43
|
+
cause: Cause.squash(cause)
|
|
44
|
+
}))));
|
|
45
|
+
const validateOrigin = (request)=>Effect["try"]({
|
|
46
|
+
try: ()=>{
|
|
47
|
+
const origin = request.headers['origin'];
|
|
48
|
+
if (origin === undefined) {
|
|
49
|
+
throw new Error('The Origin header is missing.');
|
|
50
|
+
}
|
|
51
|
+
const expectedHost = request.headers['host'];
|
|
52
|
+
if (expectedHost === undefined || new URL(origin).host !== expectedHost.toLowerCase()) {
|
|
53
|
+
throw new Error(`Origin "${origin}" does not match host "${expectedHost ?? ''}".`);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
catch: (cause)=>requestError('Rejected a cross-origin Server Function request.', 403, cause)
|
|
57
|
+
});
|
|
58
|
+
const readBody = Effect.fnUntraced(function*(request) {
|
|
59
|
+
if (request.headers.get('content-type')?.toLowerCase().startsWith('multipart/form-data')) {
|
|
60
|
+
return yield* Effect.tryPromise({
|
|
61
|
+
try: ()=>request.formData(),
|
|
62
|
+
catch: (cause)=>bodyReadError('Failed to read the Server Function request body.', cause)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return yield* Effect.tryPromise({
|
|
66
|
+
try: ()=>request.text(),
|
|
67
|
+
catch: (cause)=>bodyReadError('Failed to read the Server Function request body.', cause)
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
const prepareServerFnOperation = (identity, action, args)=>Effect.sync(()=>{
|
|
71
|
+
const invocation = action(...args);
|
|
72
|
+
const match = matchServerFnInvocation(invocation, identity);
|
|
73
|
+
switch(match._tag){
|
|
74
|
+
case 'Match':
|
|
75
|
+
return {
|
|
76
|
+
effect: normalizeServerFnFailure(match.effect),
|
|
77
|
+
middleware: match.middleware
|
|
78
|
+
};
|
|
79
|
+
case 'IdentityMismatch':
|
|
80
|
+
return {
|
|
81
|
+
effect: Effect.die(new TypeError('Server Function was created by a different ERSC module.')),
|
|
82
|
+
middleware: NoMiddleware
|
|
83
|
+
};
|
|
84
|
+
case 'Native':
|
|
85
|
+
return {
|
|
86
|
+
effect: normalizeServerFnFailure(Effect.promise(()=>Promise.resolve(invocation))),
|
|
87
|
+
middleware: NoMiddleware
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}).pipe(normalizeServerFnFailure);
|
|
91
|
+
const prepareClientServerFn = Effect.fnUntraced(function*(request, actionId, identity) {
|
|
92
|
+
const temporaryReferences = createTemporaryReferenceSet();
|
|
93
|
+
const body = yield* readBody(request);
|
|
94
|
+
const args = yield* Effect.tryPromise({
|
|
95
|
+
try: ()=>decodeReply(body, {
|
|
96
|
+
arraySizeLimit: ServerFnArraySizeLimit,
|
|
97
|
+
temporaryReferences
|
|
98
|
+
}),
|
|
99
|
+
catch: (cause)=>requestError('Failed to decode Server Function arguments.', 400, cause)
|
|
100
|
+
});
|
|
101
|
+
const action = yield* Effect["try"]({
|
|
102
|
+
try: ()=>loadServerAction(actionId),
|
|
103
|
+
catch: (cause)=>requestError('The requested Server Function does not exist.', 400, cause)
|
|
104
|
+
});
|
|
105
|
+
const operation = yield* prepareServerFnOperation(identity, action, args);
|
|
106
|
+
const prepared = {
|
|
107
|
+
execute: serverFnOutcome(operation.effect).pipe(Effect.map((outcome)=>({
|
|
108
|
+
formState: null,
|
|
109
|
+
...outcome,
|
|
110
|
+
temporaryReferences
|
|
111
|
+
}))),
|
|
112
|
+
middleware: operation.middleware
|
|
113
|
+
};
|
|
114
|
+
return prepared;
|
|
115
|
+
});
|
|
116
|
+
const prepareProgressiveServerFn = Effect.fnUntraced(function*(request, identity) {
|
|
117
|
+
const formData = yield* Effect.tryPromise({
|
|
118
|
+
try: ()=>request.formData(),
|
|
119
|
+
catch: (cause)=>bodyReadError('Failed to read the Server Function form body.', cause)
|
|
120
|
+
});
|
|
121
|
+
const decodedAction = yield* Effect.tryPromise({
|
|
122
|
+
try: ()=>Promise.resolve(decodeAction(formData)),
|
|
123
|
+
catch: (cause)=>requestError('Failed to decode the Server Function form action.', 400, cause)
|
|
124
|
+
});
|
|
125
|
+
if (decodedAction === null) {
|
|
126
|
+
return yield* requestError('The submitted form does not contain a Server Function action.', 400, new Error('decodeAction returned null.'));
|
|
127
|
+
}
|
|
128
|
+
const operation = yield* prepareServerFnOperation(identity, decodedAction, []);
|
|
129
|
+
const execute = Effect.gen(function*() {
|
|
130
|
+
const actionResult = yield* operation.effect.pipe(Effect.mapError((error)=>requestError('The Server Function form action failed.', 500, error.cause)));
|
|
131
|
+
const decodedFormState = yield* Effect.tryPromise({
|
|
132
|
+
try: ()=>decodeFormState(actionResult, formData),
|
|
133
|
+
catch: (cause)=>requestError('Failed to decode React form state.', 500, cause)
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
formState: decodedFormState,
|
|
137
|
+
serverFnResult: null,
|
|
138
|
+
status: 200
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
const prepared = {
|
|
142
|
+
execute,
|
|
143
|
+
middleware: operation.middleware
|
|
144
|
+
};
|
|
145
|
+
return prepared;
|
|
146
|
+
});
|
|
147
|
+
const prepareServerFnRequest = Effect.fnUntraced(function*(request, identity) {
|
|
148
|
+
yield* validateOrigin(request);
|
|
149
|
+
const signal = yield* Effect.abortSignal;
|
|
150
|
+
const webRequest = yield* HttpServerRequest.toWeb(request, {
|
|
151
|
+
signal
|
|
152
|
+
});
|
|
153
|
+
const actionId = request.headers[ServerFnIdHeader];
|
|
154
|
+
const prepareRequest = actionId === undefined ? prepareProgressiveServerFn(webRequest, identity) : prepareClientServerFn(webRequest, actionId, identity);
|
|
155
|
+
return yield* prepareRequest;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
export { ServerFnRequestError, prepareServerFnRequest };
|
|
159
|
+
|
|
160
|
+
//# sourceMappingURL=server-fn-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server/server-fn-request.js","sources":["../../src/server/server-fn-request.ts"],"sourcesContent":["import { Cause, Effect, Schema } from 'effect';\nimport { HttpServerRequest } from 'effect/unstable/http';\nimport {\n createTemporaryReferenceSet,\n decodeAction,\n decodeFormState,\n decodeReply,\n loadServerAction,\n} from 'react-server-dom-rspack/server.node';\n\nimport type { ERSCIdentity } from '../application/ersc-identity';\nimport type { AnyMiddleware } from '../application/middleware';\nimport { matchServerFnInvocation } from '../application/server-fn';\nimport { ServerFnIdHeader } from '../rsc/flight';\nimport type { RequestOutcome } from './request-outcome';\nimport { serverFnOutcome } from './server-fn-outcome';\n\nconst ServerFnArraySizeLimit = 10_000;\nconst NoMiddleware = Object.freeze([]);\n\nexport class ServerFnRequestError extends Schema.TaggedError<ServerFnRequestError>()(\n 'ServerFnRequestError',\n {\n cause: Schema.Defect(),\n message: Schema.String,\n status: Schema.Literals([400, 403, 500]),\n },\n) {}\n\nclass ServerFnExecutionError extends Schema.TaggedError<ServerFnExecutionError>()(\n 'ServerFnExecutionError',\n { cause: Schema.Defect() },\n) {}\n\nconst requestError = (message: string, status: 400 | 403 | 500, cause: unknown) =>\n new ServerFnRequestError({ cause, message, status });\n\nconst bodyReadError = (message: string, cause: unknown) => requestError(message, 400, cause);\n\nconst normalizeServerFnFailure = <Output, Error, Services>(\n effect: Effect.Effect<Output, Error, Services>,\n): Effect.Effect<Output, ServerFnExecutionError, Services> =>\n effect.pipe(\n Effect.catchCause((cause) =>\n Cause.hasInterrupts(cause)\n ? Effect.interrupt\n : Effect.fail(new ServerFnExecutionError({ cause: Cause.squash(cause) })),\n ),\n );\n\ntype ServerFnOperation<ApplicationServices> = {\n readonly effect: Effect.Effect<unknown, ServerFnExecutionError, ApplicationServices>;\n readonly middleware: ReadonlyArray<AnyMiddleware<ApplicationServices>>;\n};\n\nexport type PreparedServerFnRequest<ApplicationServices> = {\n readonly execute: Effect.Effect<RequestOutcome, ServerFnRequestError, ApplicationServices>;\n readonly middleware: ReadonlyArray<AnyMiddleware<ApplicationServices>>;\n};\n\nconst validateOrigin = (request: HttpServerRequest.HttpServerRequest) =>\n Effect.try({\n try: () => {\n const origin = request.headers['origin'];\n if (origin === undefined) {\n throw new Error('The Origin header is missing.');\n }\n\n const expectedHost = request.headers['host'];\n if (expectedHost === undefined || new URL(origin).host !== expectedHost.toLowerCase()) {\n throw new Error(`Origin \"${origin}\" does not match host \"${expectedHost ?? ''}\".`);\n }\n },\n catch: (cause) => requestError('Rejected a cross-origin Server Function request.', 403, cause),\n });\n\nconst readBody = Effect.fnUntraced(function* (request: Request) {\n if (request.headers.get('content-type')?.toLowerCase().startsWith('multipart/form-data')) {\n return yield* Effect.tryPromise({\n try: () => request.formData(),\n catch: (cause) => bodyReadError('Failed to read the Server Function request body.', cause),\n });\n }\n\n return yield* Effect.tryPromise({\n try: () => request.text(),\n catch: (cause) => bodyReadError('Failed to read the Server Function request body.', cause),\n });\n});\n\nconst prepareServerFnOperation = <ApplicationServices>(\n identity: ERSCIdentity<ApplicationServices>,\n action: (...args: ReadonlyArray<unknown>) => unknown,\n args: ReadonlyArray<unknown>,\n) =>\n Effect.sync((): ServerFnOperation<ApplicationServices> => {\n const invocation = action(...args);\n const match = matchServerFnInvocation(invocation, identity);\n switch (match._tag) {\n case 'Match':\n return {\n effect: normalizeServerFnFailure(match.effect),\n middleware: match.middleware,\n };\n case 'IdentityMismatch':\n return {\n effect: Effect.die(\n new TypeError('Server Function was created by a different ERSC module.'),\n ),\n middleware: NoMiddleware,\n };\n case 'Native':\n return {\n effect: normalizeServerFnFailure(Effect.promise(() => Promise.resolve(invocation))),\n middleware: NoMiddleware,\n };\n }\n }).pipe(normalizeServerFnFailure);\n\nconst prepareClientServerFn = Effect.fnUntraced(function* <ApplicationServices>(\n request: Request,\n actionId: string,\n identity: ERSCIdentity<ApplicationServices>,\n) {\n const temporaryReferences = createTemporaryReferenceSet();\n const body = yield* readBody(request);\n const args = yield* Effect.tryPromise({\n try: () =>\n decodeReply(body, {\n arraySizeLimit: ServerFnArraySizeLimit,\n temporaryReferences,\n }),\n catch: (cause) => requestError('Failed to decode Server Function arguments.', 400, cause),\n });\n const action = yield* Effect.try({\n try: () => loadServerAction(actionId),\n catch: (cause) => requestError('The requested Server Function does not exist.', 400, cause),\n });\n const operation = yield* prepareServerFnOperation(identity, action, args);\n\n const prepared: PreparedServerFnRequest<ApplicationServices> = {\n execute: serverFnOutcome(operation.effect).pipe(\n Effect.map(\n (outcome) =>\n ({\n formState: null,\n ...outcome,\n temporaryReferences,\n }) satisfies RequestOutcome,\n ),\n ),\n middleware: operation.middleware,\n };\n return prepared;\n});\n\nconst prepareProgressiveServerFn = Effect.fnUntraced(function* <ApplicationServices>(\n request: Request,\n identity: ERSCIdentity<ApplicationServices>,\n) {\n const formData = yield* Effect.tryPromise({\n try: () => request.formData(),\n catch: (cause) => bodyReadError('Failed to read the Server Function form body.', cause),\n });\n const decodedAction = yield* Effect.tryPromise({\n try: () => Promise.resolve(decodeAction(formData)),\n catch: (cause) => requestError('Failed to decode the Server Function form action.', 400, cause),\n });\n if (decodedAction === null) {\n return yield* requestError(\n 'The submitted form does not contain a Server Function action.',\n 400,\n new Error('decodeAction returned null.'),\n );\n }\n\n const operation = yield* prepareServerFnOperation(identity, decodedAction, []);\n const execute = Effect.gen(function* () {\n const actionResult = yield* operation.effect.pipe(\n Effect.mapError((error) =>\n requestError('The Server Function form action failed.', 500, error.cause),\n ),\n );\n const decodedFormState = yield* Effect.tryPromise({\n try: () => decodeFormState(actionResult, formData),\n catch: (cause) => requestError('Failed to decode React form state.', 500, cause),\n });\n\n return {\n formState: decodedFormState,\n serverFnResult: null,\n status: 200,\n } satisfies RequestOutcome;\n });\n\n const prepared: PreparedServerFnRequest<ApplicationServices> = {\n execute,\n middleware: operation.middleware,\n };\n return prepared;\n});\n\nexport const prepareServerFnRequest = Effect.fnUntraced(function* <Services>(\n request: HttpServerRequest.HttpServerRequest,\n identity: ERSCIdentity<Services>,\n) {\n yield* validateOrigin(request);\n const signal = yield* Effect.abortSignal;\n const webRequest = yield* HttpServerRequest.toWeb(request, { signal });\n const actionId = request.headers[ServerFnIdHeader];\n const prepareRequest =\n actionId === undefined\n ? prepareProgressiveServerFn(webRequest, identity)\n : prepareClientServerFn(webRequest, actionId, identity);\n\n return yield* prepareRequest;\n});\n\nexport type ServerFnRequestFailure = Effect.Error<ReturnType<typeof prepareServerFnRequest>>;\n"],"names":["Cause","Effect","Schema","HttpServerRequest","createTemporaryReferenceSet","decodeAction","decodeFormState","decodeReply","loadServerAction","matchServerFnInvocation","ServerFnIdHeader","serverFnOutcome","ServerFnArraySizeLimit","NoMiddleware","Object","ServerFnRequestError","ServerFnExecutionError","requestError","message","status","cause","bodyReadError","normalizeServerFnFailure","effect","validateOrigin","request","origin","undefined","Error","expectedHost","URL","readBody","prepareServerFnOperation","identity","action","args","invocation","match","TypeError","Promise","prepareClientServerFn","actionId","temporaryReferences","body","operation","prepared","outcome","prepareProgressiveServerFn","formData","decodedAction","execute","actionResult","error","decodedFormState","prepareServerFnRequest","signal","webRequest","prepareRequest"],"mappings":"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"}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsupported.js","sources":["../src/unsupported.ts"],"sourcesContent":["throw new TypeError(\n 'The \"effective-rsc\" package can only be imported in a React Server Components environment.',\n);\n"],"names":["TypeError"],"mappings":"AAAA,MAAM,IAAIA,UACR"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title A minimal application
|
|
3
|
+
*
|
|
4
|
+
* Create values from one ERSC identity and close it with ERSC.make.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect } from 'effect';
|
|
7
|
+
import { Application } from 'effective-rsc';
|
|
8
|
+
|
|
9
|
+
const ERSC = Application.ersc();
|
|
10
|
+
|
|
11
|
+
const RootLayout = ERSC.Layout.make({
|
|
12
|
+
render: ({ children }) =>
|
|
13
|
+
Effect.succeed(
|
|
14
|
+
<html lang='en'>
|
|
15
|
+
<body>{children}</body>
|
|
16
|
+
</html>,
|
|
17
|
+
),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const HomePage = ERSC.Page.make({
|
|
21
|
+
render: () => Effect.succeed(<h1>Hello from effective-rsc</h1>),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default ERSC.make({
|
|
25
|
+
routes: ERSC.Routes.make({ layout: RootLayout }).page('/', HomePage),
|
|
26
|
+
});
|