effective-rsc 0.1.1 → 0.1.3

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.
Files changed (42) hide show
  1. package/LLMS.md +74 -3
  2. package/README.md +12 -6
  3. package/dist/build/build.d.ts +4 -2
  4. package/dist/build/build.js +19 -2
  5. package/dist/build/build.js.map +1 -1
  6. package/dist/build/deployment.d.ts +10 -0
  7. package/dist/build/deployment.js +59 -0
  8. package/dist/build/deployment.js.map +1 -0
  9. package/dist/build/hook.d.ts +8 -0
  10. package/dist/build/hook.js +5 -0
  11. package/dist/build/hook.js.map +1 -0
  12. package/dist/build/rspack.js +1 -1
  13. package/dist/build/rspack.js.map +1 -1
  14. package/dist/cli.js +11 -22
  15. package/dist/cli.js.map +1 -1
  16. package/dist/client/application.js +8 -8
  17. package/dist/client/application.js.map +1 -1
  18. package/dist/client/browser-capabilities.d.ts +2 -9
  19. package/dist/client/browser-capabilities.js +3 -14
  20. package/dist/client/browser-capabilities.js.map +1 -1
  21. package/dist/client/navigation-api.js +5 -4
  22. package/dist/client/navigation-api.js.map +1 -1
  23. package/dist/dev/client.js +14 -1
  24. package/dist/dev/client.js.map +1 -1
  25. package/dist/dev/panel.d.ts +6 -0
  26. package/dist/dev/panel.js +55 -2
  27. package/dist/dev/panel.js.map +1 -1
  28. package/dist/server/html-renderer.js +12 -11
  29. package/dist/server/html-renderer.js.map +1 -1
  30. package/dist/server/serve.d.ts +7 -0
  31. package/dist/server/serve.js +31 -0
  32. package/dist/server/serve.js.map +1 -0
  33. package/dist/server/start.d.ts +3 -0
  34. package/dist/server/start.js +24 -0
  35. package/dist/server/start.js.map +1 -0
  36. package/docs/02-guides/06-deploying-to-vercel/index.md +34 -0
  37. package/docs/02-guides/index.md +1 -0
  38. package/docs/03-advanced/02-client-navigation/index.md +6 -3
  39. package/docs/03-advanced/04-production-startup/01_server.ts +11 -0
  40. package/docs/03-advanced/04-production-startup/index.md +22 -0
  41. package/docs/03-advanced/index.md +1 -0
  42. package/package.json +9 -2
@@ -1,6 +1,7 @@
1
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx } from "react/jsx-runtime";
2
2
  import { Context, Effect, FiberSet, Layer, Schema } from "effect";
3
3
  import { use } from "react";
4
+ import { preinit } from "react-dom";
4
5
  import { renderToReadableStream } from "react-dom/server.bun";
5
6
  import { createFromReadableStream } from "react-server-dom-rspack/client";
6
7
  import { RouteTree } from "../client/route-tree.js";
@@ -23,6 +24,8 @@ import { ServerConfig } from "./server-config.js";
23
24
 
24
25
 
25
26
 
27
+
28
+
26
29
  class HtmlRenderError extends Schema.TaggedError()('HtmlRenderError', {
27
30
  cause: Schema.Defect()
28
31
  }) {
@@ -38,22 +41,20 @@ class HtmlRenderer extends Context.Service()('ersc/server/html-renderer/HtmlRend
38
41
  const [ssrFlightStream, browserFlightStream] = flight.stream.tee();
39
42
  let payload = null;
40
43
  function SsrRoot() {
44
+ // Emit CSS without server-only siblings that shift hydration's useId paths.
45
+ for (const href of clientStylesheets){
46
+ preinit(href, {
47
+ as: 'style',
48
+ precedence: 'default'
49
+ });
50
+ }
41
51
  const { routeTree } = use(payload ??= createFromReadableStream(ssrFlightStream));
42
52
  return /*#__PURE__*/ jsx(RouteTree, {
43
53
  root: routeTree
44
54
  });
45
55
  }
46
56
  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
+ try: ()=>renderToReadableStream(/*#__PURE__*/ jsx(SsrRoot, {}), {
57
58
  bootstrapScripts: [
58
59
  ...clientBootstrapScripts
59
60
  ],
@@ -1 +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"}
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 { preinit } from 'react-dom';\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 // Emit CSS without server-only siblings that shift hydration's useId paths.\n for (const href of clientStylesheets) {\n preinit(href, { as: 'style', precedence: 'default' });\n }\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(<SsrRoot />, {\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 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","preinit","renderToReadableStream","createFromReadableStream","RouteTree","FlightHtmlInjector","ServerConfig","HtmlRenderError","HtmlRenderer","clientBootstrapScripts","clientStylesheets","flightHtmlInjector","flight","formState","signal","runtime","ssrFlightStream","browserFlightStream","payload","SsrRoot","href","routeTree","htmlStream","error","errorInfo","cause"],"mappings":";;;;;;;;;;;;;;;;;;;;AAA8E;AAClD;AACQ;AAC0B;AACY;AAEzB;AAES;AAEX;AAIxC,MAAMY,eAAeA,SAASR,kBAAkB,GAAoB,mBAAmB;IAC5F,OAAOA,aAAa;AACtB;AAAI;AAEG,MAAMS,YAAYA,SAASb,eAAe,GAC/C,0CACA;IACE,MAAMC,UAAU,CAAC;QACf,MAAM,EAAEa,sBAAsB,EAAEC,iBAAiB,EAAE,GAAG,OAAOJ,YAAYA;QACzE,MAAMK,qBAAqB,OAAON,kBAAkBA;QAEpD,OAAO;YACL,QAAQT,SAAS,CAAC,uBAAuB,UAAW,EAClDgB,MAAM,EACNC,SAAS,EAIV;gBACC,MAAMC,SAAS,OAAOlB,kBAAkB;gBACxC,MAAMmB,UAAU,OAAOlB,2BAA2B;gBAClD,MAAM,CAACmB,iBAAiBC,oBAAoB,GAAGL,OAAO,MAAM,CAAC,GAAG;gBAChE,IAAIM,UAA6C;gBAEjD,SAASC;oBACP,4EAA4E;oBAC5E,KAAK,MAAMC,QAAQV,kBAAmB;wBACpCT,OAAOA,CAACmB,MAAM;4BAAE,IAAI;4BAAS,YAAY;wBAAU;oBACrD;oBACA,MAAM,EAAEC,SAAS,EAAE,GAAGrB,GAAGA,CACtBkB,YAAYf,wBAAwBA,CAAgBa;oBAEvD,qBAAO,IAACZ,SAASA;wBAAC,MAAMiB;;gBAC1B;gBAEA,MAAMC,aAAa,OAAO1B,iBAAiB,CAAC;oBAC1C,KAAK,IACHM,sBAAsBA,eAAC,IAACiB,cAAY;4BAClC,kBAAkB;mCAAIV;6BAAuB;4BAC7CI;4BACA,SAAS,CAACU,OAAOC;gCACf,IAAI,CAACV,OAAO,OAAO,IAAI,CAACF,OAAO,MAAM,CAAC,OAAO,EAAE;oCAC7C,KAAKG,QACHnB,eAAe,CAAC,uBAAuB2B,OAAOC,UAAU,cAAc;gCAE1E;4BACF;4BACAV;wBACF;oBACF,OAAO,CAACW,QAAU,IAAIlB,eAAeA,CAAC;4BAAEkB;wBAAM;gBAChD;gBAEA,OAAOH,WAAW,WAAW,CAACX,mBAAmB,MAAM,CAACM;YAC1D;QACF;IACF;AACF;IAEA,OAAgB,QAAQnB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACxD"}
@@ -0,0 +1,7 @@
1
+ import { Effect } from 'effect';
2
+ export type StartOptions = {
3
+ readonly hostname: string;
4
+ readonly port: number;
5
+ readonly root: string;
6
+ };
7
+ export declare const serve: (options: StartOptions) => Effect.Effect<void, import("../build/compiled-server.js").CompiledServerError | import("effect/Types").unhandled, import("effect/Scope").Scope>;
@@ -0,0 +1,31 @@
1
+ import { Effect, Layer } from "effect";
2
+ import { loadCompiledServer, makeRunnableServerLayer } from "../build/compiled-server.js";
3
+ import { EnvironmentConfig } from "../build/contract.js";
4
+
5
+ import * as __rspack_external__effect_platform_bun_BunServices_112addf2 from "@effect/platform-bun/BunServices";
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+ const resolveServerLayer = Effect.fnUntraced(function*({ hostname, port, root }) {
15
+ const bundle = yield* loadCompiledServer(root);
16
+ return yield* makeRunnableServerLayer({
17
+ bundle,
18
+ clientAssetsCacheControl: EnvironmentConfig.production.clientAssetsCacheControl,
19
+ clientOutputDir: EnvironmentConfig.production.clientOutputDir,
20
+ hostname,
21
+ port,
22
+ root
23
+ });
24
+ });
25
+ const serve = Effect.fn('ersc/server/serve')(function*(options) {
26
+ yield* Layer.build(Layer.unwrap(resolveServerLayer(options)).pipe(Layer.provide(__rspack_external__effect_platform_bun_BunServices_112addf2.layer)));
27
+ });
28
+
29
+ export { serve };
30
+
31
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server/serve.js","sources":["../../src/server/serve.ts"],"sourcesContent":["import * as BunServices from '@effect/platform-bun/BunServices';\nimport { Effect, Layer } from 'effect';\n\nimport { loadCompiledServer, makeRunnableServerLayer } from '../build/compiled-server';\nimport { EnvironmentConfig } from '../build/contract';\n\nexport type StartOptions = {\n readonly hostname: string;\n readonly port: number;\n readonly root: string;\n};\n\nconst resolveServerLayer = Effect.fnUntraced(function* ({ hostname, port, root }: StartOptions) {\n const bundle = yield* loadCompiledServer(root);\n return yield* makeRunnableServerLayer({\n bundle,\n clientAssetsCacheControl: EnvironmentConfig.production.clientAssetsCacheControl,\n clientOutputDir: EnvironmentConfig.production.clientOutputDir,\n hostname,\n port,\n root,\n });\n});\n\nexport const serve = Effect.fn('ersc/server/serve')(function* (options: StartOptions) {\n yield* Layer.build(\n Layer.unwrap(resolveServerLayer(options)).pipe(Layer.provide(BunServices.layer)),\n );\n});\n"],"names":["BunServices","Effect","Layer","loadCompiledServer","makeRunnableServerLayer","EnvironmentConfig","resolveServerLayer","hostname","port","root","bundle","serve","options"],"mappings":";;;;;;;;;AAAgE;AACzB;AAEgD;AACjC;AAQtD,MAAMM,kBAAkBA,GAAGL,iBAAiB,CAAC,UAAW,EAAEM,QAAQ,EAAEC,IAAI,EAAEC,IAAI,EAAgB;IAC5F,MAAMC,SAAS,OAAOP,kBAAkBA,CAACM;IACzC,OAAO,OAAOL,uBAAuBA,CAAC;QACpCM;QACA,0BAA0BL,qDAAqD;QAC/E,iBAAiBA,4CAA4C;QAC7DE;QACAC;QACAC;IACF;AACF;AAEO,MAAME,KAAKA,GAAGV,SAAS,CAAC,qBAAqB,UAAWW,OAAqB;IAClF,OAAOV,WAAW,CAChBA,YAAY,CAACI,kBAAkBA,CAACM,UAAU,IAAI,CAACV,aAAa,CAACF,iEAAiB;AAElF,GAAG"}
@@ -0,0 +1,3 @@
1
+ import { type StartOptions } from './serve.js';
2
+ export type { StartOptions } from './serve.js';
3
+ export declare const start: (options: StartOptions) => Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { Deferred, Effect, Runtime } from "effect";
2
+ import { serve } from "./serve.js";
3
+
4
+ import * as __rspack_external__effect_platform_bun_BunRuntime_e8fbdd23 from "@effect/platform-bun/BunRuntime";
5
+
6
+
7
+
8
+
9
+
10
+
11
+ const start = (options)=>{
12
+ const ready = Deferred.makeUnsafe();
13
+ __rspack_external__effect_platform_bun_BunRuntime_e8fbdd23.runMain(serve(options).pipe(Effect.tap(()=>Deferred.succeed(ready, undefined)), Effect.andThen(Effect.never), Effect.scoped, Effect.onExit((exit)=>Deferred.done(ready, exit))), {
14
+ // Let the caller observe readiness rejection before runMain exits the process.
15
+ teardown: (exit, onExit)=>{
16
+ setImmediate(()=>Runtime.defaultTeardown(exit, onExit));
17
+ }
18
+ });
19
+ return Effect.runPromise(Deferred["await"](ready));
20
+ };
21
+
22
+ export { start };
23
+
24
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server/start.js","sources":["../../src/server/start.ts"],"sourcesContent":["import * as BunRuntime from '@effect/platform-bun/BunRuntime';\nimport { Deferred, Effect, Runtime } from 'effect';\n\nimport { serve, type StartOptions } from './serve';\n\nexport type { StartOptions } from './serve';\n\nexport const start = (options: StartOptions): Promise<void> => {\n const ready = Deferred.makeUnsafe<void, Effect.Error<ReturnType<typeof serve>>>();\n\n BunRuntime.runMain(\n serve(options).pipe(\n Effect.tap(() => Deferred.succeed(ready, undefined)),\n Effect.andThen(Effect.never),\n Effect.scoped,\n Effect.onExit((exit) => Deferred.done(ready, exit)),\n ),\n {\n // Let the caller observe readiness rejection before runMain exits the process.\n teardown: (exit, onExit) => {\n setImmediate(() => Runtime.defaultTeardown(exit, onExit));\n },\n },\n );\n\n return Effect.runPromise(Deferred.await(ready));\n};\n"],"names":["BunRuntime","Deferred","Effect","Runtime","serve","start","options","ready","undefined","exit","onExit","setImmediate"],"mappings":";;;;;;;AAA8D;AACX;AAEA;AAI5C,MAAMK,KAAKA,GAAG,CAACC;IACpB,MAAMC,QAAQN,mBAAmB;IAEjCD,kEAAkB,CAChBI,KAAKA,CAACE,SAAS,IAAI,CACjBJ,UAAU,CAAC,IAAMD,gBAAgB,CAACM,OAAOC,aACzCN,cAAc,CAACA,YAAY,GAC3BA,aAAa,EACbA,aAAa,CAAC,CAACO,OAASR,aAAa,CAACM,OAAOE,SAE/C;QACE,+EAA+E;QAC/E,UAAU,CAACA,MAAMC;YACfC,aAAa,IAAMR,uBAAuB,CAACM,MAAMC;QACnD;IACF;IAGF,OAAOR,iBAAiB,CAACD,iBAAc,CAACM;AAC1C,EAAE"}
@@ -0,0 +1,34 @@
1
+ ### Deploying to Vercel
2
+
3
+ Deploy to Vercel with Bun 1.4+. Match the adapter version to `effective-rsc`.
4
+
5
+ ```sh
6
+ bun add --dev @ersc/vercel
7
+ ```
8
+
9
+ Set the build script in `package.json`:
10
+
11
+ ```json
12
+ {
13
+ "scripts": {
14
+ "build": "ersc build --adapter @ersc/vercel"
15
+ }
16
+ }
17
+ ```
18
+
19
+ Commit `bun.lock`, connect your GitHub repository in Vercel, and set:
20
+
21
+ | Setting | Value |
22
+ | ---------------- | ------------------------------- |
23
+ | Framework Preset | Other |
24
+ | Root Directory | Application directory |
25
+ | Install Command | `bun install --frozen-lockfile` |
26
+ | Build Command | `bun run --bun build` |
27
+ | Output Directory | Leave the override disabled |
28
+
29
+ Add your environment variables and deploy. The adapter generates `.vercel/output/`; no custom
30
+ server entry or `vercel.json` is needed.
31
+
32
+ For [monorepos](https://vercel.com/docs/monorepos/monorepo-faq), enable **Include source files outside
33
+ of the Root Directory in the Build Step**. With Turborepo, use
34
+ `bun run --bun turbo run build --filter=your-app` to build the app and its workspace dependencies.
@@ -9,3 +9,4 @@ Familiarity with React Server Components and Effect is assumed.
9
9
  - [Routing](./03-routing/index.md)
10
10
  - [Middleware](./04-middleware/index.md)
11
11
  - [Userland HTTP](./05-http/index.md)
12
+ - [Deploying to Vercel](./06-deploying-to-vercel/index.md)
@@ -2,9 +2,12 @@
2
2
 
3
3
  ERSC handles eligible document navigations through the browser Navigation API and
4
4
  `NavigationPrecommitController`. There is no History API fallback. A browser missing either one
5
- never hydrates: the streamed document stays as served and the application behaves as a plain
6
- multi-page application, with document navigations and natively submitted forms but no interactive
7
- Client Components.
5
+ still hydrates Client Components and supports Server Functions and streamed current-page refreshes,
6
+ including HMR. Links use full-page navigation instead of the client router. Without JavaScript,
7
+ the server-rendered document retains working links and natively submitted forms.
8
+
9
+ Development reports a missing navigation API in the console and a dismissible development-panel
10
+ warning. The warning does not block interaction and is absent in production.
8
11
 
9
12
  An intercepted Page navigation has two milestones:
10
13
 
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @title Server entry
3
+ * Save this as `server.ts` in the application root and run it with `bun server.ts` after building.
4
+ */
5
+ import { start } from 'effective-rsc/server';
6
+
7
+ await start({
8
+ hostname: 'localhost',
9
+ port: 18193,
10
+ root: import.meta.dir,
11
+ });
@@ -0,0 +1,22 @@
1
+ ## Production startup
2
+
3
+ Run `ersc build`, then `ersc start`. A custom Bun entry can await
4
+ `start({ root, hostname, port })` from `effective-rsc/server`. All options are required;
5
+ `root` is the application directory. Deploy its `.ersc/`, `public/`, and runtime dependencies.
6
+
7
+ The Promise resolves when ready; startup failures reject and exit. ERSC owns signal handling
8
+ and cleanup, so do not wrap it in `BunRuntime.runMain`.
9
+
10
+ ### Deployment adapters
11
+
12
+ `ersc build --adapter <package>` runs an installed adapter after compilation; it does not upload.
13
+ Without the flag, packaging is skipped and previous output remains.
14
+
15
+ Adapters export `build: BuildHook` from `./build`, with types from `effective-rsc/build`.
16
+ The hook receives absolute `root`, `serverDir`, `clientDir`, and `publicDir` paths and returns
17
+ `Effect<void, Error, Scope>`. Inputs are read-only; adapters provide dependencies and ERSC owns
18
+ cleanup/cancellation. Failures stop the build.
19
+
20
+ <!-- source-navigation -->
21
+
22
+ - [Deploying to Vercel](../../02-guides/06-deploying-to-vercel/index.md)
@@ -9,3 +9,4 @@ before adopting them.
9
9
  - [Request runtime and lifetimes](./01-request-runtime-and-lifetimes/index.md)
10
10
  - [Client navigation](./02-client-navigation/index.md)
11
11
  - [Server Function execution and refresh](./03-server-function-execution-and-refresh/index.md)
12
+ - [Production startup](./04-production-startup/index.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effective-rsc",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "An experimental, Effect-native React Server Components framework for Bun.",
5
5
  "keywords": [
6
6
  "bun",
@@ -37,6 +37,14 @@
37
37
  "types": "./dist/index.d.ts",
38
38
  "react-server": "./dist/index.js",
39
39
  "default": "./dist/unsupported.js"
40
+ },
41
+ "./server": {
42
+ "types": "./dist/server/start.d.ts",
43
+ "default": "./dist/server/start.js"
44
+ },
45
+ "./build": {
46
+ "types": "./dist/build/hook.d.ts",
47
+ "default": "./dist/build/hook.js"
40
48
  }
41
49
  },
42
50
  "publishConfig": {
@@ -47,7 +55,6 @@
47
55
  "docs:check": "bun ../../scripts/generate-docs.ts --check",
48
56
  "prepack": "bun run build && bun run docs:check",
49
57
  "test": "bun run --bun vitest run",
50
- "test:types": "tsc --noEmit -p tests/types/tsconfig.json",
51
58
  "test:watch": "bun run --bun vitest"
52
59
  },
53
60
  "dependencies": {