effective-rsc 0.1.0 → 0.1.2

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 (59) hide show
  1. package/LLMS.md +54 -10
  2. package/README.md +3 -3
  3. package/dist/application/page.d.ts +14 -7
  4. package/dist/application/page.js +5 -3
  5. package/dist/application/page.js.map +1 -1
  6. package/dist/application/route-graph.d.ts +1 -1
  7. package/dist/application/route-graph.js.map +1 -1
  8. package/dist/application/server-fn.d.ts +10 -6
  9. package/dist/application/server-fn.js +9 -4
  10. package/dist/application/server-fn.js.map +1 -1
  11. package/dist/build/dev.d.ts +11 -11
  12. package/dist/build/dev.js +21 -5
  13. package/dist/build/dev.js.map +1 -1
  14. package/dist/cli.js +2 -0
  15. package/dist/cli.js.map +1 -1
  16. package/dist/client/application.js +12 -9
  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/browser-render-status.d.ts +25 -0
  22. package/dist/client/browser-render-status.js +20 -0
  23. package/dist/client/browser-render-status.js.map +1 -0
  24. package/dist/client/call-server.js +7 -2
  25. package/dist/client/call-server.js.map +1 -1
  26. package/dist/client/navigation-api.js +5 -4
  27. package/dist/client/navigation-api.js.map +1 -1
  28. package/dist/client/react-dom-renderer.d.ts +3 -2
  29. package/dist/client/react-dom-renderer.js +43 -2
  30. package/dist/client/react-dom-renderer.js.map +1 -1
  31. package/dist/client/route-refresh.d.ts +1 -2
  32. package/dist/client/route-refresh.js +5 -18
  33. package/dist/client/route-refresh.js.map +1 -1
  34. package/dist/dev/client.d.ts +2 -1
  35. package/dist/dev/client.js +46 -6
  36. package/dist/dev/client.js.map +1 -1
  37. package/dist/dev/panel.d.ts +12 -1
  38. package/dist/dev/panel.js +73 -5
  39. package/dist/dev/panel.js.map +1 -1
  40. package/dist/dev/runtime-failure.d.ts +1 -0
  41. package/dist/dev/runtime-failure.js +7 -1
  42. package/dist/dev/runtime-failure.js.map +1 -1
  43. package/dist/rsc/render-route-tree.d.ts +3 -3
  44. package/dist/rsc/render-route-tree.js +2 -2
  45. package/dist/rsc/render-route-tree.js.map +1 -1
  46. package/dist/server/application.js +54 -35
  47. package/dist/server/application.js.map +1 -1
  48. package/dist/server/server-fn-request.js +3 -1
  49. package/dist/server/server-fn-request.js.map +1 -1
  50. package/docs/02-guides/01-server-functions/50_greet.ts +18 -0
  51. package/docs/02-guides/01-server-functions/60_greeting-form.tsx +23 -0
  52. package/docs/02-guides/01-server-functions/index.md +7 -0
  53. package/docs/02-guides/03-routing/index.md +5 -1
  54. package/docs/03-advanced/02-client-navigation/index.md +6 -3
  55. package/docs/03-advanced/03-server-function-execution-and-refresh/index.md +3 -1
  56. package/docs/04-api-reference/02-page/index.md +8 -3
  57. package/docs/04-api-reference/06-middleware/index.md +2 -2
  58. package/docs/04-api-reference/08-server-fn/index.md +23 -0
  59. package/package.json +1 -1
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["import * as BunHttpServer from '@effect/platform-bun/BunHttpServer';\nimport * as BunRuntime from '@effect/platform-bun/BunRuntime';\nimport * as BunServices from '@effect/platform-bun/BunServices';\nimport { Config, Effect, Layer, Schema } from 'effect';\nimport * as Command from 'effect/unstable/cli/Command';\nimport * as Flag from 'effect/unstable/cli/Flag';\n\nimport PackageJson from '../package.json' with { type: 'json' };\nimport { loadCompiledServer, makeRunnableServerLayer } from './build/compiled-server';\nimport { EnvironmentConfig } from './build/contract';\nimport {\n ApplicationIdleTimeoutSeconds,\n ApplicationMaxRequestBodySizeBytes,\n DefaultApplicationHostname,\n DefaultApplicationPort,\n} from './server/server-config';\n\nexport class BuildModuleLoadError extends Schema.TaggedError<BuildModuleLoadError>()(\n 'BuildModuleLoadError',\n {\n message: Schema.String,\n cause: Schema.Defect(),\n },\n) {}\n\nexport class DevModuleLoadError extends Schema.TaggedError<DevModuleLoadError>()(\n 'DevModuleLoadError',\n {\n message: Schema.String,\n cause: Schema.Defect(),\n },\n) {}\n\nconst start = Effect.fn('ersc/cli/start')(function* ({\n hostname,\n port,\n root,\n}: {\n readonly hostname: string;\n readonly port: number;\n readonly root: string;\n}) {\n const bundle = yield* loadCompiledServer(root);\n const ServerLayer = yield* makeRunnableServerLayer({\n bundle,\n clientAssetsCacheControl: EnvironmentConfig.production.clientAssetsCacheControl,\n clientOutputDir: EnvironmentConfig.production.clientOutputDir,\n hostname,\n port,\n root,\n });\n\n return yield* Layer.launch(ServerLayer);\n});\n\nconst runBuild = Effect.fnUntraced(function* () {\n const { buildApplication } = yield* Effect.tryPromise({\n try: () => import('./build/build'),\n catch: (cause) =>\n new BuildModuleLoadError({\n message: 'Failed to load the effective-rsc application compiler.',\n cause,\n }),\n });\n\n yield* buildApplication({ root: process.cwd() });\n});\n\nconst buildCommand = Command.make('build').pipe(\n Command.withDescription('Compile an effective-rsc application with Rspack.'),\n Command.withHandler(runBuild),\n);\n\nconst hostname = Flag.string('hostname').pipe(\n Flag.withDescription('Hostname to bind (defaults to HOST or localhost)'),\n Flag.withFallbackConfig(\n Config.string('HOST').pipe(Config.withDefault(DefaultApplicationHostname)),\n ),\n Flag.withSchema(Schema.NonEmptyString),\n);\n\nconst port = Flag.integer('port').pipe(\n Flag.withDescription(`Port to bind (defaults to PORT or ${DefaultApplicationPort})`),\n Flag.withFallbackConfig(Config.int('PORT').pipe(Config.withDefault(DefaultApplicationPort))),\n Flag.withSchema(\n Schema.Int.check(\n Schema.isBetween({\n minimum: 1,\n maximum: 65_535,\n }),\n ),\n ),\n);\n\nconst runDev = Effect.fnUntraced(function* ({\n hostname,\n port,\n}: {\n readonly hostname: string;\n readonly port: number;\n}) {\n const { devApplication } = yield* Effect.tryPromise({\n try: () => import('./build/dev'),\n catch: (cause) =>\n new DevModuleLoadError({\n message: 'Failed to load the effective-rsc development compiler.',\n cause,\n }),\n });\n\n yield* devApplication({\n hostname,\n port,\n root: process.cwd(),\n }).pipe(\n Effect.provide(\n BunHttpServer.layer({\n development: true,\n hostname,\n idleTimeout: ApplicationIdleTimeoutSeconds,\n maxRequestBodySize: ApplicationMaxRequestBodySizeBytes,\n port,\n }),\n ),\n Effect.scoped,\n );\n});\n\nconst devCommand = Command.make('dev', { hostname, port }).pipe(\n Command.withDescription('Start an effective-rsc application in development mode.'),\n Command.withHandler(runDev),\n);\n\nconst startCommand = Command.make('start', { hostname, port }).pipe(\n Command.withDescription('Start the compiled application with Bun.'),\n Command.withHandler(({ hostname, port }) => start({ hostname, port, root: process.cwd() })),\n);\n\nconst cli = Command.make('ersc').pipe(\n Command.withDescription('Build and run an effective-rsc application.'),\n Command.withSubcommands([devCommand, buildCommand, startCommand]),\n);\n\nconst program = Command.run(cli, { version: PackageJson.version }).pipe(\n Effect.provide(BunServices.layer),\n);\n\nBunRuntime.runMain(program);\n"],"names":["BunHttpServer","BunRuntime","BunServices","Config","Effect","Layer","Schema","Command","Flag","PackageJson","loadCompiledServer","makeRunnableServerLayer","EnvironmentConfig","ApplicationIdleTimeoutSeconds","ApplicationMaxRequestBodySizeBytes","DefaultApplicationHostname","DefaultApplicationPort","BuildModuleLoadError","DevModuleLoadError","start","hostname","port","root","bundle","ServerLayer","runBuild","buildApplication","cause","process","buildCommand","runDev","devApplication","devCommand","startCommand","cli","program"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["import * as BunHttpServer from '@effect/platform-bun/BunHttpServer';\nimport * as BunRuntime from '@effect/platform-bun/BunRuntime';\nimport * as BunServices from '@effect/platform-bun/BunServices';\nimport { Config, Effect, Layer, Schema } from 'effect';\nimport * as Command from 'effect/unstable/cli/Command';\nimport * as Flag from 'effect/unstable/cli/Flag';\n\nimport PackageJson from '../package.json' with { type: 'json' };\nimport { loadCompiledServer, makeRunnableServerLayer } from './build/compiled-server';\nimport { EnvironmentConfig } from './build/contract';\nimport {\n ApplicationIdleTimeoutSeconds,\n ApplicationMaxRequestBodySizeBytes,\n DefaultApplicationHostname,\n DefaultApplicationPort,\n} from './server/server-config';\n\nexport class BuildModuleLoadError extends Schema.TaggedError<BuildModuleLoadError>()(\n 'BuildModuleLoadError',\n {\n message: Schema.String,\n cause: Schema.Defect(),\n },\n) {}\n\nexport class DevModuleLoadError extends Schema.TaggedError<DevModuleLoadError>()(\n 'DevModuleLoadError',\n {\n message: Schema.String,\n cause: Schema.Defect(),\n },\n) {}\n\nconst start = Effect.fn('ersc/cli/start')(function* ({\n hostname,\n port,\n root,\n}: {\n readonly hostname: string;\n readonly port: number;\n readonly root: string;\n}) {\n const bundle = yield* loadCompiledServer(root);\n const ServerLayer = yield* makeRunnableServerLayer({\n bundle,\n clientAssetsCacheControl: EnvironmentConfig.production.clientAssetsCacheControl,\n clientOutputDir: EnvironmentConfig.production.clientOutputDir,\n hostname,\n port,\n root,\n });\n\n return yield* Layer.launch(ServerLayer);\n});\n\nconst runBuild = Effect.fnUntraced(function* () {\n const { buildApplication } = yield* Effect.tryPromise({\n try: () => import('./build/build'),\n catch: (cause) =>\n new BuildModuleLoadError({\n message: 'Failed to load the effective-rsc application compiler.',\n cause,\n }),\n });\n\n yield* buildApplication({ root: process.cwd() });\n});\n\nconst buildCommand = Command.make('build').pipe(\n Command.withDescription('Compile an effective-rsc application with Rspack.'),\n Command.withHandler(runBuild),\n);\n\nconst hostname = Flag.string('hostname').pipe(\n Flag.withDescription('Hostname to bind (defaults to HOST or localhost)'),\n Flag.withFallbackConfig(\n Config.string('HOST').pipe(Config.withDefault(DefaultApplicationHostname)),\n ),\n Flag.withSchema(Schema.NonEmptyString),\n);\n\nconst port = Flag.integer('port').pipe(\n Flag.withDescription(`Port to bind (defaults to PORT or ${DefaultApplicationPort})`),\n Flag.withFallbackConfig(Config.int('PORT').pipe(Config.withDefault(DefaultApplicationPort))),\n Flag.withSchema(\n Schema.Int.check(\n Schema.isBetween({\n minimum: 1,\n maximum: 65_535,\n }),\n ),\n ),\n);\n\nconst runDev = Effect.fnUntraced(function* ({\n hostname,\n port,\n}: {\n readonly hostname: string;\n readonly port: number;\n}) {\n const { devApplication } = yield* Effect.tryPromise({\n try: () => import('./build/dev'),\n catch: (cause) =>\n new DevModuleLoadError({\n message: 'Failed to load the effective-rsc development compiler.',\n cause,\n }),\n });\n\n yield* devApplication({\n hostname,\n port,\n root: process.cwd(),\n }).pipe(\n Effect.provide(\n BunHttpServer.layer({\n development: true,\n // Explicit dev shutdown interrupts request scopes before releasing their generations.\n disablePreemptiveShutdown: true,\n hostname,\n idleTimeout: ApplicationIdleTimeoutSeconds,\n maxRequestBodySize: ApplicationMaxRequestBodySizeBytes,\n port,\n }),\n ),\n Effect.scoped,\n );\n});\n\nconst devCommand = Command.make('dev', { hostname, port }).pipe(\n Command.withDescription('Start an effective-rsc application in development mode.'),\n Command.withHandler(runDev),\n);\n\nconst startCommand = Command.make('start', { hostname, port }).pipe(\n Command.withDescription('Start the compiled application with Bun.'),\n Command.withHandler(({ hostname, port }) => start({ hostname, port, root: process.cwd() })),\n);\n\nconst cli = Command.make('ersc').pipe(\n Command.withDescription('Build and run an effective-rsc application.'),\n Command.withSubcommands([devCommand, buildCommand, startCommand]),\n);\n\nconst program = Command.run(cli, { version: PackageJson.version }).pipe(\n Effect.provide(BunServices.layer),\n);\n\nBunRuntime.runMain(program);\n"],"names":["BunHttpServer","BunRuntime","BunServices","Config","Effect","Layer","Schema","Command","Flag","PackageJson","loadCompiledServer","makeRunnableServerLayer","EnvironmentConfig","ApplicationIdleTimeoutSeconds","ApplicationMaxRequestBodySizeBytes","DefaultApplicationHostname","DefaultApplicationPort","BuildModuleLoadError","DevModuleLoadError","start","hostname","port","root","bundle","ServerLayer","runBuild","buildApplication","cause","process","buildCommand","runDev","devApplication","devCommand","startCommand","cli","program"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { Effect, Layer } from "effect";
2
- import { checkBrowserCapabilities } from "./browser-capabilities.js";
2
+ import { navigationMode } from "./browser-capabilities.js";
3
3
  import { BrowserEffectRunner } from "./browser-effect-runner.js";
4
+ import { BrowserRenderStatus } from "./browser-render-status.js";
4
5
  import { BrowserRenderer } from "./browser-renderer.js";
5
6
  import { showBrowserFailure } from "./browser-screen.js";
6
7
  import { installCallServer } from "./call-server.js";
@@ -27,6 +28,7 @@ import * as __rspack_external__effect_platform_browser_BrowserHttpClient_04160dc
27
28
 
28
29
 
29
30
 
31
+
30
32
  // oxlint-disable effecttsgo/process-env, effecttsgo/process-env-in-effect -- Rspack replaces NODE_ENV at compile time.
31
33
 
32
34
 
@@ -42,28 +44,29 @@ import * as __rspack_external__effect_platform_browser_BrowserHttpClient_04160dc
42
44
 
43
45
 
44
46
 
45
- const BrowserServicesLayer = Layer.mergeAll(BrowserEffectRunner.layer, BrowserRenderer.layer, FlightClient.layer, NavigationApi.layer).pipe(Layer.provide(InitialFlightStream.layer));
47
+
48
+ const BrowserServicesLayer = Layer.mergeAll(BrowserEffectRunner.layer, BrowserRenderer.layer, BrowserRenderStatus.layer, FlightClient.layer, NavigationApi.layer).pipe(Layer.provide(InitialFlightStream.layer));
46
49
  const BrowserLayer = Layer.mergeAll(ReactDOMRenderer.layer, RouteLoader.layer, RouteRefresher.layer).pipe(Layer.provideMerge(BrowserServicesLayer), Layer.provide(__rspack_external__effect_platform_browser_BrowserHttpClient_04160dce.layerFetch));
47
50
  const renderBrowserFailure = Effect.sync(showBrowserFailure);
48
- const skipHydration = (missingApi)=>process.env.NODE_ENV === 'development' ? Effect.logWarning(`effective-rsc did not hydrate because this browser does not provide ${missingApi}. The server-rendered document remains a plain multi-page application.`) : Effect["void"];
49
- const activateClientNavigation = Effect.gen(function*() {
51
+ const activateBrowser = Effect.gen(function*() {
50
52
  const routeLoader = yield* RouteLoader;
51
53
  const reactDOMRenderer = yield* ReactDOMRenderer;
52
54
  const initialPayload = yield* routeLoader.loadInitial;
53
55
  yield* reactDOMRenderer.hydrate(document, initialPayload);
54
- yield* installClientRouter;
55
56
  yield* installRouteRefresh;
56
57
  yield* installCallServer;
58
+ const mode = yield* navigationMode;
59
+ if (mode === 'Client') {
60
+ yield* installClientRouter;
61
+ }
57
62
  });
58
63
  const browserMain = Effect.scoped(Effect.gen(function*() {
59
64
  if (process.env.NODE_ENV === 'development') {
60
65
  yield* Effect.tryPromise(()=>import("../dev/client.js")).pipe(Effect.flatMap(({ startDevClient })=>startDevClient), Effect["catch"]((cause)=>Effect.logError('Development client stopped.', cause)), Effect.forkScoped);
61
66
  }
62
- yield* checkBrowserCapabilities.pipe(Effect.andThen(activateClientNavigation), Effect.catchTags({
67
+ yield* activateBrowser.pipe(Effect.catchTags({
63
68
  FlightLoadError: ()=>renderBrowserFailure,
64
- ReactDOMHydrationError: ()=>renderBrowserFailure,
65
- NavigationApiUnavailableError: ()=>skipHydration('the Navigation API'),
66
- NavigationPrecommitUnavailableError: ()=>skipHydration('NavigationPrecommitController')
69
+ ReactDOMHydrationError: ()=>renderBrowserFailure
67
70
  }));
68
71
  return yield* Effect.never;
69
72
  })).pipe(Effect.provide(BrowserLayer));
@@ -1 +1 @@
1
- {"version":3,"file":"client/application.js","sources":["../../src/client/application.ts"],"sourcesContent":["// oxlint-disable effecttsgo/process-env, effecttsgo/process-env-in-effect -- Rspack replaces NODE_ENV at compile time.\nimport * as BrowserHttpClient from '@effect/platform-browser/BrowserHttpClient';\nimport { Effect, Layer } from 'effect';\n\nimport { checkBrowserCapabilities } from './browser-capabilities';\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderer } from './browser-renderer';\nimport { showBrowserFailure } from './browser-screen';\nimport { installCallServer } from './call-server';\nimport { installClientRouter } from './client-router';\nimport { FlightClient } from './flight-client';\nimport { InitialFlightStream } from './initial-flight-stream';\nimport { NavigationApi } from './navigation-api';\nimport { ReactDOMRenderer } from './react-dom-renderer';\nimport { RouteLoader } from './route-loader';\nimport { installRouteRefresh, RouteRefresher } from './route-refresh';\n\nconst BrowserServicesLayer = Layer.mergeAll(\n BrowserEffectRunner.layer,\n BrowserRenderer.layer,\n FlightClient.layer,\n NavigationApi.layer,\n).pipe(Layer.provide(InitialFlightStream.layer));\n\nconst BrowserLayer = Layer.mergeAll(\n ReactDOMRenderer.layer,\n RouteLoader.layer,\n RouteRefresher.layer,\n).pipe(Layer.provideMerge(BrowserServicesLayer), Layer.provide(BrowserHttpClient.layerFetch));\n\nconst renderBrowserFailure = Effect.sync(showBrowserFailure);\n\nconst skipHydration = (missingApi: string) =>\n process.env.NODE_ENV === 'development'\n ? Effect.logWarning(\n `effective-rsc did not hydrate because this browser does not provide ${missingApi}. The server-rendered document remains a plain multi-page application.`,\n )\n : Effect.void;\n\nconst activateClientNavigation = Effect.gen(function* () {\n const routeLoader = yield* RouteLoader;\n const reactDOMRenderer = yield* ReactDOMRenderer;\n\n const initialPayload = yield* routeLoader.loadInitial;\n yield* reactDOMRenderer.hydrate(document, initialPayload);\n yield* installClientRouter;\n yield* installRouteRefresh;\n yield* installCallServer;\n});\n\nexport const browserMain = Effect.scoped(\n Effect.gen(function* () {\n if (process.env.NODE_ENV === 'development') {\n yield* Effect.tryPromise(() => import('../dev/client')).pipe(\n Effect.flatMap(({ startDevClient }) => startDevClient),\n Effect.catch((cause) => Effect.logError('Development client stopped.', cause)),\n Effect.forkScoped,\n );\n }\n\n yield* checkBrowserCapabilities.pipe(\n Effect.andThen(activateClientNavigation),\n Effect.catchTags({\n FlightLoadError: () => renderBrowserFailure,\n ReactDOMHydrationError: () => renderBrowserFailure,\n NavigationApiUnavailableError: () => skipHydration('the Navigation API'),\n NavigationPrecommitUnavailableError: () => skipHydration('NavigationPrecommitController'),\n }),\n );\n return yield* Effect.never;\n }),\n).pipe(Effect.provide(BrowserLayer));\n"],"names":["BrowserHttpClient","Effect","Layer","checkBrowserCapabilities","BrowserEffectRunner","BrowserRenderer","showBrowserFailure","installCallServer","installClientRouter","FlightClient","InitialFlightStream","NavigationApi","ReactDOMRenderer","RouteLoader","installRouteRefresh","RouteRefresher","BrowserServicesLayer","BrowserLayer","renderBrowserFailure","skipHydration","missingApi","process","activateClientNavigation","routeLoader","reactDOMRenderer","initialPayload","document","browserMain","startDevClient","cause"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uHAAuH;AACvC;AACzC;AAE2B;AACJ;AACT;AACC;AACJ;AACI;AACP;AACe;AACb;AACO;AACX;AACyB;AAEtE,MAAMgB,oBAAoBA,GAAGd,cAAc,CACzCE,yBAAyB,EACzBC,qBAAqB,EACrBI,kBAAkB,EAClBE,mBAAmB,EACnB,IAAI,CAACT,aAAa,CAACQ,yBAAyB;AAE9C,MAAMO,YAAYA,GAAGf,cAAc,CACjCU,sBAAsB,EACtBC,iBAAiB,EACjBE,oBAAoB,EACpB,IAAI,CAACb,kBAAkB,CAACc,oBAAoBA,GAAGd,aAAa,CAACF,gFAA4B;AAE3F,MAAMkB,oBAAoBA,GAAGjB,WAAW,CAACK,kBAAkBA;AAE3D,MAAMa,aAAaA,GAAG,CAACC,aACrBC,QAAQ,GAAG,CAAC,QAAQ,KAAK,gBACrBpB,iBAAiB,CACf,CAAC,oEAAoE,EAAEmB,WAAW,sEAAsE,CAAC,IAE3JnB,cAAW;AAEjB,MAAMqB,wBAAwBA,GAAGrB,UAAU,CAAC;IAC1C,MAAMsB,cAAc,OAAOV,WAAWA;IACtC,MAAMW,mBAAmB,OAAOZ,gBAAgBA;IAEhD,MAAMa,iBAAiB,OAAOF,YAAY,WAAW;IACrD,OAAOC,iBAAiB,OAAO,CAACE,UAAUD;IAC1C,OAAOjB,mBAAmBA;IAC1B,OAAOM,mBAAmBA;IAC1B,OAAOP,iBAAiBA;AAC1B;AAEO,MAAMoB,WAAWA,GAAG1B,aAAa,CACtCA,UAAU,CAAC;IACT,IAAIoB,QAAQ,GAAG,CAAC,QAAQ,KAAK,eAAe;QAC1C,OAAOpB,iBAAiB,CAAC,IAAM,0BAAuB,EAAE,IAAI,CAC1DA,cAAc,CAAC,CAAC,EAAE2B,cAAc,EAAE,GAAKA,iBACvC3B,eAAY,CAAC,CAAC4B,QAAU5B,eAAe,CAAC,+BAA+B4B,SACvE5B,iBAAiB;IAErB;IAEA,OAAOE,6BAA6B,CAClCF,cAAc,CAACqB,wBAAwBA,GACvCrB,gBAAgB,CAAC;QACf,iBAAiB,IAAMiB,oBAAoBA;QAC3C,wBAAwB,IAAMA,oBAAoBA;QAClD,+BAA+B,IAAMC,aAAaA,CAAC;QACnD,qCAAqC,IAAMA,aAAaA,CAAC;IAC3D;IAEF,OAAO,OAAOlB,YAAY;AAC5B,IACA,IAAI,CAACA,cAAc,CAACgB,YAAYA,GAAG"}
1
+ {"version":3,"file":"client/application.js","sources":["../../src/client/application.ts"],"sourcesContent":["// oxlint-disable effecttsgo/process-env, effecttsgo/process-env-in-effect -- Rspack replaces NODE_ENV at compile time.\nimport * as BrowserHttpClient from '@effect/platform-browser/BrowserHttpClient';\nimport { Effect, Layer } from 'effect';\n\nimport { navigationMode } from './browser-capabilities';\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderStatus } from './browser-render-status';\nimport { BrowserRenderer } from './browser-renderer';\nimport { showBrowserFailure } from './browser-screen';\nimport { installCallServer } from './call-server';\nimport { installClientRouter } from './client-router';\nimport { FlightClient } from './flight-client';\nimport { InitialFlightStream } from './initial-flight-stream';\nimport { NavigationApi } from './navigation-api';\nimport { ReactDOMRenderer } from './react-dom-renderer';\nimport { RouteLoader } from './route-loader';\nimport { installRouteRefresh, RouteRefresher } from './route-refresh';\n\nconst BrowserServicesLayer = Layer.mergeAll(\n BrowserEffectRunner.layer,\n BrowserRenderer.layer,\n BrowserRenderStatus.layer,\n FlightClient.layer,\n NavigationApi.layer,\n).pipe(Layer.provide(InitialFlightStream.layer));\n\nconst BrowserLayer = Layer.mergeAll(\n ReactDOMRenderer.layer,\n RouteLoader.layer,\n RouteRefresher.layer,\n).pipe(Layer.provideMerge(BrowserServicesLayer), Layer.provide(BrowserHttpClient.layerFetch));\n\nconst renderBrowserFailure = Effect.sync(showBrowserFailure);\n\nconst activateBrowser = Effect.gen(function* () {\n const routeLoader = yield* RouteLoader;\n const reactDOMRenderer = yield* ReactDOMRenderer;\n\n const initialPayload = yield* routeLoader.loadInitial;\n yield* reactDOMRenderer.hydrate(document, initialPayload);\n yield* installRouteRefresh;\n yield* installCallServer;\n const mode = yield* navigationMode;\n if (mode === 'Client') {\n yield* installClientRouter;\n }\n});\n\nexport const browserMain = Effect.scoped(\n Effect.gen(function* () {\n if (process.env.NODE_ENV === 'development') {\n yield* Effect.tryPromise(() => import('../dev/client')).pipe(\n Effect.flatMap(({ startDevClient }) => startDevClient),\n Effect.catch((cause) => Effect.logError('Development client stopped.', cause)),\n Effect.forkScoped,\n );\n }\n\n yield* activateBrowser.pipe(\n Effect.catchTags({\n FlightLoadError: () => renderBrowserFailure,\n ReactDOMHydrationError: () => renderBrowserFailure,\n }),\n );\n return yield* Effect.never;\n }),\n).pipe(Effect.provide(BrowserLayer));\n"],"names":["BrowserHttpClient","Effect","Layer","navigationMode","BrowserEffectRunner","BrowserRenderStatus","BrowserRenderer","showBrowserFailure","installCallServer","installClientRouter","FlightClient","InitialFlightStream","NavigationApi","ReactDOMRenderer","RouteLoader","installRouteRefresh","RouteRefresher","BrowserServicesLayer","BrowserLayer","renderBrowserFailure","activateBrowser","routeLoader","reactDOMRenderer","initialPayload","document","mode","browserMain","process","startDevClient","cause"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uHAAuH;AACvC;AACzC;AAEiB;AACM;AACA;AACT;AACC;AACJ;AACI;AACP;AACe;AACb;AACO;AACX;AACyB;AAEtE,MAAMiB,oBAAoBA,GAAGf,cAAc,CACzCE,yBAAyB,EACzBE,qBAAqB,EACrBD,yBAAyB,EACzBK,kBAAkB,EAClBE,mBAAmB,EACnB,IAAI,CAACV,aAAa,CAACS,yBAAyB;AAE9C,MAAMO,YAAYA,GAAGhB,cAAc,CACjCW,sBAAsB,EACtBC,iBAAiB,EACjBE,oBAAoB,EACpB,IAAI,CAACd,kBAAkB,CAACe,oBAAoBA,GAAGf,aAAa,CAACF,gFAA4B;AAE3F,MAAMmB,oBAAoBA,GAAGlB,WAAW,CAACM,kBAAkBA;AAE3D,MAAMa,eAAeA,GAAGnB,UAAU,CAAC;IACjC,MAAMoB,cAAc,OAAOP,WAAWA;IACtC,MAAMQ,mBAAmB,OAAOT,gBAAgBA;IAEhD,MAAMU,iBAAiB,OAAOF,YAAY,WAAW;IACrD,OAAOC,iBAAiB,OAAO,CAACE,UAAUD;IAC1C,OAAOR,mBAAmBA;IAC1B,OAAOP,iBAAiBA;IACxB,MAAMiB,OAAO,OAAOtB,cAAcA;IAClC,IAAIsB,SAAS,UAAU;QACrB,OAAOhB,mBAAmBA;IAC5B;AACF;AAEO,MAAMiB,WAAWA,GAAGzB,aAAa,CACtCA,UAAU,CAAC;IACT,IAAI0B,QAAQ,GAAG,CAAC,QAAQ,KAAK,eAAe;QAC1C,OAAO1B,iBAAiB,CAAC,IAAM,0BAAuB,EAAE,IAAI,CAC1DA,cAAc,CAAC,CAAC,EAAE2B,cAAc,EAAE,GAAKA,iBACvC3B,eAAY,CAAC,CAAC4B,QAAU5B,eAAe,CAAC,+BAA+B4B,SACvE5B,iBAAiB;IAErB;IAEA,OAAOmB,eAAeA,CAAC,IAAI,CACzBnB,gBAAgB,CAAC;QACf,iBAAiB,IAAMkB,oBAAoBA;QAC3C,wBAAwB,IAAMA,oBAAoBA;IACpD;IAEF,OAAO,OAAOlB,YAAY;AAC5B,IACA,IAAI,CAACA,cAAc,CAACiB,YAAYA,GAAG"}
@@ -1,9 +1,2 @@
1
- import { Effect, Schema } from 'effect';
2
- declare const NavigationApiUnavailableError_base: Schema.Class<NavigationApiUnavailableError, Schema.TaggedStruct<"NavigationApiUnavailableError", {}>, import("effect/Cause").YieldableError>;
3
- export declare class NavigationApiUnavailableError extends NavigationApiUnavailableError_base {
4
- }
5
- declare const NavigationPrecommitUnavailableError_base: Schema.Class<NavigationPrecommitUnavailableError, Schema.TaggedStruct<"NavigationPrecommitUnavailableError", {}>, import("effect/Cause").YieldableError>;
6
- export declare class NavigationPrecommitUnavailableError extends NavigationPrecommitUnavailableError_base {
7
- }
8
- export declare const checkBrowserCapabilities: Effect.Effect<undefined, NavigationApiUnavailableError | NavigationPrecommitUnavailableError, never>;
9
- export {};
1
+ import { Effect } from 'effect';
2
+ export declare const navigationMode: Effect.Effect<"Client" | "Document", never, never>;
@@ -1,20 +1,9 @@
1
- import { Effect, Schema } from "effect";
1
+ import { Effect } from "effect";
2
2
 
3
3
 
4
4
 
5
- class NavigationApiUnavailableError extends Schema.TaggedError()('NavigationApiUnavailableError', {}) {
6
- }
7
- class NavigationPrecommitUnavailableError extends Schema.TaggedError()('NavigationPrecommitUnavailableError', {}) {
8
- }
9
- const checkBrowserCapabilities = Effect.gen(function*() {
10
- if (window.navigation === undefined) {
11
- return yield* new NavigationApiUnavailableError();
12
- }
13
- if (window.NavigationPrecommitController === undefined) {
14
- return yield* new NavigationPrecommitUnavailableError();
15
- }
16
- });
5
+ const navigationMode = Effect.sync(()=>window.navigation !== undefined && window.NavigationPrecommitController !== undefined ? 'Client' : 'Document');
17
6
 
18
- export { NavigationApiUnavailableError, NavigationPrecommitUnavailableError, checkBrowserCapabilities };
7
+ export { navigationMode };
19
8
 
20
9
  //# sourceMappingURL=browser-capabilities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client/browser-capabilities.js","sources":["../../src/client/browser-capabilities.ts"],"sourcesContent":["import { Effect, Schema } from 'effect';\n\nexport class NavigationApiUnavailableError extends Schema.TaggedError<NavigationApiUnavailableError>()(\n 'NavigationApiUnavailableError',\n {},\n) {}\n\nexport class NavigationPrecommitUnavailableError extends Schema.TaggedError<NavigationPrecommitUnavailableError>()(\n 'NavigationPrecommitUnavailableError',\n {},\n) {}\n\nexport const checkBrowserCapabilities = Effect.gen(function* () {\n if (window.navigation === undefined) {\n return yield* new NavigationApiUnavailableError();\n }\n if (window.NavigationPrecommitController === undefined) {\n return yield* new NavigationPrecommitUnavailableError();\n }\n});\n"],"names":["Effect","Schema","NavigationApiUnavailableError","NavigationPrecommitUnavailableError","checkBrowserCapabilities","window","undefined"],"mappings":";;;AAAwC;AAEjC,MAAME,6BAA6BA,SAASD,kBAAkB,GACnE,iCACA,CAAC;AACA;AAEI,MAAME,mCAAmCA,SAASF,kBAAkB,GACzE,uCACA,CAAC;AACA;AAEI,MAAMG,wBAAwBA,GAAGJ,UAAU,CAAC;IACjD,IAAIK,OAAO,UAAU,KAAKC,WAAW;QACnC,OAAO,OAAO,IAAIJ,6BAA6BA;IACjD;IACA,IAAIG,OAAO,6BAA6B,KAAKC,WAAW;QACtD,OAAO,OAAO,IAAIH,mCAAmCA;IACvD;AACF,GAAG"}
1
+ {"version":3,"file":"client/browser-capabilities.js","sources":["../../src/client/browser-capabilities.ts"],"sourcesContent":["import { Effect } from 'effect';\n\nexport const navigationMode = Effect.sync(() =>\n window.navigation !== undefined && window.NavigationPrecommitController !== undefined\n ? ('Client' as const)\n : ('Document' as const),\n);\n"],"names":["Effect","navigationMode","window","undefined"],"mappings":";;;AAAgC;AAEzB,MAAMC,cAAcA,GAAGD,WAAW,CAAC,IACxCE,OAAO,UAAU,KAAKC,aAAaD,OAAO,6BAA6B,KAAKC,YACvE,WACA,YACL"}
@@ -0,0 +1,25 @@
1
+ import { Context, Effect, Layer } from 'effect';
2
+ type RenderStatus = {
3
+ readonly _tag: 'Waiting';
4
+ } | {
5
+ readonly _tag: 'Rendered';
6
+ } | {
7
+ readonly _tag: 'Failed';
8
+ readonly error: unknown;
9
+ readonly componentStack: string | null;
10
+ };
11
+ declare const BrowserRenderStatus_base: Context.ServiceClass<BrowserRenderStatus, "ersc/client/BrowserRenderStatus", {
12
+ changes: import("effect/Stream").Stream<RenderStatus, never, never>;
13
+ get: Effect.Effect<RenderStatus, never, never>;
14
+ report: (value: RenderStatus) => Effect.Effect<void, never, never>;
15
+ }> & {
16
+ readonly make: Effect.Effect<{
17
+ changes: import("effect/Stream").Stream<RenderStatus, never, never>;
18
+ get: Effect.Effect<RenderStatus, never, never>;
19
+ report: (value: RenderStatus) => Effect.Effect<void, never, never>;
20
+ }, never, never>;
21
+ };
22
+ export declare class BrowserRenderStatus extends BrowserRenderStatus_base {
23
+ static readonly layer: Layer.Layer<BrowserRenderStatus, never, never>;
24
+ }
25
+ export {};
@@ -0,0 +1,20 @@
1
+ import { Context, Effect, Layer, SubscriptionRef } from "effect";
2
+
3
+
4
+
5
+ // Replay the latest status: diagnostics may start after hydration has already failed.
6
+ class BrowserRenderStatus extends Context.Service()('ersc/client/BrowserRenderStatus', {
7
+ make: SubscriptionRef.make({
8
+ _tag: 'Waiting'
9
+ }).pipe(Effect.map((status)=>({
10
+ changes: SubscriptionRef.changes(status),
11
+ get: SubscriptionRef.get(status),
12
+ report: (value)=>SubscriptionRef.set(status, value)
13
+ })))
14
+ }) {
15
+ static layer = Layer.effect(this, this.make);
16
+ }
17
+
18
+ export { BrowserRenderStatus };
19
+
20
+ //# sourceMappingURL=browser-render-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client/browser-render-status.js","sources":["../../src/client/browser-render-status.ts"],"sourcesContent":["import { Context, Effect, Layer, SubscriptionRef } from 'effect';\n\ntype RenderStatus =\n | { readonly _tag: 'Waiting' }\n | { readonly _tag: 'Rendered' }\n | { readonly _tag: 'Failed'; readonly error: unknown; readonly componentStack: string | null };\n\n// Replay the latest status: diagnostics may start after hydration has already failed.\nexport class BrowserRenderStatus extends Context.Service<BrowserRenderStatus>()(\n 'ersc/client/BrowserRenderStatus',\n {\n make: SubscriptionRef.make<RenderStatus>({ _tag: 'Waiting' }).pipe(\n Effect.map((status) => ({\n changes: SubscriptionRef.changes(status),\n get: SubscriptionRef.get(status),\n report: (value: RenderStatus) => SubscriptionRef.set(status, value),\n })),\n ),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n}\n"],"names":["Context","Effect","Layer","SubscriptionRef","BrowserRenderStatus","status","value"],"mappings":";;;AAAiE;AAOjE,sFAAsF;AAC/E,MAAMI,mBAAmBA,SAASJ,eAAe,GACtD,mCACA;IACE,MAAMG,oBAAoB,CAAe;QAAE,MAAM;IAAU,GAAG,IAAI,CAChEF,UAAU,CAAC,CAACI,SAAY;YACtB,SAASF,uBAAuB,CAACE;YACjC,KAAKF,mBAAmB,CAACE;YACzB,QAAQ,CAACC,QAAwBH,mBAAmB,CAACE,QAAQC;QAC/D;AAEJ;IAEA,OAAgB,QAAQJ,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACxD"}
@@ -113,12 +113,17 @@ const installCallServer = Effect.gen(function*() {
113
113
  // React registered its Action reactions before the request completed. Registering ERSC's
114
114
  // continuation after settlement lets React close that Action before the refresh Transition.
115
115
  yield* Effect.promise(()=>invocationResult.promise.then(()=>undefined, ()=>undefined));
116
- if (selectRefreshSource(invocation) === 'CurrentRoute') {
116
+ let refreshSource = selectRefreshSource(invocation);
117
+ if (refreshSource === 'Response') {
118
+ yield* routeRefresher.interruptCurrentRouteRefresh;
119
+ // Interruption awaits cleanup, during which navigation or another invocation can win.
120
+ refreshSource = selectRefreshSource(invocation);
121
+ }
122
+ if (refreshSource === 'CurrentRoute') {
117
123
  yield* resource.release;
118
124
  yield* routeRefresher.refreshCurrentRoute('server-function');
119
125
  return;
120
126
  }
121
- yield* routeRefresher.interruptCurrentRouteRefresh;
122
127
  const commitRefresh = routeLoader.prepareRefresh(resource.payload.routeTree);
123
128
  let renderCommitted;
124
129
  yield* Effect.sync(()=>{
@@ -1 +1 @@
1
- {"version":3,"file":"client/call-server.js","sources":["../../src/client/call-server.ts"],"sourcesContent":["import { Effect, MutableRef, Schema } from 'effect';\nimport { addTransitionType, startTransition } from 'react';\nimport {\n createTemporaryReferenceSet,\n encodeReply,\n setServerCallback,\n} from 'react-server-dom-rspack/client.browser';\n\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderer } from './browser-renderer';\nimport { FlightClient } from './flight-client';\nimport { NavigationApi } from './navigation-api';\nimport { RouteLoader } from './route-loader';\nimport { RouteRefresher } from './route-refresh';\n\nclass ServerFnCallError extends Schema.TaggedError<ServerFnCallError>()('ServerFnCallError', {\n cause: Schema.Defect(),\n message: Schema.String,\n}) {}\n\ntype ServerFnInvocation =\n | {\n readonly _tag: 'HistoryEntry';\n readonly id: string;\n readonly order: number;\n readonly url: string;\n }\n | { readonly _tag: 'CurrentUrl'; readonly order: number; readonly url: string };\n\ntype ServerFnRefreshSource = 'Response' | 'CurrentRoute';\n\nexport const installCallServer = Effect.gen(function* () {\n const browserRenderer = yield* BrowserRenderer;\n const navigationApi = yield* NavigationApi;\n const run = yield* BrowserEffectRunner;\n const flightClient = yield* FlightClient;\n const routeLoader = yield* RouteLoader;\n const routeRefresher = yield* RouteRefresher;\n const latestInvocationOrder = MutableRef.make(0);\n const selectRefreshSource = (invocation: ServerFnInvocation): ServerFnRefreshSource => {\n if (\n MutableRef.get(latestInvocationOrder) !== invocation.order ||\n navigationApi.getTransition() !== null\n ) {\n return 'CurrentRoute';\n }\n\n const currentEntry = navigationApi.getCurrentEntry();\n switch (invocation._tag) {\n case 'HistoryEntry':\n return currentEntry?.id === invocation.id ? 'Response' : 'CurrentRoute';\n case 'CurrentUrl':\n return currentEntry === null && navigationApi.getCurrentUrl() === invocation.url\n ? 'Response'\n : 'CurrentRoute';\n }\n };\n const callServer = Effect.fnUntraced(function* (\n id: string,\n args: ReadonlyArray<unknown>,\n invocationResult: PromiseWithResolvers<unknown>,\n ) {\n const currentEntry = navigationApi.getCurrentEntry();\n const order = MutableRef.incrementAndGet(latestInvocationOrder);\n const invocation: ServerFnInvocation =\n currentEntry === null\n ? { _tag: 'CurrentUrl', order, url: navigationApi.getCurrentUrl() }\n : {\n _tag: 'HistoryEntry',\n id: currentEntry.id,\n order,\n url: currentEntry.url ?? navigationApi.getCurrentUrl(),\n };\n const temporaryReferences = createTemporaryReferenceSet();\n const body = yield* Effect.tryPromise({\n try: () => encodeReply(args, { temporaryReferences }),\n catch: (cause) => new ServerFnCallError({ cause, message: 'Failed to encode arguments.' }),\n });\n const resource = yield* flightClient\n .load({\n _tag: 'ServerFunction',\n body,\n destination: new URL(invocation.url),\n id,\n temporaryReferences,\n })\n .pipe(\n Effect.mapError(\n (cause) => new ServerFnCallError({ cause, message: 'Server Function request failed.' }),\n ),\n );\n if (resource._tag === 'Document') {\n yield* resource.release;\n return yield* new ServerFnCallError({\n cause: new Error('A Server Function response cannot request document navigation.'),\n message: 'Server Function response was incompatible with Flight.',\n });\n }\n const serverFnResult = resource.payload.serverFnResult;\n if (serverFnResult === null) {\n yield* resource.release;\n return yield* new ServerFnCallError({\n cause: new Error('The Flight payload omitted the Server Function return value.'),\n message: 'Server Function response was incomplete.',\n });\n }\n switch (serverFnResult._tag) {\n case 'Failure':\n invocationResult.reject(\n new ServerFnCallError({\n cause: serverFnResult.error,\n message: 'Server Function execution failed.',\n }),\n );\n break;\n case 'Success':\n invocationResult.resolve(serverFnResult.value);\n break;\n }\n // React registered its Action reactions before the request completed. Registering ERSC's\n // continuation after settlement lets React close that Action before the refresh Transition.\n yield* Effect.promise(() =>\n invocationResult.promise.then(\n () => undefined,\n () => undefined,\n ),\n );\n if (selectRefreshSource(invocation) === 'CurrentRoute') {\n yield* resource.release;\n yield* routeRefresher.refreshCurrentRoute('server-function');\n return;\n }\n\n yield* routeRefresher.interruptCurrentRouteRefresh;\n const commitRefresh = routeLoader.prepareRefresh(resource.payload.routeTree);\n let renderCommitted!: Promise<void>;\n yield* Effect.sync(() => {\n startTransition(() => {\n addTransitionType('server-function');\n // Do not return the commit Promise from React's Transition Action. React cannot commit the\n // render until that Action ends.\n renderCommitted = browserRenderer.refresh(resource.payload.routeTree);\n });\n });\n yield* Effect.all([resource.completed, Effect.promise(() => renderCommitted)], {\n concurrency: 'unbounded',\n discard: true,\n }).pipe(\n Effect.andThen(Effect.sync(commitRefresh)),\n Effect.onError(() => resource.release),\n Effect.forkScoped,\n );\n });\n\n yield* Effect.sync(() => {\n setServerCallback((id, args) => {\n const invocationResult = Promise.withResolvers<unknown>();\n void run(callServer(id, args, invocationResult)).catch(invocationResult.reject);\n return invocationResult.promise;\n });\n });\n});\n"],"names":["Effect","MutableRef","Schema","addTransitionType","startTransition","createTemporaryReferenceSet","encodeReply","setServerCallback","BrowserEffectRunner","BrowserRenderer","FlightClient","NavigationApi","RouteLoader","RouteRefresher","ServerFnCallError","installCallServer","browserRenderer","navigationApi","run","flightClient","routeLoader","routeRefresher","latestInvocationOrder","selectRefreshSource","invocation","currentEntry","callServer","id","args","invocationResult","order","temporaryReferences","body","cause","resource","URL","Error","serverFnResult","undefined","commitRefresh","renderCommitted","Promise"],"mappings":"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"}
1
+ {"version":3,"file":"client/call-server.js","sources":["../../src/client/call-server.ts"],"sourcesContent":["import { Effect, MutableRef, Schema } from 'effect';\nimport { addTransitionType, startTransition } from 'react';\nimport {\n createTemporaryReferenceSet,\n encodeReply,\n setServerCallback,\n} from 'react-server-dom-rspack/client.browser';\n\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderer } from './browser-renderer';\nimport { FlightClient } from './flight-client';\nimport { NavigationApi } from './navigation-api';\nimport { RouteLoader } from './route-loader';\nimport { RouteRefresher } from './route-refresh';\n\nclass ServerFnCallError extends Schema.TaggedError<ServerFnCallError>()('ServerFnCallError', {\n cause: Schema.Defect(),\n message: Schema.String,\n}) {}\n\ntype ServerFnInvocation =\n | {\n readonly _tag: 'HistoryEntry';\n readonly id: string;\n readonly order: number;\n readonly url: string;\n }\n | { readonly _tag: 'CurrentUrl'; readonly order: number; readonly url: string };\n\ntype ServerFnRefreshSource = 'Response' | 'CurrentRoute';\n\nexport const installCallServer = Effect.gen(function* () {\n const browserRenderer = yield* BrowserRenderer;\n const navigationApi = yield* NavigationApi;\n const run = yield* BrowserEffectRunner;\n const flightClient = yield* FlightClient;\n const routeLoader = yield* RouteLoader;\n const routeRefresher = yield* RouteRefresher;\n const latestInvocationOrder = MutableRef.make(0);\n const selectRefreshSource = (invocation: ServerFnInvocation): ServerFnRefreshSource => {\n if (\n MutableRef.get(latestInvocationOrder) !== invocation.order ||\n navigationApi.getTransition() !== null\n ) {\n return 'CurrentRoute';\n }\n\n const currentEntry = navigationApi.getCurrentEntry();\n switch (invocation._tag) {\n case 'HistoryEntry':\n return currentEntry?.id === invocation.id ? 'Response' : 'CurrentRoute';\n case 'CurrentUrl':\n return currentEntry === null && navigationApi.getCurrentUrl() === invocation.url\n ? 'Response'\n : 'CurrentRoute';\n }\n };\n const callServer = Effect.fnUntraced(function* (\n id: string,\n args: ReadonlyArray<unknown>,\n invocationResult: PromiseWithResolvers<unknown>,\n ) {\n const currentEntry = navigationApi.getCurrentEntry();\n const order = MutableRef.incrementAndGet(latestInvocationOrder);\n const invocation: ServerFnInvocation =\n currentEntry === null\n ? { _tag: 'CurrentUrl', order, url: navigationApi.getCurrentUrl() }\n : {\n _tag: 'HistoryEntry',\n id: currentEntry.id,\n order,\n url: currentEntry.url ?? navigationApi.getCurrentUrl(),\n };\n const temporaryReferences = createTemporaryReferenceSet();\n const body = yield* Effect.tryPromise({\n try: () => encodeReply(args, { temporaryReferences }),\n catch: (cause) => new ServerFnCallError({ cause, message: 'Failed to encode arguments.' }),\n });\n const resource = yield* flightClient\n .load({\n _tag: 'ServerFunction',\n body,\n destination: new URL(invocation.url),\n id,\n temporaryReferences,\n })\n .pipe(\n Effect.mapError(\n (cause) => new ServerFnCallError({ cause, message: 'Server Function request failed.' }),\n ),\n );\n if (resource._tag === 'Document') {\n yield* resource.release;\n return yield* new ServerFnCallError({\n cause: new Error('A Server Function response cannot request document navigation.'),\n message: 'Server Function response was incompatible with Flight.',\n });\n }\n const serverFnResult = resource.payload.serverFnResult;\n if (serverFnResult === null) {\n yield* resource.release;\n return yield* new ServerFnCallError({\n cause: new Error('The Flight payload omitted the Server Function return value.'),\n message: 'Server Function response was incomplete.',\n });\n }\n switch (serverFnResult._tag) {\n case 'Failure':\n invocationResult.reject(\n new ServerFnCallError({\n cause: serverFnResult.error,\n message: 'Server Function execution failed.',\n }),\n );\n break;\n case 'Success':\n invocationResult.resolve(serverFnResult.value);\n break;\n }\n // React registered its Action reactions before the request completed. Registering ERSC's\n // continuation after settlement lets React close that Action before the refresh Transition.\n yield* Effect.promise(() =>\n invocationResult.promise.then(\n () => undefined,\n () => undefined,\n ),\n );\n let refreshSource = selectRefreshSource(invocation);\n if (refreshSource === 'Response') {\n yield* routeRefresher.interruptCurrentRouteRefresh;\n // Interruption awaits cleanup, during which navigation or another invocation can win.\n refreshSource = selectRefreshSource(invocation);\n }\n\n if (refreshSource === 'CurrentRoute') {\n yield* resource.release;\n yield* routeRefresher.refreshCurrentRoute('server-function');\n return;\n }\n const commitRefresh = routeLoader.prepareRefresh(resource.payload.routeTree);\n let renderCommitted!: Promise<void>;\n yield* Effect.sync(() => {\n startTransition(() => {\n addTransitionType('server-function');\n // Do not return the commit Promise from React's Transition Action. React cannot commit the\n // render until that Action ends.\n renderCommitted = browserRenderer.refresh(resource.payload.routeTree);\n });\n });\n yield* Effect.all([resource.completed, Effect.promise(() => renderCommitted)], {\n concurrency: 'unbounded',\n discard: true,\n }).pipe(\n Effect.andThen(Effect.sync(commitRefresh)),\n Effect.onError(() => resource.release),\n Effect.forkScoped,\n );\n });\n\n yield* Effect.sync(() => {\n setServerCallback((id, args) => {\n const invocationResult = Promise.withResolvers<unknown>();\n void run(callServer(id, args, invocationResult)).catch(invocationResult.reject);\n return invocationResult.promise;\n });\n });\n});\n"],"names":["Effect","MutableRef","Schema","addTransitionType","startTransition","createTemporaryReferenceSet","encodeReply","setServerCallback","BrowserEffectRunner","BrowserRenderer","FlightClient","NavigationApi","RouteLoader","RouteRefresher","ServerFnCallError","installCallServer","browserRenderer","navigationApi","run","flightClient","routeLoader","routeRefresher","latestInvocationOrder","selectRefreshSource","invocation","currentEntry","callServer","id","args","invocationResult","order","temporaryReferences","body","cause","resource","URL","Error","serverFnResult","undefined","refreshSource","commitRefresh","renderCommitted","Promise"],"mappings":"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"}
@@ -4,15 +4,16 @@ import { Context, Effect, Layer } from "effect";
4
4
 
5
5
  class NavigationApi extends Context.Service()('ersc/client/NavigationApi', {
6
6
  make: Effect.succeed({
7
- getCurrentEntry: ()=>window.navigation.currentEntry,
7
+ getCurrentEntry: ()=>window.navigation?.currentEntry ?? null,
8
8
  getCurrentUrl: ()=>window.location.href,
9
- getTransition: ()=>window.navigation.transition,
9
+ getTransition: ()=>window.navigation?.transition ?? null,
10
10
  navigate: (url, options)=>window.navigation.navigate(url, options),
11
11
  reloadDocument: ()=>window.location.reload(),
12
12
  replaceDocument: (url)=>window.location.replace(url),
13
13
  subscribe: (listener)=>{
14
- window.navigation.addEventListener('navigate', listener);
15
- return ()=>window.navigation.removeEventListener('navigate', listener);
14
+ const navigation = window.navigation;
15
+ navigation?.addEventListener('navigate', listener);
16
+ return ()=>navigation?.removeEventListener('navigate', listener);
16
17
  }
17
18
  })
18
19
  }) {
@@ -1 +1 @@
1
- {"version":3,"file":"client/navigation-api.js","sources":["../../src/client/navigation-api.ts"],"sourcesContent":["import { Context, Effect, Layer } from 'effect';\n\ntype NavigateListener = (event: NavigateEvent) => void;\n\nexport class NavigationApi extends Context.Service<NavigationApi>()('ersc/client/NavigationApi', {\n make: Effect.succeed({\n getCurrentEntry: () => window.navigation.currentEntry,\n getCurrentUrl: () => window.location.href,\n getTransition: () => window.navigation.transition,\n navigate: (url: string | URL, options?: NavigationNavigateOptions) =>\n window.navigation.navigate(url, options),\n reloadDocument: () => window.location.reload(),\n replaceDocument: (url: string) => window.location.replace(url),\n subscribe: (listener: NavigateListener) => {\n window.navigation.addEventListener('navigate', listener);\n return () => window.navigation.removeEventListener('navigate', listener);\n },\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","NavigationApi","window","url","options","listener"],"mappings":";;;AAAgD;AAIzC,MAAMG,aAAaA,SAASH,eAAe,GAAkB,6BAA6B;IAC/F,MAAMC,cAAc,CAAC;QACnB,iBAAiB,IAAMG,OAAO,UAAU,CAAC,YAAY;QACrD,eAAe,IAAMA,OAAO,QAAQ,CAAC,IAAI;QACzC,eAAe,IAAMA,OAAO,UAAU,CAAC,UAAU;QACjD,UAAU,CAACC,KAAmBC,UAC5BF,OAAO,UAAU,CAAC,QAAQ,CAACC,KAAKC;QAClC,gBAAgB,IAAMF,OAAO,QAAQ,CAAC,MAAM;QAC5C,iBAAiB,CAACC,MAAgBD,OAAO,QAAQ,CAAC,OAAO,CAACC;QAC1D,WAAW,CAACE;YACVH,OAAO,UAAU,CAAC,gBAAgB,CAAC,YAAYG;YAC/C,OAAO,IAAMH,OAAO,UAAU,CAAC,mBAAmB,CAAC,YAAYG;QACjE;IACF;AACF;IACE,OAAgB,QAAQL,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IAEtD,OAAgB,YAAYA,UAAU,CAAC,IAAI,EAAE;AAC/C"}
1
+ {"version":3,"file":"client/navigation-api.js","sources":["../../src/client/navigation-api.ts"],"sourcesContent":["import { Context, Effect, Layer } from 'effect';\n\ntype NavigateListener = (event: NavigateEvent) => void;\n\nexport class NavigationApi extends Context.Service<NavigationApi>()('ersc/client/NavigationApi', {\n make: Effect.succeed({\n getCurrentEntry: () => window.navigation?.currentEntry ?? null,\n getCurrentUrl: () => window.location.href,\n getTransition: () => window.navigation?.transition ?? null,\n navigate: (url: string | URL, options?: NavigationNavigateOptions) =>\n window.navigation.navigate(url, options),\n reloadDocument: () => window.location.reload(),\n replaceDocument: (url: string) => window.location.replace(url),\n subscribe: (listener: NavigateListener) => {\n const navigation = window.navigation;\n navigation?.addEventListener('navigate', listener);\n return () => navigation?.removeEventListener('navigate', listener);\n },\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","NavigationApi","window","url","options","listener","navigation"],"mappings":";;;AAAgD;AAIzC,MAAMG,aAAaA,SAASH,eAAe,GAAkB,6BAA6B;IAC/F,MAAMC,cAAc,CAAC;QACnB,iBAAiB,IAAMG,OAAO,UAAU,EAAE,gBAAgB;QAC1D,eAAe,IAAMA,OAAO,QAAQ,CAAC,IAAI;QACzC,eAAe,IAAMA,OAAO,UAAU,EAAE,cAAc;QACtD,UAAU,CAACC,KAAmBC,UAC5BF,OAAO,UAAU,CAAC,QAAQ,CAACC,KAAKC;QAClC,gBAAgB,IAAMF,OAAO,QAAQ,CAAC,MAAM;QAC5C,iBAAiB,CAACC,MAAgBD,OAAO,QAAQ,CAAC,OAAO,CAACC;QAC1D,WAAW,CAACE;YACV,MAAMC,aAAaJ,OAAO,UAAU;YACpCI,YAAY,iBAAiB,YAAYD;YACzC,OAAO,IAAMC,YAAY,oBAAoB,YAAYD;QAC3D;IACF;AACF;IACE,OAAgB,QAAQL,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IAEtD,OAAgB,YAAYA,UAAU,CAAC,IAAI,EAAE;AAC/C"}
@@ -1,6 +1,7 @@
1
1
  import { Context, Effect, Layer, Schema } from 'effect';
2
2
  import type { FlightPayload } from '../rsc/flight.js';
3
3
  import { BrowserEffectRunner } from './browser-effect-runner.js';
4
+ import { BrowserRenderStatus } from './browser-render-status.js';
4
5
  import { BrowserRenderer } from './browser-renderer.js';
5
6
  declare const ReactDOMHydrationError_base: Schema.Class<ReactDOMHydrationError, Schema.TaggedStruct<"ReactDOMHydrationError", {
6
7
  readonly cause: Schema.Defect;
@@ -12,9 +13,9 @@ declare const ReactDOMRenderer_base: Context.ServiceClass<ReactDOMRenderer, "ers
12
13
  }> & {
13
14
  readonly make: Effect.Effect<{
14
15
  hydrate: (container: Document | Element, initialPayload: FlightPayload) => Effect.Effect<void, ReactDOMHydrationError, import("effect/Scope").Scope>;
15
- }, never, BrowserEffectRunner | BrowserRenderer>;
16
+ }, never, BrowserEffectRunner | BrowserRenderStatus | BrowserRenderer>;
16
17
  };
17
18
  export declare class ReactDOMRenderer extends ReactDOMRenderer_base {
18
- static readonly layer: Layer.Layer<ReactDOMRenderer, never, BrowserEffectRunner | BrowserRenderer>;
19
+ static readonly layer: Layer.Layer<ReactDOMRenderer, never, BrowserEffectRunner | BrowserRenderStatus | BrowserRenderer>;
19
20
  }
20
21
  export {};
@@ -3,6 +3,7 @@ import { Context, Effect, Layer, Schema } from "effect";
3
3
  import { Component, StrictMode, useLayoutEffect, useState } from "react";
4
4
  import { hydrateRoot } from "react-dom/client";
5
5
  import { BrowserEffectRunner } from "./browser-effect-runner.js";
6
+ import { BrowserRenderStatus } from "./browser-render-status.js";
6
7
  import { BrowserRenderer } from "./browser-renderer.js";
7
8
  import { BrowserFailureScreen } from "./browser-screen.js";
8
9
  import { RouteTree } from "./route-tree.js";
@@ -23,22 +24,47 @@ import { RouteTree } from "./route-tree.js";
23
24
 
24
25
 
25
26
 
27
+
28
+
26
29
  class ReactDOMHydrationError extends Schema.TaggedError()('ReactDOMHydrationError', {
27
30
  cause: Schema.Defect()
28
31
  }) {
29
32
  }
30
33
  class BrowserErrorBoundary extends Component {
31
34
  state = {
32
- _tag: 'Ready'
35
+ _tag: 'Uninitialized'
33
36
  };
34
37
  static getDerivedStateFromError() {
35
38
  return {
36
39
  _tag: 'Failed'
37
40
  };
38
41
  }
42
+ static getDerivedStateFromProps(props, state) {
43
+ if (state._tag !== 'Uninitialized' && props.render === state.render) {
44
+ return null;
45
+ }
46
+ // Discard acknowledges a cancelled candidate, not a request to retry the failed tree.
47
+ return state._tag === 'Failed' && props.render._tag === 'Discard' ? {
48
+ _tag: 'Failed',
49
+ render: props.render
50
+ } : {
51
+ _tag: 'Ready',
52
+ render: props.render
53
+ };
54
+ }
39
55
  componentDidCatch(error, info) {
40
56
  this.props.onError(error, info);
41
57
  }
58
+ componentDidMount() {
59
+ if (this.state._tag === 'Ready') {
60
+ this.props.onRendered();
61
+ }
62
+ }
63
+ componentDidUpdate() {
64
+ if (this.state._tag === 'Ready') {
65
+ this.props.onRendered();
66
+ }
67
+ }
42
68
  render() {
43
69
  if (this.state._tag === 'Failed') {
44
70
  return /*#__PURE__*/ jsx(BrowserFailureScreen, {});
@@ -50,10 +76,23 @@ class ReactDOMRenderer extends Context.Service()('ersc/client/ReactDOMRenderer',
50
76
  make: Effect.gen(function*() {
51
77
  const browserRenderer = yield* BrowserRenderer;
52
78
  const run = yield* BrowserEffectRunner;
79
+ const renderStatus = yield* BrowserRenderStatus;
53
80
  const hydrate = Effect.fnUntraced(function*(container, initialPayload) {
54
81
  const browserRendererReady = Promise.withResolvers();
55
82
  const reportError = (error, info)=>{
56
- void run(Effect.logError('Uncaught client render error.', error, info.componentStack));
83
+ void run(Effect.gen(function*() {
84
+ yield* renderStatus.report({
85
+ _tag: 'Failed',
86
+ error,
87
+ componentStack: info.componentStack ?? null
88
+ });
89
+ yield* Effect.logError('Uncaught client render error.', error, info.componentStack);
90
+ }));
91
+ };
92
+ const reportRendered = ()=>{
93
+ void run(renderStatus.report({
94
+ _tag: 'Rendered'
95
+ }));
57
96
  };
58
97
  function Root() {
59
98
  const [render, setRender] = useState(()=>({
@@ -71,6 +110,8 @@ class ReactDOMRenderer extends Context.Service()('ersc/client/ReactDOMRenderer',
71
110
  ]);
72
111
  return /*#__PURE__*/ jsx(BrowserErrorBoundary, {
73
112
  onError: reportError,
113
+ onRendered: reportRendered,
114
+ render: render,
74
115
  children: /*#__PURE__*/ jsx(RouteTree, {
75
116
  root: render.routeTree
76
117
  })
@@ -1 +1 @@
1
- {"version":3,"file":"client/react-dom-renderer.js","sources":["../../src/client/react-dom-renderer.tsx"],"sourcesContent":["import { Context, Effect, Layer, Schema } from 'effect';\nimport {\n Component,\n type ErrorInfo,\n type ReactNode,\n StrictMode,\n useLayoutEffect,\n useState,\n} from 'react';\nimport { hydrateRoot } from 'react-dom/client';\n\nimport type { FlightPayload } from '../rsc/flight';\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { type BrowserRender, BrowserRenderer } from './browser-renderer';\nimport { BrowserFailureScreen } from './browser-screen';\nimport { RouteTree } from './route-tree';\n\nexport class ReactDOMHydrationError extends Schema.TaggedError<ReactDOMHydrationError>()(\n 'ReactDOMHydrationError',\n { cause: Schema.Defect() },\n) {}\n\ntype BrowserErrorBoundaryState = { readonly _tag: 'Ready' } | { readonly _tag: 'Failed' };\ntype BrowserErrorBoundaryProps = {\n readonly children: ReactNode;\n readonly onError: (error: unknown, info: ErrorInfo) => void;\n};\n\nclass BrowserErrorBoundary extends Component<BrowserErrorBoundaryProps, BrowserErrorBoundaryState> {\n override readonly state: BrowserErrorBoundaryState = { _tag: 'Ready' };\n\n static getDerivedStateFromError(): BrowserErrorBoundaryState {\n return { _tag: 'Failed' };\n }\n\n override componentDidCatch(error: unknown, info: ErrorInfo) {\n this.props.onError(error, info);\n }\n\n override render() {\n if (this.state._tag === 'Failed') {\n return <BrowserFailureScreen />;\n }\n\n return this.props.children;\n }\n}\n\nexport class ReactDOMRenderer extends Context.Service<ReactDOMRenderer>()(\n 'ersc/client/ReactDOMRenderer',\n {\n make: Effect.gen(function* () {\n const browserRenderer = yield* BrowserRenderer;\n const run = yield* BrowserEffectRunner;\n\n const hydrate = Effect.fnUntraced(function* (\n container: Element | Document,\n initialPayload: FlightPayload,\n ) {\n const browserRendererReady = Promise.withResolvers<void>();\n const reportError = (error: unknown, info: ErrorInfo) => {\n void run(Effect.logError('Uncaught client render error.', error, info.componentStack));\n };\n\n function Root() {\n const [render, setRender] = useState<BrowserRender>(() => ({\n _tag: 'Initial',\n routeTree: initialPayload.routeTree,\n }));\n\n useLayoutEffect(() => {\n browserRenderer.initialize(initialPayload.routeTree, setRender);\n browserRendererReady.resolve();\n }, []);\n\n useLayoutEffect(() => {\n browserRenderer.commit(render);\n }, [render]);\n\n return (\n <BrowserErrorBoundary onError={reportError}>\n <RouteTree root={render.routeTree} />\n </BrowserErrorBoundary>\n );\n }\n\n yield* Effect.acquireRelease(\n Effect.try({\n try: () =>\n hydrateRoot(\n container,\n <StrictMode>\n <Root />\n </StrictMode>,\n { formState: initialPayload.formState },\n ),\n catch: (cause) => new ReactDOMHydrationError({ cause }),\n }),\n (root) => Effect.sync(() => root.unmount()),\n );\n yield* Effect.promise(() => browserRendererReady.promise);\n });\n\n return { hydrate };\n }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n}\n"],"names":["Context","Effect","Layer","Schema","Component","StrictMode","useLayoutEffect","useState","hydrateRoot","BrowserEffectRunner","BrowserRenderer","BrowserFailureScreen","RouteTree","ReactDOMHydrationError","BrowserErrorBoundary","error","info","ReactDOMRenderer","browserRenderer","run","hydrate","container","initialPayload","browserRendererReady","Promise","reportError","Root","render","setRender","cause","root"],"mappings":";;;;;;;;;;;;;;;;;;AAAwD;AAQzC;AACgC;AAGe;AACW;AACjB;AACf;AAElC,MAAMa,sBAAsBA,SAASV,kBAAkB,GAC5D,0BACA;IAAE,OAAOA,aAAa;AAAG;AACxB;AAQH,MAAMW,oBAAoBA,SAASV,SAASA;IACxB,QAAmC;QAAE,MAAM;IAAQ,EAAE;IAEvE,OAAO,2BAAsD;QAC3D,OAAO;YAAE,MAAM;QAAS;IAC1B;IAES,kBAAkBW,KAAc,EAAEC,IAAe,EAAE;QAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAACD,OAAOC;IAC5B;IAES,SAAS;QAChB,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU;YAChC,qBAAO,IAACL,oBAAoBA;QAC9B;QAEA,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ;IAC5B;AACF;AAEO,MAAMM,gBAAgBA,SAASjB,eAAe,GACnD,gCACA;IACE,MAAMC,UAAU,CAAC;QACf,MAAMiB,kBAAkB,OAAOR,eAAeA;QAC9C,MAAMS,MAAM,OAAOV,mBAAmBA;QAEtC,MAAMW,UAAUnB,iBAAiB,CAAC,UAChCoB,SAA6B,EAC7BC,cAA6B;YAE7B,MAAMC,uBAAuBC,QAAQ,aAAa;YAClD,MAAMC,cAAc,CAACV,OAAgBC;gBACnC,KAAKG,IAAIlB,eAAe,CAAC,iCAAiCc,OAAOC,KAAK,cAAc;YACtF;YAEA,SAASU;gBACP,MAAM,CAACC,QAAQC,UAAU,GAAGrB,QAAQA,CAAgB,IAAO;wBACzD,MAAM;wBACN,WAAWe,eAAe,SAAS;oBACrC;gBAEAhB,eAAeA,CAAC;oBACdY,gBAAgB,UAAU,CAACI,eAAe,SAAS,EAAEM;oBACrDL,qBAAqB,OAAO;gBAC9B,GAAG,EAAE;gBAELjB,eAAeA,CAAC;oBACdY,gBAAgB,MAAM,CAACS;gBACzB,GAAG;oBAACA;iBAAO;gBAEX,qBACE,IAACb,oBAAoBA;oBAAC,SAASW;8BAC7B,kBAACb,SAASA;wBAAC,MAAMe,OAAO,SAAS;;;YAGvC;YAEA,OAAO1B,qBAAqB,CAC1BA,aAAU,CAAC;gBACT,KAAK,IACHO,WAAWA,CACTa,yBACA,IAAChB,UAAUA;kCACT,kBAACqB;wBAEH;wBAAE,WAAWJ,eAAe,SAAS;oBAAC;gBAE1C,OAAO,CAACO,QAAU,IAAIhB,sBAAsBA,CAAC;wBAAEgB;oBAAM;YACvD,IACA,CAACC,OAAS7B,WAAW,CAAC,IAAM6B,KAAK,OAAO;YAE1C,OAAO7B,cAAc,CAAC,IAAMsB,qBAAqB,OAAO;QAC1D;QAEA,OAAO;YAAEH;QAAQ;IACnB;AACF;IAEA,OAAgB,QAAQlB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACxD"}
1
+ {"version":3,"file":"client/react-dom-renderer.js","sources":["../../src/client/react-dom-renderer.tsx"],"sourcesContent":["import { Context, Effect, Layer, Schema } from 'effect';\nimport {\n Component,\n type ErrorInfo,\n type ReactNode,\n StrictMode,\n useLayoutEffect,\n useState,\n} from 'react';\nimport { hydrateRoot } from 'react-dom/client';\n\nimport type { FlightPayload } from '../rsc/flight';\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderStatus } from './browser-render-status';\nimport { type BrowserRender, BrowserRenderer } from './browser-renderer';\nimport { BrowserFailureScreen } from './browser-screen';\nimport { RouteTree } from './route-tree';\n\nexport class ReactDOMHydrationError extends Schema.TaggedError<ReactDOMHydrationError>()(\n 'ReactDOMHydrationError',\n { cause: Schema.Defect() },\n) {}\n\ntype BrowserErrorBoundaryState =\n | { readonly _tag: 'Uninitialized' }\n | { readonly _tag: 'Ready'; readonly render: BrowserRender }\n | { readonly _tag: 'Failed'; readonly render: BrowserRender };\ntype BrowserErrorBoundaryProps = {\n readonly children: ReactNode;\n readonly onError: (error: unknown, info: ErrorInfo) => void;\n readonly onRendered: () => void;\n readonly render: BrowserRender;\n};\n\nclass BrowserErrorBoundary extends Component<BrowserErrorBoundaryProps, BrowserErrorBoundaryState> {\n override readonly state: BrowserErrorBoundaryState = { _tag: 'Uninitialized' };\n\n static getDerivedStateFromError() {\n return { _tag: 'Failed' } as const;\n }\n\n static getDerivedStateFromProps(\n props: BrowserErrorBoundaryProps,\n state: BrowserErrorBoundaryState,\n ): BrowserErrorBoundaryState | null {\n if (state._tag !== 'Uninitialized' && props.render === state.render) {\n return null;\n }\n // Discard acknowledges a cancelled candidate, not a request to retry the failed tree.\n return state._tag === 'Failed' && props.render._tag === 'Discard'\n ? { _tag: 'Failed', render: props.render }\n : { _tag: 'Ready', render: props.render };\n }\n\n override componentDidCatch(error: unknown, info: ErrorInfo) {\n this.props.onError(error, info);\n }\n\n override componentDidMount() {\n if (this.state._tag === 'Ready') {\n this.props.onRendered();\n }\n }\n\n override componentDidUpdate() {\n if (this.state._tag === 'Ready') {\n this.props.onRendered();\n }\n }\n\n override render() {\n if (this.state._tag === 'Failed') {\n return <BrowserFailureScreen />;\n }\n\n return this.props.children;\n }\n}\n\nexport class ReactDOMRenderer extends Context.Service<ReactDOMRenderer>()(\n 'ersc/client/ReactDOMRenderer',\n {\n make: Effect.gen(function* () {\n const browserRenderer = yield* BrowserRenderer;\n const run = yield* BrowserEffectRunner;\n const renderStatus = yield* BrowserRenderStatus;\n\n const hydrate = Effect.fnUntraced(function* (\n container: Element | Document,\n initialPayload: FlightPayload,\n ) {\n const browserRendererReady = Promise.withResolvers<void>();\n const reportError = (error: unknown, info: ErrorInfo) => {\n void run(\n Effect.gen(function* () {\n yield* renderStatus.report({\n _tag: 'Failed',\n error,\n componentStack: info.componentStack ?? null,\n });\n yield* Effect.logError('Uncaught client render error.', error, info.componentStack);\n }),\n );\n };\n\n const reportRendered = () => {\n void run(renderStatus.report({ _tag: 'Rendered' }));\n };\n\n function Root() {\n const [render, setRender] = useState<BrowserRender>(() => ({\n _tag: 'Initial',\n routeTree: initialPayload.routeTree,\n }));\n\n useLayoutEffect(() => {\n browserRenderer.initialize(initialPayload.routeTree, setRender);\n browserRendererReady.resolve();\n }, []);\n\n useLayoutEffect(() => {\n browserRenderer.commit(render);\n }, [render]);\n\n return (\n <BrowserErrorBoundary onError={reportError} onRendered={reportRendered} render={render}>\n <RouteTree root={render.routeTree} />\n </BrowserErrorBoundary>\n );\n }\n\n yield* Effect.acquireRelease(\n Effect.try({\n try: () =>\n hydrateRoot(\n container,\n <StrictMode>\n <Root />\n </StrictMode>,\n { formState: initialPayload.formState },\n ),\n catch: (cause) => new ReactDOMHydrationError({ cause }),\n }),\n (root) => Effect.sync(() => root.unmount()),\n );\n yield* Effect.promise(() => browserRendererReady.promise);\n });\n\n return { hydrate };\n }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n}\n"],"names":["Context","Effect","Layer","Schema","Component","StrictMode","useLayoutEffect","useState","hydrateRoot","BrowserEffectRunner","BrowserRenderStatus","BrowserRenderer","BrowserFailureScreen","RouteTree","ReactDOMHydrationError","BrowserErrorBoundary","props","state","error","info","ReactDOMRenderer","browserRenderer","run","renderStatus","hydrate","container","initialPayload","browserRendererReady","Promise","reportError","reportRendered","Root","render","setRender","cause","root"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAwD;AAQzC;AACgC;AAGe;AACA;AACW;AACjB;AACf;AAElC,MAAMc,sBAAsBA,SAASX,kBAAkB,GAC5D,0BACA;IAAE,OAAOA,aAAa;AAAG;AACxB;AAaH,MAAMY,oBAAoBA,SAASX,SAASA;IACxB,QAAmC;QAAE,MAAM;IAAgB,EAAE;IAE/E,OAAO,2BAA2B;QAChC,OAAO;YAAE,MAAM;QAAS;IAC1B;IAEA,OAAO,yBACLY,KAAgC,EAChCC,KAAgC,EACE;QAClC,IAAIA,MAAM,IAAI,KAAK,mBAAmBD,MAAM,MAAM,KAAKC,MAAM,MAAM,EAAE;YACnE,OAAO;QACT;QACA,sFAAsF;QACtF,OAAOA,MAAM,IAAI,KAAK,YAAYD,MAAM,MAAM,CAAC,IAAI,KAAK,YACpD;YAAE,MAAM;YAAU,QAAQA,MAAM,MAAM;QAAC,IACvC;YAAE,MAAM;YAAS,QAAQA,MAAM,MAAM;QAAC;IAC5C;IAES,kBAAkBE,KAAc,EAAEC,IAAe,EAAE;QAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,CAACD,OAAOC;IAC5B;IAES,oBAAoB;QAC3B,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;YAC/B,IAAI,CAAC,KAAK,CAAC,UAAU;QACvB;IACF;IAES,qBAAqB;QAC5B,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS;YAC/B,IAAI,CAAC,KAAK,CAAC,UAAU;QACvB;IACF;IAES,SAAS;QAChB,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU;YAChC,qBAAO,IAACP,oBAAoBA;QAC9B;QAEA,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ;IAC5B;AACF;AAEO,MAAMQ,gBAAgBA,SAASpB,eAAe,GACnD,gCACA;IACE,MAAMC,UAAU,CAAC;QACf,MAAMoB,kBAAkB,OAAOV,eAAeA;QAC9C,MAAMW,MAAM,OAAOb,mBAAmBA;QACtC,MAAMc,eAAe,OAAOb,mBAAmBA;QAE/C,MAAMc,UAAUvB,iBAAiB,CAAC,UAChCwB,SAA6B,EAC7BC,cAA6B;YAE7B,MAAMC,uBAAuBC,QAAQ,aAAa;YAClD,MAAMC,cAAc,CAACX,OAAgBC;gBACnC,KAAKG,IACHrB,UAAU,CAAC;oBACT,OAAOsB,aAAa,MAAM,CAAC;wBACzB,MAAM;wBACNL;wBACA,gBAAgBC,KAAK,cAAc,IAAI;oBACzC;oBACA,OAAOlB,eAAe,CAAC,iCAAiCiB,OAAOC,KAAK,cAAc;gBACpF;YAEJ;YAEA,MAAMW,iBAAiB;gBACrB,KAAKR,IAAIC,aAAa,MAAM,CAAC;oBAAE,MAAM;gBAAW;YAClD;YAEA,SAASQ;gBACP,MAAM,CAACC,QAAQC,UAAU,GAAG1B,QAAQA,CAAgB,IAAO;wBACzD,MAAM;wBACN,WAAWmB,eAAe,SAAS;oBACrC;gBAEApB,eAAeA,CAAC;oBACde,gBAAgB,UAAU,CAACK,eAAe,SAAS,EAAEO;oBACrDN,qBAAqB,OAAO;gBAC9B,GAAG,EAAE;gBAELrB,eAAeA,CAAC;oBACde,gBAAgB,MAAM,CAACW;gBACzB,GAAG;oBAACA;iBAAO;gBAEX,qBACE,IAACjB,oBAAoBA;oBAAC,SAASc;oBAAa,YAAYC;oBAAgB,QAAQE;8BAC9E,kBAACnB,SAASA;wBAAC,MAAMmB,OAAO,SAAS;;;YAGvC;YAEA,OAAO/B,qBAAqB,CAC1BA,aAAU,CAAC;gBACT,KAAK,IACHO,WAAWA,CACTiB,yBACA,IAACpB,UAAUA;kCACT,kBAAC0B;wBAEH;wBAAE,WAAWL,eAAe,SAAS;oBAAC;gBAE1C,OAAO,CAACQ,QAAU,IAAIpB,sBAAsBA,CAAC;wBAAEoB;oBAAM;YACvD,IACA,CAACC,OAASlC,WAAW,CAAC,IAAMkC,KAAK,OAAO;YAE1C,OAAOlC,cAAc,CAAC,IAAM0B,qBAAqB,OAAO;QAC1D;QAEA,OAAO;YAAEH;QAAQ;IACnB;AACF;IAEA,OAAgB,QAAQtB,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;AACxD"}
@@ -1,5 +1,4 @@
1
1
  import { Context, Effect, Layer } from 'effect';
2
- import { BrowserEffectRunner } from './browser-effect-runner.js';
3
2
  import { BrowserRenderer } from './browser-renderer.js';
4
3
  import { NavigationApi } from './navigation-api.js';
5
4
  import { RouteLoader } from './route-loader.js';
@@ -27,5 +26,5 @@ export declare class RouteRefresher extends RouteRefresher_base {
27
26
  replace?: (replacement: RouteRefreshImplementation) => Effect.Effect<void, never, never>;
28
27
  }) => Layer.Layer<RouteRefresher, never, never>;
29
28
  }
30
- export declare const installRouteRefresh: Effect.Effect<void, never, BrowserEffectRunner | BrowserRenderer | NavigationApi | RouteLoader | RouteRefresher | import("effect/Scope").Scope>;
29
+ export declare const installRouteRefresh: Effect.Effect<void, never, BrowserRenderer | NavigationApi | RouteLoader | RouteRefresher | import("effect/Scope").Scope>;
31
30
  export {};
@@ -1,6 +1,5 @@
1
- import { Context, Effect, FiberMap, Layer, Ref, Schema } from "effect";
1
+ import { Context, Effect, FiberMap, Layer, Ref } from "effect";
2
2
  import { addTransitionType, startTransition } from "react";
3
- import { BrowserEffectRunner } from "./browser-effect-runner.js";
4
3
  import { BrowserRenderer } from "./browser-renderer.js";
5
4
  import { NavigationApi } from "./navigation-api.js";
6
5
  import { isRoutedNavigation, preserveRequestedHash } from "./navigation-routing.js";
@@ -18,13 +17,7 @@ import { RouteLoader } from "./route-loader.js";
18
17
 
19
18
 
20
19
 
21
-
22
-
23
20
  const CurrentRouteRefreshKey = 'CurrentRouteRefresh';
24
- class RouteRefreshError extends Schema.TaggedError()('RouteRefreshError', {
25
- cause: Schema.Defect()
26
- }) {
27
- }
28
21
  class RouteRefresher extends Context.Service()('ersc/client/RouteRefresher', {
29
22
  make: Effect.gen(function*() {
30
23
  const navigationApi = yield* NavigationApi;
@@ -55,7 +48,6 @@ const installRouteRefresh = Effect.gen(function*() {
55
48
  const navigationApi = yield* NavigationApi;
56
49
  const routeLoader = yield* RouteLoader;
57
50
  const routeRefresher = yield* RouteRefresher;
58
- const run = yield* BrowserEffectRunner;
59
51
  const refreshes = yield* FiberMap.make();
60
52
  const waitForNavigationIdle = Effect.suspend(()=>{
61
53
  const transition = navigationApi.getTransition();
@@ -109,18 +101,13 @@ const installRouteRefresh = Effect.gen(function*() {
109
101
  discard: true
110
102
  }).pipe(Effect.andThen(Effect.sync(commitRefresh)), Effect.ensuring(resource.release));
111
103
  });
112
- const refreshInReactTransition = (transitionType)=>Effect.callback((resume, signal)=>{
113
- startTransition(()=>run(refreshRoute(transitionType), {
114
- signal
115
- }).then(()=>resume(Effect["void"]), (cause)=>resume(Effect.fail(new RouteRefreshError({
116
- cause
117
- })))));
118
- });
119
104
  const refreshCurrentRoute = Effect.fnUntraced(function*(transitionType) {
120
105
  routeLoader.invalidate();
121
106
  yield* waitForNavigationIdle;
122
- yield* Effect.raceFirst(refreshInReactTransition(transitionType), waitForRoutedNavigation);
123
- }, Effect["catch"]((cause)=>Effect.logError('Failed to refresh the current route.', cause)));
107
+ // Only publication belongs to a React Transition. An async Action waiting for this
108
+ // effect would prevent the very UI commit that completes the refresh.
109
+ yield* Effect.raceFirst(refreshRoute(transitionType), waitForRoutedNavigation);
110
+ }, Effect.scoped, Effect["catch"]((cause)=>Effect.logError('Failed to refresh the current route.', cause)));
124
111
  yield* routeRefresher.replace({
125
112
  interruptCurrentRouteRefresh: FiberMap.remove(refreshes, CurrentRouteRefreshKey),
126
113
  refreshCurrentRoute: (transitionType)=>FiberMap.run(refreshes, CurrentRouteRefreshKey, refreshCurrentRoute(transitionType)).pipe(Effect.asVoid)
@@ -1 +1 @@
1
- {"version":3,"file":"client/route-refresh.js","sources":["../../src/client/route-refresh.ts"],"sourcesContent":["import { Context, Effect, FiberMap, Layer, Ref, Schema } from 'effect';\nimport { addTransitionType, startTransition } from 'react';\n\nimport { BrowserEffectRunner } from './browser-effect-runner';\nimport { BrowserRenderer } from './browser-renderer';\nimport { NavigationApi } from './navigation-api';\nimport { isRoutedNavigation, preserveRequestedHash } from './navigation-routing';\nimport { RouteLoader } from './route-loader';\n\nconst CurrentRouteRefreshKey = 'CurrentRouteRefresh';\n\nclass RouteRefreshError extends Schema.TaggedError<RouteRefreshError>()('RouteRefreshError', {\n cause: Schema.Defect(),\n}) {}\n\nexport type RouteRefreshTransitionType = 'hmr-refresh' | 'server-function';\n\ntype RouteRefreshImplementation = {\n readonly interruptCurrentRouteRefresh: Effect.Effect<void>;\n readonly refreshCurrentRoute: (transitionType: RouteRefreshTransitionType) => Effect.Effect<void>;\n};\n\nexport class RouteRefresher extends Context.Service<RouteRefresher>()(\n 'ersc/client/RouteRefresher',\n {\n make: Effect.gen(function* () {\n const navigationApi = yield* NavigationApi;\n const implementation = yield* Ref.make<RouteRefreshImplementation>({\n interruptCurrentRouteRefresh: Effect.void,\n refreshCurrentRoute: () => Effect.sync(navigationApi.reloadDocument),\n });\n\n const interruptCurrentRouteRefresh = Effect.gen(function* () {\n const current = yield* Ref.get(implementation);\n yield* current.interruptCurrentRouteRefresh;\n });\n\n const refreshCurrentRoute = Effect.fn('RouteRefresher.refreshCurrentRoute')(function* (\n transitionType: RouteRefreshTransitionType,\n ) {\n const current = yield* Ref.get(implementation);\n yield* current.refreshCurrentRoute(transitionType);\n });\n\n return {\n interruptCurrentRouteRefresh,\n refreshCurrentRoute,\n replace: (replacement: RouteRefreshImplementation) => Ref.set(implementation, replacement),\n };\n }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n\n static readonly layerTest = Layer.mock(this);\n}\n\nexport const installRouteRefresh = Effect.gen(function* () {\n const browserRenderer = yield* BrowserRenderer;\n const navigationApi = yield* NavigationApi;\n const routeLoader = yield* RouteLoader;\n const routeRefresher = yield* RouteRefresher;\n const run = yield* BrowserEffectRunner;\n const refreshes = yield* FiberMap.make<typeof CurrentRouteRefreshKey>();\n\n const waitForNavigationIdle = Effect.suspend(() => {\n const transition = navigationApi.getTransition();\n return transition === null\n ? Effect.succeed('Idle' as const)\n : Effect.promise(() =>\n transition.finished.then(\n () => undefined,\n () => undefined,\n ),\n ).pipe(Effect.as('Settled' as const));\n }).pipe(Effect.repeat({ while: (state) => state === 'Settled' }), Effect.asVoid);\n\n const waitForRoutedNavigation = Effect.callback<void>((resume) => {\n const onNavigate = (event: NavigateEvent) => {\n if (isRoutedNavigation(event)) {\n resume(Effect.void);\n }\n };\n const unsubscribe = navigationApi.subscribe(onNavigate);\n return Effect.sync(unsubscribe);\n });\n\n const refreshRoute = Effect.fnUntraced(function* (transitionType: RouteRefreshTransitionType) {\n const currentEntry = navigationApi.getCurrentEntry();\n const destination = new URL(currentEntry?.url ?? navigationApi.getCurrentUrl());\n const resource = yield* routeLoader.load({\n destination: {\n id: currentEntry?.id ?? '',\n url: destination.href,\n },\n navigationType: 'replace',\n });\n\n if (resource._tag === 'Document') {\n yield* resource.release;\n yield* Effect.sync(navigationApi.reloadDocument);\n return;\n }\n\n const resolvedDestination = preserveRequestedHash(destination, resource.resolvedUrl);\n if (resolvedDestination.href !== destination.href) {\n yield* resource.release;\n yield* Effect.sync(() => navigationApi.replaceDocument(resolvedDestination.href));\n return;\n }\n\n const commitRefresh = routeLoader.prepareRefresh(resource.routeTree);\n let renderCommitted!: Promise<void>;\n yield* Effect.sync(() => {\n startTransition(() => {\n addTransitionType(transitionType);\n renderCommitted = browserRenderer.refresh(resource.routeTree);\n });\n });\n yield* Effect.all([Effect.promise(() => renderCommitted), resource.completed], {\n concurrency: 'unbounded',\n discard: true,\n }).pipe(Effect.andThen(Effect.sync(commitRefresh)), Effect.ensuring(resource.release));\n });\n\n const refreshInReactTransition = (transitionType: RouteRefreshTransitionType) =>\n Effect.callback<void, RouteRefreshError>((resume, signal) => {\n startTransition(() =>\n run(refreshRoute(transitionType), { signal }).then(\n () => resume(Effect.void),\n (cause) => resume(Effect.fail(new RouteRefreshError({ cause }))),\n ),\n );\n });\n\n const refreshCurrentRoute = Effect.fnUntraced(\n function* (transitionType: RouteRefreshTransitionType) {\n routeLoader.invalidate();\n yield* waitForNavigationIdle;\n yield* Effect.raceFirst(refreshInReactTransition(transitionType), waitForRoutedNavigation);\n },\n Effect.catch((cause) => Effect.logError('Failed to refresh the current route.', cause)),\n );\n\n yield* routeRefresher.replace({\n interruptCurrentRouteRefresh: FiberMap.remove(refreshes, CurrentRouteRefreshKey),\n refreshCurrentRoute: (transitionType) =>\n FiberMap.run(refreshes, CurrentRouteRefreshKey, refreshCurrentRoute(transitionType)).pipe(\n Effect.asVoid,\n ),\n });\n});\n"],"names":["Context","Effect","FiberMap","Layer","Ref","Schema","addTransitionType","startTransition","BrowserEffectRunner","BrowserRenderer","NavigationApi","isRoutedNavigation","preserveRequestedHash","RouteLoader","CurrentRouteRefreshKey","RouteRefreshError","RouteRefresher","navigationApi","implementation","interruptCurrentRouteRefresh","current","refreshCurrentRoute","transitionType","replacement","installRouteRefresh","browserRenderer","routeLoader","routeRefresher","run","refreshes","waitForNavigationIdle","transition","undefined","state","waitForRoutedNavigation","resume","onNavigate","event","unsubscribe","refreshRoute","currentEntry","destination","URL","resource","resolvedDestination","commitRefresh","renderCommitted","refreshInReactTransition","signal","cause"],"mappings":"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"}
1
+ {"version":3,"file":"client/route-refresh.js","sources":["../../src/client/route-refresh.ts"],"sourcesContent":["import { Context, Effect, FiberMap, Layer, Ref } from 'effect';\nimport { addTransitionType, startTransition } from 'react';\n\nimport { BrowserRenderer } from './browser-renderer';\nimport { NavigationApi } from './navigation-api';\nimport { isRoutedNavigation, preserveRequestedHash } from './navigation-routing';\nimport { RouteLoader } from './route-loader';\n\nconst CurrentRouteRefreshKey = 'CurrentRouteRefresh';\n\nexport type RouteRefreshTransitionType = 'hmr-refresh' | 'server-function';\n\ntype RouteRefreshImplementation = {\n readonly interruptCurrentRouteRefresh: Effect.Effect<void>;\n readonly refreshCurrentRoute: (transitionType: RouteRefreshTransitionType) => Effect.Effect<void>;\n};\n\nexport class RouteRefresher extends Context.Service<RouteRefresher>()(\n 'ersc/client/RouteRefresher',\n {\n make: Effect.gen(function* () {\n const navigationApi = yield* NavigationApi;\n const implementation = yield* Ref.make<RouteRefreshImplementation>({\n interruptCurrentRouteRefresh: Effect.void,\n refreshCurrentRoute: () => Effect.sync(navigationApi.reloadDocument),\n });\n\n const interruptCurrentRouteRefresh = Effect.gen(function* () {\n const current = yield* Ref.get(implementation);\n yield* current.interruptCurrentRouteRefresh;\n });\n\n const refreshCurrentRoute = Effect.fn('RouteRefresher.refreshCurrentRoute')(function* (\n transitionType: RouteRefreshTransitionType,\n ) {\n const current = yield* Ref.get(implementation);\n yield* current.refreshCurrentRoute(transitionType);\n });\n\n return {\n interruptCurrentRouteRefresh,\n refreshCurrentRoute,\n replace: (replacement: RouteRefreshImplementation) => Ref.set(implementation, replacement),\n };\n }),\n },\n) {\n static readonly layer = Layer.effect(this, this.make);\n\n static readonly layerTest = Layer.mock(this);\n}\n\nexport const installRouteRefresh = Effect.gen(function* () {\n const browserRenderer = yield* BrowserRenderer;\n const navigationApi = yield* NavigationApi;\n const routeLoader = yield* RouteLoader;\n const routeRefresher = yield* RouteRefresher;\n const refreshes = yield* FiberMap.make<typeof CurrentRouteRefreshKey>();\n\n const waitForNavigationIdle = Effect.suspend(() => {\n const transition = navigationApi.getTransition();\n return transition === null\n ? Effect.succeed('Idle' as const)\n : Effect.promise(() =>\n transition.finished.then(\n () => undefined,\n () => undefined,\n ),\n ).pipe(Effect.as('Settled' as const));\n }).pipe(Effect.repeat({ while: (state) => state === 'Settled' }), Effect.asVoid);\n\n const waitForRoutedNavigation = Effect.callback<void>((resume) => {\n const onNavigate = (event: NavigateEvent) => {\n if (isRoutedNavigation(event)) {\n resume(Effect.void);\n }\n };\n const unsubscribe = navigationApi.subscribe(onNavigate);\n return Effect.sync(unsubscribe);\n });\n\n const refreshRoute = Effect.fnUntraced(function* (transitionType: RouteRefreshTransitionType) {\n const currentEntry = navigationApi.getCurrentEntry();\n const destination = new URL(currentEntry?.url ?? navigationApi.getCurrentUrl());\n const resource = yield* routeLoader.load({\n destination: {\n id: currentEntry?.id ?? '',\n url: destination.href,\n },\n navigationType: 'replace',\n });\n\n if (resource._tag === 'Document') {\n yield* resource.release;\n yield* Effect.sync(navigationApi.reloadDocument);\n return;\n }\n\n const resolvedDestination = preserveRequestedHash(destination, resource.resolvedUrl);\n if (resolvedDestination.href !== destination.href) {\n yield* resource.release;\n yield* Effect.sync(() => navigationApi.replaceDocument(resolvedDestination.href));\n return;\n }\n\n const commitRefresh = routeLoader.prepareRefresh(resource.routeTree);\n let renderCommitted!: Promise<void>;\n yield* Effect.sync(() => {\n startTransition(() => {\n addTransitionType(transitionType);\n renderCommitted = browserRenderer.refresh(resource.routeTree);\n });\n });\n yield* Effect.all([Effect.promise(() => renderCommitted), resource.completed], {\n concurrency: 'unbounded',\n discard: true,\n }).pipe(Effect.andThen(Effect.sync(commitRefresh)), Effect.ensuring(resource.release));\n });\n\n const refreshCurrentRoute = Effect.fnUntraced(\n function* (transitionType: RouteRefreshTransitionType) {\n routeLoader.invalidate();\n yield* waitForNavigationIdle;\n // Only publication belongs to a React Transition. An async Action waiting for this\n // effect would prevent the very UI commit that completes the refresh.\n yield* Effect.raceFirst(refreshRoute(transitionType), waitForRoutedNavigation);\n },\n Effect.scoped,\n Effect.catch((cause) => Effect.logError('Failed to refresh the current route.', cause)),\n );\n\n yield* routeRefresher.replace({\n interruptCurrentRouteRefresh: FiberMap.remove(refreshes, CurrentRouteRefreshKey),\n refreshCurrentRoute: (transitionType) =>\n FiberMap.run(refreshes, CurrentRouteRefreshKey, refreshCurrentRoute(transitionType)).pipe(\n Effect.asVoid,\n ),\n });\n});\n"],"names":["Context","Effect","FiberMap","Layer","Ref","addTransitionType","startTransition","BrowserRenderer","NavigationApi","isRoutedNavigation","preserveRequestedHash","RouteLoader","CurrentRouteRefreshKey","RouteRefresher","navigationApi","implementation","interruptCurrentRouteRefresh","current","refreshCurrentRoute","transitionType","replacement","installRouteRefresh","browserRenderer","routeLoader","routeRefresher","refreshes","waitForNavigationIdle","transition","undefined","state","waitForRoutedNavigation","resume","onNavigate","event","unsubscribe","refreshRoute","currentEntry","destination","URL","resource","resolvedDestination","commitRefresh","renderCommitted","cause"],"mappings":"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"}