effective-rsc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/LICENSE +21 -0
  2. package/LLMS.md +382 -0
  3. package/README.md +235 -0
  4. package/bin/ersc.js +3 -0
  5. package/dist/application/component.d.ts +15 -0
  6. package/dist/application/component.js +20 -0
  7. package/dist/application/component.js.map +1 -0
  8. package/dist/application/definition.d.ts +31 -0
  9. package/dist/application/definition.js +57 -0
  10. package/dist/application/definition.js.map +1 -0
  11. package/dist/application/ersc-identity.d.ts +25 -0
  12. package/dist/application/ersc-identity.js +23 -0
  13. package/dist/application/ersc-identity.js.map +1 -0
  14. package/dist/application/ersc.d.ts +28 -0
  15. package/dist/application/ersc.js +72 -0
  16. package/dist/application/ersc.js.map +1 -0
  17. package/dist/application/layout.d.ts +18 -0
  18. package/dist/application/layout.js +17 -0
  19. package/dist/application/layout.js.map +1 -0
  20. package/dist/application/loading.d.ts +17 -0
  21. package/dist/application/loading.js +14 -0
  22. package/dist/application/loading.js.map +1 -0
  23. package/dist/application/middleware.d.ts +36 -0
  24. package/dist/application/middleware.js +46 -0
  25. package/dist/application/middleware.js.map +1 -0
  26. package/dist/application/page.d.ts +58 -0
  27. package/dist/application/page.js +50 -0
  28. package/dist/application/page.js.map +1 -0
  29. package/dist/application/render-runtime.d.ts +9 -0
  30. package/dist/application/render-runtime.js +28 -0
  31. package/dist/application/render-runtime.js.map +1 -0
  32. package/dist/application/route-graph.d.ts +22 -0
  33. package/dist/application/route-graph.js +68 -0
  34. package/dist/application/route-graph.js.map +1 -0
  35. package/dist/application/route-path.d.ts +31 -0
  36. package/dist/application/route-path.js +67 -0
  37. package/dist/application/route-path.js.map +1 -0
  38. package/dist/application/routes.d.ts +66 -0
  39. package/dist/application/routes.js +159 -0
  40. package/dist/application/routes.js.map +1 -0
  41. package/dist/application/server-fn.d.ts +30 -0
  42. package/dist/application/server-fn.js +58 -0
  43. package/dist/application/server-fn.js.map +1 -0
  44. package/dist/build/build.d.ts +26 -0
  45. package/dist/build/build.js +59 -0
  46. package/dist/build/build.js.map +1 -0
  47. package/dist/build/compiled-server.d.ts +53 -0
  48. package/dist/build/compiled-server.js +77 -0
  49. package/dist/build/compiled-server.js.map +1 -0
  50. package/dist/build/contract.d.ts +23 -0
  51. package/dist/build/contract.js +41 -0
  52. package/dist/build/contract.js.map +1 -0
  53. package/dist/build/dev-channel.d.ts +20 -0
  54. package/dist/build/dev-channel.js +71 -0
  55. package/dist/build/dev-channel.js.map +1 -0
  56. package/dist/build/dev.d.ts +41 -0
  57. package/dist/build/dev.js +186 -0
  58. package/dist/build/dev.js.map +1 -0
  59. package/dist/build/rsc-entry.d.ts +4 -0
  60. package/dist/build/rsc-entry.js +17 -0
  61. package/dist/build/rsc-entry.js.map +1 -0
  62. package/dist/build/rspack-config.d.ts +17 -0
  63. package/dist/build/rspack-config.js +259 -0
  64. package/dist/build/rspack-config.js.map +1 -0
  65. package/dist/build/rspack.d.ts +45 -0
  66. package/dist/build/rspack.js +231 -0
  67. package/dist/build/rspack.js.map +1 -0
  68. package/dist/build/terminal.d.ts +9 -0
  69. package/dist/build/terminal.js +17 -0
  70. package/dist/build/terminal.js.map +1 -0
  71. package/dist/cli.d.ts +14 -0
  72. package/dist/cli.js +116 -0
  73. package/dist/cli.js.map +1 -0
  74. package/dist/client/application.d.ts +2 -0
  75. package/dist/client/application.js +73 -0
  76. package/dist/client/application.js.map +1 -0
  77. package/dist/client/browser-capabilities.d.ts +9 -0
  78. package/dist/client/browser-capabilities.js +20 -0
  79. package/dist/client/browser-capabilities.js.map +1 -0
  80. package/dist/client/browser-effect-runner.d.ts +13 -0
  81. package/dist/client/browser-effect-runner.js +13 -0
  82. package/dist/client/browser-effect-runner.js.map +1 -0
  83. package/dist/client/browser-renderer.d.ts +70 -0
  84. package/dist/client/browser-renderer.js +159 -0
  85. package/dist/client/browser-renderer.js.map +1 -0
  86. package/dist/client/browser-screen.d.ts +2 -0
  87. package/dist/client/browser-screen.js +215 -0
  88. package/dist/client/browser-screen.js.map +1 -0
  89. package/dist/client/call-server.d.ts +8 -0
  90. package/dist/client/call-server.js +151 -0
  91. package/dist/client/call-server.js.map +1 -0
  92. package/dist/client/client-router.d.ts +6 -0
  93. package/dist/client/client-router.js +588 -0
  94. package/dist/client/client-router.js.map +1 -0
  95. package/dist/client/entry.d.ts +1 -0
  96. package/dist/client/entry.js +12 -0
  97. package/dist/client/entry.js.map +1 -0
  98. package/dist/client/flight-client.d.ts +83 -0
  99. package/dist/client/flight-client.js +146 -0
  100. package/dist/client/flight-client.js.map +1 -0
  101. package/dist/client/initial-flight-stream.d.ts +18 -0
  102. package/dist/client/initial-flight-stream.js +60 -0
  103. package/dist/client/initial-flight-stream.js.map +1 -0
  104. package/dist/client/navigation-api.d.ts +34 -0
  105. package/dist/client/navigation-api.js +25 -0
  106. package/dist/client/navigation-api.js.map +1 -0
  107. package/dist/client/navigation-routing.d.ts +3 -0
  108. package/dist/client/navigation-routing.js +8 -0
  109. package/dist/client/navigation-routing.js.map +1 -0
  110. package/dist/client/react-dom-renderer.d.ts +20 -0
  111. package/dist/client/react-dom-renderer.js +101 -0
  112. package/dist/client/react-dom-renderer.js.map +1 -0
  113. package/dist/client/route-loader.d.ts +81 -0
  114. package/dist/client/route-loader.js +128 -0
  115. package/dist/client/route-loader.js.map +1 -0
  116. package/dist/client/route-refresh.d.ts +31 -0
  117. package/dist/client/route-refresh.js +132 -0
  118. package/dist/client/route-refresh.js.map +1 -0
  119. package/dist/client/route-tree.d.ts +7 -0
  120. package/dist/client/route-tree.js +34 -0
  121. package/dist/client/route-tree.js.map +1 -0
  122. package/dist/dev/channel.d.ts +18 -0
  123. package/dist/dev/channel.js +30 -0
  124. package/dist/dev/channel.js.map +1 -0
  125. package/dist/dev/client.d.ts +3 -0
  126. package/dist/dev/client.js +132 -0
  127. package/dist/dev/client.js.map +1 -0
  128. package/dist/dev/hmr-update.d.ts +20 -0
  129. package/dist/dev/hmr-update.js +28 -0
  130. package/dist/dev/hmr-update.js.map +1 -0
  131. package/dist/dev/panel.d.ts +41 -0
  132. package/dist/dev/panel.js +9946 -0
  133. package/dist/dev/panel.js.map +1 -0
  134. package/dist/dev/runtime-failure.d.ts +11 -0
  135. package/dist/dev/runtime-failure.js +32 -0
  136. package/dist/dev/runtime-failure.js.map +1 -0
  137. package/dist/index.d.ts +1 -0
  138. package/dist/index.js +6 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/rsc/flight.d.ts +16 -0
  141. package/dist/rsc/flight.js +6 -0
  142. package/dist/rsc/flight.js.map +1 -0
  143. package/dist/rsc/render-route-tree.d.ts +11 -0
  144. package/dist/rsc/render-route-tree.js +52 -0
  145. package/dist/rsc/render-route-tree.js.map +1 -0
  146. package/dist/rsc/route-tree.d.ts +6 -0
  147. package/dist/rsc/route-tree.js +5 -0
  148. package/dist/rsc/route-tree.js.map +1 -0
  149. package/dist/server/application.d.ts +17 -0
  150. package/dist/server/application.js +178 -0
  151. package/dist/server/application.js.map +1 -0
  152. package/dist/server/flight-html-stream.d.ts +19 -0
  153. package/dist/server/flight-html-stream.js +193 -0
  154. package/dist/server/flight-html-stream.js.map +1 -0
  155. package/dist/server/flight-renderer.d.ts +31 -0
  156. package/dist/server/flight-renderer.js +47 -0
  157. package/dist/server/flight-renderer.js.map +1 -0
  158. package/dist/server/html-renderer.d.ts +28 -0
  159. package/dist/server/html-renderer.js +82 -0
  160. package/dist/server/html-renderer.js.map +1 -0
  161. package/dist/server/request-outcome.d.ts +8 -0
  162. package/dist/server/request-outcome.js +5 -0
  163. package/dist/server/request-outcome.js.map +1 -0
  164. package/dist/server/server-config.d.ts +17 -0
  165. package/dist/server/server-config.js +18 -0
  166. package/dist/server/server-config.js.map +1 -0
  167. package/dist/server/server-fn-outcome.d.ts +13 -0
  168. package/dist/server/server-fn-outcome.js +24 -0
  169. package/dist/server/server-fn-outcome.js.map +1 -0
  170. package/dist/server/server-fn-request.d.ts +24 -0
  171. package/dist/server/server-fn-request.js +160 -0
  172. package/dist/server/server-fn-request.js.map +1 -0
  173. package/dist/unsupported.d.ts +0 -0
  174. package/dist/unsupported.js +5 -0
  175. package/dist/unsupported.js.map +1 -0
  176. package/docs/01-getting-started/01_first-application.tsx +26 -0
  177. package/docs/01-getting-started/20_styling.tsx +6 -0
  178. package/docs/01-getting-started/index.md +27 -0
  179. package/docs/01-getting-started/styles.css +1 -0
  180. package/docs/02-guides/01-server-functions/10_ersc.ts +6 -0
  181. package/docs/02-guides/01-server-functions/20_follow-author.ts +15 -0
  182. package/docs/02-guides/01-server-functions/30_follow-author-button.tsx +19 -0
  183. package/docs/02-guides/01-server-functions/40_application.tsx +30 -0
  184. package/docs/02-guides/01-server-functions/index.md +19 -0
  185. package/docs/02-guides/02-services/10_catalog.ts +22 -0
  186. package/docs/02-guides/02-services/20_application.tsx +44 -0
  187. package/docs/02-guides/02-services/index.md +17 -0
  188. package/docs/02-guides/03-routing/10_ersc.ts +6 -0
  189. package/docs/02-guides/03-routing/10_layouts.tsx +36 -0
  190. package/docs/02-guides/03-routing/20_pages.tsx +23 -0
  191. package/docs/02-guides/03-routing/30_routes.tsx +17 -0
  192. package/docs/02-guides/03-routing/40_application.ts +7 -0
  193. package/docs/02-guides/03-routing/index.md +22 -0
  194. package/docs/02-guides/04-middleware/10_auth.ts +27 -0
  195. package/docs/02-guides/04-middleware/20_account-page.tsx +13 -0
  196. package/docs/02-guides/04-middleware/30_update-profile.ts +16 -0
  197. package/docs/02-guides/04-middleware/40_application.tsx +20 -0
  198. package/docs/02-guides/04-middleware/index.md +25 -0
  199. package/docs/02-guides/05-http/10_application-layer.tsx +60 -0
  200. package/docs/02-guides/05-http/index.md +23 -0
  201. package/docs/02-guides/index.md +11 -0
  202. package/docs/03-advanced/01-request-runtime-and-lifetimes/index.md +13 -0
  203. package/docs/03-advanced/02-client-navigation/index.md +61 -0
  204. package/docs/03-advanced/03-server-function-execution-and-refresh/index.md +17 -0
  205. package/docs/03-advanced/index.md +11 -0
  206. package/docs/04-api-reference/01-application/index.md +8 -0
  207. package/docs/04-api-reference/02-page/index.md +12 -0
  208. package/docs/04-api-reference/03-layout/index.md +4 -0
  209. package/docs/04-api-reference/04-loading/index.md +4 -0
  210. package/docs/04-api-reference/05-component/10_component.tsx +18 -0
  211. package/docs/04-api-reference/05-component/index.md +10 -0
  212. package/docs/04-api-reference/06-middleware/index.md +33 -0
  213. package/docs/04-api-reference/07-routes/index.md +20 -0
  214. package/docs/04-api-reference/08-server-fn/index.md +43 -0
  215. package/docs/04-api-reference/index.md +17 -0
  216. package/docs/environment.d.ts +1 -0
  217. package/docs/index.md +15 -0
  218. package/logo-dark.svg +6 -0
  219. package/logo.svg +6 -0
  220. package/package.json +87 -0
@@ -0,0 +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":";;;;;;;;;;;;;;;;;;;AAAoD;AACO;AAKX;AAEc;AACT;AACN;AACE;AACJ;AACI;AAEjD,MAAMc,iBAAiBA,SAASZ,kBAAkB,GAAsB,qBAAqB;IAC3F,OAAOA,aAAa;IACpB,SAASA,aAAa;AACxB;AAAI;AAaG,MAAMa,iBAAiBA,GAAGf,UAAU,CAAC;IAC1C,MAAMgB,kBAAkB,OAAOP,eAAeA;IAC9C,MAAMQ,gBAAgB,OAAON,aAAaA;IAC1C,MAAMO,MAAM,OAAOV,mBAAmBA;IACtC,MAAMW,eAAe,OAAOT,YAAYA;IACxC,MAAMU,cAAc,OAAOR,WAAWA;IACtC,MAAMS,iBAAiB,OAAOR,cAAcA;IAC5C,MAAMS,wBAAwBrB,eAAe,CAAC;IAC9C,MAAMsB,sBAAsB,CAACC;QAC3B,IACEvB,cAAc,CAACqB,2BAA2BE,WAAW,KAAK,IAC1DP,cAAc,aAAa,OAAO,MAClC;YACA,OAAO;QACT;QAEA,MAAMQ,eAAeR,cAAc,eAAe;QAClD,OAAQO,WAAW,IAAI;YACrB,KAAK;gBACH,OAAOC,cAAc,OAAOD,WAAW,EAAE,GAAG,aAAa;YAC3D,KAAK;gBACH,OAAOC,iBAAiB,QAAQR,cAAc,aAAa,OAAOO,WAAW,GAAG,GAC5E,aACA;QACR;IACF;IACA,MAAME,aAAa1B,iBAAiB,CAAC,UACnC2B,EAAU,EACVC,IAA4B,EAC5BC,gBAA+C;QAE/C,MAAMJ,eAAeR,cAAc,eAAe;QAClD,MAAMa,QAAQ7B,0BAA0B,CAACqB;QACzC,MAAME,aACJC,iBAAiB,OACb;YAAE,MAAM;YAAcK;YAAO,KAAKb,cAAc,aAAa;QAAG,IAChE;YACE,MAAM;YACN,IAAIQ,aAAa,EAAE;YACnBK;YACA,KAAKL,aAAa,GAAG,IAAIR,cAAc,aAAa;QACtD;QACN,MAAMc,sBAAsB1B,2BAA2BA;QACvD,MAAM2B,OAAO,OAAOhC,iBAAiB,CAAC;YACpC,KAAK,IAAMM,WAAWA,CAACsB,MAAM;oBAAEG;gBAAoB;YACnD,OAAO,CAACE,QAAU,IAAInB,iBAAiBA,CAAC;oBAAEmB;oBAAO,SAAS;gBAA8B;QAC1F;QACA,MAAMC,WAAW,OAAOf,aACrB,IAAI,CAAC;YACJ,MAAM;YACNa;YACA,aAAa,IAAIG,IAAIX,WAAW,GAAG;YACnCG;YACAI;QACF,GACC,IAAI,CACH/B,eAAe,CACb,CAACiC,QAAU,IAAInB,iBAAiBA,CAAC;gBAAEmB;gBAAO,SAAS;YAAkC;QAG3F,IAAIC,SAAS,IAAI,KAAK,YAAY;YAChC,OAAOA,SAAS,OAAO;YACvB,OAAO,OAAO,IAAIpB,iBAAiBA,CAAC;gBAClC,OAAO,IAAIsB,MAAM;gBACjB,SAAS;YACX;QACF;QACA,MAAMC,iBAAiBH,SAAS,OAAO,CAAC,cAAc;QACtD,IAAIG,mBAAmB,MAAM;YAC3B,OAAOH,SAAS,OAAO;YACvB,OAAO,OAAO,IAAIpB,iBAAiBA,CAAC;gBAClC,OAAO,IAAIsB,MAAM;gBACjB,SAAS;YACX;QACF;QACA,OAAQC,eAAe,IAAI;YACzB,KAAK;gBACHR,iBAAiB,MAAM,CACrB,IAAIf,iBAAiBA,CAAC;oBACpB,OAAOuB,eAAe,KAAK;oBAC3B,SAAS;gBACX;gBAEF;YACF,KAAK;gBACHR,iBAAiB,OAAO,CAACQ,eAAe,KAAK;gBAC7C;QACJ;QACA,yFAAyF;QACzF,4FAA4F;QAC5F,OAAOrC,cAAc,CAAC,IACpB6B,iBAAiB,OAAO,CAAC,IAAI,CAC3B,IAAMS,WACN,IAAMA;QAGV,IAAIf,oBAAoBC,gBAAgB,gBAAgB;YACtD,OAAOU,SAAS,OAAO;YACvB,OAAOb,eAAe,mBAAmB,CAAC;YAC1C;QACF;QAEA,OAAOA,eAAe,4BAA4B;QAClD,MAAMkB,gBAAgBnB,YAAY,cAAc,CAACc,SAAS,OAAO,CAAC,SAAS;QAC3E,IAAIM;QACJ,OAAOxC,WAAW,CAAC;YACjBI,eAAeA,CAAC;gBACdD,iBAAiBA,CAAC;gBAClB,2FAA2F;gBAC3F,iCAAiC;gBACjCqC,kBAAkBxB,gBAAgB,OAAO,CAACkB,SAAS,OAAO,CAAC,SAAS;YACtE;QACF;QACA,OAAOlC,UAAU,CAAC;YAACkC,SAAS,SAAS;YAAElC,cAAc,CAAC,IAAMwC;SAAiB,EAAE;YAC7E,aAAa;YACb,SAAS;QACX,GAAG,IAAI,CACLxC,cAAc,CAACA,WAAW,CAACuC,iBAC3BvC,cAAc,CAAC,IAAMkC,SAAS,OAAO,GACrClC,iBAAiB;IAErB;IAEA,OAAOA,WAAW,CAAC;QACjBO,iBAAiBA,CAAC,CAACoB,IAAIC;YACrB,MAAMC,mBAAmBY,QAAQ,aAAa;YAC9C,KAAKvB,IAAIQ,WAAWC,IAAIC,MAAMC,mBAAmB,KAAK,CAACA,iBAAiB,MAAM;YAC9E,OAAOA,iBAAiB,OAAO;QACjC;IACF;AACF,GAAG"}
@@ -0,0 +1,6 @@
1
+ import { Effect } from 'effect';
2
+ import { BrowserEffectRunner } from './browser-effect-runner.js';
3
+ import { BrowserRenderer } from './browser-renderer.js';
4
+ import { NavigationApi } from './navigation-api.js';
5
+ import { RouteLoader } from './route-loader.js';
6
+ export declare const installClientRouter: Effect.Effect<void, never, BrowserEffectRunner | BrowserRenderer | NavigationApi | RouteLoader | import("effect/Scope").Scope>;
@@ -0,0 +1,588 @@
1
+ import { Effect, Exit, MutableRef } from "effect";
2
+ import { addTransitionType, startTransition } from "react";
3
+ import { BrowserEffectRunner } from "./browser-effect-runner.js";
4
+ import { BrowserRenderer } from "./browser-renderer.js";
5
+ import { NavigationApi } from "./navigation-api.js";
6
+ import { NativeDocumentNavigationInfo, isRoutedNavigation, preserveRequestedHash } from "./navigation-routing.js";
7
+ import { RouteLoader } from "./route-loader.js";
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+ const getNavigationTransitionTypes = (event, fromIndex)=>{
24
+ const types = [
25
+ 'navigation',
26
+ `navigation-${event.navigationType}`,
27
+ ...event.hasUAVisualTransition ? [
28
+ 'navigation-ua-visual-transition'
29
+ ] : []
30
+ ];
31
+ if (event.navigationType === 'push') {
32
+ return [
33
+ ...types,
34
+ 'navigation-forward'
35
+ ];
36
+ }
37
+ if (event.navigationType !== 'traverse') {
38
+ return types;
39
+ }
40
+ if (fromIndex === null || fromIndex === event.destination.index) {
41
+ return types;
42
+ }
43
+ return [
44
+ ...types,
45
+ event.destination.index < fromIndex ? 'navigation-backward' : 'navigation-forward'
46
+ ];
47
+ };
48
+ const reduceRouterState = (state, event)=>{
49
+ if (state.visible._tag === 'Navigation' && state.visible.generation === event.generation) {
50
+ const visible = state.visible;
51
+ switch(event._tag){
52
+ case 'RenderRetired':
53
+ return {
54
+ command: visible.flight._tag === 'Streaming' ? {
55
+ _tag: 'ReleaseRoute',
56
+ resource: visible.flight.resource
57
+ } : null,
58
+ state: {
59
+ ...state,
60
+ visible: {
61
+ _tag: 'Settled'
62
+ }
63
+ }
64
+ };
65
+ case 'HistoryCommitted':
66
+ if (visible.entry._tag === 'Committed') {
67
+ throw new TypeError('A visible navigation history entry cannot commit twice.');
68
+ }
69
+ if (visible.flight._tag === 'Streaming') {
70
+ return {
71
+ command: null,
72
+ state: {
73
+ ...state,
74
+ visible: {
75
+ ...visible,
76
+ entry: {
77
+ _tag: 'Committed',
78
+ entry: event.entry
79
+ }
80
+ }
81
+ }
82
+ };
83
+ }
84
+ return {
85
+ command: event.entry === null ? null : {
86
+ _tag: 'CacheRoute',
87
+ cache: visible.flight.cache,
88
+ entry: event.entry
89
+ },
90
+ state: {
91
+ ...state,
92
+ visible: {
93
+ _tag: 'Settled'
94
+ }
95
+ }
96
+ };
97
+ case 'FlightCompleted':
98
+ if (visible.flight._tag === 'Completed') {
99
+ throw new TypeError('A visible navigation Flight stream cannot complete twice.');
100
+ }
101
+ if (visible.entry._tag === 'PendingCommit') {
102
+ return {
103
+ command: {
104
+ _tag: 'ReleaseRoute',
105
+ resource: visible.flight.resource
106
+ },
107
+ state: {
108
+ ...state,
109
+ visible: {
110
+ ...visible,
111
+ flight: {
112
+ _tag: 'Completed',
113
+ cache: visible.flight.resource.cache
114
+ }
115
+ }
116
+ }
117
+ };
118
+ }
119
+ return {
120
+ command: visible.entry.entry === null ? {
121
+ _tag: 'ReleaseRoute',
122
+ resource: visible.flight.resource
123
+ } : {
124
+ _tag: 'CacheAndReleaseRoute',
125
+ entry: visible.entry.entry,
126
+ resource: visible.flight.resource
127
+ },
128
+ state: {
129
+ ...state,
130
+ visible: {
131
+ _tag: 'Settled'
132
+ }
133
+ }
134
+ };
135
+ case 'FlightFailed':
136
+ if (visible.flight._tag === 'Completed') {
137
+ throw new TypeError('A completed visible Flight stream cannot fail.');
138
+ }
139
+ return {
140
+ command: {
141
+ _tag: 'ReleaseRoute',
142
+ resource: visible.flight.resource
143
+ },
144
+ state: {
145
+ ...state,
146
+ visible: {
147
+ _tag: 'Settled'
148
+ }
149
+ }
150
+ };
151
+ }
152
+ }
153
+ if (event._tag === 'RenderRetired' || event._tag === 'HistoryCommitted' || event._tag === 'FlightCompleted') {
154
+ return {
155
+ command: null,
156
+ state
157
+ };
158
+ }
159
+ if (event._tag === 'BeginNavigation') {
160
+ const nextState = {
161
+ _tag: 'Navigating',
162
+ candidate: {
163
+ _tag: 'Loading',
164
+ generation: event.generation,
165
+ lifetime: event.lifetime
166
+ },
167
+ visible: state.visible
168
+ };
169
+ if (state._tag === 'Ready') {
170
+ return {
171
+ command: null,
172
+ state: nextState
173
+ };
174
+ }
175
+ return {
176
+ command: {
177
+ _tag: 'SupersedeCandidate',
178
+ candidate: state.candidate
179
+ },
180
+ state: nextState
181
+ };
182
+ }
183
+ if (state._tag === 'Ready' || state.candidate.generation !== event.generation) {
184
+ switch(event._tag){
185
+ case 'RouteLoaded':
186
+ return {
187
+ command: {
188
+ _tag: 'ReleaseRoute',
189
+ resource: event.resource
190
+ },
191
+ state
192
+ };
193
+ case 'RenderScheduled':
194
+ return {
195
+ command: {
196
+ _tag: 'DiscardRoute',
197
+ rendererNavigation: event.rendererNavigation,
198
+ resource: event.resource
199
+ },
200
+ state
201
+ };
202
+ default:
203
+ return {
204
+ command: null,
205
+ state
206
+ };
207
+ }
208
+ }
209
+ switch(event._tag){
210
+ case 'DocumentLoaded':
211
+ case 'FlightFailed':
212
+ if (state.candidate._tag !== 'Loading') {
213
+ throw new TypeError(`${event._tag} cannot follow ${state.candidate._tag}.`);
214
+ }
215
+ return {
216
+ command: null,
217
+ state: {
218
+ _tag: 'Ready',
219
+ visible: state.visible
220
+ }
221
+ };
222
+ case 'RouteLoaded':
223
+ if (state.candidate._tag !== 'Loading') {
224
+ throw new TypeError(`RouteLoaded cannot follow ${state.candidate._tag}.`);
225
+ }
226
+ return {
227
+ command: {
228
+ _tag: 'PublishRoute',
229
+ generation: event.generation,
230
+ resource: event.resource
231
+ },
232
+ state: {
233
+ _tag: 'Navigating',
234
+ candidate: {
235
+ _tag: 'Publishing',
236
+ generation: event.generation,
237
+ lifetime: state.candidate.lifetime,
238
+ resource: event.resource
239
+ },
240
+ visible: state.visible
241
+ }
242
+ };
243
+ case 'RenderScheduled':
244
+ if (state.candidate._tag !== 'Publishing') {
245
+ throw new TypeError(`RenderScheduled cannot follow ${state.candidate._tag}.`);
246
+ }
247
+ if (state.candidate.resource !== event.resource) {
248
+ throw new TypeError('RenderScheduled must use the published route resource.');
249
+ }
250
+ return {
251
+ command: null,
252
+ state: {
253
+ _tag: 'Navigating',
254
+ candidate: {
255
+ _tag: 'Rendering',
256
+ generation: event.generation,
257
+ lifetime: state.candidate.lifetime,
258
+ rendererNavigation: event.rendererNavigation,
259
+ resource: event.resource
260
+ },
261
+ visible: state.visible
262
+ }
263
+ };
264
+ case 'RenderCommitted':
265
+ if (state.candidate._tag !== 'Rendering') {
266
+ throw new TypeError(`RenderCommitted cannot follow ${state.candidate._tag}.`);
267
+ }
268
+ return {
269
+ command: null,
270
+ state: {
271
+ _tag: 'Ready',
272
+ visible: {
273
+ _tag: 'Navigation',
274
+ entry: {
275
+ _tag: 'PendingCommit'
276
+ },
277
+ flight: {
278
+ _tag: 'Streaming',
279
+ resource: state.candidate.resource
280
+ },
281
+ generation: event.generation
282
+ }
283
+ }
284
+ };
285
+ case 'NavigationAborted':
286
+ if (state.candidate._tag === 'Loading') {
287
+ return {
288
+ command: null,
289
+ state: {
290
+ _tag: 'Ready',
291
+ visible: state.visible
292
+ }
293
+ };
294
+ }
295
+ if (state.candidate._tag === 'Publishing') {
296
+ return {
297
+ command: {
298
+ _tag: 'ReleaseRoute',
299
+ resource: state.candidate.resource
300
+ },
301
+ state: {
302
+ _tag: 'Ready',
303
+ visible: state.visible
304
+ }
305
+ };
306
+ }
307
+ return {
308
+ command: {
309
+ _tag: 'DiscardRoute',
310
+ rendererNavigation: state.candidate.rendererNavigation,
311
+ resource: state.candidate.resource
312
+ },
313
+ state: {
314
+ _tag: 'Ready',
315
+ visible: state.visible
316
+ }
317
+ };
318
+ }
319
+ };
320
+ const installClientRouter = Effect.gen(function*() {
321
+ const browserRenderer = yield* BrowserRenderer;
322
+ const navigationApi = yield* NavigationApi;
323
+ const routeLoader = yield* RouteLoader;
324
+ const run = yield* BrowserEffectRunner;
325
+ const routerState = MutableRef.make({
326
+ _tag: 'Ready',
327
+ visible: {
328
+ _tag: 'Settled'
329
+ }
330
+ });
331
+ const dispatch = (event)=>{
332
+ const transition = reduceRouterState(MutableRef.get(routerState), event);
333
+ MutableRef.set(routerState, transition.state);
334
+ return transition;
335
+ };
336
+ const executeRouterCommand = Effect.fnUntraced(function*(command) {
337
+ if (command === null) {
338
+ return;
339
+ }
340
+ switch(command._tag){
341
+ case 'SupersedeCandidate':
342
+ {
343
+ const candidate = command.candidate;
344
+ yield* Effect.sync(()=>candidate.lifetime.abort());
345
+ if (candidate._tag === 'Loading') {
346
+ break;
347
+ }
348
+ if (candidate._tag === 'Publishing') {
349
+ yield* candidate.resource.release;
350
+ break;
351
+ }
352
+ yield* Effect.promise(()=>candidate.rendererNavigation.discard()).pipe(Effect.ensuring(candidate.resource.release));
353
+ break;
354
+ }
355
+ case 'ReleaseRoute':
356
+ yield* command.resource.release;
357
+ break;
358
+ case 'CacheRoute':
359
+ yield* Effect.sync(()=>command.cache(command.entry));
360
+ break;
361
+ case 'CacheAndReleaseRoute':
362
+ yield* Effect.sync(()=>command.resource.cache(command.entry));
363
+ yield* command.resource.release;
364
+ break;
365
+ case 'DiscardRoute':
366
+ yield* Effect.promise(()=>command.rendererNavigation.discard()).pipe(Effect.ensuring(command.resource.release));
367
+ break;
368
+ default:
369
+ throw new TypeError(`Cannot execute the ${command._tag} lifecycle command here.`);
370
+ }
371
+ });
372
+ const openDocument = (event, destination)=>{
373
+ if (event.navigationType === 'traverse') {
374
+ navigationApi.replaceDocument(destination.href);
375
+ return;
376
+ }
377
+ navigationApi.navigate(destination.href, {
378
+ history: event.navigationType === 'replace' ? 'replace' : 'push',
379
+ info: NativeDocumentNavigationInfo
380
+ });
381
+ };
382
+ const onNavigate = (event)=>{
383
+ if (!isRoutedNavigation(event)) {
384
+ return;
385
+ }
386
+ const destination = new URL(event.destination.url);
387
+ const generation = Symbol('NavigationGeneration');
388
+ const lifetime = new AbortController();
389
+ const navigationSignal = AbortSignal.any([
390
+ event.signal,
391
+ lifetime.signal
392
+ ]);
393
+ const begin = dispatch({
394
+ _tag: 'BeginNavigation',
395
+ generation,
396
+ lifetime
397
+ });
398
+ void run(executeRouterCommand(begin.command)).catch(()=>undefined);
399
+ // oxlint-disable-next-line effecttsgo/async-function -- Navigation handlers are native Promise boundaries.
400
+ const handler = async (precommitController)=>{
401
+ const preparation = Promise.withResolvers();
402
+ const navigationAction = Effect.gen(function*() {
403
+ const resource = yield* routeLoader.load({
404
+ destination: event.destination,
405
+ navigationType: event.navigationType
406
+ }).pipe(Effect.onError(()=>Effect.sync(()=>{
407
+ dispatch({
408
+ _tag: 'FlightFailed',
409
+ generation
410
+ });
411
+ })));
412
+ if (resource._tag === 'Document') {
413
+ dispatch({
414
+ _tag: 'DocumentLoaded',
415
+ generation
416
+ });
417
+ yield* resource.release;
418
+ openDocument(event, destination);
419
+ return {
420
+ _tag: 'Handled'
421
+ };
422
+ }
423
+ const resolvedDestination = preserveRequestedHash(destination, resource.resolvedUrl);
424
+ if (resolvedDestination.origin !== destination.origin) {
425
+ dispatch({
426
+ _tag: 'DocumentLoaded',
427
+ generation
428
+ });
429
+ yield* resource.release;
430
+ openDocument(event, resolvedDestination);
431
+ return {
432
+ _tag: 'Handled'
433
+ };
434
+ }
435
+ if (destination.href !== resolvedDestination.href && (precommitController === undefined || event.navigationType === 'traverse')) {
436
+ dispatch({
437
+ _tag: 'DocumentLoaded',
438
+ generation
439
+ });
440
+ yield* resource.release;
441
+ navigationApi.replaceDocument(resolvedDestination.href);
442
+ return {
443
+ _tag: 'Handled'
444
+ };
445
+ }
446
+ const transition = dispatch({
447
+ _tag: 'RouteLoaded',
448
+ generation,
449
+ resource
450
+ });
451
+ const command = transition.command;
452
+ if (command?._tag === 'ReleaseRoute') {
453
+ yield* command.resource.release;
454
+ return {
455
+ _tag: 'Handled'
456
+ };
457
+ }
458
+ if (command?._tag === 'PublishRoute') {
459
+ const rendererNavigation = yield* Effect.sync(()=>{
460
+ let navigation;
461
+ startTransition(()=>{
462
+ const fromIndex = navigationApi.getTransition()?.from.index ?? navigationApi.getCurrentEntry()?.index ?? null;
463
+ for (const type of getNavigationTransitionTypes(event, fromIndex)){
464
+ addTransitionType(type);
465
+ }
466
+ navigation = browserRenderer.navigate(command.resource.routeTree);
467
+ });
468
+ return navigation;
469
+ });
470
+ const scheduled = dispatch({
471
+ _tag: 'RenderScheduled',
472
+ generation: command.generation,
473
+ rendererNavigation,
474
+ resource: command.resource
475
+ });
476
+ if (scheduled.command !== null) {
477
+ yield* executeRouterCommand(scheduled.command);
478
+ return {
479
+ _tag: 'Handled'
480
+ };
481
+ }
482
+ return {
483
+ _tag: 'Rendered',
484
+ rendererNavigation,
485
+ resolvedDestination,
486
+ resource: command.resource
487
+ };
488
+ }
489
+ throw new TypeError('A loaded route must be published or released.');
490
+ });
491
+ // oxlint-disable-next-line effecttsgo/async-function -- React Transition Actions are a native Promise boundary.
492
+ startTransition(async ()=>{
493
+ await run(navigationAction, {
494
+ signal: navigationSignal
495
+ }).then(preparation.resolve).catch(preparation.reject);
496
+ });
497
+ const abortNavigation = Effect.suspend(()=>{
498
+ const transition = dispatch({
499
+ _tag: 'NavigationAborted',
500
+ generation
501
+ });
502
+ return executeRouterCommand(transition.command);
503
+ });
504
+ const outcome = await preparation.promise.catch((cause)=>{
505
+ if (navigationSignal.aborted) {
506
+ return run(abortNavigation.pipe(Effect.as({
507
+ _tag: 'Handled'
508
+ })));
509
+ }
510
+ if (!event.cancelable && event.navigationType === 'traverse') {
511
+ navigationApi.reloadDocument();
512
+ return {
513
+ _tag: 'Handled'
514
+ };
515
+ }
516
+ throw cause;
517
+ });
518
+ if (outcome._tag === 'Handled') {
519
+ return;
520
+ }
521
+ const { rendererNavigation, resolvedDestination, resource } = outcome;
522
+ const waitForNavigationCommit = Effect.uninterruptibleMask((restore)=>restore(Effect.promise(()=>rendererNavigation.committed)).pipe(Effect.tap(Effect.suspend(()=>{
523
+ const transition = dispatch({
524
+ _tag: 'RenderCommitted',
525
+ generation
526
+ });
527
+ return executeRouterCommand(transition.command);
528
+ })), Effect.onExit((exit)=>Exit.isFailure(exit) ? abortNavigation : Effect["void"])));
529
+ try {
530
+ await run(waitForNavigationCommit, {
531
+ signal: navigationSignal
532
+ });
533
+ } catch (cause) {
534
+ if (navigationSignal.aborted) {
535
+ return;
536
+ }
537
+ throw cause;
538
+ }
539
+ const waitForRenderRetirement = Effect.gen(function*() {
540
+ yield* Effect.promise(()=>rendererNavigation.retired);
541
+ const transition = dispatch({
542
+ _tag: 'RenderRetired',
543
+ generation
544
+ });
545
+ yield* executeRouterCommand(transition.command);
546
+ }).pipe(Effect.ignore);
547
+ void run(waitForRenderRetirement).catch(()=>undefined);
548
+ const waitForFlightCompletion = Effect.gen(function*() {
549
+ const flightExit = yield* Effect.exit(resource.completed);
550
+ const transition = dispatch({
551
+ _tag: Exit.isSuccess(flightExit) ? 'FlightCompleted' : 'FlightFailed',
552
+ generation
553
+ });
554
+ yield* executeRouterCommand(transition.command);
555
+ });
556
+ void run(waitForFlightCompletion).catch(()=>undefined);
557
+ if (destination.href !== resolvedDestination.href && precommitController !== undefined) {
558
+ precommitController.redirect(resolvedDestination.href, {
559
+ history: 'auto'
560
+ });
561
+ }
562
+ const commitHistory = Effect.suspend(()=>{
563
+ const transition = dispatch({
564
+ _tag: 'HistoryCommitted',
565
+ entry: navigationApi.getCurrentEntry(),
566
+ generation
567
+ });
568
+ return executeRouterCommand(transition.command);
569
+ });
570
+ if (precommitController === undefined) {
571
+ await run(commitHistory);
572
+ } else {
573
+ precommitController.addHandler(()=>run(commitHistory));
574
+ }
575
+ };
576
+ event.intercept(event.cancelable ? {
577
+ precommitHandler: handler
578
+ } : {
579
+ // Browsers make some traversals non-cancelable so an application cannot trap users.
580
+ handler
581
+ });
582
+ };
583
+ yield* Effect.acquireRelease(Effect.sync(()=>navigationApi.subscribe(onNavigate)), (unsubscribe)=>Effect.sync(unsubscribe));
584
+ });
585
+
586
+ export { installClientRouter };
587
+
588
+ //# sourceMappingURL=client-router.js.map